anyway/frontend/lib/modules/landmarks_overview.dart
Remy Moll 71d9554d97
All checks were successful
Build and push docker image / Build (pull_request) Successful in 1m48s
Build and release APK / Build APK (pull_request) Successful in 4m51s
ui improvements for trips and landmarks
2024-08-05 10:18:00 +02:00

169 lines
5.1 KiB
Dart

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';
class LandmarksOverview extends StatefulWidget {
final Trip? trip;
const LandmarksOverview({super.key, this.trip});
@override
State<LandmarksOverview> createState() => _LandmarksOverviewState();
}
class _LandmarksOverviewState extends State<LandmarksOverview> {
// final Future<List<Landmark>> _landmarks = fetchLandmarks();
@override
Widget build(BuildContext context) {
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,
size: 60,
),
Padding(
padding: const EdgeInsets.only(top: 16),
child: Text('Error: ${trip.cityName}'),
),
];
} else {
if (trip.landmarks.length <= 1) {
children = [
const Text("No landmarks in this trip"),
];
} else {
children = [
landmarksWithSteps(),
saveButton(),
];
}
}
return Column(
children: children,
);
},
);
}
Widget saveButton() => ElevatedButton(
onPressed: () async {
Trip? trip = await widget.trip;
SharedPreferences prefs = await SharedPreferences.getInstance();
trip?.toPrefs(prefs);
},
child: const Text('Save'),
);
Widget landmarksWithSteps() {
return ListenableBuilder(
listenable: widget.trip!,
builder: (BuildContext context, Widget? child) {
List<Widget> children = [];
for (Landmark landmark in widget.trip!.landmarks) {
children.add(
Dismissible(
key: ValueKey<int>(landmark.hashCode),
child: LandmarkCard(landmark),
dismissThresholds: {DismissDirection.endToStart: 0.6},
onDismissed: (direction) {
// Remove the item from the data source.
log(landmark.name);
setState(() {
widget.trip!.removeLandmark(landmark);
});
// Then show a snackbar.
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text("We won't show ${landmark.name} again")));
},
background: Container(color: Colors.red),
secondaryBackground: Container(
color: Colors.red,
child: Icon(
Icons.delete,
color: Colors.white,
),
padding: EdgeInsets.all(15),
alignment: Alignment.centerRight,
),
)
);
if (landmark.next != null) {
Widget step = stepBetweenLandmarks(landmark, landmark.next!);
children.add(step);
}
}
return Column(
children: children
);
},
);
}
}
Widget stepBetweenLandmarks(Landmark current, Landmark next) {
// 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
// There is also a button to open the navigation instructions as a new intent
// next landmark is not actually required, but it ensures that the widget is deleted when the next landmark is removed (which makes sense, because then there will be another step)
int timeRounded = 5 * (current.tripTime?.inMinutes ?? 0) ~/ 5;
// ~/ is integer division (rounding)
return Container(
margin: EdgeInsets.all(10),
padding: EdgeInsets.all(10),
decoration: BoxDecoration(
border: Border(
left: BorderSide(width: 3.0, color: Colors.black),
),
// gradient: LinearGradient(
// begin: Alignment.topLeft,
// end: Alignment.bottomRight,
// colors: [Colors.grey, Colors.white, Colors.white],
// ),
),
child: Row(
children: [
Column(
children: [
Icon(Icons.directions_walk),
Text("~$timeRounded min", style: TextStyle(fontSize: 10)),
],
),
Spacer(),
ElevatedButton(
onPressed: () {
// Open navigation instructions
},
child: Row(
children: [
Icon(Icons.directions),
Text("Directions"),
],
),
)
],
),
);
}