Refresh the Flutter snapshot: UI-01 through UI-09, plus a fresh installable APK

Full UI redesign pass complete: persistent tab shell with an always-visible
background map, Modern Professional Dark theme, monochrome dark map tiles,
offline skeleton map, a shared GlassPanel/FloatingPill component kit,
customizable HUD telemetry widgets, and the Map HUD / Plan & Route Planning /
Rides History screen rebuilds. 374 tests passing, up from 316.

The APK is a fresh release build (debug-signed, no release signing config
exists yet).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Xki7YAcc2TiN2PRZJ2tXr
This commit is contained in:
2026-08-24 14:41:35 -05:00
parent 4c634354bd
commit 587f75eab4
42 changed files with 4401 additions and 89 deletions

View File

@@ -3,6 +3,7 @@ import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:rippr/src/domain/models.dart';
import 'package:rippr/src/ui/components/ride_map.dart';
import 'package:rippr/src/ui/components/skeleton_map_layer.dart';
import 'package:rippr/src/ui/theme.dart';
/// Tiles are never fetched here — a widget test cannot serve them — but the polyline
@@ -117,4 +118,67 @@ void main() {
expect(map.options.maxZoom, maxTileZoom,
reason: 'exceeding OSM max tile zoom renders an empty grid');
});
group('skeleton mode (UI-02)', () {
testWidgets('shows the skeleton and no TileLayer when skeletonMode is true',
(tester) async {
await tester.pumpWidget(MaterialApp(
theme: ripprTheme(),
home: const Scaffold(
body: RideMap(points: [], segments: [], showEmptyLabel: false, skeletonMode: true),
),
));
await tester.pump();
expect(find.byType(SkeletonMapLayer), findsOneWidget);
expect(find.byType(TileLayer), findsNothing);
});
testWidgets('shows the TileLayer and no skeleton when skeletonMode is false',
(tester) async {
await tester.pumpWidget(MaterialApp(
theme: ripprTheme(),
home: const Scaffold(
body: RideMap(points: [], segments: [], showEmptyLabel: false),
),
));
await tester.pump();
expect(find.byType(TileLayer), findsOneWidget);
expect(find.byType(SkeletonMapLayer), findsNothing);
});
testWidgets('the recorded path still renders on top of the skeleton',
(tester) async {
final points = [
for (var i = 0; i < 4; i++)
TrackPoint(
id: i + 1,
tripId: 1,
segmentId: 1,
timestamp: 1000 + i * 1000,
latitude: 51.0 + i * 0.0005,
longitude: -114.0,
speedKmh: 40,
altitudeM: 1000,
),
];
const segments = [Segment(id: 1, tripId: 1, startedAt: 0, endedAt: 1)];
await tester.pumpWidget(MaterialApp(
theme: ripprTheme(),
home: Scaffold(
body: RideMap(points: points, segments: segments, skeletonMode: true),
),
));
await tester.pump();
expect(find.byType(SkeletonMapLayer), findsOneWidget);
expect(find.byType(TileLayer), findsNothing);
final layer = tester.widget<PolylineLayer>(find.byType(PolylineLayer));
expect(layer.polylines, isNotEmpty,
reason: 'the path comes from local data, not tiles -- it must not vanish '
'just because the tile fetch is failing');
});
});
}