Flutter Provider中检测Consumer数据更新及经纬度缓冲实现
Hey there! Let's break down your two requirements with practical code adjustments based on your existing setup:
1. Detecting When Consumer Receives Updated Data
The ChangeNotifier + Consumer setup triggers a rebuild of the Consumer's builder whenever notifyListeners() is called in your Provider. To explicitly track when data updates, you have two straightforward approaches:
Option A: Track Changes Directly in the Consumer Builder
In your widget's state class, add variables to store the last seen coordinates, then compare them to new values in the Consumer:
// Inside your State class double? _lastLat; double? _lastLang; // Then in your Consumer builder Consumer<PuhserDataProvider>(builder: (context, data, child) { if (data.devicePusherData != null) { final lat = extractLat("${data.devicePusherData.gps}"); final lang = extractLang("${data.devicePusherData.gps}"); // Check if coordinates have changed if (lat != _lastLat || lang != _lastLang) { log.w("Coordinates updated: $lat, $lang"); _lastLat = lat; _lastLang = lang; } return GoogleMap(/* ... your existing map code ... */); } else { return Container( height: MediaQuery.of(context).size.height * 0.8, width: double.infinity, child: const Center(child: CircularProgressIndicator()), ); } })
Option B: Optimize Provider to Avoid Unnecessary Notifications
Modify the Provider to only call notifyListeners() when GPS data actually changes—this reduces redundant rebuilds and acts as an update tracker:
void _setDeviceData(DevicePusherData devicePusherData) { final newGps = devicePusherData.gps; final oldGps = _devicePusherData?.gps; // Only update and notify if GPS data has changed if (newGps != oldGps) { this._devicePusherData = devicePusherData; log.w("New valid GPS received: $newGps"); notifyListeners(); } }
2. Adding 5-Time Validation Buffer for LatLng Coordinates
To ensure you only update the Google Map after 5 consistent GPS readings, add a buffer queue in your PuhserDataProvider to track recent values and validate them before notifying listeners.
Modified Provider Code with Validation Buffer
import 'dart:convert'; import 'package:pusher_client/pusher_client.dart'; // Import your model classes (DevicePusherData, OBDPuhserData) here class PuhserDataProvider extends ChangeNotifier { final Pusher pusher; Logger log = getLogger("PuhserDataProvider"); DevicePusherData? _devicePusherData; DevicePusherData? get devicePusherData => _devicePusherData; OBDPuhserData? _obdPusherData; OBDPuhserData? get obdPusherData => _obdPusherData; // Buffer to store up to 5 recent GPS readings final List<String> _gpsValidationBuffer = []; static const int _requiredValidations = 5; PuhserDataProvider(String imei, String token, String pusherKey) : pusher = Pusher( pusherKey, PusherOptions( cluster: 'eu', authEndpoint: AUTH_URL, auth: PusherAuth(headers: { 'Authorization': 'Bearer $token', 'Content-Type': 'application/json', 'Accept': 'application/json' })) ) { Channel channel = pusher.subscribe('private-$imei-send'); channel.bind('obd-event', (data) => _setOBDData(OBDPuhserData.fromJson(json.decode(data)[0]))); channel.bind('deviceevent', (data) => _setDeviceData(DevicePusherData.fromJson(json.decode(data)[0]))); } void _setDeviceData(DevicePusherData newDeviceData) { final newGps = newDeviceData.gps; _gpsValidationBuffer.add(newGps); // Keep buffer limited to 5 entries if (_gpsValidationBuffer.length > _requiredValidations) { _gpsValidationBuffer.removeAt(0); } // Check if all 5 entries in the buffer match the latest GPS final allReadingsMatch = _gpsValidationBuffer.every((gps) => gps == newGps); // Only update and notify if we have 5 consistent readings if (_gpsValidationBuffer.length == _requiredValidations && allReadingsMatch) { _devicePusherData = newDeviceData; log.w("GPS validated after 5 checks: $newGps"); notifyListeners(); // Clear buffer to start fresh for next validation cycle _gpsValidationBuffer.clear(); } } void _setOBDData(OBDPuhserData obdPusherData) { this._obdPusherData = obdPusherData; notifyListeners(); } }
How This Works:
- Every new
deviceeventadds the GPS string to a buffer. - Once the buffer has 5 entries, we check if all are identical (adjust to proximity checks if minor variations are acceptable).
- If 5 consistent readings are confirmed, we update the Provider data and trigger the Consumer to rebuild the Google Map.
- The buffer resets after successful validation to start fresh for the next cycle.
Optional: Proximity Check Instead of Exact Matches
If you need to allow minor coordinate variations, replace the allReadingsMatch check with a distance threshold (e.g., 5 meters):
// Add a helper to calculate distance between LatLng points double _calculateDistance(LatLng a, LatLng b) { // Use a package like geolocator for accurate distance calculation return Geolocator.distanceBetween(a.latitude, a.longitude, b.latitude, b.longitude); } // In _setDeviceData: final newLat = extractLat(newGps); final newLng = extractLang(newGps); final newLatLng = LatLng(newLat, newLng); final allReadingsCloseEnough = _gpsValidationBuffer.every((gps) { final lat = extractLat(gps); final lng = extractLang(gps); return _calculateDistance(LatLng(lat, lng), newLatLng) <= 5; // 5-meter threshold });
内容的提问来源于stack exchange,提问作者Javeed Ishaq

