add back button functionality
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
<application
|
<application
|
||||||
android:label="mileograph_flutter"
|
android:label="mileograph_flutter"
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
|
|||||||
@@ -5,31 +5,6 @@ import 'package:mileograph_flutter/services/apiService.dart';
|
|||||||
import 'package:mileograph_flutter/services/dataService.dart';
|
import 'package:mileograph_flutter/services/dataService.dart';
|
||||||
import './routeSummaryWidget.dart';
|
import './routeSummaryWidget.dart';
|
||||||
|
|
||||||
class RouteCalculatorLoader extends StatelessWidget {
|
|
||||||
const RouteCalculatorLoader({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final data = context.read<DataService>();
|
|
||||||
|
|
||||||
return FutureBuilder<List<Station>>(
|
|
||||||
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 {
|
class StationAutocomplete extends StatefulWidget {
|
||||||
const StationAutocomplete({
|
const StationAutocomplete({
|
||||||
super.key,
|
super.key,
|
||||||
@@ -105,16 +80,14 @@ class _StationAutocompleteState extends State<StationAutocomplete> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class RouteCalculator extends StatefulWidget {
|
class RouteCalculator extends StatefulWidget {
|
||||||
final List<Station> allStations;
|
const RouteCalculator({super.key});
|
||||||
|
|
||||||
const RouteCalculator({super.key, required this.allStations});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<RouteCalculator> createState() => _RouteCalculatorState();
|
State<RouteCalculator> createState() => _RouteCalculatorState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _RouteCalculatorState extends State<RouteCalculator> {
|
class _RouteCalculatorState extends State<RouteCalculator> {
|
||||||
List<String> stations = [''];
|
List<Station> allStations = [];
|
||||||
|
|
||||||
RouteResult? _routeResult;
|
RouteResult? _routeResult;
|
||||||
RouteResult? get result => _routeResult;
|
RouteResult? get result => _routeResult;
|
||||||
@@ -122,6 +95,23 @@ class _RouteCalculatorState extends State<RouteCalculator> {
|
|||||||
|
|
||||||
bool _showDetails = false;
|
bool _showDetails = false;
|
||||||
|
|
||||||
|
bool _fetched = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didChangeDependencies() {
|
||||||
|
super.didChangeDependencies();
|
||||||
|
if (!_fetched) {
|
||||||
|
_fetched = true;
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||||
|
final data = context.read<DataService>();
|
||||||
|
final result = await data.fetchStations();
|
||||||
|
if (mounted) {
|
||||||
|
setState(() => allStations = result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _calculateRoute(List<String> stations) async {
|
Future<void> _calculateRoute(List<String> stations) async {
|
||||||
setState(() {
|
setState(() {
|
||||||
_errorMessage = null;
|
_errorMessage = null;
|
||||||
@@ -146,25 +136,29 @@ class _RouteCalculatorState extends State<RouteCalculator> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _addStation() {
|
void _addStation() {
|
||||||
|
final data = context.read<DataService>();
|
||||||
setState(() {
|
setState(() {
|
||||||
stations.add('');
|
data.stations.add('');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _removeStation(int index) {
|
void _removeStation(int index) {
|
||||||
|
final data = context.read<DataService>();
|
||||||
setState(() {
|
setState(() {
|
||||||
stations.removeAt(index);
|
data.stations.removeAt(index);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _updateStation(int index, String value) {
|
void _updateStation(int index, String value) {
|
||||||
|
final data = context.read<DataService>();
|
||||||
setState(() {
|
setState(() {
|
||||||
stations[index] = value;
|
data.stations[index] = value;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final data = context.watch<DataService>();
|
||||||
if (_showDetails && _routeResult != null) {
|
if (_showDetails && _routeResult != null) {
|
||||||
return RouteDetailsView(
|
return RouteDetailsView(
|
||||||
route: _routeResult!.calculatedRoute,
|
route: _routeResult!.calculatedRoute,
|
||||||
@@ -180,13 +174,13 @@ class _RouteCalculatorState extends State<RouteCalculator> {
|
|||||||
onReorder: (oldIndex, newIndex) {
|
onReorder: (oldIndex, newIndex) {
|
||||||
if (newIndex > oldIndex) newIndex -= 1;
|
if (newIndex > oldIndex) newIndex -= 1;
|
||||||
setState(() {
|
setState(() {
|
||||||
final moved = stations.removeAt(oldIndex);
|
final moved = data.stations.removeAt(oldIndex);
|
||||||
stations.insert(newIndex, moved);
|
data.stations.insert(newIndex, moved);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
children: List.generate(stations.length, (index) {
|
children: List.generate(data.stations.length, (index) {
|
||||||
return Container(
|
return Container(
|
||||||
key: ValueKey('$index-${stations[index]}'),
|
key: ValueKey('$index-${data.stations[index]}'),
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@@ -207,8 +201,8 @@ class _RouteCalculatorState extends State<RouteCalculator> {
|
|||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: StationAutocomplete(
|
child: StationAutocomplete(
|
||||||
allStations: widget.allStations,
|
allStations: allStations,
|
||||||
initialValue: stations[index],
|
initialValue: data.stations[index],
|
||||||
onChanged: (val) =>
|
onChanged: (val) =>
|
||||||
_updateStation(index, val),
|
_updateStation(index, val),
|
||||||
),
|
),
|
||||||
@@ -244,7 +238,13 @@ class _RouteCalculatorState extends State<RouteCalculator> {
|
|||||||
else
|
else
|
||||||
SizedBox.shrink(),
|
SizedBox.shrink(),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
Row(
|
LayoutBuilder(
|
||||||
|
builder: (context, constraints) {
|
||||||
|
double screenWidth = constraints.maxWidth;
|
||||||
|
|
||||||
|
return Padding(
|
||||||
|
padding: EdgeInsets.only(right: screenWidth < 450 ? 70 : 0),
|
||||||
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
ElevatedButton.icon(
|
ElevatedButton.icon(
|
||||||
@@ -257,11 +257,15 @@ class _RouteCalculatorState extends State<RouteCalculator> {
|
|||||||
icon: const Icon(Icons.route),
|
icon: const Icon(Icons.route),
|
||||||
label: const Text('Calculate Route'),
|
label: const Text('Calculate Route'),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await _calculateRoute(stations);
|
await _calculateRoute(data.stations);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -5,6 +5,6 @@ import 'package:mileograph_flutter/services/dataService.dart';
|
|||||||
|
|
||||||
class CalculatorPage extends StatelessWidget {
|
class CalculatorPage extends StatelessWidget {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return RouteCalculatorLoader();
|
return RouteCalculator();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,7 +85,11 @@ class DashboardHeader extends StatelessWidget {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: ListView(
|
child: ListView(
|
||||||
scrollDirection: Axis.vertical,
|
scrollDirection: Axis.vertical,
|
||||||
children: [TopTractionPanel(), LeaderboardPanel()],
|
children: [
|
||||||
|
TopTractionPanel(),
|
||||||
|
LeaderboardPanel(),
|
||||||
|
SizedBox(height: 80),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ import 'components/login/login.dart';
|
|||||||
import 'components/pages/dashboard.dart';
|
import 'components/pages/dashboard.dart';
|
||||||
import 'components/dashboard/topTractionPanel.dart';
|
import 'components/dashboard/topTractionPanel.dart';
|
||||||
|
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
late ApiService api;
|
late ApiService api;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
@@ -46,19 +48,6 @@ void main() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class AppRoot extends StatelessWidget {
|
|
||||||
const AppRoot({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Consumer<AuthService>(
|
|
||||||
builder: (context, auth, child) {
|
|
||||||
return auth.isLoggedIn ? MyHomePage() : LoginScreen();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class MyApp extends StatelessWidget {
|
class MyApp extends StatelessWidget {
|
||||||
MyApp({super.key});
|
MyApp({super.key});
|
||||||
|
|
||||||
@@ -67,13 +56,48 @@ class MyApp extends StatelessWidget {
|
|||||||
seedColor: Colors.red,
|
seedColor: Colors.red,
|
||||||
brightness: Brightness.dark,
|
brightness: Brightness.dark,
|
||||||
);
|
);
|
||||||
|
|
||||||
// This widget is the root of your application.
|
// This widget is the root of your application.
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final GoRouter router = GoRouter(
|
||||||
|
refreshListenable: context
|
||||||
|
.read<AuthService>(), // `AuthService` extends `ChangeNotifier`
|
||||||
|
redirect: (context, state) {
|
||||||
|
final auth = Provider.of<AuthService>(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(
|
return DynamicColorBuilder(
|
||||||
builder: (ColorScheme? lightDynamic, ColorScheme? darkDynamic) {
|
builder: (ColorScheme? lightDynamic, ColorScheme? darkDynamic) {
|
||||||
return MaterialApp(
|
return MaterialApp.router(
|
||||||
title: 'Flutter Demo',
|
title: 'Flutter Demo',
|
||||||
|
routerConfig: router,
|
||||||
theme: ThemeData(
|
theme: ThemeData(
|
||||||
useMaterial3: true,
|
useMaterial3: true,
|
||||||
// This is the theme of your application.
|
// This is the theme of your application.
|
||||||
@@ -98,7 +122,6 @@ class MyApp extends StatelessWidget {
|
|||||||
colorScheme: darkDynamic ?? defaultDark,
|
colorScheme: darkDynamic ?? defaultDark,
|
||||||
),
|
),
|
||||||
themeMode: ThemeMode.system,
|
themeMode: ThemeMode.system,
|
||||||
home: AppRoot(),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -106,7 +129,8 @@ class MyApp extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class MyHomePage extends StatefulWidget {
|
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
|
// 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
|
// 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<MyHomePage> {
|
class _MyHomePageState extends State<MyHomePage> {
|
||||||
int pageIndex = 0;
|
int pageIndex = 0;
|
||||||
final List<Widget> contentPages = [
|
final List<String> contentPages = [
|
||||||
Dashboard(),
|
"/",
|
||||||
CalculatorPage(),
|
"/calculator",
|
||||||
LegsPage(),
|
"/legs",
|
||||||
TractionPage(),
|
"/traction",
|
||||||
Center(child: Text("Trips Page")),
|
"/",
|
||||||
];
|
];
|
||||||
|
|
||||||
bool loggedIn = false;
|
bool loggedIn = false;
|
||||||
@@ -165,7 +189,7 @@ class _MyHomePageState extends State<MyHomePage> {
|
|||||||
final auth = context.read<AuthService>();
|
final auth = context.read<AuthService>();
|
||||||
|
|
||||||
if (data.homepageStats != null) {
|
if (data.homepageStats != null) {
|
||||||
currentPage = contentPages[pageIndex];
|
currentPage = widget.child;
|
||||||
} else {
|
} else {
|
||||||
currentPage = Center(child: CircularProgressIndicator());
|
currentPage = Center(child: CircularProgressIndicator());
|
||||||
}
|
}
|
||||||
@@ -210,6 +234,7 @@ class _MyHomePageState extends State<MyHomePage> {
|
|||||||
onDestinationSelected: (int index) {
|
onDestinationSelected: (int index) {
|
||||||
setState(() {
|
setState(() {
|
||||||
pageIndex = index;
|
pageIndex = index;
|
||||||
|
context.push(contentPages[index]);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
destinations: [
|
destinations: [
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ class DataService extends ChangeNotifier {
|
|||||||
List<Station>? _cachedStations;
|
List<Station>? _cachedStations;
|
||||||
DateTime? _stationsFetchedAt;
|
DateTime? _stationsFetchedAt;
|
||||||
|
|
||||||
|
List<String> stations = [""];
|
||||||
|
|
||||||
bool _isHomepageLoading = false;
|
bool _isHomepageLoading = false;
|
||||||
bool get isHomepageLoading => _isHomepageLoading;
|
bool get isHomepageLoading => _isHomepageLoading;
|
||||||
|
|
||||||
|
|||||||
23
pubspec.lock
23
pubspec.lock
@@ -83,6 +83,19 @@ packages:
|
|||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
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:
|
http:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -131,6 +144,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.0.0"
|
version: "6.0.0"
|
||||||
|
logging:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: logging
|
||||||
|
sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.3.0"
|
||||||
matcher:
|
matcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -266,4 +287,4 @@ packages:
|
|||||||
version: "1.1.1"
|
version: "1.1.1"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.8.1 <4.0.0"
|
dart: ">=3.8.1 <4.0.0"
|
||||||
flutter: ">=3.18.0-18.0.pre.54"
|
flutter: ">=3.27.0"
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ dependencies:
|
|||||||
# The following adds the Cupertino Icons font to your application.
|
# The following adds the Cupertino Icons font to your application.
|
||||||
# Use with the CupertinoIcons class for iOS style icons.
|
# Use with the CupertinoIcons class for iOS style icons.
|
||||||
cupertino_icons: ^1.0.8
|
cupertino_icons: ^1.0.8
|
||||||
|
go_router: ^16.0.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|||||||
Reference in New Issue
Block a user