Theme, go_router shell, record screen, trips list with selection, trip detail with stats and charts, rename/delete/merge. 15 widget tests -- the first this project has ever had, closing the gap docs/v3/BACKLOG.md names as v2's largest. The theme sets bodyColor and displayColor explicitly rather than relying on a wrapping widget, so the black-on-black regression cannot recur by someone removing a container, and a widget test now asserts the headline names a colour distinct from the ground. Those tests immediately found a real layout bug: with a ride active the record screen grows to six stat rows and overflows a short screen. Compose clipped this silently, so it may be latent in the native app; Flutter reports it. Fixed by making the screen scrollable while still centring when there is room. Two harness lessons. pumpAndSettle never settles against a repeating timer, so the elapsed clock now runs only while a ride is active -- better behaviour anyway, since an idle screen has no clock to advance. And flutter_test asserts no Timer is pending after disposal, which Drift's stream-query keep-alive cache trips; tests now tear the tree down and pump past that window. 160 tests passing, analyze clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
40 lines
1.1 KiB
Dart
40 lines
1.1 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
|
|
import 'src/app/providers.dart';
|
|
import 'src/ui/router.dart';
|
|
import 'src/ui/theme.dart';
|
|
|
|
void main() {
|
|
runApp(const ProviderScope(child: RipprApp()));
|
|
}
|
|
|
|
class RipprApp extends ConsumerStatefulWidget {
|
|
const RipprApp({super.key});
|
|
|
|
@override
|
|
ConsumerState<RipprApp> createState() => _RipprAppState();
|
|
}
|
|
|
|
class _RipprAppState extends ConsumerState<RipprApp> {
|
|
final _router = buildRouter();
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
// Re-attach to a ride that was in progress when the process died. Must run before any
|
|
// user interaction, and the database is the only thing that knows a ride was open.
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
ref.read(recordingEngineProvider).restoreAfterProcessDeath();
|
|
});
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) => MaterialApp.router(
|
|
title: 'Rippr',
|
|
debugShowCheckedModeBanner: false,
|
|
theme: ripprTheme(),
|
|
routerConfig: _router,
|
|
);
|
|
}
|