Closed astraube closed 5 months ago
Hi @astraube,
The only way for this error to occur is if the state of the internal map 'viewer' becomes detached from the state of the internal controller - which could cause the initState
of one to be fired 'without' the other - which should not happen when using FM correctly.
Your fix might fix the issue for you for now, but it likely hides an issue with your usage - are you using it in some kind of other state which is not intialised/disposed of correctly?
Please check you are running the latest version of FM, and try using MapOptions.keepAlive
if necessary. You may also need to try the keep alive mixin on any states above the map.
I'm getting this a lot as well. Not sure how to reproduce though. Sorry I don't have the whole stack but it was on the order of:
Exception caught by widgets library The following LateError was thrown building LayoutBuilder:
LateInitializationError: Field '_interactiveViewerState@1070162146' has already been initialized.
The relevant error-causing widget was: FlutterMap FlutterMap
We need an MRE to reproduce this. Similar issues in the past have been very hard to reproduce.
I would also suggest trying keepAlive
, that might make a difference?
This can apparently be reproduced by providing a UniqueKey
to the FlutterMap
, then rebuilding.
Encountering this as well during a rebuild. This only happened after instantiating the MapController
outside of the widget that used FlutterMap
. The difference in my case is that I am not utilizing keys. FYI The code below results in a different exception/error, but this is essentially the same layout I have in my non-MRE, production app.
This can apparently be reproduced by providing a
UniqueKey
to theFlutterMap
, then rebuilding.
Just curious if anyone has tried this to reproduce, and, if it had the same effect as for me.
If you need to insert your controller into some state management, this is the way I do it:
Define the map controller on the widget with the map, outside of the build
method. i.e. it's only accessible to that widget and map.
Attach the controller to the map as normal.
Use the onMapReady
callback, and set the controller to whichever state you use.
Be careful not to use it when the widget is built, or define the destroy method on the widget and set the state controlled controller to null
If you're not trying to do state management and/or this doesn't work, can you explain what you're trying to do?
In my case, I'm trying to control the map outside of FlutterMap, thus attempting to define the controller outside of the widget class
Ok, so my suggestion above should work. That's what I needed to do as well.
Just wanted to confirm that the method JaffaKetchup suggested worked. This was my implementation:
MapController
in your map widget and pass it to the FlutterMap
widget.MapController
variable somewhere globally accessible. Ex. GlobalHandler.mapController
MapOptions.onMapReady
callback assigns the map controller to GlobalHandler.mapController
FlutterMap
parent widget's overloaded dispose()
where you call MapController.dispose()
, I assign null to GlobalHandler.mapController
just to ensure I'm not using a disposed controller.I'm going to close this for now. I think some careful consideration is required when hooking the controller up to state, but as the above suggestion seems to work fine (and that's part of the purpose of the onMapReady
callback), I think this is all good.
Not sure what the fix would be for my case.
Here is the entirety of how I use MapController:
@override
Widget build(BuildContext context) {
final mapState = Provider.of<MapState>(context);
FlutterMap(
mapController: mapState.map,
And in mapState file:
class MapState extends ChangeNotifier {
final MapController _mapController = MapController();
LatLng _centerPosition = const LatLng(35, -100);
double zoomLevel = 7;
double minZoom = 4;
double maxZoom = 12;
double _imgScale = 1.8;
MapController get map => _mapController;
LatLng get center => _centerPosition;
double get zoom => zoomLevel;
double get imgScale => _imgScale;
}
Wasn't exactly sure how to implement the fix since I'm using provider. Thanks!
This was the same situation I was in.
Follow the guide above. Initialise the MapController
directly in the widget around the map, not in the state provider. Use this controller directly in the map. Then use the onMapReady
callback to add the controller into state at that point only.
Also consider implementing better disposal using the widget lifecycle method if you find yourself inadvertantly using it after disposal.
+ final mapController = MapController();
@override
Widget build(BuildContext context) {
- final mapState = Provider.of<MapState>(context);
FlutterMap(
- mapController: mapState.map,
+ mapController: mapController,
+ options: MapOptions(
+ onMapReady: () {
+ context.read<MapState>().map = mapController; // Can't remember if that's exactly correct or not
+ },
+ ),
class MapState extends ChangeNotifier {
- final MapController _mapController = MapController();
+ MapController? _mapController;
LatLng _centerPosition = const LatLng(35, -100);
double zoomLevel = 7;
double minZoom = 4;
double maxZoom = 12;
double _imgScale = 1.8;
- MapController get map => _mapController;
+ MapController get map => _mapController ?? (throw StateError('Not yet attached to map'));
+ set map(MapController newController) { // Consider also accepting null to implement disposal
+ _mapController = newController;
+ }
LatLng get center => _centerPosition;
double get zoom => zoomLevel;
double get imgScale => _imgScale;
}
I've updated the documentation which should help! https://docs.fleaflet.dev/v/v7-beta/usage/programmatic-interaction/external-custom-controllers#usage-within-a-state-system-model
What is the bug?
Error Message: LateInitializationError: Field '_interactiveViewerState@4245162146' has already been initialized.
LateInitializationError: Field '_interactiveViewerState@4245162146' has already been initialized.
How can we reproduce it?
Do you have a potential solution?
_interactiveViewerState
Platforms
Android 13
Severity
Minimum: Allows normal functioning