navigation.dart 3.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. import 'dart:async';
  2. import 'package:flutter/material.dart';
  3. import 'package:geolocator/geolocator.dart';
  4. import 'package:latlong2/latlong.dart';
  5. import 'package:physigo/navigation/models/directions.dart';
  6. import '../services/directions_service.dart';
  7. import 'directions_instruction.dart';
  8. import 'navigation_map.dart';
  9. /// Time in seconds before two consecutive calls to the api
  10. const throttleCallApis = 30;
  11. /// Displays map with directions instruction to go from current position
  12. /// to [destination]
  13. class Navigation extends StatefulWidget {
  14. final LatLng destination;
  15. const Navigation({required this.destination, Key? key}) : super(key: key);
  16. @override
  17. State<Navigation> createState() => _NavigationState();
  18. }
  19. class _NavigationState extends State<Navigation> {
  20. final StreamController<int> _currentWaypointIndexController = StreamController.broadcast();
  21. final StreamController<Directions> _directionsController = StreamController.broadcast();
  22. late DateTime _lastDirectionsCalculation;
  23. Stream<LatLng> get _currentPositionStream {
  24. final currentPositionStream = Geolocator.getPositionStream();
  25. return currentPositionStream.map((position) => LatLng(position.latitude, position.longitude));
  26. }
  27. @override
  28. void initState() {
  29. _lastDirectionsCalculation = DateTime.now();
  30. Geolocator.getCurrentPosition().then((position) {
  31. final currentPosition = LatLng(position.latitude, position.longitude);
  32. _getDirections(currentPosition);
  33. });
  34. super.initState();
  35. }
  36. /// Applies a throttle before getting new directions from [currentPosition] to [widget.destination]
  37. /// to prevent spam
  38. ///
  39. /// If new directions are calculated, returns true
  40. bool _recalculateDirections(LatLng currentPosition) {
  41. if (DateTime.now().difference(_lastDirectionsCalculation).inSeconds > throttleCallApis) {
  42. _getDirections(currentPosition);
  43. setState(() {
  44. _lastDirectionsCalculation = DateTime.now();
  45. });
  46. return true;
  47. }
  48. return false;
  49. }
  50. Future<void> _getDirections(LatLng currentPosition) async {
  51. final directions = await DirectionsService.getDirections(currentPosition, widget.destination);
  52. _directionsController.add(directions);
  53. }
  54. @override
  55. Widget build(BuildContext context) {
  56. return Scaffold(
  57. body: Column(
  58. children: [
  59. NavigationMap(
  60. waypointsCoordinates: _directionsController.stream.map((directions) => directions.waypointsCoordinates),
  61. currentWaypointIndexStream: _currentWaypointIndexController.stream,
  62. ),
  63. StreamBuilder<Directions>(
  64. stream: _directionsController.stream,
  65. builder: (context, snapshot) {
  66. if (snapshot.hasData) {
  67. return DirectionsInstruction(
  68. directions: snapshot.data!,
  69. currentPositionStream: _currentPositionStream,
  70. currentWaypointIndexController: _currentWaypointIndexController,
  71. recalculateDirections: _recalculateDirections,
  72. );
  73. }
  74. return const CircularProgressIndicator();
  75. }),
  76. ],
  77. ),
  78. );
  79. }
  80. @override
  81. void dispose() {
  82. _currentWaypointIndexController.close();
  83. _directionsController.close();
  84. super.dispose();
  85. }
  86. }