import 'dart:async'; import 'package:flutter/material.dart'; import 'package:geolocator/geolocator.dart'; import 'package:latlong2/latlong.dart'; import 'package:physigo/navigation/models/directions.dart'; import '../services/directions_service.dart'; import 'directions_instruction.dart'; import 'navigation_map.dart'; /// Time in seconds before two consecutive calls to the api const throttleCallApis = 30; /// Displays map with directions instruction to go from current position /// to [destination] class Navigation extends StatefulWidget { final LatLng destination; const Navigation({required this.destination, Key? key}) : super(key: key); @override State createState() => _NavigationState(); } class _NavigationState extends State { final StreamController _currentWaypointIndexController = StreamController.broadcast(); final StreamController _directionsController = StreamController.broadcast(); late DateTime _lastDirectionsCalculation; Stream get _currentPositionStream { final currentPositionStream = Geolocator.getPositionStream(); return currentPositionStream.map((position) => LatLng(position.latitude, position.longitude)); } @override void initState() { _lastDirectionsCalculation = DateTime.now(); Geolocator.getCurrentPosition().then((position) { final currentPosition = LatLng(position.latitude, position.longitude); _getDirections(currentPosition); }); super.initState(); } /// Applies a throttle before getting new directions from [currentPosition] to [widget.destination] /// to prevent spam /// /// If new directions are calculated, returns true bool _recalculateDirections(LatLng currentPosition) { if (DateTime.now().difference(_lastDirectionsCalculation).inSeconds > throttleCallApis) { _getDirections(currentPosition); setState(() { _lastDirectionsCalculation = DateTime.now(); }); return true; } return false; } Future _getDirections(LatLng currentPosition) async { final directions = await DirectionsService.getDirections(currentPosition, widget.destination); _directionsController.add(directions); } @override Widget build(BuildContext context) { return Scaffold( body: Column( children: [ NavigationMap( waypointsCoordinates: _directionsController.stream.map((directions) => directions.waypointsCoordinates), currentWaypointIndexStream: _currentWaypointIndexController.stream, ), StreamBuilder( stream: _directionsController.stream, builder: (context, snapshot) { if (snapshot.hasData) { return DirectionsInstruction( directions: snapshot.data!, currentPositionStream: _currentPositionStream, currentWaypointIndexController: _currentWaypointIndexController, recalculateDirections: _recalculateDirections, ); } return const CircularProgressIndicator(); }), ], ), ); } @override void dispose() { _currentWaypointIndexController.close(); _directionsController.close(); super.dispose(); } }