Local-first Flutter app that identifies the single behavioral change most likely to extend lifespan using hazard-based modeling. Features: - Risk engine with hazard ratios from meta-analyses - 50 countries mapped to 4 mortality groups - 6 modifiable factors: smoking, alcohol, sleep, activity, driving, work hours - SQLite local storage (no cloud, no accounts) - Muted clinical UI theme - 23 unit tests for risk engine Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
128 lines
3.7 KiB
Dart
128 lines
3.7 KiB
Dart
import 'package:flutter/material.dart';
|
|
import '../theme.dart';
|
|
import 'baseline_screen.dart';
|
|
|
|
class WelcomeScreen extends StatelessWidget {
|
|
const WelcomeScreen({super.key});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
body: SafeArea(
|
|
child: Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 32),
|
|
child: Column(
|
|
children: [
|
|
const Spacer(flex: 2),
|
|
// Icon or logo area
|
|
Container(
|
|
width: 80,
|
|
height: 80,
|
|
decoration: BoxDecoration(
|
|
color: AppColors.surfaceVariant,
|
|
borderRadius: BorderRadius.circular(20),
|
|
),
|
|
child: const Icon(
|
|
Icons.timeline,
|
|
size: 40,
|
|
color: AppColors.primary,
|
|
),
|
|
),
|
|
const SizedBox(height: 32),
|
|
// Title
|
|
Text(
|
|
'Add Months',
|
|
style: Theme.of(context).textTheme.headlineLarge,
|
|
textAlign: TextAlign.center,
|
|
),
|
|
const SizedBox(height: 16),
|
|
// Description
|
|
Text(
|
|
'Identify the single change most likely to extend your lifespan.',
|
|
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
|
|
color: AppColors.textSecondary,
|
|
),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
const SizedBox(height: 48),
|
|
// Features list
|
|
_buildFeatureItem(
|
|
context,
|
|
Icons.shield_outlined,
|
|
'Private',
|
|
'All data stays on your device',
|
|
),
|
|
const SizedBox(height: 16),
|
|
_buildFeatureItem(
|
|
context,
|
|
Icons.science_outlined,
|
|
'Evidence-based',
|
|
'Hazard ratios from meta-analyses',
|
|
),
|
|
const SizedBox(height: 16),
|
|
_buildFeatureItem(
|
|
context,
|
|
Icons.trending_up_outlined,
|
|
'Actionable',
|
|
'Focus on what matters most',
|
|
),
|
|
const Spacer(flex: 3),
|
|
// Start button
|
|
SizedBox(
|
|
width: double.infinity,
|
|
child: ElevatedButton(
|
|
onPressed: () => _navigateToBaseline(context),
|
|
child: const Text('Start'),
|
|
),
|
|
),
|
|
const SizedBox(height: 32),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildFeatureItem(
|
|
BuildContext context,
|
|
IconData icon,
|
|
String title,
|
|
String description,
|
|
) {
|
|
return Row(
|
|
children: [
|
|
Container(
|
|
width: 44,
|
|
height: 44,
|
|
decoration: BoxDecoration(
|
|
color: AppColors.surfaceVariant,
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
child: Icon(icon, color: AppColors.primary, size: 22),
|
|
),
|
|
const SizedBox(width: 16),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
title,
|
|
style: Theme.of(context).textTheme.labelLarge,
|
|
),
|
|
Text(
|
|
description,
|
|
style: Theme.of(context).textTheme.bodySmall,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
void _navigateToBaseline(BuildContext context) {
|
|
Navigator.of(context).push(
|
|
MaterialPageRoute(builder: (_) => const BaselineScreen()),
|
|
);
|
|
}
|
|
}
|