From 2b0a75e41497577ab4a4286afa8fd9196d782f0a Mon Sep 17 00:00:00 2001 From: Gereon Elvers Date: Fri, 12 Jan 2024 23:03:54 +0100 Subject: [PATCH] Finish SelectionScreen for now --- assets/img/db-logo.png | Bin 0 -> 25792 bytes assets/img/flix-logo.png | Bin 0 -> 15124 bytes lib/screens/landing_screen.dart | 231 ++++++++++-------- lib/screens/main_screen.dart | 2 +- lib/screens/selection_screen.dart | 100 ++++++-- lib/state/application/application_bloc.dart | 111 ++++++++- lib/util/route.dart | 252 ++++++++++++++++++++ pubspec.lock | 42 +++- pubspec.yaml | 5 + 9 files changed, 609 insertions(+), 134 deletions(-) create mode 100644 assets/img/db-logo.png create mode 100644 assets/img/flix-logo.png create mode 100644 lib/util/route.dart diff --git a/assets/img/db-logo.png b/assets/img/db-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..286a6623299df9536e31ac89c57c6d4a5e58f708 GIT binary patch literal 25792 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 submit(applicationBloc, context), ), - ], + ), ), Padding( - padding: const EdgeInsets.all(8.0), - child: TextField( - textInputAction: TextInputAction.search, - controller: applicationBloc.timeTextController, - onSubmitted: (value) { - applicationBloc.currentScreen = ApplicationScreen.selection; - applicationBloc.add(UpdateScreenEvent()); - }, - decoration: const InputDecoration( - border: OutlineInputBorder(), - labelText: 'On', + padding: const EdgeInsets.only(left: 8.0), + child: GestureDetector( + onTap: () async => showDateTimePicker(applicationBloc, context), + child: Row( + children: [ + const Icon(Icons.calendar_month, size: 22), + Padding( + padding: const EdgeInsets.only(left: 4), + child: Text( + DateFormat("dd.MM, hh:mm a").format(applicationBloc.time), + style: GoogleFonts.inter(textStyle: const TextStyle(fontSize: 16)), + ), + ), + ], ), - onTap: () async { - applicationBloc.time = await showOmniDateTimePicker( - context: context, initialDate: DateTime.now(), firstDate: DateTime.now(), lastDate: DateTime.now().add(const Duration(days: 90))) ?? - DateTime.now(); - applicationBloc.timeTextController.text = DateFormat("dd.mm.yyyy 'after' kk:mm (cccc)").format(applicationBloc.time); - }, ), ), - Padding( - padding: const EdgeInsets.fromLTRB(64, 0, 64, 0), - child: ClipRRect( - borderRadius: BorderRadius.circular(10), - child: Container( - color: Colors.green, - child: IconButton( - icon: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Padding( - padding: EdgeInsets.only(right: 6.0), - child: Icon( - Icons.search, - color: Colors.white, - ), - ), - Text( - "Search", - style: GoogleFonts.inter(textStyle: const TextStyle(color: Colors.white, fontSize: 16)), - ), - ], - ), - onPressed: () { - applicationBloc.currentScreen = ApplicationScreen.selection; - applicationBloc.add(UpdateScreenEvent()); - })), - ), - ) ], ), - ), - ], + Padding( + padding: const EdgeInsets.fromLTRB(64, 8, 64, 0), + child: ClipRRect( + borderRadius: BorderRadius.circular(10), + child: Container( + color: Colors.green, + child: IconButton( + icon: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Padding( + padding: EdgeInsets.only(right: 6.0), + child: Icon( + Icons.search, + color: Colors.white, + ), + ), + Text( + "Search", + style: GoogleFonts.inter(textStyle: const TextStyle(color: Colors.white, fontSize: 16)), + ), + ], + ), + onPressed: () async => submit(applicationBloc, context), + )), + ), + ) + ], + ), ), - ), + ], ), ), - ], + ), ), ], ), @@ -182,7 +171,7 @@ class LandingScreen extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ Text( - "ValueVoyage was created at TUM as part of the BPG seminar. No real insurance shall be provided.", + "ValueVoyage was created at TUM as part of the BPG seminar. No real product or service shall be provided.", style: TextStyle(color: Colors.grey), ) ], @@ -194,4 +183,38 @@ class LandingScreen extends StatelessWidget { ); }); } + + // oof ugly + showDateTimePicker(ApplicationBloc bloc, BuildContext context) async { + bloc.time = await showOmniDateTimePicker( + context: context, + initialDate: DateTime.now(), + firstDate: DateTime.now(), + lastDate: DateTime.now().add(const Duration(days: 90)), + isForce2Digits: true, + minutesInterval: 5) ?? + DateTime.now(); + bloc.add(UpdateScreenEvent()); + } + + submit(ApplicationBloc bloc, BuildContext context) async { + bloc.getMockRoutes(); + if (bloc.originTextController.text != "" && bloc.destinationTextController.text != "") { + bloc.currentScreen = ApplicationScreen.selection; + bloc.add(UpdateScreenEvent()); + } else { + MotionToast( + icon: Icons.error_outline, + displaySideBar: false, + primaryColor: Colors.red, + title: const Text("Error"), + description: const Text("Please fill out all fields."), + position: MotionToastPosition.bottom, + animationType: AnimationType.fromBottom, + animationCurve: Curves.bounceOut, + animationDuration: const Duration(milliseconds: 250), + toastDuration: const Duration(seconds: 2), + ).show(context); + } + } } diff --git a/lib/screens/main_screen.dart b/lib/screens/main_screen.dart index 90d40f6..287189d 100644 --- a/lib/screens/main_screen.dart +++ b/lib/screens/main_screen.dart @@ -60,7 +60,7 @@ class MainScreen extends StatelessWidget { Padding( padding: const EdgeInsets.only(left: 6.0), child: Text( - "Login", + "My Account", style: GoogleFonts.inter(textStyle: const TextStyle(fontSize: 14, color: Colors.white)), ), ) diff --git a/lib/screens/selection_screen.dart b/lib/screens/selection_screen.dart index ab90e23..9bfe317 100644 --- a/lib/screens/selection_screen.dart +++ b/lib/screens/selection_screen.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:intl/intl.dart'; import '../state/application/application_bloc.dart'; import '../util/application_screen.dart'; @@ -12,31 +14,85 @@ class SelectionScreen extends StatelessWidget { ApplicationBloc applicationBloc = BlocProvider.of(context); return BlocBuilder(builder: (context, state) { return Padding( - padding: const EdgeInsets.fromLTRB(64, 96, 64, 32), + padding: const EdgeInsets.fromLTRB(128, 70, 128, 32), child: Card( - color: Colors.white, - child: Column( + color: const Color(0xfff1fffb), + child: Stack( children: [ - Row( - children: [ - Padding( - padding: const EdgeInsets.all(8.0), - child: IconButton(onPressed: (){ - applicationBloc.currentScreen = ApplicationScreen.landing; - applicationBloc.add(UpdateScreenEvent()); - }, icon: const Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Padding( - padding: EdgeInsets.only(right: 6.0), - child: Icon(Icons.arrow_back_rounded), - ), - Text("Zurück") - ], - ),), + Align( + alignment: Alignment.topLeft, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: IconButton( + onPressed: () => applicationBloc.navigateTo(ApplicationScreen.landing), + icon: const Text("← Back"), ), - ], + ), + ), + Align( + alignment: Alignment.topCenter, + child: Column( + children: [ + DefaultTextStyle( + style: GoogleFonts.inter(textStyle: const TextStyle(fontSize: 32, color: Colors.black54)), + child: Padding( + padding: const EdgeInsets.fromLTRB(256, 24, 256, 16), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text("Going from "), + MouseRegion( + cursor: SystemMouseCursors.click, + child: GestureDetector( + onTap: () => applicationBloc.navigateTo(ApplicationScreen.landing), + child: Text( + applicationBloc.originTextController.text, + style: const TextStyle(decoration: TextDecoration.underline, color: Colors.black45), + )), + ), + const Text(" to "), + MouseRegion( + cursor: SystemMouseCursors.click, + child: GestureDetector( + onTap: () => applicationBloc.navigateTo(ApplicationScreen.landing), + child: Text( + applicationBloc.destinationTextController.text, + style: const TextStyle(decoration: TextDecoration.underline, color: Colors.black45), + )), + ), + const Text(" on "), + MouseRegion( + cursor: SystemMouseCursors.click, + child: GestureDetector( + onTap: () => applicationBloc.navigateTo(ApplicationScreen.landing), + child: Text( + DateFormat("dd.MM.yy 'after' hh:mm a").format(applicationBloc.time), + style: const TextStyle(decoration: TextDecoration.underline, color: Colors.black45), + )), + ), + ], + ), + ), + ), + applicationBloc.routes.isEmpty + ? const Align( + alignment: Alignment.center, + child: Padding( + padding: EdgeInsets.all(32.0), + child: CircularProgressIndicator(), + )) + : Expanded( + child: Padding( + padding: const EdgeInsets.only(left: 196, right: 196), + child: GridView.count( + physics: const ScrollPhysics(), + shrinkWrap: true, + childAspectRatio: 18 / 3, + crossAxisCount: 1, + children: applicationBloc.routes), + )) + ], + ), ), ], )), diff --git a/lib/state/application/application_bloc.dart b/lib/state/application/application_bloc.dart index 5121486..a54c391 100644 --- a/lib/state/application/application_bloc.dart +++ b/lib/state/application/application_bloc.dart @@ -1,28 +1,127 @@ +import 'dart:convert'; +import 'dart:math'; + import 'package:flutter/cupertino.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:http/http.dart' as http; import '../../util/application_screen.dart'; +import '../../util/route.dart'; part 'application_event.dart'; + part 'application_state.dart'; class ApplicationBloc extends Bloc { ApplicationScreen currentScreen = ApplicationScreen.landing; + String sessionID = ""; + + List cities = const ["Hamburg", "Berlin", "Munich"]; + List routes = []; + int selectedRoute = 0; + double currentInsuranceValue = 2000; int selectedInsuranceCard = 0; - TextEditingController startTextController = TextEditingController(); TextEditingController originTextController = TextEditingController(); - TextEditingController timeTextController = TextEditingController(); - String origin = "Hamburg"; - String destination = "München"; + TextEditingController destinationTextController = TextEditingController(); DateTime time = DateTime.now(); ApplicationBloc() : super(ApplicationInitial()) { - on((event, emit) { - }); + sessionID = generateRandomString(10); + on((event, emit) {}); on((event, emit) { emit(ApplicationUpdating()); emit(ApplicationInitial()); }); } + + // Using TextControllers for maintaining state is _aweful_ code style lmao + getRoutes() async { + final queryParameters = { + 'origin': originTextController.text, + 'destination': destinationTextController.text, + 'mode': "transit", + 'language': 'EN', + 'departure_time': (time.toUtc().millisecondsSinceEpoch ~/ 1000).toString(), + }; + final uri = Uri.https('valuevoyageserver-production.up.railway.app', '/directions', queryParameters); + final res = await http.get(uri); + final parsed = jsonDecode(res.body); + routes = (parsed['routes'] as List).map((json) => NavigationRoute.fromJson(json)).toList(); + add(UpdateScreenEvent()); + } + + getMockRoutes() { + routes = [ + NavigationRoute( + arrivalTime: DateTime.now().add(const Duration(hours: 6)), + departureTime: DateTime.now(), + startAddress: "Hamburg Central Station", + endAddress: "Munich Central Station", + steps: [ + NavigationRouteStep( + departureStop: "Hamburg Central Station", + arrivalStop: "Berlin Central Station", + departureTime: DateTime.now().add(const Duration(hours: 1, minutes: 5)), + arrivalTime: DateTime.now().add(const Duration(hours: 6, minutes: 12)), + agency: 'Deutsche Bahn', + lineShortName: 'ICE518', + punctuality: 0.95), + NavigationRouteStep( + departureStop: "Berlin Central Station", + arrivalStop: "Munich Central Station", + departureTime: DateTime.now().add(const Duration(hours: 2, minutes: 6)), + arrivalTime: DateTime.now().add(const Duration(hours: 11, minutes: 26)), + agency: 'Flixbus', + lineShortName: 'N150', + punctuality: 0.58), + ], + ), + NavigationRoute( + arrivalTime: DateTime.now().add(const Duration(hours: 6)), + departureTime: DateTime.now(), + startAddress: "Hamburg Central Station", + endAddress: "Munich Central Station", + steps: [ + NavigationRouteStep( + departureStop: "Hamburg Central Station", + arrivalStop: "Berlin Central Station", + departureTime: DateTime.now().add(const Duration(hours: 1, minutes: 5)), + arrivalTime: DateTime.now().add(const Duration(hours: 6, minutes: 12)), + agency: 'Deutsche Bahn', + lineShortName: 'ICE518', + punctuality: 0.95), + NavigationRouteStep( + departureStop: "Berlin Central Station", + arrivalStop: "Munich Central Station", + departureTime: DateTime.now().add(const Duration(hours: 2, minutes: 6)), + arrivalTime: DateTime.now().add(const Duration(hours: 11, minutes: 26)), + agency: 'Flixbus', + lineShortName: 'N150', + punctuality: 0.58), + ], + ), + ]; + add(UpdateScreenEvent()); + } + + autocomplete(String input) async { + // https://maps.googleapis.com/maps/api/place/autocomplete/json?input=Goldacher+Stra&sessiontoken=placeholder + final queryParameters = {'input': input, 'sessiontoken': sessionID, 'language': 'EN'}; + final uri = Uri.https('valuevoyageserver-production.up.railway.app', '/autocomplete', queryParameters); + final res = await http.get(uri); + print("Autocomplete:"); + print(res.statusCode); + print(res.body); + } + + String generateRandomString(int len) { + var r = Random(); + return String.fromCharCodes(List.generate(len, (index) => r.nextInt(33) + 89)); + } + + navigateTo(ApplicationScreen screen) { + currentScreen = screen; + add(UpdateScreenEvent()); + } } diff --git a/lib/util/route.dart b/lib/util/route.dart new file mode 100644 index 0000000..5917b3c --- /dev/null +++ b/lib/util/route.dart @@ -0,0 +1,252 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:intl/intl.dart'; +import 'package:timeline_tile/timeline_tile.dart'; +import 'package:value_voyage/util/application_screen.dart'; + +import '../state/application/application_bloc.dart'; + +class NavigationRoute extends StatelessWidget { + final DateTime arrivalTime; + final DateTime departureTime; + final String startAddress; + final String endAddress; + final List steps; + + const NavigationRoute({ + super.key, + required this.arrivalTime, + required this.departureTime, + required this.startAddress, + required this.endAddress, + required this.steps, + }); + + factory NavigationRoute.fromJson(Map json) { + var legs = json['legs'][0]; + return NavigationRoute( + arrivalTime: DateTime.fromMillisecondsSinceEpoch(legs['arrival_time']['value'] * 1000), + departureTime: DateTime.fromMillisecondsSinceEpoch(legs['departure_time']['value'] * 1000), + startAddress: legs['start_address'], + endAddress: legs['end_address'], + //steps: (legs['steps'] as List).map((step) => NavigationRouteStep.fromJson(step)).toList(), // TODO + steps: [ + NavigationRouteStep( + departureStop: "Hamburg Central Station", + arrivalStop: "Berlin Central Station", + departureTime: DateTime.now().add(const Duration(hours: 1, minutes: 5)), + arrivalTime: DateTime.now().add(const Duration(hours: 6, minutes: 12)), + agency: 'Deutsche Bahn', + lineShortName: 'ICE518', + punctuality: 0.95), + NavigationRouteStep( + departureStop: "Berlin Central Station", + arrivalStop: "Munich Central Station", + departureTime: DateTime.now().add(const Duration(hours: 2, minutes: 6)), + arrivalTime: DateTime.now().add(const Duration(hours: 11, minutes: 26)), + agency: 'Flixbus', + lineShortName: 'N150', + punctuality: 0.58), + ], + ); + } + + @override + Widget build(BuildContext context) { + ApplicationBloc applicationBloc = BlocProvider.of(context); + return SizedBox( + height: 250, + width: 500, + child: Padding( + padding: const EdgeInsets.fromLTRB(64, 4, 64, 4), + child: Card( + shape: RoundedRectangleBorder( + side: BorderSide( + color: Theme.of(context).colorScheme.outline, + ), + borderRadius: const BorderRadius.all(Radius.circular(12)), + ), + elevation: 0, + child: Stack( + children: [ + Align( + alignment: Alignment.topLeft, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Text( + "${DateFormat("hh:mm a").format(departureTime)} - ${DateFormat("hh:mm a").format(arrivalTime)} | ${formatDuration(arrivalTime.difference(departureTime))}", + style: GoogleFonts.inter( + textStyle: const TextStyle(fontSize: 18, color: Colors.grey), + ), + )), + ), + Row( + children: [ + Expanded( + flex: 4, + child: Padding( + padding: const EdgeInsets.all(12.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.center, + children: generateTimeline(), + ), + )), + const VerticalDivider( + color: Colors.black, + width: 5, + ), + Expanded( + flex: 1, + child: Padding( + padding: const EdgeInsets.fromLTRB(24, 4, 24, 4), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text("Punctuality", style: GoogleFonts.inter(textStyle: const TextStyle(fontSize: 16, color: Colors.black45))), + Text("87.00%", style: GoogleFonts.inter(textStyle: const TextStyle(fontSize: 26, color: Colors.black45))), + const SizedBox(height: 8), + Text("Total price", style: GoogleFonts.inter(textStyle: const TextStyle(fontSize: 16, color: Colors.black45))), + Text("99.90€", style: GoogleFonts.inter(textStyle: const TextStyle(fontSize: 26, color: Colors.black45))), + Padding( + padding: const EdgeInsets.all(8.0), + child: ElevatedButton( + onPressed: () { + applicationBloc.navigateTo(ApplicationScreen.booking); + }, + child: const Text("💳 Book"), + ), + ), + ], + ), + )) + ], + ) + ], + ), + ), + ), + ); + } + + String formatDuration(Duration duration) { + int minutes = duration.inMinutes + (duration.inSeconds.remainder(60) >= 30 ? 1 : 0); + int hours = minutes ~/ 60; // Calculate hours, taking into account the rounded minutes + minutes = minutes.remainder(60); // Get the remainder minutes + String twoDigits(int n) => n.toString().padLeft(2, '0'); + return "${twoDigits(hours)}h ${twoDigits(minutes)}min"; + } + + Color getPunctualityColor(double punctuality) { + switch (punctuality) { + case < 0.3: + return Colors.red.shade800; + case < 0.5: + return Colors.orange.shade800; + case < 0.7: + return Colors.yellow.shade800; + default: + return Colors.green.shade800; + } + } + + generateTimeline() { + List tiles = []; + for (NavigationRouteStep step in steps) { + // Add stop + tiles.add(TimelineTile( + axis: TimelineAxis.horizontal, + alignment: TimelineAlign.center, + isFirst: tiles.isEmpty, + endChild: Text(step.departureStop, style: GoogleFonts.inter(textStyle: const TextStyle(fontSize: 16, color: Colors.black87))), + )); + // Add travel + tiles.add(Expanded( + child: TimelineTile( + axis: TimelineAxis.horizontal, + alignment: TimelineAlign.center, + hasIndicator: false, + startChild: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text("${DateFormat("hh:mm").format(step.departureTime)} - ${DateFormat("hh:mm").format(step.arrivalTime)}", + style: GoogleFonts.inter(textStyle: const TextStyle(fontSize: 18, color: Colors.black87))), + ], + ), + endChild: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + (step.agency).contains("Flixbus") + ? Padding( + padding: const EdgeInsets.only(top: 8.0, right: 4), + child: Image(image: AssetImage("assets/img/flix-logo.png"), height: 21), + ) + : Padding( + padding: const EdgeInsets.only(top: 8.0, right: 4), + child: Image(image: AssetImage("assets/img/db-logo.png"), height: 21), + ), + Container( + decoration: BoxDecoration(border: Border.all(color: getPunctualityColor(step.punctuality)), borderRadius: BorderRadius.circular(4)), + child: Text( + "${(step.punctuality * 100).toInt()}%", + style: TextStyle(color: getPunctualityColor(step.punctuality)), + ), + ) + ], + ), + ], + ), + ), + )); + // Add last tile + if (tiles.length == steps.length * 2) { + tiles.add(TimelineTile( + axis: TimelineAxis.horizontal, + alignment: TimelineAlign.center, + isLast: true, + endChild: Text(step.arrivalStop, style: GoogleFonts.inter(textStyle: const TextStyle(fontSize: 16, color: Colors.black87))), + )); + } + } + return tiles; + } +} + +class NavigationRouteStep { + final String departureStop; + final String arrivalStop; + final DateTime departureTime; + final DateTime arrivalTime; + final String agency; + final String lineShortName; + final double punctuality; + + NavigationRouteStep( + {required this.departureStop, + required this.arrivalStop, + required this.departureTime, + required this.arrivalTime, + required this.agency, + required this.lineShortName, + required this.punctuality}); + + factory NavigationRouteStep.fromJson(Map json) { + var transitDetails = json['transit_details']; + var agencyName = transitDetails['line']['agencies'][0]['name']; + var rng = Random(); + return NavigationRouteStep( + departureStop: transitDetails['departure_stop']['name'], + arrivalStop: transitDetails['arrival_stop']['name'], + departureTime: DateTime.fromMillisecondsSinceEpoch(transitDetails['departure_time']['value'] * 1000), + arrivalTime: DateTime.fromMillisecondsSinceEpoch(transitDetails['arrival_time']['value'] * 1000), + agency: agencyName.contains('FlixBus') ? 'FlixBus' : 'Deutsche Bahn', + lineShortName: transitDetails['line']['short_name'], + punctuality: 0.10 + rng.nextDouble() * 0.89); + } +} diff --git a/pubspec.lock b/pubspec.lock index bdf45ee..c8b805d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -25,6 +25,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.11.0" + autocomplete_textfield: + dependency: "direct main" + description: + name: autocomplete_textfield + sha256: "8170e66d381c21623f1cfbb957ab9c6b5a45d9c50a6daac7fc57dbc3ba94abb4" + url: "https://pub.dev" + source: hosted + version: "2.0.1" bloc: dependency: transitive description: @@ -184,8 +192,16 @@ packages: url: "https://pub.dev" source: hosted version: "6.1.0" + google_maps_apis: + dependency: "direct main" + description: + name: google_maps_apis + sha256: "4257287ac35ac26bd626fb25f3de3f00cd5bcbac44a68a4301105ad2ccc28bf9" + url: "https://pub.dev" + source: hosted + version: "1.0.1+1" http: - dependency: transitive + dependency: "direct main" description: name: http sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" @@ -216,6 +232,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.7" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 + url: "https://pub.dev" + source: hosted + version: "4.8.1" lints: dependency: transitive description: @@ -256,6 +280,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.1" + motion_toast: + dependency: "direct main" + description: + name: motion_toast + sha256: "1bdd11696de9151804644d3dadcbcfaa55749db0353aeca150389ecdeb2eaaac" + url: "https://pub.dev" + source: hosted + version: "2.7.10" nested: dependency: transitive description: @@ -413,6 +445,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.0" + timeline_tile: + dependency: "direct main" + description: + name: timeline_tile + sha256: "85ec2023c67137397c2812e3e848b2fb20b410b67cd9aff304bb5480c376fc0c" + url: "https://pub.dev" + source: hosted + version: "2.0.0" typed_data: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index bcbd8b0..29d0af7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -44,6 +44,11 @@ dependencies: omni_datetime_picker: ^1.0.9 intl: ^0.19.0 animated_text_kit: ^4.2.2 + motion_toast: ^2.7.10 + google_maps_apis: ^1.0.1+1 + http: ^1.1.0 + timeline_tile: ^2.0.0 + autocomplete_textfield: ^2.0.1 dev_dependencies: flutter_test: