Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LateInitializationError for MapState in map.dart #926

Closed
SebastianEngel opened this issue Jun 8, 2021 · 89 comments
Closed

LateInitializationError for MapState in map.dart #926

SebastianEngel opened this issue Jun 8, 2021 · 89 comments
Labels
bug This issue reports broken functionality or another error

Comments

@SebastianEngel
Copy link

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
Copy link
Contributor

kengu commented Jun 8, 2021

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
Copy link

Ciock commented Jun 9, 2021

I keep encountering this problem too

@jexme
Copy link

jexme commented Jun 9, 2021

I confirm, this problem also constantly arises

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

@ibrierley
Copy link
Collaborator

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

@jexme
Copy link

jexme commented Jun 9, 2021

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
Copy link
Collaborator

ibrierley commented Jun 9, 2021 via email

@jexme
Copy link

jexme commented Jun 9, 2021

@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
Copy link
Collaborator

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
Copy link
Contributor

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
Copy link

jexme commented Jun 11, 2021

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
Copy link
Contributor

@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
Copy link

Ciock commented Jun 18, 2021

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

@ibrierley
Copy link
Collaborator

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

@kuhnroyal
Copy link
Contributor

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
Copy link
Collaborator

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

@kuhnroyal
Copy link
Contributor

kuhnroyal commented Jun 18, 2021

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
Copy link
Collaborator

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
Copy link

Ciock commented Jun 23, 2021

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
Copy link

Ciock commented Jun 23, 2021

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
Copy link

sm2017 commented Jun 26, 2021

@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
Copy link
Collaborator

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
Copy link

sm2017 commented Jun 29, 2021

Is there any updates?

@ElectrifyPowr
Copy link

ElectrifyPowr commented Jul 3, 2021

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
Copy link
Collaborator

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
Copy link

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
Copy link
Collaborator

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
Copy link

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
Copy link
Collaborator

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
Copy link

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
Copy link
Collaborator

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!).

@JaffaKetchup
Copy link
Member

Marking as high priority because this is an especially bad bug.

@JaffaKetchup JaffaKetchup pinned this issue Feb 8, 2022
@zhenghuiwin
Copy link

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;
    }
  ),
 );
}

thanks, I solved this problem by using this way.

@alirezat66
Copy link

it seems anybody does not develop this package.

@alirezat66
Copy link

I only have this issue on the 0.14.0 version, everything works fine on the 0.13.1

no, the 0.13.1 does not work too.

@JaffaKetchup
Copy link
Member

Hi @alirezat66,
This library is still actively maintained, by myself and a number of others.
Long discussion has taken place on the best way to move this project forward, and the verdict was 'with care'. Therefore, activity is somewhat slow at the moment, by rest assured we are still here for now.
Thanks.

@JaffaKetchup
Copy link
Member

@alirezat66 Could you provide more information to help us debug and fix this issue? Would be greatly appreciated :)

@alirezat66
Copy link

@alirezat66 Could you provide more information to help us debug and fix this issue? Would be greatly appreciated :)

sory, I just though you put out this issue. no actually I check it carefully and it has problem with controller. the problem accured when you use it in state full widget more than once for example you use map in a screen and again back to that screen so it seems the controller was not disposed

@alirezat66
Copy link

@alirezat66 Could you provide more information to help us debug and fix this issue? Would be greatly appreciated :)

I use get_it for solving problem and create a singleton map controller object but the problem didn't solve

@alirezat66
Copy link

the problem solved by this :
MapController? controller;

Widget build(BuildContext context) {
return FlutterMap(
/// mapController: controller, /// this line should be deleted if you want to update map
options: MapOptions(
onMapCreated: (c) {
controller = c;
}
),
);
}

but you have to avoid defining controller directly for FlutterMap

@ibrierley
Copy link
Collaborator

You can define and use mapController, you just need to be careful not to remove one of them from the widget tree and leave the other I think (so may need wrapping in an outer widget, or wrap flutter_map in an Offstage widget or similar).

@Benjf29
Copy link

Benjf29 commented Mar 13, 2022

the problem solved by this : MapController? controller;

Widget build(BuildContext context) { return FlutterMap( /// mapController: controller, /// this line should be deleted if you want to update map options: MapOptions( onMapCreated: (c) { controller = c; } ), ); }

but you have to avoid defining controller directly for FlutterMap

Remove the mapController param was the solution for me, thank :-)

@JaffaKetchup JaffaKetchup unpinned this issue Mar 20, 2022
@twogood
Copy link

twogood commented Mar 25, 2022

Related to #986?

@JaffaKetchup
Copy link
Member

@twogood Yeah, I'm pretty sure they were talking about similar things

@alirezat66
Copy link

@twogood Yeah, I'm pretty sure they were talking about similar things

Hi Jaff, thanks again for your fantastic package. It helps me in many projects.

@JaffaKetchup
Copy link
Member

@alirezat66 thank you very much, it means a lot. But if your talking about this package, I've only recently become a maintainer! If your talking about my package, extra thanks :)

@protodevsystems
Copy link

protodevsystems commented Mar 30, 2022

This worked for me.

class _MyHomePageState extends State<MyHomePage> {
  late MapController mapController;

  @override
  void initState(){
    super.initState();
    setState((){
      mapController = MapController();
    });
  }

  Position? _position;


  void _getCurrentLocation() async {
    Position position = await _determinePosition();
    setState(() {
      _position = position;
      debugPrint("POSITION: " + _position.toString());
    });
  }

  Future<Position> _determinePosition() async {
    debugPrint('_determinePosition triggered');
    LocationPermission permission;
    permission = await Geolocator.checkPermission();
    if(permission == LocationPermission.denied){
      permission = await Geolocator.requestPermission();
      if(permission == LocationPermission.denied){
        return Future.error('Location Permissions are denied.');
      }
    }
    return await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.high);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: FlutterMap(
        mapController: mapController,
        options: MapOptions(
          center: LatLng(
              _position != null ? _position!.latitude : xx.xx,
              _position != null ? _position!.longitude : xx.xx,
          ),
          zoom: 13.0,
        ),
        layers: [
          TileLayerOptions(
            urlTemplate: "xxx",
            // subdomains: ['a', 'b', 'c'],
            additionalOptions: {
              'accessToken':'xxx',
              'id':'mapbox.satellite'
            },
            attributionBuilder: (_) {
              return Text("© OpenStreeMaps");
            },
          ),
          // MarkerLayerOptions(
          //   markers: [
          //     Marker(
          //       width: 80.0,
          //       height: 80.0,
          //       point: LatLng(51.5, -0.09),
          //       builder: (ctx) =>
          //           Container(
          //             child: FlutterLogo(),
          //           ),
          //     ),
          //   ],
          // ),
        ],
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          debugPrint('Floating action button pressed.');
          _getCurrentLocation();
          double zoom = 18; //the zoom you want
          debugPrint("Latitude: "+ _position!.latitude.toString());
          debugPrint("Longitude: "+ _position!.longitude.toString());
          mapController.move(LatLng(_position!.latitude,_position!.longitude),zoom);

        },
        tooltip: "Center map to current location",
        child: const Icon(Icons.my_location),

      ),
    );
  }
}

@ayechan-maung
Copy link

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
...
════════════════════════════════════════════════════════════════════════════════

I also have the same error as this.

@ibrierley
Copy link
Collaborator

Try passing the mapController as a parameter to FlutterMap widget. You're also probably better off initialising the mapController in an init() method as well. There is a mapController example in the examples/pages directory that's worth following first before changing.

@Mandax78
Copy link

Mandax78 commented Apr 23, 2022 via email

@github-actions
Copy link

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.

@github-actions github-actions bot added the Stale label May 24, 2022
@JaffaKetchup
Copy link
Member

I'm going to close this now, as there are workarounds available, and without a big refactoring, many of these issues will be hidden. If anyone wants to keep this open, just ping me!
Thanks.

@vidklopcic
Copy link

Another (hackish) way to achieve the old behavior is to copy MapControllerImpl and change the following:

late final MapState _state;

@override
StreamSink<MapEvent> get mapEventSink => _mapEventSink.sink;

to

late MapState _state;

@override
StreamSink<MapEvent> get mapEventSink {
  final stream = StreamController<MapEvent>.broadcast();
  final sub = stream.stream.listen((event) => _mapEventSink.sink.add(event));
  stream.sink.done.then((value) => sub.cancel());
  return stream.sink;
}

@halhwadi
Copy link

halhwadi commented Jul 7, 2022

MapController? _mapController;
FlutterMap(
      mapController: _mapController,
      options: MapOptions(
          onMapCreated: (c) {
            _mapController = c;
          }
      ),
)

@sotex
Copy link

sotex commented Jul 15, 2022

import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map/src/map/flutter_map_state.dart';


///  Used to get MapState 
final _mapKey = GlobalKey<FlutterMapState>();

///  Used to get mapController
MapController? get mapController => _mapKey.currentState?.mapController;

FlutterMap(
      key: _mapKey,
      mapController: MapControllerImpl(),
      options: MapOptions(
          ......
      ),
)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug This issue reports broken functionality or another error
Projects
None yet
Development

No branches or pull requests