diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 7772c02..cd526ac 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,4 +1,5 @@ + (); - - return FutureBuilder>( - future: data.fetchStations(), - builder: (context, snapshot) { - if (snapshot.connectionState != ConnectionState.done) { - return const Center(child: CircularProgressIndicator()); - } - - if (snapshot.hasError) { - return Center(child: Text('Error: ${snapshot.error}')); - } - - final stations = snapshot.data!; - return RouteCalculator(allStations: stations); - }, - ); - } -} - class StationAutocomplete extends StatefulWidget { const StationAutocomplete({ super.key, @@ -105,16 +80,14 @@ class _StationAutocompleteState extends State { } class RouteCalculator extends StatefulWidget { - final List allStations; - - const RouteCalculator({super.key, required this.allStations}); + const RouteCalculator({super.key}); @override State createState() => _RouteCalculatorState(); } class _RouteCalculatorState extends State { - List stations = ['']; + List allStations = []; RouteResult? _routeResult; RouteResult? get result => _routeResult; @@ -122,6 +95,23 @@ class _RouteCalculatorState extends State { bool _showDetails = false; + bool _fetched = false; + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + if (!_fetched) { + _fetched = true; + WidgetsBinding.instance.addPostFrameCallback((_) async { + final data = context.read(); + final result = await data.fetchStations(); + if (mounted) { + setState(() => allStations = result); + } + }); + } + } + Future _calculateRoute(List stations) async { setState(() { _errorMessage = null; @@ -146,25 +136,29 @@ class _RouteCalculatorState extends State { } void _addStation() { + final data = context.read(); setState(() { - stations.add(''); + data.stations.add(''); }); } void _removeStation(int index) { + final data = context.read(); setState(() { - stations.removeAt(index); + data.stations.removeAt(index); }); } void _updateStation(int index, String value) { + final data = context.read(); setState(() { - stations[index] = value; + data.stations[index] = value; }); } @override Widget build(BuildContext context) { + final data = context.watch(); if (_showDetails && _routeResult != null) { return RouteDetailsView( route: _routeResult!.calculatedRoute, @@ -180,13 +174,13 @@ class _RouteCalculatorState extends State { onReorder: (oldIndex, newIndex) { if (newIndex > oldIndex) newIndex -= 1; setState(() { - final moved = stations.removeAt(oldIndex); - stations.insert(newIndex, moved); + final moved = data.stations.removeAt(oldIndex); + data.stations.insert(newIndex, moved); }); }, - children: List.generate(stations.length, (index) { + children: List.generate(data.stations.length, (index) { return Container( - key: ValueKey('$index-${stations[index]}'), + key: ValueKey('$index-${data.stations[index]}'), margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), child: Row( crossAxisAlignment: CrossAxisAlignment.start, @@ -207,8 +201,8 @@ class _RouteCalculatorState extends State { children: [ Expanded( child: StationAutocomplete( - allStations: widget.allStations, - initialValue: stations[index], + allStations: allStations, + initialValue: data.stations[index], onChanged: (val) => _updateStation(index, val), ), @@ -244,24 +238,34 @@ class _RouteCalculatorState extends State { else SizedBox.shrink(), const SizedBox(height: 10), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - ElevatedButton.icon( - icon: const Icon(Icons.add), - label: const Text('Add Station'), - onPressed: _addStation, - ), - const SizedBox(width: 16), - ElevatedButton.icon( - icon: const Icon(Icons.route), - label: const Text('Calculate Route'), - onPressed: () async { - await _calculateRoute(stations); - }, - ), - ], + LayoutBuilder( + builder: (context, constraints) { + double screenWidth = constraints.maxWidth; + + return Padding( + padding: EdgeInsets.only(right: screenWidth < 450 ? 70 : 0), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton.icon( + icon: const Icon(Icons.add), + label: const Text('Add Station'), + onPressed: _addStation, + ), + const SizedBox(width: 16), + ElevatedButton.icon( + icon: const Icon(Icons.route), + label: const Text('Calculate Route'), + onPressed: () async { + await _calculateRoute(data.stations); + }, + ), + ], + ), + ); + }, ), + const SizedBox(height: 16), ], ); diff --git a/lib/components/pages/calculator.dart b/lib/components/pages/calculator.dart index 5f5c083..4954838 100644 --- a/lib/components/pages/calculator.dart +++ b/lib/components/pages/calculator.dart @@ -5,6 +5,6 @@ import 'package:mileograph_flutter/services/dataService.dart'; class CalculatorPage extends StatelessWidget { Widget build(BuildContext context) { - return RouteCalculatorLoader(); + return RouteCalculator(); } } diff --git a/lib/components/pages/dashboard.dart b/lib/components/pages/dashboard.dart index 0d40597..012c11c 100644 --- a/lib/components/pages/dashboard.dart +++ b/lib/components/pages/dashboard.dart @@ -85,7 +85,11 @@ class DashboardHeader extends StatelessWidget { Expanded( child: ListView( scrollDirection: Axis.vertical, - children: [TopTractionPanel(), LeaderboardPanel()], + children: [ + TopTractionPanel(), + LeaderboardPanel(), + SizedBox(height: 80), + ], ), ), ], diff --git a/lib/main.dart b/lib/main.dart index 9fefb34..b9305ae 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -13,6 +13,8 @@ import 'components/login/login.dart'; import 'components/pages/dashboard.dart'; import 'components/dashboard/topTractionPanel.dart'; +import 'package:go_router/go_router.dart'; + late ApiService api; void main() { @@ -46,19 +48,6 @@ void main() { ); } -class AppRoot extends StatelessWidget { - const AppRoot({super.key}); - - @override - Widget build(BuildContext context) { - return Consumer( - builder: (context, auth, child) { - return auth.isLoggedIn ? MyHomePage() : LoginScreen(); - }, - ); - } -} - class MyApp extends StatelessWidget { MyApp({super.key}); @@ -67,13 +56,48 @@ class MyApp extends StatelessWidget { seedColor: Colors.red, brightness: Brightness.dark, ); + // This widget is the root of your application. @override Widget build(BuildContext context) { + final GoRouter router = GoRouter( + refreshListenable: context + .read(), // `AuthService` extends `ChangeNotifier` + redirect: (context, state) { + final auth = Provider.of(context, listen: false); + final loggedIn = auth.isLoggedIn; + final loggingIn = state.uri.toString() == '/login'; + + // Redirect to login if not logged in and trying to access protected pages + if (!loggedIn && !loggingIn) return '/login'; + + // Redirect to home if already logged in and trying to go to login + if (loggedIn && loggingIn) return '/'; + + // No redirection + return null; + }, + routes: [ + ShellRoute( + builder: (context, state, child) { + return MyHomePage(child: child); + }, + routes: [ + GoRoute(path: '/', builder: (_, __) => const Dashboard()), + GoRoute(path: '/calculator', builder: (_, __) => CalculatorPage()), + GoRoute(path: '/legs', builder: (_, __) => LegsPage()), + GoRoute(path: '/traction', builder: (_, __) => TractionPage()), + ], + ), + GoRoute(path: '/login', builder: (_, __) => const LoginScreen()), + ], + ); + return DynamicColorBuilder( builder: (ColorScheme? lightDynamic, ColorScheme? darkDynamic) { - return MaterialApp( + return MaterialApp.router( title: 'Flutter Demo', + routerConfig: router, theme: ThemeData( useMaterial3: true, // This is the theme of your application. @@ -98,7 +122,6 @@ class MyApp extends StatelessWidget { colorScheme: darkDynamic ?? defaultDark, ), themeMode: ThemeMode.system, - home: AppRoot(), ); }, ); @@ -106,7 +129,8 @@ class MyApp extends StatelessWidget { } class MyHomePage extends StatefulWidget { - const MyHomePage({super.key}); + final Widget child; + const MyHomePage({super.key, required this.child}); // This widget is the home page of your application. It is stateful, meaning // that it has a State object (defined below) that contains fields that affect @@ -123,12 +147,12 @@ class MyHomePage extends StatefulWidget { class _MyHomePageState extends State { int pageIndex = 0; - final List contentPages = [ - Dashboard(), - CalculatorPage(), - LegsPage(), - TractionPage(), - Center(child: Text("Trips Page")), + final List contentPages = [ + "/", + "/calculator", + "/legs", + "/traction", + "/", ]; bool loggedIn = false; @@ -165,7 +189,7 @@ class _MyHomePageState extends State { final auth = context.read(); if (data.homepageStats != null) { - currentPage = contentPages[pageIndex]; + currentPage = widget.child; } else { currentPage = Center(child: CircularProgressIndicator()); } @@ -210,6 +234,7 @@ class _MyHomePageState extends State { onDestinationSelected: (int index) { setState(() { pageIndex = index; + context.push(contentPages[index]); }); }, destinations: [ diff --git a/lib/services/dataService.dart b/lib/services/dataService.dart index 8cbaadc..1f2153e 100644 --- a/lib/services/dataService.dart +++ b/lib/services/dataService.dart @@ -23,6 +23,8 @@ class DataService extends ChangeNotifier { List? _cachedStations; DateTime? _stationsFetchedAt; + List stations = [""]; + bool _isHomepageLoading = false; bool get isHomepageLoading => _isHomepageLoading; diff --git a/pubspec.lock b/pubspec.lock index 45b5fb8..2995498 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -83,6 +83,19 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + go_router: + dependency: "direct main" + description: + name: go_router + sha256: c489908a54ce2131f1d1b7cc631af9c1a06fac5ca7c449e959192089f9489431 + url: "https://pub.dev" + source: hosted + version: "16.0.0" http: dependency: "direct main" description: @@ -131,6 +144,14 @@ packages: url: "https://pub.dev" source: hosted version: "6.0.0" + logging: + dependency: transitive + description: + name: logging + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 + url: "https://pub.dev" + source: hosted + version: "1.3.0" matcher: dependency: transitive description: @@ -266,4 +287,4 @@ packages: version: "1.1.1" sdks: dart: ">=3.8.1 <4.0.0" - flutter: ">=3.18.0-18.0.pre.54" + flutter: ">=3.27.0" diff --git a/pubspec.yaml b/pubspec.yaml index cfe70d4..25ec526 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -37,6 +37,7 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.8 + go_router: ^16.0.0 dev_dependencies: flutter_test: