frontend groundwork
Some checks failed
Build and release APK / Build APK (pull_request) Has been cancelled
Build and push docker image / Build (pull_request) Successful in 1m33s

This commit is contained in:
2024-08-01 14:39:15 +02:00
parent 07dde5ab58
commit 86bcec6b29
16 changed files with 417 additions and 168 deletions

View File

@@ -1,9 +1,10 @@
import 'dart:collection';
import 'package:flutter/material.dart';
import 'package:anyway/structs/landmark.dart';
import 'package:anyway/structs/trip.dart';
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:the_widget_marker/the_widget_marker.dart';
class MapWidget extends StatefulWidget {
@@ -25,6 +26,7 @@ class _MapWidgetState extends State<MapWidget> {
zoom: 11.0,
);
Set<Marker> markers = <Marker>{};
final GlobalKey globalKey = GlobalKey();
void _onMapCreated(GoogleMapController controller) async {
@@ -49,28 +51,81 @@ class _MapWidgetState extends State<MapWidget> {
Trip? trip = await widget.trip;
LinkedList<Landmark>? landmarks = trip?.landmarks;
if (landmarks != null){
setState(() {
for (Landmark landmark in landmarks) {
markers.add(Marker(
markerId: MarkerId(landmark.name),
position: LatLng(landmark.location[0], landmark.location[1]),
infoWindow: InfoWindow(title: landmark.name, snippet: landmark.type.name),
));
}
});
for (Landmark landmark in landmarks) {
markers.add(Marker(
markerId: MarkerId(landmark.name),
position: LatLng(landmark.location[0], landmark.location[1]),
// infoWindow: InfoWindow(title: landmark.name, snippet: landmark.type.name),
icon: await MarkerIcon.widgetToIcon(globalKey),
));
}
setState(() {});
}
}
@override
Widget build(BuildContext context) {
return GoogleMap(
onMapCreated: _onMapCreated,
initialCameraPosition: _cameraPosition,
onCameraIdle: _onCameraIdle,
// onLongPress: ,
markers: markers,
cloudMapId: '41c21ac9b81dbfd8',
return Stack(
children: [
MyMarker(globalKey),
GoogleMap(
onMapCreated: _onMapCreated,
initialCameraPosition: _cameraPosition,
onCameraIdle: _onCameraIdle,
// onLongPress: ,
markers: markers,
cloudMapId: '41c21ac9b81dbfd8',
)
]
);
}
}
class MyMarker extends StatelessWidget {
// declare a global key and get it trough Constructor
MyMarker(this.globalKeyMyWidget);
final GlobalKey globalKeyMyWidget;
@override
Widget build(BuildContext context) {
// This returns an outlined circle, with an icon corresponding to the landmark type
// As a small dot, the number of the landmark is displayed in the top right
return RepaintBoundary(
key: globalKeyMyWidget,
child: Stack(
children: [
Container(
width: 75,
height: 75,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [Colors.red, Colors.yellow]
),
shape: BoxShape.circle,
border: Border.all(color: Colors.black, width: 5),
),
child: Icon(Icons.location_on, color: Colors.black, size: 50),
),
Positioned(
top: 0,
right: 0,
child: Container(
padding: EdgeInsets.all(5),
decoration: BoxDecoration(
color: Theme.of(context).primaryColor,
shape: BoxShape.circle,
),
child: Text('1', style: TextStyle(color: Colors.white, fontSize: 20)),
),
),
],
),
);
}
}

View File

@@ -1,5 +1,11 @@
import 'package:anyway/layout.dart';
import 'package:anyway/structs/preferences.dart';
import 'package:anyway/utils/fetch_trip.dart';
import 'package:flutter/material.dart';
import "package:anyway/structs/trip.dart";
class NewTripPage extends StatefulWidget {
const NewTripPage({Key? key}) : super(key: key);
@@ -9,22 +15,71 @@ class NewTripPage extends StatefulWidget {
}
class _NewTripPageState extends State<NewTripPage> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final TextEditingController latController = TextEditingController();
final TextEditingController lonController = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('New Trip'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'Create a new trip',
body: Form(
key: _formKey,
child:
Padding(
padding: const EdgeInsets.all(15.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
TextFormField(
decoration: const InputDecoration(hintText: 'Lat'),
controller: latController,
validator: (String? value) {
if (value == null || value.isEmpty || double.tryParse(value) == null){
return 'Please enter a floating point number';
}
return null;
},
),
TextFormField(
decoration: const InputDecoration(hintText: 'Lon'),
controller: lonController,
validator: (String? value) {
if (value == null || value.isEmpty || double.tryParse(value) == null){
return 'Please enter a floating point number';
}
return null;
},
),
Divider(height: 15, color: Colors.transparent),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
List<double> startPoint = [
double.parse(latController.text),
double.parse(lonController.text)
];
UserPreferences preferences = UserPreferences();
preferences.load();
Future<Trip> trip = fetchTrip(startPoint, preferences);
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => BasePage(mainScreen: "map", trip: trip)
)
);
}
},
child: const Text('Create trip'),
),
],
),
],
),
),
)
)
);
}
}
}

View File

@@ -1,14 +0,0 @@
import "package:anyway/structs/landmark.dart";
class Route {
final String name;
final Duration duration;
final List<Landmark> landmarks;
Route({
required this.name,
required this.duration,
required this.landmarks
});
}

View File

@@ -1,54 +0,0 @@
import "package:anyway/structs/landmark.dart";
import "package:anyway/structs/linked_landmarks.dart";
import 'package:dio/dio.dart';
final dio = Dio();
// Future<List<Landmark>> fetchLandmarks() async {
// // final response = await http
// // .get(Uri.parse('https://nav.kluster.moll.re/v1/destination/1'));
// // if (response.statusCode == 200) {
// // If the server did return a 200 OK response,
// // then parse the JSON.
// List<Landmark> landmarks = [
// // 48°5129.6″N 2°1740.2″E
// Landmark(
// name: "Eiffel Tower",
// location: [48.51296, 2.17402],
// type: LandmarkType(name: "Tower"),
// imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a8/Tour_Eiffel_Wikimedia_Commons.jpg/1037px-Tour_Eiffel_Wikimedia_Commons.jpg"
// ),
// Landmark(
// name: "Notre Dame Cathedral",
// location: [48.8530, 2.3498],
// type: LandmarkType(name: "Monument"),
// imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/f/f7/Notre-Dame_de_Paris%2C_4_October_2017.jpg/440px-Notre-Dame_de_Paris%2C_4_October_2017.jpg"
// ),
// Landmark(
// name: "Louvre palace",
// location: [48.8606, 2.3376],
// type: LandmarkType(name: "Museum"),
// imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/6/66/Louvre_Museum_Wikimedia_Commons.jpg/540px-Louvre_Museum_Wikimedia_Commons.jpg"
// ),
// Landmark(
// name: "Pont-des-arts",
// location: [48.5130, 2.2015],
// type: LandmarkType(name: "Bridge"),
// imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/d/d1/Pont_des_Arts%2C_6e_Arrondissement%2C_Paris_%28HDR%29_20140320_1.jpg/560px-Pont_des_Arts%2C_6e_Arrondissement%2C_Paris_%28HDR%29_20140320_1.jpg"),
// Landmark(
// name: "Panthéon",
// location: [48.5046, 2.2046],
// type: LandmarkType(name: "Monument"),
// imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/8/80/Pantheon_of_Paris_007.JPG/1280px-Pantheon_of_Paris_007.JPG"
// ),
// ];
// // sleep 10 seconds
// await Future.delayed(Duration(seconds: 5));
// return landmarks;
// // } else {
// // // If the server did not return a 200 OK response,
// // // then throw an exception.
// // throw Exception('Failed to load destination');
// // }
// }

View File

@@ -0,0 +1,45 @@
import 'package:dio/dio.dart';
import 'package:anyway/constants.dart';
import "package:anyway/structs/landmark.dart";
import "package:anyway/structs/trip.dart";
import "package:anyway/structs/preferences.dart";
import "package:anyway/structs/linked_landmarks.dart";
Dio dio = Dio(
BaseOptions(
baseUrl: API_URL_BASE,
connectTimeout: const Duration(seconds: 5),
receiveTimeout: const Duration(seconds: 60),
// api is notoriously slow
// headers: {
// HttpHeaders.userAgentHeader: 'dio',
// 'api': '1.0.0',
// },
contentType: Headers.jsonContentType,
responseType: ResponseType.json,
),
);
Future<Trip> fetchTrip(
List<double> startPoint,
UserPreferences preferences,
) async {
final response = await dio.post(
"/trip/new",
data: {
// 'preferences': preferences.toJson(),
'start': [48,2.3]
}
);
// handle errors
if (response.statusCode != 200) {
throw Exception('Failed to load trip');
}
if (response.data["error"] != null) {
throw Exception(response.data["error"]);
}
return Trip.fromJson(response.data);
}