diff --git a/lib/constant/links.dart b/lib/constant/links.dart index f81f131..1da8429 100644 --- a/lib/constant/links.dart +++ b/lib/constant/links.dart @@ -67,6 +67,10 @@ class AppLink { static const String addCarsLocationByPassenger = "$location/add.php"; static const String deleteCarsLocationByPassenger = "$location/delete.php"; static const String updateCarsLocationByPassenger = "$location/update.php"; + static const String getTotalDriverDuration = + "$location/getTotalDriverDuration.php"; + static const String getTotalDriverDurationToday = + "$location/getTotalDriverDurationToday.php"; //==================Blog============= static const String profile = 'https://ride.mobile-app.store/ride/profile'; diff --git a/lib/constant/style.dart b/lib/constant/style.dart index 70aa4ce..5b9a136 100644 --- a/lib/constant/style.dart +++ b/lib/constant/style.dart @@ -36,14 +36,14 @@ class AppStyle { BoxShadow( color: AppColor.accentColor, offset: Offset(-3, -3), - blurRadius: 1, - spreadRadius: 1, + blurRadius: 0, + spreadRadius: 0, blurStyle: BlurStyle.outer), BoxShadow( color: AppColor.accentColor, offset: Offset(3, 3), - blurRadius: 1, - spreadRadius: 1, + blurRadius: 0, + spreadRadius: 0, blurStyle: BlurStyle.outer) ]); } diff --git a/lib/controller/functions/custom_pant.dart b/lib/controller/functions/custom_pant.dart new file mode 100644 index 0000000..8b85ce4 --- /dev/null +++ b/lib/controller/functions/custom_pant.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; + +class LineChartPainter extends CustomPainter { + final List data; + + LineChartPainter(this.data); + + @override + void paint(Canvas canvas, Size size) { + // Calculate the scale factor. + final scaleFactor = size.height / 240; + + // Draw the line chart. + for (var i = 0; i < data.length - 1; i++) { + final x1 = i * size.width / data.length; + final y1 = data[i] * scaleFactor; + final x2 = (i + 1) * size.width / data.length; + final y2 = data[i + 1] * scaleFactor; + + canvas.drawLine(Offset(x1, y1), Offset(x2, y2), Paint()); + } + } + + @override + bool shouldRepaint(LineChartPainter oldDelegate) => false; +} diff --git a/lib/controller/home/captin/LocationService.dart b/lib/controller/home/captin/LocationService.dart deleted file mode 100644 index 8b13789..0000000 --- a/lib/controller/home/captin/LocationService.dart +++ /dev/null @@ -1 +0,0 @@ - diff --git a/lib/controller/home/captin/duration_controller .dart b/lib/controller/home/captin/duration_controller .dart new file mode 100644 index 0000000..e246a3f --- /dev/null +++ b/lib/controller/home/captin/duration_controller .dart @@ -0,0 +1,77 @@ +import 'dart:convert'; + +import 'package:flutter/animation.dart'; +import 'package:get/get.dart'; +import 'package:ride/constant/box_name.dart'; +import 'package:ride/constant/links.dart'; +import 'package:ride/controller/functions/crud.dart'; +import 'package:ride/main.dart'; + +class DurationController extends GetxController + with GetSingleTickerProviderStateMixin { + final data = [].obs; + late AnimationController animationController; + + @override + void onInit() { + super.onInit(); + fetchData(); + animationController = AnimationController( + vsync: this, + duration: const Duration( + milliseconds: 500), // Adjust the animation duration as needed + ); + animationController.forward(); // Start the animation + } + + @override + void onClose() { + animationController.dispose(); + super.onClose(); + } + + Map jsonData = {}; + bool isLoading = false; + String totalDurationToday = ''; + Future fetchData() async { + isLoading = true; + update(); // Notify the observers about the loading state change + + var res = await CRUD().get( + link: AppLink.getTotalDriverDuration, + payload: {'driver_id': box.read(BoxName.driverID)}, + ); + + jsonData = jsonDecode(res); + + final parsedData = parseData(jsonData['message']); + data.value = parsedData; + + isLoading = false; + update(); // Notify the observers about the data and loading state change + } + + List parseData(List json) { + return json.map((entry) { + final Map entryMap = entry; + final day = DateTime.parse(entryMap['day']); + final totalDuration = _parseDuration(entryMap['total_duration']); + return DurationData(day, totalDuration); + }).toList(); + } + + Duration _parseDuration(String durationString) { + final parts = durationString.split(':'); + final hours = int.parse(parts[0]); + final minutes = int.parse(parts[1]); + final seconds = int.parse(parts[2]); + return Duration(hours: hours, minutes: minutes, seconds: seconds); + } +} + +class DurationData { + final DateTime day; + final Duration totalDuration; + + DurationData(this.day, this.totalDuration); +} diff --git a/lib/controller/home/captin/home_captain_controller.dart b/lib/controller/home/captin/home_captain_controller.dart index fcbfbc1..606e05c 100644 --- a/lib/controller/home/captin/home_captain_controller.dart +++ b/lib/controller/home/captin/home_captain_controller.dart @@ -15,7 +15,9 @@ class HomeCaptainController extends GetxController { Duration activeDuration = Duration.zero; Timer? activeTimer; Map data = {}; - String totalToday = '0'; + String totalMoneyToday = '0'; + String totalDurationToday = '0'; + Timer? timer; // Inject the LocationController class final locationController = Get.find(); @@ -55,10 +57,22 @@ class HomeCaptainController extends GetxController { return totalDuration; } + void startPeriodicExecution() { + Timer.periodic(const Duration(seconds: 30), (timer) async { + await getCaptainDurationOnToday(); + }); + } + + void stopTimer() { + print('Stopping timer'); + timer?.cancel(); + } + @override void onInit() async { addToken(); getPaymentToday(); + startPeriodicExecution(); super.onInit(); } @@ -75,13 +89,24 @@ class HomeCaptainController extends GetxController { link: AppLink.getDriverpaymentToday, payload: {'driverID': box.read(BoxName.driverID).toString()}); data = jsonDecode(res); - totalToday = data['message'][0]['total_amount']; + totalMoneyToday = data['message'][0]['total_amount']; + update(); + } + + Future getCaptainDurationOnToday() async { + var res = await CRUD().get( + link: AppLink.getTotalDriverDurationToday, + payload: {'driver_id': box.read(BoxName.driverID).toString()}); + + data = jsonDecode(res); + totalDurationToday = data['message'][0]['total_duration']; update(); } @override void dispose() { activeTimer?.cancel(); + stopTimer(); super.dispose(); } } diff --git a/lib/controller/home/map_passenger_controller.dart b/lib/controller/home/map_passenger_controller.dart index 79343e2..a1b5f22 100644 --- a/lib/controller/home/map_passenger_controller.dart +++ b/lib/controller/home/map_passenger_controller.dart @@ -393,51 +393,47 @@ class MapPassengerController extends GetxController { } Future getCarsLocationByPassenger() async { - if (rideConfirm == false) { - carsLocationByPassenger = []; - LatLngBounds bounds = - calculateBounds(myLocation.latitude, myLocation.longitude, 8000); - print( - 'Southwest: ${bounds.southwest.latitude}, ${bounds.southwest.longitude}'); - print( - 'Northeast: ${bounds.northeast.latitude}, ${bounds.northeast.longitude}'); + // if (rideConfirm == false) { + carsLocationByPassenger = []; + LatLngBounds bounds = + calculateBounds(myLocation.latitude, myLocation.longitude, 8000); + print( + 'Southwest: ${bounds.southwest.latitude}, ${bounds.southwest.longitude}'); + print( + 'Northeast: ${bounds.northeast.latitude}, ${bounds.northeast.longitude}'); - var res = - await CRUD().get(link: AppLink.getCarsLocationByPassenger, payload: { - 'southwestLat': southwest.latitude.toString(), - 'southwestLon': southwest.longitude.toString(), - 'northeastLat': northeast.latitude.toString(), - 'northeastLon': northeast.longitude.toString(), - }); - if (res == 'failure') { - Get.defaultDialog( - title: 'No Car in your site.Sorry!', - middleText: '', - confirm: MyElevatedButton( - title: 'Back', - onPressed: () { - Get.back(); - markerReloadingTimer.cancel(); - })); - } else { - dataCarsLocationByPassenger = jsonDecode(res); - // print(dataCarsLocationByPassenger); - driverId = dataCarsLocationByPassenger['message'][carsOrder] - ['driver_id'] - .toString(); - // print('driverId==============$driverId'); - for (var i = 0; - i < dataCarsLocationByPassenger['message'].length; - i++) { - carsLocationByPassenger.add(LatLng( - double.parse( - dataCarsLocationByPassenger['message'][i]['latitude']), - double.parse( - dataCarsLocationByPassenger['message'][i]['longitude']))); - } - - update(); + var res = + await CRUD().get(link: AppLink.getCarsLocationByPassenger, payload: { + 'southwestLat': southwest.latitude.toString(), + 'southwestLon': southwest.longitude.toString(), + 'northeastLat': northeast.latitude.toString(), + 'northeastLon': northeast.longitude.toString(), + }); + if (res == 'failure') { + Get.defaultDialog( + title: 'No Car in your site.Sorry!', + middleText: '', + confirm: MyElevatedButton( + title: 'Back', + onPressed: () { + Get.back(); + markerReloadingTimer.cancel(); + })); + } else { + dataCarsLocationByPassenger = jsonDecode(res); + // print(dataCarsLocationByPassenger); + driverId = dataCarsLocationByPassenger['message'][carsOrder]['driver_id'] + .toString(); + // print('driverId==============$driverId'); + for (var i = 0; i < dataCarsLocationByPassenger['message'].length; i++) { + carsLocationByPassenger.add(LatLng( + double.parse(dataCarsLocationByPassenger['message'][i]['latitude']), + double.parse( + dataCarsLocationByPassenger['message'][i]['longitude']))); } + + update(); + // } } } @@ -771,67 +767,72 @@ class MapPassengerController extends GetxController { void getNearestDriverByPassengerLocation() async { if (polyLines.isEmpty || data.isEmpty) { - double nearestDistance = double.infinity; - for (var i = 0; i < dataCarsLocationByPassenger['message'].length; i++) { - var carLocation = dataCarsLocationByPassenger['message'][i]; + if (rideConfirm == false) { + double nearestDistance = double.infinity; + for (var i = 0; + i < dataCarsLocationByPassenger['message'].length; + i++) { + var carLocation = dataCarsLocationByPassenger['message'][i]; - // double distance1 = Geolocator.distanceBetween( - // mylocation.latitude, - // mylocation.longitude, - // double.parse(carLocation['latitude']), - // double.parse(carLocation['longitude']), - // ); - // if (distance1 < nearestDistance) { - // nearestDistance = distance1; - // // nearestCarLocation = carLocation; - // nearestCar = CarLocation( - // distance: distance1, - // id: carLocation['driver_id'], - // latitude: double.parse(carLocation['latitude']), - // longitude: double.parse(carLocation['longitude']), - // ); - // } - // isloading = true; - update(); - // Make API request to get exact distance and duration - String apiUrl = - '${AppLink.googleMapsLink}distancematrix/json?destinations=${carLocation['latitude']},${carLocation['longitude']}&origins=${myLocation.latitude},${myLocation.longitude}&units=metric&key=${AppCredintials.mapAPIKEY}'; - var response = await CRUD().getGoogleApi(link: apiUrl, payload: {}); - if (response['status'] == "OK") { - var data = response; - // Extract distance and duration from the response and handle accordingly - int distance1 = data['rows'][0]['elements'][0]['distance']['value']; - distanceByPassenger = - data['rows'][0]['elements'][0]['distance']['text']; - duration1 = data['rows'][0]['elements'][0]['duration']['value']; - - durationFromDriverToPassenger = Duration(seconds: duration1.toInt()); - newTime1 = currentTime.add(durationFromDriverToPassenger); - timeFromDriverToPassenger = - newTime1.add(Duration(minutes: 2.toInt())); - durationByPassenger = - data['rows'][0]['elements'][0]['duration']['text']; + // double distance1 = Geolocator.distanceBetween( + // mylocation.latitude, + // mylocation.longitude, + // double.parse(carLocation['latitude']), + // double.parse(carLocation['longitude']), + // ); + // if (distance1 < nearestDistance) { + // nearestDistance = distance1; + // // nearestCarLocation = carLocation; + // nearestCar = CarLocation( + // distance: distance1, + // id: carLocation['driver_id'], + // latitude: double.parse(carLocation['latitude']), + // longitude: double.parse(carLocation['longitude']), + // ); + // } + // isloading = true; update(); - if (distance1 < nearestDistance) { - nearestDistance = distance1.toDouble(); + // Make API request to get exact distance and duration + String apiUrl = + '${AppLink.googleMapsLink}distancematrix/json?destinations=${carLocation['latitude']},${carLocation['longitude']}&origins=${myLocation.latitude},${myLocation.longitude}&units=metric&key=${AppCredintials.mapAPIKEY}'; + var response = await CRUD().getGoogleApi(link: apiUrl, payload: {}); + if (response['status'] == "OK") { + var data = response; + // Extract distance and duration from the response and handle accordingly + int distance1 = data['rows'][0]['elements'][0]['distance']['value']; + distanceByPassenger = + data['rows'][0]['elements'][0]['distance']['text']; + duration1 = data['rows'][0]['elements'][0]['duration']['value']; - nearestCar = CarLocation( - distance: distance1.toDouble(), - duration: duration1.toDouble(), - id: carLocation['driver_id'], - latitude: double.parse(carLocation['latitude']), - longitude: double.parse(carLocation['longitude']), - ); - // isloading = false; + durationFromDriverToPassenger = + Duration(seconds: duration1.toInt()); + newTime1 = currentTime.add(durationFromDriverToPassenger); + timeFromDriverToPassenger = + newTime1.add(Duration(minutes: 2.toInt())); + durationByPassenger = + data['rows'][0]['elements'][0]['duration']['text']; update(); - } - } + if (distance1 < nearestDistance) { + nearestDistance = distance1.toDouble(); - // Handle the distance and duration as needed - else { - print( - 'Failed to retrieve distance and duration: ${response['status']}'); - // Handle the failure case + nearestCar = CarLocation( + distance: distance1.toDouble(), + duration: duration1.toDouble(), + id: carLocation['driver_id'], + latitude: double.parse(carLocation['latitude']), + longitude: double.parse(carLocation['longitude']), + ); + // isloading = false; + update(); + } + } + + // Handle the distance and duration as needed + else { + print( + 'Failed to retrieve distance and duration: ${response['status']}'); + // Handle the failure case + } } } } diff --git a/lib/views/Rate/ride_calculate_driver.dart b/lib/views/Rate/ride_calculate_driver.dart index 8f09ef8..c3ab8d9 100644 --- a/lib/views/Rate/ride_calculate_driver.dart +++ b/lib/views/Rate/ride_calculate_driver.dart @@ -1,12 +1,126 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:ride/constant/colors.dart'; +import 'package:ride/constant/info.dart'; +import 'package:ride/constant/style.dart'; import 'package:ride/views/widgets/my_scafold.dart'; +import 'package:ride/views/widgets/mycircular.dart'; +import 'package:flutter/animation.dart'; +import '../../controller/home/captin/duration_controller .dart'; class RideCalculateDriver extends StatelessWidget { const RideCalculateDriver({super.key}); @override Widget build(BuildContext context) { - return MyScafolld(title: 'Ride Summary'.tr, body: [], isleading: true); + return MyScafolld( + title: 'Ride Summary'.tr, + body: const [ + Center( + child: BarChartWidget(), + ), + ], + isleading: true); + } +} + +class BarChartWidget extends StatelessWidget { + const BarChartWidget({super.key}); + + @override + Widget build(BuildContext context) { + final durationController = Get.put(DurationController()); + return Obx(() { + final data = durationController.data; + double maxDuration = 0; + + // Find the maximum duration to determine the maximum height of the bars + for (final entry in data) { + final durationInHours = entry.totalDuration.inHours.toDouble(); + if (durationInHours > maxDuration) { + maxDuration = durationInHours; + } + } + + return durationController.isLoading + ? const Center( + child: MyCircularProgressIndicator(), + ) + : Column( + children: [ + Text( + 'Average of Hours of ${AppInfo.appName} is ON for this month' + .tr, + style: AppStyle.title, + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + height: Get.height * .7, + decoration: AppStyle.boxDecoration, + child: ListView.builder( + scrollDirection: Axis.horizontal, + itemCount: data.length, + itemBuilder: (context, index) { + final entry = data[index]; + final durationInHours = + entry.totalDuration.inHours.toDouble(); + final dayLabel = entry.day.day.toString(); + + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: AnimatedBuilder( + animation: durationController.animationController, + builder: (context, child) { + final animationValue = + durationController.animationController.value; + final animatedValue = + (durationInHours / maxDuration) * + animationValue; + + return Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Transform( + transform: Matrix4.identity() + ..setEntry(3, 2, + 0.001) // Apply perspective for a 3D effect + ..rotateX(-0.5 * + animatedValue), // Rotate around X-axis + alignment: Alignment.bottomCenter, + child: Container( + width: 20, + height: + (durationInHours / maxDuration) * 400, + color: durationInHours > 8 + ? AppColor.greenColor + : AppColor.yellowColor, + child: Center( + child: Text( + durationInHours.toStringAsFixed( + 0, + ), // Display the duration value inside the bar + style: const TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ), + const SizedBox(height: 4), + Text(dayLabel), + ], + ); + }, + ), + ); + }, + ), + ), + ), + ], + ); + }); } } diff --git a/lib/views/home/Captin/home_captin.dart b/lib/views/home/Captin/home_captin.dart index 4bb23bf..82d1020 100644 --- a/lib/views/home/Captin/home_captin.dart +++ b/lib/views/home/Captin/home_captin.dart @@ -8,6 +8,7 @@ import 'package:ride/constant/table_names.dart'; import 'package:ride/controller/home/captin/home_captain_controller.dart'; import 'package:ride/controller/home/captin/order_request_controller.dart'; import 'package:ride/main.dart'; +import 'package:ride/views/Rate/ride_calculate_driver.dart'; import 'package:ride/views/widgets/circle_container.dart'; import 'package:ride/views/widgets/elevated_btn.dart'; import 'package:flutter_font_icons/flutter_font_icons.dart'; @@ -115,7 +116,7 @@ class HomeCaptain extends StatelessWidget { Text( ' You Earn today is '.tr + homeCaptainController - .totalToday, //Todo add here number for income + .totalMoneyToday, //Todo add here number for income style: AppStyle.title, ), ], @@ -131,15 +132,14 @@ class HomeCaptain extends StatelessWidget { ), Text( 'Total Duration:'.tr + - ' ${homeCaptainController.calculateTotalDuration()} seconds', + ' ${homeCaptainController.totalDurationToday} ', style: const TextStyle(fontSize: 20), ), TextButton( onPressed: () { - // homeCaptainController.sendSMSToRecipents( - // 'hi from Sefer', ['+962798583052']); + Get.to(() => RideCalculateDriver()); }, - child: const Text('send msg')), + child: const Text('Chart')), const Wrap( children: [ Icon(AntDesign.facebook_square), diff --git a/lib/views/home/map_widget.dart/payment_method.page.dart b/lib/views/home/map_widget.dart/payment_method.page.dart index 05dd0df..627db1e 100644 --- a/lib/views/home/map_widget.dart/payment_method.page.dart +++ b/lib/views/home/map_widget.dart/payment_method.page.dart @@ -176,7 +176,8 @@ class CreditCardWidget extends StatelessWidget { inputFormatters: [DigitObscuringFormatter()], validator: (value) { if (value!.isEmpty || value.length != 16) { - return 'Please enter a valid 16-digit card number'; + return 'Please enter a valid 16-digit card number' + .tr; } return null; }, diff --git a/lib/views/home/profile/passenger_profile_page.dart b/lib/views/home/profile/passenger_profile_page.dart index 430a9ba..c4a54cb 100644 --- a/lib/views/home/profile/passenger_profile_page.dart +++ b/lib/views/home/profile/passenger_profile_page.dart @@ -232,11 +232,11 @@ class EducationDegreePicker extends StatelessWidget { final ProfileController controller = Get.put(ProfileController()); final List degreeOptions = [ - 'High School Diploma', - 'Associate Degree', - 'Bachelor\'s Degree', - 'Master\'s Degree', - 'Doctoral Degree', + 'High School Diploma'.tr, + 'Associate Degree'.tr, + 'Bachelor\'s Degree'.tr, + 'Master\'s Degree'.tr, + 'Doctoral Degree'.tr, ]; EducationDegreePicker({Key? key}) : super(key: key); diff --git a/lib/views/orderCaptin/order_request_page.dart b/lib/views/orderCaptin/order_request_page.dart index f6778b0..418641c 100644 --- a/lib/views/orderCaptin/order_request_page.dart +++ b/lib/views/orderCaptin/order_request_page.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:ride/constant/box_name.dart'; import 'package:ride/controller/firebase/firbase_messge.dart'; -import 'package:ride/controller/home/captin/map_driver_controller.dart'; import 'package:ride/main.dart'; import 'package:ride/views/home/Captin/driver_map_page.dart'; import 'package:ride/views/widgets/my_scafold.dart'; @@ -10,7 +9,6 @@ import 'package:ride/views/widgets/my_scafold.dart'; import '../../constant/colors.dart'; import '../../constant/links.dart'; import '../../constant/style.dart'; -import '../../constant/table_names.dart'; import '../../controller/functions/crud.dart'; import '../../controller/functions/launch.dart'; import '../../controller/home/captin/order_request_controller.dart'; @@ -28,7 +26,7 @@ class OrderRequestPage extends StatelessWidget { final body = arguments['body']; orderRequestController.startTimer(myList[6].toString(), body.toString()); return MyScafolld( - title: 'Order Request Page', + title: 'Order Request Page'.tr, body: [ Column( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/pubspec.lock b/pubspec.lock index 3809a99..26ff0e1 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -145,6 +145,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.8" + decimal: + dependency: transitive + description: + name: decimal + sha256: "24a261d5d5c87e86c7651c417a5dbdf8bcd7080dd592533910e8d0505a279f21" + url: "https://pub.dev" + source: hosted + version: "2.3.3" fake_async: dependency: transitive description: @@ -214,6 +222,14 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_charts: + dependency: "direct main" + description: + name: flutter_charts + sha256: eb9d2bf98bfc779c1da9cf1fe80afd598094aed33e536cf04845d8f266f48c11 + url: "https://pub.dev" + source: hosted + version: "0.5.2" flutter_font_icons: dependency: "direct main" description: @@ -632,6 +648,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.2.0" + logger: + dependency: transitive + description: + name: logger + sha256: "6bbb9d6f7056729537a4309bda2e74e18e5d9f14302489cc1e93f33b3fe32cac" + url: "https://pub.dev" + source: hosted + version: "2.0.2+1" lottie: dependency: "direct main" description: @@ -760,6 +784,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.2.1" + rational: + dependency: transitive + description: + name: rational + sha256: ba58e9e18df9abde280e8b10051e4bce85091e41e8e7e411b6cde2e738d357cf + url: "https://pub.dev" + source: hosted + version: "2.2.2" sanitize_html: dependency: transitive description: @@ -861,6 +893,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.9.2" + tuple: + dependency: transitive + description: + name: tuple + sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151 + url: "https://pub.dev" + source: hosted + version: "2.0.2" typed_data: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index a50de5e..c9b7281 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -38,6 +38,7 @@ dependencies: crypto: ^3.0.3 flutter_rating_bar: ^4.0.1 flutter_font_icons: ^2.2.5 + flutter_charts: ^0.5.2 dev_dependencies: