This commit is contained in:
Tomas Dvorak
2026-01-06 13:46:49 +01:00
18 changed files with 464 additions and 276 deletions
@@ -16,82 +16,67 @@ class OnboardingHowItWorksScreen extends ConsumerWidget {
return AppScaffold(
body: SafeArea(
child: Column(
children: [
// Progress indicator and back button
Padding(
padding: const EdgeInsets.all(24.0),
child: Row(
children: [
IconButton(
onPressed: () {
context.pop();
},
icon: const Icon(Icons.arrow_back),
),
Expanded(
child: LinearProgressIndicator(
value: 2 / 3, // Step 2 of 3
backgroundColor: Theme.of(context).colorScheme.surfaceContainerHighest,
valueColor: AlwaysStoppedAnimation<Color>(
Theme.of(context).colorScheme.primary,
),
),
),
const SizedBox(width: 48), // Balance the back button
],
child: Padding(
padding: const EdgeInsets.only(top: 30.0, left: 24.0, right: 24.0, bottom: 30.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Progress Bar and Navigation
_OnboardingProgress(currentStep: 2, totalSteps: 3),
const SizedBox(height: 24),
Text(
'How It Works',
style: Theme.of(context).textTheme.headlineLarge?.copyWith(
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
),
// Scrollable content
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 24.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SizedBox(height: 8),
Text(
'How It Works',
style: Theme.of(context).textTheme.headlineLarge?.copyWith(
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
const SizedBox(height: 32),
const _StepCard(
number: 1,
title: 'Create Your Bucket List',
description: 'Add between 1 and 20 goals you want to achieve. Each goal can have a description, location, and image.',
icon: Icons.edit_note,
),
const SizedBox(height: 16),
const _StepCard(
number: 2,
title: 'Finalize Your List',
description: 'Once you\'re happy with your goals, confirm your bucket list. This action cannot be undone.',
icon: Icons.lock,
),
const SizedBox(height: 16),
const _StepCard(
number: 3,
title: 'Start Your 1356-Day Journey',
description: 'The countdown begins immediately. Track your progress and make every day count.',
icon: Icons.timer,
),
const SizedBox(height: 16),
Row(
children: [
Expanded(
child: TextButton(
onPressed: () => context.pop(),
child: const Text('Back'),
),
const SizedBox(height: 20),
const _StepCard(
number: 1,
title: 'Create Your Bucket List',
description: 'Add between 1 and 20 goals you want to achieve. Each goal can have a description, location, and image.',
icon: Icons.edit_note,
),
const SizedBox(height: 12),
const _StepCard(
number: 2,
title: 'Finalize Your List',
description: 'Once you\'re happy with your goals, confirm your bucket list. This action cannot be undone.',
icon: Icons.lock,
),
const SizedBox(height: 12),
const _StepCard(
number: 3,
title: 'Start Your 1356-Day Journey',
description: 'The countdown begins immediately. Track your progress and make every day count.',
icon: Icons.timer,
),
const SizedBox(height: 24),
PrimaryButton(
),
const SizedBox(width: 16),
Expanded(
flex: 2,
child: PrimaryButton(
onPressed: () {
controller.completeStep('how_it_works');
context.push('/onboarding/motivation');
},
text: 'Continue',
),
const SizedBox(height: 20),
],
),
),
],
),
),
],
const SizedBox(height: 16),
],
),
),
),
);
@@ -190,3 +175,44 @@ class _StepCard extends StatelessWidget {
);
}
}
class _OnboardingProgress extends StatelessWidget {
final int currentStep;
final int totalSteps;
const _OnboardingProgress({
required this.currentStep,
required this.totalSteps,
});
@override
Widget build(BuildContext context) {
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Step $currentStep of $totalSteps',
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6),
),
),
TextButton(
onPressed: () => context.pop(),
child: const Text('Back'),
),
],
),
const SizedBox(height: 12),
LinearProgressIndicator(
value: currentStep / totalSteps,
backgroundColor: Theme.of(context).colorScheme.primaryContainer.withOpacity(0.3),
valueColor: AlwaysStoppedAnimation<Color>(
Theme.of(context).colorScheme.primary,
),
),
],
);
}
}
@@ -16,105 +16,84 @@ class OnboardingIntroScreen extends ConsumerWidget {
return AppScaffold(
body: SafeArea(
child: Column(
children: [
// Progress indicator and back button
Padding(
padding: const EdgeInsets.all(24.0),
child: Row(
children: [
IconButton(
onPressed: () {
// Can't go back from intro, go to auth choice
context.push('/auth-choice');
},
icon: const Icon(Icons.arrow_back),
),
Expanded(
child: LinearProgressIndicator(
value: 1 / 3, // Step 1 of 3
backgroundColor: Theme.of(context).colorScheme.surfaceContainerHighest,
valueColor: AlwaysStoppedAnimation<Color>(
Theme.of(context).colorScheme.primary,
),
),
),
const SizedBox(width: 48), // Balance the back button
],
child: Padding(
padding: const EdgeInsets.only(top: 30.0, left: 24.0, right: 24.0, bottom: 30.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Progress Bar and Navigation
_OnboardingProgress(currentStep: 1, totalSteps: 3),
const SizedBox(height: 48),
const Icon(
Icons.timer_outlined,
size: 100,
color: null,
),
),
// Scrollable content
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 24.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SizedBox(height: 16),
const Icon(
Icons.timer_outlined,
size: 64,
color: null,
),
const SizedBox(height: 20),
Text(
'Welcome to LifeTimer',
style: Theme.of(context).textTheme.headlineLarge?.copyWith(
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 10),
Text(
'Your 1356-day journey starts here.\nCreate your bucket list and begin your countdown.',
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.8),
height: 1.5,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 24),
const _FeatureCard(
icon: Icons.flag,
title: 'Set Your Goals',
description: 'Create a bucket list of 1-20 meaningful goals',
),
const SizedBox(height: 10),
const _FeatureCard(
icon: Icons.lock_clock,
title: 'Fixed Timeline',
description: '1356 days to achieve everything - no extensions',
),
const SizedBox(height: 10),
const _FeatureCard(
icon: Icons.trending_up,
title: 'Track Progress',
description: 'Watch yourself grow day by day',
),
const SizedBox(height: 32),
PrimaryButton(
onPressed: () {
controller.completeStep('intro');
context.push('/onboarding/how-it-works');
},
text: 'Get Started',
),
const SizedBox(height: 10),
TextButton(
const SizedBox(height: 32),
Text(
'Welcome to 1356',
style: Theme.of(context).textTheme.headlineLarge?.copyWith(
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 16),
Text(
'Your 1356-day journey starts here.\nCreate your bucket list and begin your countdown.',
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.8),
height: 1.5,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 48),
const _FeatureCard(
icon: Icons.flag,
title: 'Set Your Goals',
description: 'Create a bucket list of 1-20 meaningful goals',
),
const SizedBox(height: 16),
const _FeatureCard(
icon: Icons.lock_clock,
title: 'Fixed Timeline',
description: '1356 days to achieve everything - no extensions',
),
const SizedBox(height: 16),
const _FeatureCard(
icon: Icons.trending_up,
title: 'Track Progress',
description: 'Watch yourself grow day by day',
),
const Spacer(),
Row(
children: [
Expanded(
child: TextButton(
onPressed: () async {
await controller.skipOnboarding();
if (context.mounted) {
context.push('/home');
}
},
child: const Text('Skip onboarding'),
child: const Text('Skip'),
),
const SizedBox(height: 20),
],
),
),
const SizedBox(width: 16),
Expanded(
flex: 2,
child: PrimaryButton(
onPressed: () {
controller.completeStep('intro');
context.push('/onboarding/how-it-works');
},
text: 'Get Started',
),
),
],
),
),
],
const SizedBox(height: 16),
],
),
),
),
);
@@ -176,3 +155,44 @@ class _FeatureCard extends StatelessWidget {
);
}
}
class _OnboardingProgress extends StatelessWidget {
final int currentStep;
final int totalSteps;
const _OnboardingProgress({
required this.currentStep,
required this.totalSteps,
});
@override
Widget build(BuildContext context) {
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Step $currentStep of $totalSteps',
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6),
),
),
TextButton(
onPressed: () => context.pop(),
child: const Text('Back'),
),
],
),
const SizedBox(height: 12),
LinearProgressIndicator(
value: currentStep / totalSteps,
backgroundColor: Theme.of(context).colorScheme.primaryContainer.withOpacity(0.3),
valueColor: AlwaysStoppedAnimation<Color>(
Theme.of(context).colorScheme.primary,
),
),
],
);
}
}
@@ -16,100 +16,86 @@ class OnboardingMotivationScreen extends ConsumerWidget {
return AppScaffold(
body: SafeArea(
child: Column(
children: [
// Progress indicator and back button
Padding(
padding: const EdgeInsets.all(24.0),
child: Row(
children: [
IconButton(
onPressed: () {
context.pop();
},
icon: const Icon(Icons.arrow_back),
),
Expanded(
child: LinearProgressIndicator(
value: 3 / 3, // Step 3 of 3 (complete)
backgroundColor: Theme.of(context).colorScheme.surfaceContainerHighest,
valueColor: AlwaysStoppedAnimation<Color>(
Theme.of(context).colorScheme.primary,
),
),
),
const SizedBox(width: 48), // Balance the back button
],
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.only(top: 30.0, left: 24.0, right: 24.0, bottom: 30.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Progress Bar and Navigation
_OnboardingProgress(currentStep: 3, totalSteps: 3),
const SizedBox(height: 24),
const Icon(
Icons.psychology_outlined,
size: 80,
color: Colors.amber,
),
),
// Scrollable content
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 24.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SizedBox(height: 8),
const Icon(
Icons.psychology_outlined,
size: 64,
color: Colors.amber,
const SizedBox(height: 24),
Text(
'Your Time is Now',
style: Theme.of(context).textTheme.headlineLarge?.copyWith(
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 16),
Text(
'1356 days is approximately 3 years and 8 months.\n\n'
'That\'s enough time to transform your life, learn new skills, '
'build meaningful relationships, and achieve your biggest dreams.\n\n'
'Every day counts. Every step matters. Your journey begins now.',
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.8),
height: 1.5,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 32),
const _MotivationCard(
icon: Icons.trending_up,
title: 'Track Progress',
description: 'Watch yourself grow as you complete goals and milestones.',
),
const SizedBox(height: 16),
const _MotivationCard(
icon: Icons.people,
title: 'Join Community',
description: 'Connect with others on similar journeys (optional).',
),
const SizedBox(height: 16),
const _MotivationCard(
icon: Icons.celebration,
title: 'Celebrate Wins',
description: 'Every achievement is worth celebrating.',
),
const SizedBox(height: 16),
Row(
children: [
Expanded(
child: TextButton(
onPressed: () => context.pop(),
child: const Text('Back'),
),
const SizedBox(height: 16),
Text(
'Your Time is Now',
style: Theme.of(context).textTheme.headlineLarge?.copyWith(
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 12),
Text(
'1356 days is approximately 3 years and 8 months.\n\n'
'That\'s enough time to transform your life, learn new skills, '
'build meaningful relationships, and achieve your biggest dreams.\n\n'
'Every day counts. Every step matters. Your journey begins now.',
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.8),
height: 1.5,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 20),
const _MotivationCard(
icon: Icons.trending_up,
title: 'Track Progress',
description: 'Watch yourself grow as you complete goals and milestones.',
),
const SizedBox(height: 10),
const _MotivationCard(
icon: Icons.people,
title: 'Join Community',
description: 'Connect with others on similar journeys (optional).',
),
const SizedBox(height: 10),
const _MotivationCard(
icon: Icons.celebration,
title: 'Celebrate Wins',
description: 'Every achievement is worth celebrating.',
),
const SizedBox(height: 24),
PrimaryButton(
),
const SizedBox(width: 16),
Expanded(
flex: 2,
child: PrimaryButton(
onPressed: () async {
controller.completeStep('motivation');
await controller.completeOnboarding();
if (context.mounted) {
context.push('/profile-setup');
context.push('/profile/create');
}
},
text: 'Get Started',
),
const SizedBox(height: 20),
],
),
),
],
),
),
],
const SizedBox(height: 16),
],
),
),
),
);
@@ -171,3 +157,44 @@ class _MotivationCard extends StatelessWidget {
);
}
}
class _OnboardingProgress extends StatelessWidget {
final int currentStep;
final int totalSteps;
const _OnboardingProgress({
required this.currentStep,
required this.totalSteps,
});
@override
Widget build(BuildContext context) {
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Step $currentStep of $totalSteps',
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6),
),
),
TextButton(
onPressed: () => context.pop(),
child: const Text('Back'),
),
],
),
const SizedBox(height: 12),
LinearProgressIndicator(
value: currentStep / totalSteps,
backgroundColor: Theme.of(context).colorScheme.primaryContainer.withOpacity(0.3),
valueColor: AlwaysStoppedAnimation<Color>(
Theme.of(context).colorScheme.primary,
),
),
],
);
}
}