garage_auth, garage_entitlements, garage_iap and garage_ui, moved out of Garage-Services and Metro-Map-Maker into one public repo. MIT, one readme, docs under docs/. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013F4NWNvYcdeSgqbWMT1VQ7
91 lines
3.5 KiB
Dart
91 lines
3.5 KiB
Dart
import "package:flutter/widgets.dart";
|
|
import "package:google_fonts/google_fonts.dart";
|
|
|
|
import "package:garage_ui/theme/theme_data.dart";
|
|
|
|
export "package:garage_ui/theme/theme_data.dart";
|
|
export "package:garage_ui/theme/colour_scheme.dart";
|
|
export "package:garage_ui/theme/typography.dart";
|
|
|
|
// The apps theme. Hands the theme data down via a plain inherited scope (no
|
|
// animated wrapper, no per frame ColorScheme.lerp flattening the whole thing -
|
|
// this is the bit that used to feel second class), AND establishes a sane
|
|
// DefaultTextStyle + IconTheme so bare Text/Icon dont fall back to flutter's
|
|
// yellow-underline "unstyled" debug look (MaterialApp only styles text inside a
|
|
// Material, and our panels arent Material).
|
|
class GarageTheme extends StatelessWidget {
|
|
const GarageTheme({super.key, required this.data, required this.child});
|
|
|
|
final ThemeData data;
|
|
final Widget child;
|
|
|
|
static ThemeData of(BuildContext context) {
|
|
final t = maybeOf(context);
|
|
assert(
|
|
t != null,
|
|
"No GarageTheme found in context. Wrap the app in an GarageTheme.",
|
|
);
|
|
return t!;
|
|
}
|
|
|
|
static ThemeData? maybeOf(BuildContext context) {
|
|
return context
|
|
.dependOnInheritedWidgetOfExactType<_GarageThemeScope>()
|
|
?.data;
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final cs = data.colorScheme;
|
|
return _GarageThemeScope(
|
|
data: data,
|
|
child: DefaultTextStyle(
|
|
// the one app-wide UI font, declared right here at the top of the tree.
|
|
// everything below that doesnt override fontFamily inherits Geist.
|
|
// (canvas-painted text - station labels, watermark - sits outside the
|
|
// widget tree so it isnt touched by this.)
|
|
style: GoogleFonts.geist(
|
|
color: cs.foreground,
|
|
// off the density, not a literal times scaling. this was the third
|
|
// independent source of text size in the package - controls read
|
|
// density.fontSize, the .xSmall()/.small()/.large() ladder now reads
|
|
// it too, and this floated free at 11 * scaling. Retune fontSize and
|
|
// page copy no longer stays behind while the controls move.
|
|
//
|
|
// fontSize, NOT textXs. The ladder above the control font is a set
|
|
// of MULTIPLIERS (xs 1.1x, sm 1.4x, lg 1.8x), so what textXs means
|
|
// depends on the tier: at A&A's 10 its 11, a hair over the control
|
|
// font and harmless, and at the product tier's 16 its 18 - body copy
|
|
// rendering BIGGER than the text inside the buttons and fields next
|
|
// to it. Thats backwards, and its what made the product surfaces
|
|
// read as oversized even with their control geometry correct.
|
|
//
|
|
// Body copy is the control font. A sentence and the text in the box
|
|
// under it are the same size, at every tier, by construction.
|
|
fontSize: data.density.fontSize,
|
|
// the important bit - kills the yellow underline.
|
|
decoration: TextDecoration.none,
|
|
fontWeight: FontWeight.w300,
|
|
),
|
|
child: IconTheme(
|
|
data: IconThemeData(
|
|
color: cs.foreground,
|
|
size: data.iconTheme.medium.size,
|
|
),
|
|
child: child,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _GarageThemeScope extends InheritedWidget {
|
|
const _GarageThemeScope({required this.data, required super.child});
|
|
|
|
final ThemeData data;
|
|
|
|
@override
|
|
bool updateShouldNotify(_GarageThemeScope oldWidget) =>
|
|
oldWidget.data != data;
|
|
}
|