Files
ImBenjiandClaude Opus 5.5 b269201919 The Garage SDKs, in the open
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
2026-09-23 18:49:21 +01:00

112 lines
3.8 KiB
Dart

import "package:flutter/widgets.dart";
import "package:garage_ui/scrollbar.dart";
import "package:garage_ui/theme/garage_theme.dart";
/// The application shell for Garage UI apps.
///
/// This deliberately uses [WidgetsApp] rather than [MaterialApp]. Garage UI
/// owns the visual system while Flutter still supplies routing, overlays,
/// media queries, directionality, and localization plumbing.
class GarageApp extends StatelessWidget {
const GarageApp.router({
super.key,
required this.routerConfig,
required this.theme,
this.title = "",
this.builder,
this.locale,
this.localizationsDelegates,
this.supportedLocales = const <Locale>[Locale("en", "US")],
this.debugShowCheckedModeBanner = false,
});
final RouterConfig<Object> routerConfig;
final ThemeData theme;
final String title;
final TransitionBuilder? builder;
final Locale? locale;
final Iterable<LocalizationsDelegate<dynamic>>? localizationsDelegates;
final Iterable<Locale> supportedLocales;
final bool debugShowCheckedModeBanner;
@override
Widget build(BuildContext context) {
return WidgetsApp.router(
key: key,
title: title,
routerConfig: routerConfig,
color: theme.colorScheme.background,
locale: locale,
localizationsDelegates: localizationsDelegates,
supportedLocales: supportedLocales,
debugShowCheckedModeBanner: debugShowCheckedModeBanner,
// the Builder matters: without it `builder` is handed the context ABOVE
// this GarageTheme, so GarageTheme.of() asserts inside the very callback
// you were given for theming.
// ScrollConfiguration, not WidgetsApp's `scrollBehavior` - that
// parameter only exists on MaterialApp/CupertinoApp.
builder: (context, child) => ScrollConfiguration(
behavior: const GarageScrollBehavior(),
child: GarageTheme(
data: theme,
child: Builder(
builder: (themedContext) =>
builder?.call(themedContext, child) ??
child ??
const SizedBox.shrink(),
),
),
),
);
}
}
/// Scrolling without Material's overscroll glow.
///
/// The default ScrollBehavior hangs a GlowingOverscrollIndicator off every
/// scrollable on Android and Fuchsia - the grey arc that swells out of the
/// top or bottom edge when you drag past the end. Its a Material idiom, and
/// this kit isnt a Material app: it paints a big soft shape over flat chrome
/// and reads as a rendering fault rather than as feedback.
///
/// Physics and drag devices are untouched, so a list still throws and settles
/// the way the OS expects - it just doesnt glow at the ends. iOS and desktop
/// never had the glow in the first place, so this only levels the other
/// platforms up to what they already do.
///
/// The scrollbar is ours too. The base behaviour hands desktop a stock
/// [RawScrollbar] - 8px, square ends, a hardcoded grey that knows nothing
/// about the scheme - and thats the pale bar that used to sit beside every
/// nav list. [GarageScrollbar] replaces it here, once, for every scrollable
/// in the app. Touch platforms keep no bar at all, same as the default.
class GarageScrollBehavior extends ScrollBehavior {
const GarageScrollBehavior();
@override
Widget buildOverscrollIndicator(
BuildContext context,
Widget child,
ScrollableDetails details,
) => child;
@override
Widget buildScrollbar(
BuildContext context,
Widget child,
ScrollableDetails details,
) {
switch (getPlatform(context)) {
case TargetPlatform.linux:
case TargetPlatform.macOS:
case TargetPlatform.windows:
return GarageScrollbar(controller: details.controller, child: child);
case TargetPlatform.android:
case TargetPlatform.fuchsia:
case TargetPlatform.iOS:
return child;
}
}
}