Integrate themed login/signup, EN/RU localization, shared dummy providers, verification recovery screens and named URL routes. Store frontend guidelines, workflow skills, validation and delivery documentation. Related to #2, #3, #5 and #6.
159 lines
4.5 KiB
Dart
159 lines
4.5 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
import '../generated/i18n/app_localizations.dart';
|
|
|
|
class BrandWordmark extends StatelessWidget {
|
|
const BrandWordmark({super.key});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final theme = Theme.of(context);
|
|
return Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Container(
|
|
width: 32,
|
|
height: 32,
|
|
decoration: BoxDecoration(
|
|
color: theme.colorScheme.primary,
|
|
borderRadius: BorderRadius.circular(9),
|
|
),
|
|
child: Icon(Icons.bolt, color: theme.colorScheme.onPrimary, size: 26),
|
|
),
|
|
const SizedBox(width: 9),
|
|
Flexible(
|
|
child: Text(
|
|
'sxchnge',
|
|
style: theme.textTheme.headlineMedium!.copyWith(
|
|
letterSpacing: -1.5,
|
|
fontWeight: FontWeight.w800,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|
|
|
|
class BrandPanel extends StatelessWidget {
|
|
const BrandPanel({super.key});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final l = AppLocalizations.of(context)!;
|
|
final theme = Theme.of(context);
|
|
final colors = theme.colorScheme;
|
|
return ColoredBox(
|
|
color: colors.surface,
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(48),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
const BrandWordmark(),
|
|
const SizedBox(height: 64),
|
|
Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
l.brandEyebrow,
|
|
style: theme.textTheme.labelMedium!.copyWith(
|
|
letterSpacing: 1.5,
|
|
color: colors.primary,
|
|
),
|
|
),
|
|
const SizedBox(height: 24),
|
|
Text(
|
|
l.brandHeading,
|
|
style: theme.textTheme.displayLarge!.copyWith(
|
|
fontSize: 68,
|
|
height: 1.03,
|
|
letterSpacing: -3,
|
|
),
|
|
),
|
|
const SizedBox(height: 32),
|
|
Text(
|
|
l.brandIntro,
|
|
style: theme.textTheme.bodyLarge!.copyWith(
|
|
color: colors.onSurfaceVariant,
|
|
),
|
|
),
|
|
const SizedBox(height: 48),
|
|
const _MotionCard(),
|
|
],
|
|
),
|
|
const SizedBox(height: 64),
|
|
Text(
|
|
l.brandFooter,
|
|
style: theme.textTheme.labelSmall!.copyWith(
|
|
letterSpacing: 1,
|
|
color: colors.onSurfaceVariant,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _MotionCard extends StatelessWidget {
|
|
const _MotionCard();
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final l = AppLocalizations.of(context)!;
|
|
final theme = Theme.of(context);
|
|
final colors = theme.colorScheme;
|
|
return TweenAnimationBuilder<double>(
|
|
tween: Tween(begin: 0, end: 1),
|
|
duration: MediaQuery.disableAnimationsOf(context)
|
|
? Duration.zero
|
|
: const Duration(milliseconds: 700),
|
|
curve: Curves.easeOutCubic,
|
|
builder: (context, value, child) => Opacity(
|
|
opacity: value,
|
|
child: Transform.translate(
|
|
offset: Offset(0, (1 - value) * 24),
|
|
child: child,
|
|
),
|
|
),
|
|
child: Transform.rotate(
|
|
angle: -0.035,
|
|
child: Card(
|
|
color: colors.primaryContainer,
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(28),
|
|
child: Row(
|
|
children: [
|
|
Expanded(
|
|
child: Text(
|
|
l.brandCard,
|
|
style: theme.textTheme.headlineSmall!.copyWith(
|
|
color: colors.onPrimaryContainer,
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(width: 12),
|
|
Container(
|
|
padding: const EdgeInsets.all(18),
|
|
decoration: BoxDecoration(
|
|
color: colors.tertiaryContainer,
|
|
shape: BoxShape.circle,
|
|
),
|
|
child: Icon(
|
|
Icons.north_east,
|
|
size: 38,
|
|
color: colors.onTertiaryContainer,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|