Wake lock via a WakelockController seam (mirrors LocationSource), gated strictly to actual recording and released on stop/discard/navigate-away/dispose. High-contrast ripprMountedTheme() applied only to the record screen. BigStat gained a scale param for the headline figure rather than a blanket TextTheme.apply, which crashes on Material 3's default theme.
118 lines
4.0 KiB
Dart
118 lines
4.0 KiB
Dart
/// Ported from `com.rippr.ui.theme.RipprTheme`.
|
|
///
|
|
/// Dark-only by design for a pocketed ride -- read at a glance, in a pocket-glance, at
|
|
/// night as much as in daylight. V3-05 adds [ripprMountedTheme], a deliberate exception
|
|
/// for the one screen that is actually looked at continuously, in direct daylight, behind
|
|
/// a visor -- where the dark ground loses to glare instead of helping.
|
|
library;
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
// Matches the launcher icon: near-black tarmac with a safety-orange accent.
|
|
const _accent = Color(0xFFFF5722);
|
|
const _ground = Color(0xFF101418);
|
|
const _surface = Color(0xFF181D23);
|
|
const _ink = Color(0xFFF2F5F8);
|
|
const _outline = Color(0xFF66727E);
|
|
const _danger = Color(0xFFE53935);
|
|
|
|
const ripprColors = ColorScheme.dark(
|
|
primary: _accent,
|
|
onPrimary: Color(0xFF100A06),
|
|
secondary: _outline,
|
|
surface: _surface,
|
|
onSurface: _ink,
|
|
surfaceContainerHighest: Color(0xFF212832),
|
|
onSurfaceVariant: _ink,
|
|
outline: _outline,
|
|
error: _danger,
|
|
onError: _ink,
|
|
);
|
|
|
|
/// The near-black ground the whole app sits on.
|
|
const ripprBackground = _ground;
|
|
|
|
ThemeData ripprTheme() {
|
|
final base = ThemeData(
|
|
colorScheme: ripprColors,
|
|
useMaterial3: true,
|
|
scaffoldBackgroundColor: _ground,
|
|
);
|
|
|
|
return base.copyWith(
|
|
// Compose's `Surface` was load-bearing, not decoration: it set `LocalContentColor`.
|
|
// Without it, any Text that did not name a colour fell back to black — invisible on
|
|
// this near-black ground, and **no test caught it; only a screenshot did**.
|
|
//
|
|
// Flutter's equivalent exposure is the default text colour. Setting it explicitly
|
|
// here, rather than relying on a wrapping widget, means the failure cannot recur by
|
|
// someone removing a container.
|
|
textTheme: base.textTheme.apply(bodyColor: _ink, displayColor: _ink),
|
|
appBarTheme: const AppBarTheme(
|
|
backgroundColor: _ground,
|
|
foregroundColor: _ink,
|
|
elevation: 0,
|
|
),
|
|
dividerTheme: const DividerThemeData(color: Color(0xFF2A323C), space: 1),
|
|
);
|
|
}
|
|
|
|
// V3-05: mounted colours. Near-white ground, near-black ink -- the inverse tradeoff from
|
|
// the pocketed theme above. Read against a visor in direct daylight, a dark ground loses
|
|
// to glare in a way a dark theme never has to answer for at a pocket glance.
|
|
const _mountedGround = Color(0xFFF5F3EF);
|
|
const _mountedInk = Color(0xFF12100D);
|
|
const _mountedAccent = Color(0xFFD84315);
|
|
|
|
const ripprMountedColors = ColorScheme.light(
|
|
primary: _mountedAccent,
|
|
onPrimary: Color(0xFFFFFFFF),
|
|
secondary: Color(0xFF4A4034),
|
|
surface: Color(0xFFFFFFFF),
|
|
onSurface: _mountedInk,
|
|
surfaceContainerHighest: Color(0xFFE7E2D9),
|
|
onSurfaceVariant: _mountedInk,
|
|
outline: Color(0xFF706A5E),
|
|
error: Color(0xFFB3261E),
|
|
onError: Color(0xFFFFFFFF),
|
|
);
|
|
|
|
const ripprMountedBackground = _mountedGround;
|
|
|
|
/// Scales body/headline text up for legibility at a glance, at speed, behind a visor.
|
|
/// Kept separate from V3-16's visual identity work -- this is a stopgap born of a real
|
|
/// constraint, not a design pass.
|
|
const double mountedTextScale = 1.25;
|
|
|
|
/// V3-05's mounted mode: high contrast, larger text. Only applied to the record screen
|
|
/// while mounted, not the whole app -- everywhere else is still read at a pocket glance.
|
|
ThemeData ripprMountedTheme() {
|
|
final base = ThemeData(
|
|
colorScheme: ripprMountedColors,
|
|
useMaterial3: true,
|
|
scaffoldBackgroundColor: _mountedGround,
|
|
);
|
|
|
|
return base.copyWith(
|
|
textTheme: base.textTheme.apply(
|
|
bodyColor: _mountedInk,
|
|
displayColor: _mountedInk,
|
|
),
|
|
appBarTheme: const AppBarTheme(
|
|
backgroundColor: _mountedGround,
|
|
foregroundColor: _mountedInk,
|
|
elevation: 0,
|
|
),
|
|
dividerTheme: const DividerThemeData(color: Color(0xFFDAD4C8), space: 1),
|
|
);
|
|
}
|
|
|
|
/// Monospace for anything numeric.
|
|
///
|
|
/// Digits that change every second must not make the layout jitter, which proportional
|
|
/// figures do. Used for every speed, distance and clock in the app.
|
|
const TextStyle monoDigits = TextStyle(
|
|
fontFamily: 'monospace',
|
|
fontFeatures: [FontFeature.tabularFigures()],
|
|
);
|