Files
sure/mobile/lib/theme/sure_theme.dart
ghost 360989c3a9 feat(mobile): align theme foundation with Sure tokens (#2237)
* feat(mobile): align theme foundation with Sure tokens

Generate typed Flutter token constants from the canonical Sure token JSON and route the app through shared light/dark ThemeData construction. This keeps the first mobile design-system step scoped to foundation wiring and regression tests without restyling screens directly.

* fix(mobile): define Sure error container colors

Populate the manual Flutter ColorScheme error container pair from Sure tokens so validation and connection banners keep visible icon/text contrast. Add theme assertions for both light and dark modes.

* fix(mobile): preserve full-width action buttons
2026-06-11 16:19:15 +02:00

92 lines
2.9 KiB
Dart

import 'package:flutter/material.dart';
import 'sure_tokens.dart';
class SureTheme {
const SureTheme._();
static ThemeData get light => _build(SureTokens.light, Brightness.light);
static ThemeData get dark => _build(SureTokens.dark, Brightness.dark);
static ThemeData _build(SureTokenPalette tokens, Brightness brightness) {
final colorScheme = ColorScheme(
brightness: brightness,
primary: tokens.link,
onPrimary: tokens.textInverse,
secondary: tokens.info,
onSecondary: tokens.textInverse,
error: tokens.destructive,
onError: tokens.textInverse,
errorContainer: tokens.destructiveSubtle,
onErrorContainer: tokens.textPrimary,
surface: tokens.surface,
onSurface: tokens.textPrimary,
surfaceContainerHighest: tokens.containerInset,
outline: tokens.borderSecondary,
outlineVariant: tokens.borderSubdued,
);
final inputBorder = OutlineInputBorder(
borderRadius: BorderRadius.circular(SureTokens.radiusLg),
borderSide: BorderSide(color: tokens.borderSecondary),
);
final base = ThemeData(
fontFamily: SureTokens.fontSans,
fontFamilyFallback: SureTokens.fontFallback,
colorScheme: colorScheme,
scaffoldBackgroundColor: tokens.surface,
useMaterial3: true,
appBarTheme: AppBarTheme(
centerTitle: true,
elevation: 0,
backgroundColor: tokens.surface,
foregroundColor: tokens.textPrimary,
surfaceTintColor: Colors.transparent,
),
cardTheme: CardThemeData(
color: tokens.container,
surfaceTintColor: Colors.transparent,
shadowColor: tokens.shadow,
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(SureTokens.radiusLg),
),
),
inputDecorationTheme: InputDecorationTheme(
border: inputBorder,
enabledBorder: inputBorder,
focusedBorder: inputBorder.copyWith(
borderSide: BorderSide(color: tokens.borderPrimary),
),
filled: true,
fillColor: tokens.container,
),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
minimumSize: const Size(double.infinity, 50),
backgroundColor: tokens.buttonPrimary,
foregroundColor: tokens.textInverse,
disabledBackgroundColor: tokens.containerInset,
disabledForegroundColor: tokens.textSubdued,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(SureTokens.radiusLg),
),
),
),
);
return base.copyWith(
textTheme: base.textTheme.apply(
bodyColor: tokens.textPrimary,
displayColor: tokens.textPrimary,
),
primaryTextTheme: base.primaryTextTheme.apply(
bodyColor: tokens.textPrimary,
displayColor: tokens.textPrimary,
),
);
}
}