app/lib/theme.dart
John Mizerek 151106aa8e Initial commit: Add Months MVP
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>
2026-02-20 21:25:00 -08:00

153 lines
4.6 KiB
Dart

import 'package:flutter/material.dart';
/// Muted clinical color palette.
class AppColors {
static const primary = Color(0xFF4A90A4); // Muted teal
static const primaryDark = Color(0xFF2D6073);
static const primaryLight = Color(0xFF7BB8CC);
static const surface = Color(0xFFF8FAFB);
static const surfaceVariant = Color(0xFFEEF2F4);
static const background = Color(0xFFFFFFFF);
static const textPrimary = Color(0xFF1A2B33);
static const textSecondary = Color(0xFF5A6B73);
static const textTertiary = Color(0xFF8A9BA3);
static const success = Color(0xFF4A9A7C);
static const warning = Color(0xFFB8934A);
static const error = Color(0xFFA45A5A);
static const divider = Color(0xFFDDE4E8);
}
/// App-wide theme.
ThemeData buildAppTheme() {
return ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.light(
primary: AppColors.primary,
onPrimary: Colors.white,
secondary: AppColors.primaryLight,
surface: AppColors.surface,
onSurface: AppColors.textPrimary,
),
scaffoldBackgroundColor: AppColors.background,
appBarTheme: const AppBarTheme(
backgroundColor: AppColors.background,
foregroundColor: AppColors.textPrimary,
elevation: 0,
centerTitle: true,
titleTextStyle: TextStyle(
fontFamily: 'SF Pro Display',
fontSize: 17,
fontWeight: FontWeight.w600,
color: AppColors.textPrimary,
letterSpacing: -0.3,
),
),
textTheme: const TextTheme(
headlineLarge: TextStyle(
fontSize: 28,
fontWeight: FontWeight.w600,
color: AppColors.textPrimary,
letterSpacing: -0.5,
height: 1.2,
),
headlineMedium: TextStyle(
fontSize: 22,
fontWeight: FontWeight.w600,
color: AppColors.textPrimary,
letterSpacing: -0.3,
height: 1.3,
),
headlineSmall: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: AppColors.textPrimary,
letterSpacing: -0.2,
),
bodyLarge: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w400,
color: AppColors.textPrimary,
height: 1.5,
),
bodyMedium: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400,
color: AppColors.textSecondary,
height: 1.5,
),
bodySmall: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w400,
color: AppColors.textTertiary,
height: 1.4,
),
labelLarge: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: AppColors.textPrimary,
letterSpacing: 0.1,
),
),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.primary,
foregroundColor: Colors.white,
elevation: 0,
padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
textStyle: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
letterSpacing: 0.2,
),
),
),
outlinedButtonTheme: OutlinedButtonThemeData(
style: OutlinedButton.styleFrom(
foregroundColor: AppColors.primary,
side: const BorderSide(color: AppColors.primary, width: 1.5),
padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
textStyle: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
letterSpacing: 0.2,
),
),
),
inputDecorationTheme: InputDecorationTheme(
filled: true,
fillColor: AppColors.surfaceVariant,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide.none,
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: AppColors.primary, width: 2),
),
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
labelStyle: const TextStyle(color: AppColors.textSecondary),
),
dividerTheme: const DividerThemeData(
color: AppColors.divider,
thickness: 1,
),
cardTheme: CardThemeData(
color: AppColors.surface,
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
side: const BorderSide(color: AppColors.divider, width: 1),
),
),
);
}