overhaul using a trip struct that notifies its ui dependencies
All checks were successful
Build and push docker image / Build (pull_request) Successful in 1m54s
Build and release APK / Build APK (pull_request) Successful in 5m32s

This commit is contained in:
2024-08-03 16:52:29 +02:00
parent 5748630b99
commit c87a01b2e8
15 changed files with 324 additions and 164 deletions

View File

@@ -3,12 +3,10 @@ import 'package:anyway/structs/trip.dart';
import 'package:flutter/material.dart';
class Greeter extends StatefulWidget {
final Future<Trip> trip;
final bool standalone;
final Trip trip;
Greeter({
required this.standalone,
required this.trip
required this.trip,
});
@override
@@ -18,55 +16,66 @@ class Greeter extends StatefulWidget {
class _GreeterState extends State<Greeter> {
Widget greeterBuild (BuildContext context, AsyncSnapshot<Trip> snapshot) {
Widget greeterBuilder (BuildContext context, Widget? child) {
ThemeData theme = Theme.of(context);
Widget topGreeter;
if (snapshot.hasData) {
topGreeter = Padding(
padding: const EdgeInsets.only(top: 20, bottom: 20),
child: Text(
'Welcome to ${snapshot.data?.cityName}!',
style: TextStyle(color: theme.primaryColor, fontWeight: FontWeight.bold, fontSize: 24),
)
);
} else if (snapshot.hasError) {
topGreeter = const Padding(
padding: EdgeInsets.only(top: 20, bottom: 20),
child: Text('Error while fetching trip')
if (widget.trip.landmarks.length > 1) {
topGreeter = FutureBuilder(
future: widget.trip.cityName,
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
if (snapshot.hasData) {
return Text(
'Welcome to ${snapshot.data}!',
style: TextStyle(color: theme.primaryColor, fontWeight: FontWeight.bold, fontSize: 24),
);
} else if (snapshot.hasError) {
return const Text('Welcome to your trip!');
} else {
return const Text('Welcome to ...');
}
}
);
} else {
// still awaiting the cityname
// still awaiting the trip
// We can hopefully infer the city name from the cityName future
// Show a linear loader at the bottom and an info message above
topGreeter = Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Padding(
padding: const EdgeInsets.only(top: 20, bottom: 20),
child: const Text('Generating your trip...', style: TextStyle(fontSize: 20),)
FutureBuilder(
future: widget.trip.cityName,
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
if (snapshot.hasData) {
return Text(
'Generating your trip to ${snapshot.data}...',
style: TextStyle(color: theme.primaryColor, fontWeight: FontWeight.bold, fontSize: 24),
);
} else if (snapshot.hasError) {
return const Text('Error while fetching city name');
}
return const Text('Generating your trip...');
}
),
const LinearProgressIndicator()
Padding(
padding: EdgeInsets.all(5),
child: const LinearProgressIndicator()
)
]
);
}
if (widget.standalone) {
return Center(
child: topGreeter,
);
} else {
return Center(
child: Column(
children: [
Padding(padding: EdgeInsets.only(top: 24.0)),
topGreeter,
bottomGreeter,
Padding(padding: EdgeInsets.only(bottom: 24.0)),
],
)
);
}
return Center(
child: Column(
children: [
// Padding(padding: EdgeInsets.only(top: 20)),
topGreeter,
Padding(
padding: EdgeInsets.all(20),
child: bottomGreeter
),
],
)
);
}
Widget bottomGreeter = const Text(
@@ -79,9 +88,9 @@ class _GreeterState extends State<Greeter> {
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: widget.trip,
builder: greeterBuild,
return ListenableBuilder(
listenable: widget.trip,
builder: greeterBuilder,
);
}
}

View File

@@ -1,4 +1,5 @@
import 'package:anyway/structs/landmark.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
@@ -31,9 +32,10 @@ class _LandmarkCardState extends State<LandmarkCard> {
height: double.infinity,
// force a fixed width
width: 160,
child: Image.network(
widget.landmark.imageURL ?? '',
errorBuilder: (context, error, stackTrace) => Icon(Icons.question_mark_outlined),
child: CachedNetworkImage(
imageUrl: widget.landmark.imageURL ?? '',
placeholder: (context, url) => CircularProgressIndicator(),
errorWidget: (context, error, stackTrace) => Icon(Icons.question_mark_outlined),
// TODO: make this a switch statement to load a placeholder if null
// cover the whole container meaning the image will be cropped
fit: BoxFit.cover,

View File

@@ -1,17 +1,17 @@
import 'dart:collection';
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:anyway/modules/landmark_card.dart';
import 'package:anyway/structs/landmark.dart';
import 'package:anyway/structs/trip.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
class LandmarksOverview extends StatefulWidget {
final Future<Trip>? trip;
final Trip? trip;
const LandmarksOverview({super.key, this.trip});
@override
@@ -23,18 +23,17 @@ class _LandmarksOverviewState extends State<LandmarksOverview> {
@override
Widget build(BuildContext context) {
final Future<LinkedList<Landmark>> _landmarks = getLandmarks(widget.trip);
return DefaultTextStyle(
style: Theme.of(context).textTheme.displayMedium!,
textAlign: TextAlign.center,
child: FutureBuilder<LinkedList<Landmark>>(
future: _landmarks,
builder: (BuildContext context, AsyncSnapshot<LinkedList<Landmark>> snapshot) {
List<Widget> children;
if (snapshot.hasData) {
children = [landmarksWithSteps(snapshot.data!), saveButton()];
} else if (snapshot.hasError) {
children = <Widget>[
return ListenableBuilder(//<LinkedList<Landmark>>
listenable: widget.trip!,
builder: (BuildContext context, Widget? child) {
Trip trip = widget.trip!;
log("Trip ${trip.uuid} ${trip.landmarks.length} landmarks");
List<Widget> children;
if (trip.uuid == 'pending') {
// the trip is still being fetched from the api
children = [Center(child: CircularProgressIndicator())];
} else if (trip.uuid == 'error') {
children = [
const Icon(
Icons.error_outline,
color: Colors.red,
@@ -42,20 +41,25 @@ class _LandmarksOverviewState extends State<LandmarksOverview> {
),
Padding(
padding: const EdgeInsets.only(top: 16),
child: Text('Error: ${snapshot.error}', style: TextStyle(fontSize: 12)),
child: Text('Error: ${trip.cityName}'),
),
];
} else {
if (trip.landmarks.length <= 1) {
children = [
const Text("No landmarks in this trip"),
];
} else {
children = [Center(child: CircularProgressIndicator())];
children = [
landmarksWithSteps(trip.landmarks),
saveButton(),
];
}
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: children,
),
);
},
),
}
return Column(
children: children,
);
},
);
}
Widget saveButton() => ElevatedButton(
@@ -100,7 +104,7 @@ Widget landmarksWithSteps(LinkedList<Landmark> landmarks) {
);
lkey++;
if (landmark.next != null) {
Widget step = stepBetweenLandmarks(landmark, landmark.next!);
Widget step = stepBetweenLandmarks(landmark);
children.add(step);
}
}
@@ -111,7 +115,7 @@ Widget landmarksWithSteps(LinkedList<Landmark> landmarks) {
}
Widget stepBetweenLandmarks(Landmark before, Landmark after) {
Widget stepBetweenLandmarks(Landmark landmark) {
// This is a simple widget that draws a line between landmark-cards
// It's a vertical dotted line
// Next to the line is the icon for the mode of transport (walking for now) and the estimated time
@@ -134,7 +138,7 @@ Widget stepBetweenLandmarks(Landmark before, Landmark after) {
Column(
children: [
Icon(Icons.directions_walk),
Text("5 min", style: TextStyle(fontSize: 10)),
Text("${landmark.tripTime} min", style: TextStyle(fontSize: 10)),
],
),
Spacer(),
@@ -149,8 +153,5 @@ Widget stepBetweenLandmarks(Landmark before, Landmark after) {
);
}
Future<LinkedList<Landmark>> getLandmarks (Future<Trip>? trip) async {
Trip tripf = await trip!;
return tripf.landmarks;
}

View File

@@ -8,7 +8,7 @@ import 'package:the_widget_marker/the_widget_marker.dart';
class MapWidget extends StatefulWidget {
final Future<Trip>? trip;
final Trip? trip;
MapWidget({
this.trip
@@ -31,8 +31,7 @@ class _MapWidgetState extends State<MapWidget> {
void _onMapCreated(GoogleMapController controller) async {
mapController = controller;
Trip? trip = await widget.trip;
List<double>? newLocation = trip?.landmarks.first.location;
List<double>? newLocation = widget.trip?.landmarks.first.location;
if (newLocation != null) {
CameraUpdate update = CameraUpdate.newLatLng(LatLng(newLocation[0], newLocation[1]));
controller.moveCamera(update);
@@ -48,8 +47,7 @@ class _MapWidgetState extends State<MapWidget> {
void drawLandmarks() async {
// (re)draws landmarks on the map
Trip? trip = await widget.trip;
LinkedList<Landmark>? landmarks = trip?.landmarks;
LinkedList<Landmark>? landmarks = widget.trip?.landmarks;
if (landmarks != null){
for (Landmark landmark in landmarks) {
markers.add(Marker(

View File

@@ -30,7 +30,7 @@ class _TripsOverviewState extends State<TripsOverview> {
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => BasePage(mainScreen: "map", trip: Future.value(trip))
builder: (context) => BasePage(mainScreen: "map", trip: trip)
)
);
},