fleaflet / flutter_map

A versatile mapping package for Flutter. Simple and easy to learn, yet completely customizable and configurable, it's the best choice for mapping in your Flutter app.
https://pub.dev/packages/flutter_map
BSD 3-Clause "New" or "Revised" License
2.76k stars 861 forks source link

LateInitializationError for MapState in map.dart #926

Closed SebastianEngel closed 2 years ago

SebastianEngel commented 3 years ago

I just upgraded to version 0.13.1 which solved already some LateInitializationErrors.

Unfortunately I just found another one in map.dart.

late final MapState _state;

which is final but can be set to another value again:

set state(MapState state) {
  _state = state;
  if (!_readyCompleter.isCompleted) {
    _readyCompleter.complete();
  }
}
kengu commented 3 years ago

Manually tested all features in the example app without hitting this error. So I don't think it is a bug that is hit often?

Ciock commented 3 years ago

I keep encountering this problem too

jexme commented 3 years ago

I confirm, this problem also constantly arises

LateInitializationError: Field '_state @ 1495051772' has already been initialized

ibrierley commented 3 years ago

Can anyone do a minimal example to reproduce ? It would be good to know what circumstances create it.

jexme commented 3 years ago

Can anyone do a minimal example to reproduce ? It would be good to know what circumstances create it.

I have a problem when I pass the mapController, if not pass it - no problem.

The error occurs when assigning state to mapController (flutter_map-0.13.1 / lib / src / map / flutter_map_state.dart: 41)

Screen of data at the first entry (no errors): image

Screen of data on subsequent visits, after this error: image

stack:

#0      LateError._throwFieldAlreadyInitialized (dart:_internal-patch/internal_patch.dart:203:5)
#1      MapControllerImpl._state= (package:flutter_map/src/map/map.dart:16:23)
#2      MapControllerImpl.state= (package:flutter_map/src/map/map.dart:26:5)
#3      FlutterMapState.initState (package:flutter_map/src/map/flutter_map_state.dart:41:19)
#4      StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:4711:57)
#5      ComponentElement.mount (package:flutter/src/widgets/framework.dart:4548:5)
#6      Element.inflateWidget (package:flutter/src/widgets/framework.dart:3611:14)
#7      Element.updateChild (package:flutter/src/widgets/framework.dart:3360:20)
#8      ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4599:16)
#9      StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4746:11)
#10     Element.rebuild (package:flutter/src/widgets/framework.dart:4267:5)
#11     StatefulElement.update (package:flutter/src/widgets/framework.dart:4778:5)
#12     Element.updateChild (package:flutter/src/widgets/framework.dart:3350:15)
#13     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4599:16)
#14     StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4746:11)
#15     Element.rebuild (package:flutter/src/widgets/framework.dart:4267:5)
#16     BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2582:33)
#17     WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:875:21)
#18     RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:328:5)
#19     SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1144:15)
#20     SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1082:9)
#21     SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:998:5)
#22     _rootRun (dart:async/zone.dart:1354:13)
#23     _CustomZone.run (dart:async/zone.dart:1258:19)
#24     _CustomZone.runGuarded (dart:async/zone.dart:1162:7)
#25     _invoke (dart:ui/hooks.dart:163:10)
#26     PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:259:5)
#27     _drawFrame (dart:ui/hooks.dart:126:31)
ibrierley commented 3 years ago

Is this happening after navigation return or something, and the mapController isn't getting reset whilst the map is maybe ?

On Wed, Jun 9, 2021 at 3:03 PM Aleksey Sannikov @.***> wrote:

Can anyone do a minimal example to reproduce ? It would be good to know what circumstances create it.

I have a problem when I pass the mapController, if not pass it - no problem.

The error occurs when assigning state to mapController (flutter_map-0.13.1 / lib / src / map / flutter_map_state.dart: 41)

Screen of data at the first entry (no errors): [image: image] https://user-images.githubusercontent.com/22722008/121369024-91751900-c94c-11eb-82da-5b0e6d26dbd6.png

Screen of data on subsequent visits, after this error: [image: image] https://user-images.githubusercontent.com/22722008/121369135-a6ea4300-c94c-11eb-8d70-99acf340f127.png

stack:

0 LateError._throwFieldAlreadyInitialized (dart:_internal-patch/internal_patch.dart:203:5)

1 MapControllerImpl._state= (package:flutter_map/src/map/map.dart:16:23)

2 MapControllerImpl.state= (package:flutter_map/src/map/map.dart:26:5)

3 FlutterMapState.initState (package:flutter_map/src/map/flutter_map_state.dart:41:19)

4 StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:4711:57)

5 ComponentElement.mount (package:flutter/src/widgets/framework.dart:4548:5)

6 Element.inflateWidget (package:flutter/src/widgets/framework.dart:3611:14)

7 Element.updateChild (package:flutter/src/widgets/framework.dart:3360:20)

8 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4599:16)

9 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4746:11)

10 Element.rebuild (package:flutter/src/widgets/framework.dart:4267:5)

11 StatefulElement.update (package:flutter/src/widgets/framework.dart:4778:5)

12 Element.updateChild (package:flutter/src/widgets/framework.dart:3350:15)

13 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4599:16)

14 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4746:11)

15 Element.rebuild (package:flutter/src/widgets/framework.dart:4267:5)

16 BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2582:33)

17 WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:875:21)

18 RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:328:5)

19 SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1144:15)

20 SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1082:9)

21 SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:998:5)

22 _rootRun (dart:async/zone.dart:1354:13)

23 _CustomZone.run (dart:async/zone.dart:1258:19)

24 _CustomZone.runGuarded (dart:async/zone.dart:1162:7)

25 _invoke (dart:ui/hooks.dart:163:10)

26 PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:259:5)

27 _drawFrame (dart:ui/hooks.dart:126:31)

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/fleaflet/flutter_map/issues/926#issuecomment-857721954, or unsubscribe https://github.com/notifications/unsubscribe-auth/AA5YN5IN5ALYDGVSILBHJCTTR5YDTANCNFSM46JL6QUA .

jexme commented 3 years ago

@ibrierley in my case it happens after removing the marker (for further rendering of the new marker), which shows the current location

I redraw the map constantly, when new geolocation data comes in, it turns out that the layers are just updating

return FlutterMap(
  options: MapOptions(
    minZoom: minZoom,
    maxZoom: maxZoom,
    center: currentPosition.latLng,
  ),
  layers: _layerOptions,
  mapController: mapController,
);
ibrierley commented 3 years ago

That sounds weird, well, as mentioned, if someone can produce a test case to highlight it (so it can be known if its fixed or not), it would be useful.

kuhnroyal commented 3 years ago

This happens when you pass a mapController to FlutterMap and the widget tree changes in a way that the map state can not be found again after the change or you cause it manually by using a key on FlutterMap.

My case is something like this:

        FlutterMap(
          key: ValueKey(MediaQuery.of(context).orientation),
          mapController: mapController,
       )

The MapState is recreated when the orientation changes but the controller stays the same and is already initialized.

jexme commented 3 years ago

In my case, I found the problem. I got a problem when I was adding a route (updated layers). Before adding the route, I called the CircularProgressIndicator display, thereby destroying the FlitterMap, and then re-creating the map. Perhaps I created a new map until the previous one had time to pass the destruction, but this is just an assumption.

As a result, I think that this is my problem.

kuhnroyal commented 3 years ago

@jexme In your case the widget tree changes probably also lead to the state being re-created. The controller should be able to be used with different instances of FlutterMap.

Ciock commented 3 years ago

Any news on this issue? I'm stuck with null safety update and this bug.

ibrierley commented 3 years ago

Can you post a minimal example highlighting the problem so it can be reproduced ?

kuhnroyal commented 3 years ago

Can you post a minimal example highlighting the problem so it can be reproduced ?

I already posted this, just add the key and turn the device on the side.

        FlutterMap(
          key: ValueKey(MediaQuery.of(context).orientation),
          mapController: mapController,
       )
ibrierley commented 3 years ago

Does switching "late final MapState _state;" for "late MapState _state;" in map.dart fix it ?

kuhnroyal commented 3 years ago

No, that throws:

flutter: Bad state: Cannot add new events after calling close
flutter: --------------------------------------------------------------------------
flutter: dart:async                                                        _StreamSinkWrapper.add
flutter: package:flutter_map/src/map/map.dart 222:19                       MapState.emitMapEvent
flutter: package:flutter_map/src/map/map.dart 208:7                        MapState._handleMoveEmit
flutter: package:flutter_map/src/map/map.dart 302:5                        MapState.move
flutter: package:flutter_map/src/map/map.dart 41:19                        MapControllerImpl.move

Also it is MapControllerImpl.state that is the problem. I removed final there to test this.

ibrierley commented 3 years ago

Doesn't on mine, however, I suspect you are trying to do something like a mapcontroller move to fluttermap which you have removed from the widget tree, by changing it's key. That's a slightly different problem (and hence why proper examples are useful).

Ciock commented 3 years ago

I managed to resolve the issue for my case.

I was using the same MapController for two different FlutterMap and that was causing the problem only on the second map widget built.

Using new MapController for each FlutterMap resolved the issue for me.

Ciock commented 3 years ago

I believe the problem lies in the controller being initialized outside of MapOptions.

the correct way to initialize the controller should be:

MapController controller;

Widget build(BuildContext context) {
return FlutterMap(
  mapController: controller,
  options: MapOptions(
    onMapCreated: (c) {
      controller = c;
    }
  ),
 );
}
sm2017 commented 3 years ago

@johnpryan I have the same issue after upgrading to 0.13.1 , I have a button that let user to switch in map and list view, As I want to keep map state the controller reserved between toggles Please run the following code and press the FAB 2 times

import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final MapController _mapController = MapController();
  bool showMap = true;

  void _toggleShowMap() {
    setState(() {
      showMap = !showMap;
    });
  }

  @override
  Widget build(BuildContext context) {
    final mapWidget = FlutterMap(
      mapController: _mapController,
      options: MapOptions(
        zoom: 13.0,
      ),
      layers: [
        TileLayerOptions(
            urlTemplate: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
            subdomains: ['a', 'b', 'c']),
      ],
    );

    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: showMap ? mapWidget : Text('List view here!'),
      floatingActionButton: FloatingActionButton(
        onPressed: _toggleShowMap,
        tooltip: 'Toggle map',
        child: Icon(showMap ? Icons.toggle_off : Icons.toggle_on),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}
ibrierley commented 3 years ago

I do think the whole mapController thing could do with a bit of reworking in flutter_map (I sometimes wonder if it's even needed), but in the meantime, I've got around this by using Offstage widgets. Put flutter_map inside that, and just mark it offstage depending on 'showMap". That will keep flutter_map in the widget tree and preserve state (hence avoiding that error).

sm2017 commented 3 years ago

Is there any updates?

ElectrifyPowr commented 3 years ago

I do think the whole mapController thing could do with a bit of reworking in flutter_map (I sometimes wonder if it's even needed), but in the meantime, I've got around this by using Offstage widgets. Put flutter_map inside that, and just mark it offstage depending on 'showMap". That will keep flutter_map in the widget tree and preserve state (hence avoiding that error).

The mapController is certainly relevant. I've used it in conjunction with the geolocator package to show the current user position:

mapController.move(LatLng(lat, long), currentZoom);

However, I'm getting the same error as @SebastianEngel when calling the move function when it reloads the widget.

@ibrierley your proposed fix for offstaging the FlutterMap widget does not quite work if you have dynamic layers. For instance, I'm using MarkerLayerOptions where I have custom markers that should be shown on the map, which should be updated depending on the situation. The markers are final, which means you cannot change them after the FlutterMap has been initialized. Changing the markers is only possible when reloading the FlutterMap.

ibrierley commented 3 years ago

If you have dynamic layers, why mark them as final ? (I suspect I'm misunderstanding something here, which is why code is always good).

QuinnFreedman commented 3 years ago

I get this error when I call MapController.move(...). Debugging the code, it looks like map.dart:25 | set state(MapState state) { ... does get called, but then when I call map.dart:40 | move() the value of _state has either been set back to uninitialized or the whole MapControllerImpl has been swapped out for a different one.

If you are passing in a MapController as a parameter to MapOptions, are you supposed to set the state on that controller yourself first? If so, how?

I am using version 0.13.1

ibrierley commented 3 years ago

Have you taken flutter_map out of the widget tree (i.e navigated away from flutter_map) but not the mapController ? Have you tried having them both in the same widget that gets removed together ?

QuinnFreedman commented 3 years ago

Yes, it happens even when the MapController is a on the state of the same widget that draws the FlutterMap. They should never be leaving the widget tree. It looks like this:

class MapView extends StatefulWidget {
  final LatLng? center;

  const MapView({
    this.center,
    Key? key,
  }) : super(key: key);

  @override
  _MapViewState createState() => _MapViewState();
}

class _MapViewState extends State<MapView> {
  final MapController _mapController = MapController();

  @override
  void didUpdateWidget(MapView oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (widget.center != null && widget.center != oldWidget.center) {
      _mapController.move(widget.center!, 9);
    }
  }

  @override
  Widget build(BuildContext context) {
    return FlutterMap(
      options: MapOptions(
        bounds: _getBounds(),
        controller: _mapController,
      ),
      children: [
        TileLayerWidget(
          options: TileLayerOptions(
            urlTemplate: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
            subdomains: ['a', 'b', 'c'],
          ),
        ),
      ],
    );
  }
}

I tried moving the MapController out to the parent and just directly calling move() when I wanted to move it (instead of changing center) but that got the exact same results. My stack trace was:

════════ Exception caught by widgets library ═══════════════════════════════════
The following LateError was thrown building Expanded(flex: 1):
LateInitializationError: Field '_state@282051772' has not been initialized.

The relevant error-causing widget was
Expanded
package:prosuite/screens/map.dart:125
When the exception was thrown, this was the stack
#0      MapControllerImpl._state (package:flutter_map/src/map/map.dart)
package:flutter_map/…/map/map.dart:1
#1      MapControllerImpl.move
package:flutter_map/…/map/map.dart:41
#2      _MapViewState.didUpdateWidget
package:prosuite/screens/map.dart:224
#3      StatefulElement.update
package:flutter/…/widgets/framework.dart:4682
#4      Element.updateChild
package:flutter/…/widgets/framework.dart:3293
...
════════════════════════════════════════════════════════════════════════════════
ibrierley commented 3 years ago

I think your mapController should be a param to FlutterMap, not to MapOptions ?

child: FlutterMap(
    mapController: _mapController,

I'd also be tempted to init mapController in an initState method.

QuinnFreedman commented 3 years ago

Yes, it seems like my only issue was setting the map controller in the options instead of the FlutterMap. Is there any reason to have a controller field in the options? Is there any time when you would want to set that?

ibrierley commented 3 years ago

In the absence of any other replies, I have no idea, and I find the docs insufficient on this and the whole mapController thing, these seem to be common trip up points, and it's difficult often to give proper help without knowing the correct intention and design for these things (I'd add to the docs if I knew myself!).

vonqo commented 3 years ago

Flutter Map was placed under multiple tab view widgets. Then i got this error every time when i move the map .

[debug] Capture from onError LateInitializationError: Field '_lastRotation@1126256441' has already been initialized.

======== Exception caught by gesture ===============================================================
The following LateError was thrown while handling a gesture:
LateInitializationError: Field '_lastRotation@1126256441' has already been initialized.

When the exception was thrown, this was the stack: 
#0      LateError._throwFieldAlreadyInitialized (dart:_internal-patch/internal_patch.dart:203:5)
#1      MapGestureMixin._lastRotation= (package:flutter_map/src/gestures/gestures.dart:36:21)
#2      MapGestureMixin.handleScaleUpdate (package:flutter_map/src/gestures/gestures.dart:428:5)
#3      ScaleGestureRecognizer._advanceStateMachine.<anonymous closure> (package:flutter/src/gestures/scale.dart:494:18)
#4      GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:182:24)
...
Handler: "onUpdate"
Recognizer: ScaleGestureRecognizer#64ce7
  debugOwner: GestureDetector
====================================================================================================
ibrierley commented 3 years ago

My guess is that flutter_map (or the map_controller) is being moved out of the widget tree and back in again, and they have got out of sync.

RonanLB commented 3 years ago

I had the same issue with a TabView containing a map in one of the pages. Navigating back and forth between pages would throw this error.

Embedding the map into its own stateful widget helped getting around this issue

maxbuchan commented 3 years ago

I had the same issue with a TabView containing a map in one of the pages. Navigating back and forth between pages would throw this error.

Embedding the map into its own stateful widget helped getting around this issue

This worked for me as well, thanks @RonanLB !

SeCo89 commented 3 years ago

I had the same issue with a TabView containing a map in one of the pages. Navigating back and forth between pages would throw this error.

Embedding the map into its own stateful widget helped getting around this issue

I have the same issue in a bottom tab view. What did you do exactly? Did you create a stateful widget for the flutter map? Thanks in advance.

arsamme commented 3 years ago

I got this too.

LateInitializationError: Field '_state@902051772' has already been initialized.

raycastsant commented 3 years ago

I had the same issue with a TabView containing a map in one of the pages. Navigating back and forth between pages would throw this error. Embedding the map into its own stateful widget helped getting around this issue

This worked for me as well, thanks @RonanLB !

I'm having the same problem recently. I'm using flutter_maps 0.14.0 and flutter 2.5.1. Could you give an example of the solution you found? Thanks in advance.

einaraglen commented 3 years ago

Init the MapController inside of IniState() with a setState(), then set your uninitialized global variable

MapController mapController;
  @override
  void initState() {
    super.initState();
    setState(() {
      mapController = MapController();
      globals.mapController = mapController;
    });
  }
raycastsant commented 3 years ago

Thanks for the reply. In fact, I am using a provider with StatelessWidget. I initialize mapController in the provider and then I get it in the widget where I show the map. LateInitializationError was displayed when on the web I tried to resize the screen. What I did was re-create a new instance of the mapController when I detected changes from a large screen size to a slimmer or vice versa one, and voila.

laurentguenver-a commented 3 years ago

Hello, any fix ?

ibrierley commented 3 years ago

Can we see your basic code, and exact error.

laurentguenver-a commented 3 years ago

I have a toggle button (Switch list/Map) with one Map controller. The error appear when the FlutterMap is build more than one.

LateInitializationError: Field '_state@902051772' has already been initialized.

Thanks

AhmedHussein22 commented 3 years ago

I believe the problem lies in the controller being initialized outside of MapOptions.

the correct way to initialize the controller should be:

MapController controller;

Widget build(BuildContext context) {
return FlutterMap(
  mapController: controller,
  options: MapOptions(
    onMapCreated: (c) {
      controller = c;
    }
  ),
 );
}

its work now ... thanks

Vandor1 commented 3 years ago

@ibrierley in my case it happens after removing the marker (for further rendering of the new marker), which shows the current location

I redraw the map constantly, when new geolocation data comes in, it turns out that the layers are just updating

return FlutterMap(
  options: MapOptions(
    minZoom: minZoom,
    maxZoom: maxZoom,
    center: currentPosition.latLng,
  ),
  layers: _layerOptions,
  mapController: mapController,
);

Adding "mapController: mapController" where the mapController is initialized as in the example provided by flutter_maps example mapController worked for me. (removed the not initialized problem) Thanks!

hallelk commented 2 years ago

I've also had those out of nowhere it seems. somehow the controller is not initialized when the MapOptions controller is checking the zoom. when I change the dependency to the local copy I cloned of flutter_map - i suddenly works again. cleaned and moved back to - flutter_map: ^0.14.0, and I get "null check operator used on a null value".

laurentguenver-a commented 2 years ago

Hi, Someone has a fix ?

JaffaKetchup commented 2 years ago

Not really, but use this as a rough guide:

magnuswikhog commented 2 years ago

@JaffaKetchup Could you explain what you mean by wrapping the FlutterMap in a Stateless widget? Where should the MapController be created?

ibrierley commented 2 years ago

There's a mapController example at https://github.com/fleaflet/flutter_map/blob/master/example/lib/pages/map_controller.dart would probably be the first check to see if it works when added like that.

JaffaKetchup commented 2 years ago

Note that the example above is at least 6 months old, but I would also recommend trying it.

If not, I usually try to pass the controller into the widget as an argument, after creating it in the stateful widget above. Then I wrap the stateless widget with a FutureBuilder that usually completes instantly (before a frame is painted), so no loader is usually needed (only add if necessary)).