2 Commits

Author SHA1 Message Date
da70dce369 drafts minor changes, edit minor changes
All checks were successful
Release / meta (push) Successful in 15s
Release / linux-build (push) Successful in 9m20s
Release / android-build (push) Successful in 25m33s
Release / release-master (push) Successful in 43s
Release / release-dev (push) Successful in 45s
2025-12-15 00:33:18 +00:00
603e117af8 add draft changes
All checks were successful
Release / meta (push) Successful in 8s
Release / linux-build (push) Successful in 6m18s
Release / android-build (push) Successful in 16m30s
Release / release-master (push) Successful in 24s
Release / release-dev (push) Successful in 27s
2025-12-14 23:30:45 +00:00
7 changed files with 916 additions and 130 deletions

View File

@@ -97,10 +97,16 @@ class _StationAutocompleteState extends State<StationAutocomplete> {
} }
class RouteCalculator extends StatefulWidget { class RouteCalculator extends StatefulWidget {
const RouteCalculator({super.key, this.onDistanceComputed, this.onApplyRoute}); const RouteCalculator({
super.key,
this.onDistanceComputed,
this.onApplyRoute,
this.initialStations,
});
final ValueChanged<double>? onDistanceComputed; final ValueChanged<double>? onDistanceComputed;
final ValueChanged<RouteResult>? onApplyRoute; final ValueChanged<RouteResult>? onApplyRoute;
final List<String>? initialStations;
@override @override
State<RouteCalculator> createState() => _RouteCalculatorState(); State<RouteCalculator> createState() => _RouteCalculatorState();
@@ -122,6 +128,9 @@ class _RouteCalculatorState extends State<RouteCalculator> {
super.didChangeDependencies(); super.didChangeDependencies();
if (!_fetched) { if (!_fetched) {
_fetched = true; _fetched = true;
if (widget.initialStations != null && widget.initialStations!.isNotEmpty) {
context.read<DataService>().stations = List.from(widget.initialStations!);
}
WidgetsBinding.instance.addPostFrameCallback((_) async { WidgetsBinding.instance.addPostFrameCallback((_) async {
final data = context.read<DataService>(); final data = context.read<DataService>();
final result = await data.fetchStations(); final result = await data.fetchStations();

View File

@@ -1,6 +1,7 @@
import 'dart:convert'; import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:mileograph_flutter/objects/objects.dart'; import 'package:mileograph_flutter/objects/objects.dart';
import 'package:mileograph_flutter/services/dataService.dart'; import 'package:mileograph_flutter/services/dataService.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@@ -279,6 +280,11 @@ class _LegsPageState extends State<LegsPage> {
trailing: Column( trailing: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
IconButton(
tooltip: 'Edit entry',
icon: const Icon(Icons.edit),
onPressed: () => context.push('/legs/edit/${leg.id}'),
),
Text( Text(
'${leg.mileage.toStringAsFixed(1)} mi', '${leg.mileage.toStringAsFixed(1)} mi',
style: style:

File diff suppressed because it is too large Load Diff

View File

@@ -301,6 +301,25 @@ class _TripsPageState extends State<TripsPage> {
], ],
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
if (!loading && items.isNotEmpty) ...[
Wrap(
spacing: 8,
runSpacing: 8,
children: [
Chip(
avatar: const Icon(Icons.train, size: 16),
label: Text('Total had: ${items.length}'),
),
Chip(
avatar: const Icon(Icons.star, size: 16),
label: Text(
'Winners: ${items.where((e) => e.won == true).length}',
),
),
],
),
const SizedBox(height: 8),
],
if (loading) if (loading)
const Center( const Center(
child: Padding( child: Padding(

View File

@@ -11,6 +11,7 @@ import 'package:mileograph_flutter/components/pages/legs.dart';
import 'package:mileograph_flutter/services/apiService.dart'; import 'package:mileograph_flutter/services/apiService.dart';
import 'package:mileograph_flutter/services/authservice.dart'; import 'package:mileograph_flutter/services/authservice.dart';
import 'package:mileograph_flutter/services/dataService.dart'; import 'package:mileograph_flutter/services/dataService.dart';
import 'package:mileograph_flutter/services/navigation_guard.dart';
import 'components/login/login.dart'; import 'components/login/login.dart';
import 'components/pages/dashboard.dart'; import 'components/pages/dashboard.dart';
@@ -100,6 +101,14 @@ class MyApp extends StatelessWidget {
), ),
GoRoute(path: '/trips', builder: (_, __) => TripsPage()), GoRoute(path: '/trips', builder: (_, __) => TripsPage()),
GoRoute(path: '/add', builder: (_, __) => NewEntryPage()), GoRoute(path: '/add', builder: (_, __) => NewEntryPage()),
GoRoute(
path: '/legs/edit/:id',
builder: (_, state) {
final idParam = state.pathParameters['id'];
final legId = idParam == null ? null : int.tryParse(idParam);
return NewEntryPage(editLegId: legId);
},
),
], ],
), ),
GoRoute(path: '/login', builder: (_, __) => const LoginScreen()), GoRoute(path: '/login', builder: (_, __) => const LoginScreen()),
@@ -180,12 +189,14 @@ class _MyHomePageState extends State<MyHomePage> {
return newIndex; return newIndex;
} }
void _onItemTapped(int index, int currentIndex) { Future<void> _onItemTapped(int index, int currentIndex) async {
if (index < 0 || index >= contentPages.length || index == currentIndex) { if (index < 0 || index >= contentPages.length || index == currentIndex) {
return; return;
} }
context.push(contentPages[index]); await NavigationGuard.attemptNavigation(() async {
_getIndexFromLocation(contentPages[index]); if (!mounted) return;
context.go(contentPages[index]);
});
} }
bool loggedIn = false; bool loggedIn = false;

View File

@@ -0,0 +1,40 @@
typedef NavigationGuardCallback = Future<bool> Function();
class NavigationGuard {
static NavigationGuardCallback? _callback;
static void register(NavigationGuardCallback callback) {
_callback = callback;
}
static void unregister(NavigationGuardCallback callback) {
if (_callback == callback) {
_callback = null;
}
}
static Future<void> attemptNavigation(
Future<void> Function() performNavigation,
) async {
if (_promptActive) return;
final cb = _callback;
if (cb == null) {
await performNavigation();
return;
}
_promptActive = true;
bool allow = false;
try {
allow = await cb();
} catch (_) {
allow = false;
} finally {
_promptActive = false;
}
if (allow) {
await performNavigation();
}
}
static bool _promptActive = false;
}

View File

@@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts # In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix. # of the product and file versions while build-number is used as the build suffix.
version: 0.1.3+1 version: 0.1.5+1
environment: environment:
sdk: ^3.8.1 sdk: ^3.8.1