mirror of
https://github.com/we-promise/sure.git
synced 2026-07-12 21:05:20 +00:00
* feat(mobile): standardize money typography and semantic amount color Add a brightness-aware SureColors theme extension and a MoneyText/SureMoney primitive (semantic success/destructive/subdued tokens + tabular figures for column-aligned digits), then migrate the transaction lists and balance cards off raw Colors.green/red/grey. Step 2 of the mobile design-system sequence (#2235), after #2237's theme foundation. Primitive-first: screens consume shared tokens/typography. * fix: review feedback — brightness-aware token fallback, de-flake Setting tests, Pipelock localhost FP - SureColors.of falls back to the palette matching the active brightness (not always light) when the extension is missing, so dark surfaces stay correct. - Clear the rails-settings-cached cache before each test; its in-memory cache survives the per-test transaction rollback, leaking Setting.* across tests and flaking Settings::HostingsControllerTest (stale empty string vs nil). Full unit suite: 4952 runs, 0 failures. - Suppress the localhost test-DB DATABASE_URL false positive with line-level `# pipelock:ignore` in ci.yml + llm-evals.yml instead of excluding whole files, so those workflows stay scanned for real secrets.
94 lines
3.0 KiB
Dart
94 lines
3.0 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
import 'sure_colors.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,
|
|
extensions: <ThemeExtension<dynamic>>[SureColors(tokens)],
|
|
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,
|
|
),
|
|
);
|
|
}
|
|
}
|