chore(wip): upgrade dependencies, begin refactor
This commit is contained in:
23
frontend/lib/presentation/widgets/create_trip_location.dart
Normal file
23
frontend/lib/presentation/widgets/create_trip_location.dart
Normal file
@@ -0,0 +1,23 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:your_project/models/trip_request.dart';
|
||||
import 'package:your_project/presentation/widgets/new_trip_map.dart';
|
||||
import 'package:your_project/presentation/widgets/new_trip_location_search.dart';
|
||||
|
||||
class CreateTripLocation extends StatelessWidget {
|
||||
final TripRequest tripRequest;
|
||||
|
||||
const CreateTripLocation(this.tripRequest, {Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
children: [
|
||||
NewTripMap(tripRequest),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(15),
|
||||
child: NewTripLocationSearch(tripRequest),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
105
frontend/lib/presentation/widgets/create_trip_location_map.dart
Normal file
105
frontend/lib/presentation/widgets/create_trip_location_map.dart
Normal file
@@ -0,0 +1,105 @@
|
||||
// A map that allows the user to select a location for a new trip.
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||
import 'package:widget_to_marker/widget_to_marker.dart';
|
||||
|
||||
import 'package:anyway/constants.dart';
|
||||
|
||||
import 'package:anyway/structs/trip.dart';
|
||||
import 'package:anyway/structs/landmark.dart';
|
||||
import 'package:anyway/modules/landmark_map_marker.dart';
|
||||
|
||||
|
||||
class NewTripMap extends StatefulWidget {
|
||||
|
||||
@override
|
||||
State<NewTripMap> createState() => _NewTripMapState();
|
||||
}
|
||||
|
||||
class _NewTripMapState extends State<NewTripMap> {
|
||||
final CameraPosition _cameraPosition = const CameraPosition(
|
||||
target: LatLng(48.8566, 2.3522),
|
||||
zoom: 11.0,
|
||||
);
|
||||
GoogleMapController? _mapController;
|
||||
final Set<Marker> _markers = <Marker>{};
|
||||
|
||||
_onLongPress(LatLng location) {
|
||||
// widget.trip.landmarks.clear();
|
||||
// widget.trip.addLandmark(
|
||||
// Landmark(
|
||||
// uuid: 'pending',
|
||||
// name: 'start',
|
||||
// location: [location.latitude, location.longitude],
|
||||
// type: typeStart
|
||||
// )
|
||||
// );
|
||||
}
|
||||
|
||||
updateTripDetails() async {
|
||||
_markers.clear();
|
||||
if (widget.trip.landmarks.isNotEmpty) {
|
||||
Landmark landmark = widget.trip.landmarks.first;
|
||||
_markers.add(
|
||||
Marker(
|
||||
markerId: MarkerId(landmark.uuid),
|
||||
position: LatLng(landmark.location[0], landmark.location[1]),
|
||||
icon: await ThemedMarker(landmark: landmark, position: 0).toBitmapDescriptor(
|
||||
logicalSize: const Size(150, 150),
|
||||
imageSize: const Size(150, 150)
|
||||
),
|
||||
)
|
||||
);
|
||||
// check if the controller is ready
|
||||
|
||||
if (_mapController != null) {
|
||||
_mapController!.animateCamera(
|
||||
CameraUpdate.newLatLng(
|
||||
LatLng(landmark.location[0], landmark.location[1])
|
||||
)
|
||||
);
|
||||
}
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
|
||||
void _onMapCreated(GoogleMapController controller) async {
|
||||
_mapController = controller;
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
widget.trip.addListener(updateTripDetails);
|
||||
Future<SharedPreferences> preferences = SharedPreferences.getInstance();
|
||||
|
||||
|
||||
return FutureBuilder(
|
||||
future: preferences,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
SharedPreferences prefs = snapshot.data as SharedPreferences;
|
||||
bool useLocation = prefs.getBool('useLocation') ?? true;
|
||||
return _buildMap(useLocation);
|
||||
} else {
|
||||
return const CircularProgressIndicator();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMap(bool useLocation) {
|
||||
return GoogleMap(
|
||||
onMapCreated: _onMapCreated,
|
||||
initialCameraPosition: _cameraPosition,
|
||||
onLongPress: _onLongPress,
|
||||
markers: _markers,
|
||||
cloudMapId: MAP_ID,
|
||||
mapToolbarEnabled: false,
|
||||
zoomControlsEnabled: false,
|
||||
myLocationButtonEnabled: false,
|
||||
myLocationEnabled: useLocation,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
|
||||
// A search bar that allow the user to enter a city name
|
||||
import 'dart:developer';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:geocoding/geocoding.dart';
|
||||
|
||||
import 'package:geolocator/geolocator.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
const Map<String, List> debugLocations = {
|
||||
'paris': [48.8575, 2.3514],
|
||||
'london': [51.5074, -0.1278],
|
||||
'new york': [40.7128, -74.0060],
|
||||
'tokyo': [35.6895, 139.6917],
|
||||
};
|
||||
|
||||
|
||||
|
||||
class NewTripLocationSearch extends StatefulWidget {
|
||||
Future<SharedPreferences> prefs = SharedPreferences.getInstance();
|
||||
Trip trip;
|
||||
|
||||
NewTripLocationSearch(
|
||||
this.trip,
|
||||
);
|
||||
|
||||
|
||||
@override
|
||||
State<NewTripLocationSearch> createState() => _NewTripLocationSearchState();
|
||||
}
|
||||
|
||||
class _NewTripLocationSearchState extends State<NewTripLocationSearch> {
|
||||
final TextEditingController _controller = TextEditingController();
|
||||
|
||||
setTripLocation (String query) async {
|
||||
List<Location> locations = [];
|
||||
Location startLocation;
|
||||
log('Searching for: $query');
|
||||
if (GeocodingPlatform.instance != null) {
|
||||
locations.addAll(await locationFromAddress(query));
|
||||
}
|
||||
|
||||
if (locations.isNotEmpty) {
|
||||
startLocation = locations.first;
|
||||
} else {
|
||||
log('No results found for: $query. Is geocoding available?');
|
||||
log('Setting Fallback location');
|
||||
List coordinates = debugLocations[query.toLowerCase()] ?? [48.8575, 2.3514];
|
||||
startLocation = Location(
|
||||
latitude: coordinates[0],
|
||||
longitude: coordinates[1],
|
||||
timestamp: DateTime.now(),
|
||||
);
|
||||
}
|
||||
|
||||
widget.trip.landmarks.clear();
|
||||
widget.trip.addLandmark(
|
||||
Landmark(
|
||||
uuid: 'pending',
|
||||
name: query,
|
||||
location: [startLocation.latitude, startLocation.longitude],
|
||||
type: typeStart
|
||||
)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
late Widget locationSearchBar = SearchBar(
|
||||
hintText: 'Enter a city name or long press on the map.',
|
||||
onSubmitted: setTripLocation,
|
||||
controller: _controller,
|
||||
leading: const Icon(Icons.search),
|
||||
trailing: [
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
setTripLocation(_controller.text);
|
||||
},
|
||||
child: const Text('Search'),
|
||||
)
|
||||
]
|
||||
);
|
||||
|
||||
|
||||
late Widget useCurrentLocationButton = ElevatedButton(
|
||||
onPressed: () async {
|
||||
// this widget is only shown if the user has already granted location permissions
|
||||
Position position = await Geolocator.getCurrentPosition();
|
||||
widget.trip.landmarks.clear();
|
||||
widget.trip.addLandmark(
|
||||
Landmark(
|
||||
uuid: 'pending',
|
||||
name: 'start',
|
||||
location: [position.latitude, position.longitude],
|
||||
type: typeStart
|
||||
)
|
||||
);
|
||||
},
|
||||
child: const Text('Use current location'),
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureBuilder(
|
||||
future: widget.prefs,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
final useLocation = snapshot.data!.getBool('useLocation') ?? false;
|
||||
if (useLocation) {
|
||||
return Column(
|
||||
children: [
|
||||
locationSearchBar,
|
||||
useCurrentLocationButton,
|
||||
],
|
||||
);
|
||||
} else {
|
||||
return locationSearchBar;
|
||||
}
|
||||
} else {
|
||||
return locationSearchBar;
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
121
frontend/lib/presentation/widgets/onbarding_agreement_card.dart
Normal file
121
frontend/lib/presentation/widgets/onbarding_agreement_card.dart
Normal file
@@ -0,0 +1,121 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_markdown_plus/flutter_markdown_plus.dart';
|
||||
import 'package:anyway/presentation/providers/onboarding_state_provider.dart';
|
||||
import 'package:anyway/presentation/widgets/onboarding_card.dart';
|
||||
|
||||
|
||||
|
||||
class OnboardingAgreementCard extends ConsumerStatefulWidget {
|
||||
final String title;
|
||||
final String description;
|
||||
final String imagePath;
|
||||
final String agreementTextPath;
|
||||
final ValueChanged<bool> onAgreementChanged;
|
||||
|
||||
|
||||
const OnboardingAgreementCard({
|
||||
super.key,
|
||||
required this.title,
|
||||
required this.description,
|
||||
required this.imagePath,
|
||||
required this.agreementTextPath,
|
||||
required this.onAgreementChanged
|
||||
});
|
||||
|
||||
@override
|
||||
ConsumerState<OnboardingAgreementCard> createState() => _OnboardingAgreementCardState();
|
||||
|
||||
}
|
||||
|
||||
class _OnboardingAgreementCardState extends ConsumerState<OnboardingAgreementCard> {
|
||||
|
||||
// @override
|
||||
// void initState() {
|
||||
// super.initState();
|
||||
// // You can use ref here if needed in initState
|
||||
// }
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
OnboardingCard(title: widget.title, description: widget.description, imagePath: widget.imagePath),
|
||||
const Padding(padding: EdgeInsets.only(top: 20)),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
// The checkbox of the agreement
|
||||
FutureBuilder(
|
||||
future: ref.read(onboardingStateProvider.future),
|
||||
builder: (context, snapshot) {
|
||||
bool agreed = false;
|
||||
if (snapshot.hasData) {
|
||||
agreed = snapshot.data!;
|
||||
}
|
||||
return Checkbox(
|
||||
value: agreed,
|
||||
onChanged: (bool? value) {
|
||||
if (value != null) {
|
||||
widget.onAgreementChanged(value);
|
||||
}
|
||||
},
|
||||
activeColor: Colors.white,
|
||||
checkColor: Colors.black,
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
// The text of the agreement
|
||||
Text(
|
||||
"I agree to the ",
|
||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
|
||||
// The clickable text of the agreement that shows the agreement text
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
// show a dialog with the agreement text
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
scrollable: true,
|
||||
content: FutureBuilder(
|
||||
future: DefaultAssetBundle.of(context).loadString(widget.agreementTextPath),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
return MarkdownBody(
|
||||
data: snapshot.data.toString(),
|
||||
);
|
||||
} else {
|
||||
return const CircularProgressIndicator();
|
||||
}
|
||||
|
||||
},
|
||||
)
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
},
|
||||
child: Text(
|
||||
"Terms of Service (click to view)",
|
||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
45
frontend/lib/presentation/widgets/onboarding_card.dart
Normal file
45
frontend/lib/presentation/widgets/onboarding_card.dart
Normal file
@@ -0,0 +1,45 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
|
||||
class OnboardingCard extends StatelessWidget {
|
||||
final String title;
|
||||
final String description;
|
||||
final String imagePath;
|
||||
|
||||
const OnboardingCard({super.key,
|
||||
required this.title,
|
||||
required this.description,
|
||||
required this.imagePath,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: Theme.of(context).textTheme.headlineLarge!.copyWith(
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
const Padding(padding: EdgeInsets.only(top: 20)),
|
||||
SvgPicture.asset(
|
||||
imagePath,
|
||||
height: 200,
|
||||
),
|
||||
const Padding(padding: EdgeInsets.only(top: 20)),
|
||||
Text(
|
||||
description,
|
||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
]
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user