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.73k stars 860 forks source link

[BUG] LateInitializationError: Field '_interactiveViewerState' has already been initialized #1817

Closed astraube closed 5 months ago

astraube commented 7 months ago

What is the bug?

image

Error Message: LateInitializationError: Field '_interactiveViewerState@4245162146' has already been initialized.


LateInitializationError: Field '_interactiveViewerState@4245162146' has already been initialized.

StackTrace: {#0 LateError._throwFieldAlreadyInitialized (dart:_internal-patch/internal_patch.dart:184:5)
MapControllerImpl._interactiveViewerState= (package:flutter_map/src/map/controller/map_controller_impl.dart:19:40)
MapControllerImpl.interactiveViewerState= (package:flutter_map/src/map/controller/map_controller_impl.dart:47:7)
MapInteractiveViewerState.initState (package:flutter_map/src/gestures/map_interactive_viewer.dart:103:23)
StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:5602:55)
ComponentElement.mount (package:flutter/src/widgets/framework.dart:5447:5)
Element.inflateWidget (package:flutter/src/widgets/framework.dart:4326:16)
Element.updateChild (package:flutter/src/widgets/framework.dart:3831:20)
_LayoutBuilderElement._layout.layoutCallback (package:flutter/src/widgets/layout_builder.dart:132:18)
BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2835:19)
 _LayoutBuilderElement._layout (package:flutter/src/widgets/layout_builder.dart:150:12)
RenderObject.invokeLayoutCallback. (package:flutter/src/rendering/object.dart:2657:59)
PipelineOwner._enableMutationsToDirtySubtrees (package:flutter/src/rendering/object.dart:1071:15)
RenderObject.invokeLayoutCallback (package:flutter/src/rendering/object.dart:2657:14)
RenderConstrainedLayoutBuilder.rebuildIfNecessary (package:flutter/src/widgets/layout_builder.dart:225:7)
_RenderLayoutBuilder.performLayout (package:flutter/src/widgets/layout_builder.dart:308:5)
RenderObject.layout (package:flutter/src/rendering/object.dart:2546:7)
RenderBox.layout (package:flutter/src/rendering/box.dart:2389:11)
RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:104:21)
RenderObject.layout (package:flutter/src/rendering/object.dart:2546:7)
RenderBox.layout (package:flutter/src/rendering/box.dart:2389:11)
ChildLayoutHelper.layoutChild (package:flutter/src/rendering/layout_helper.dart:52:11)
RenderStack._computeSize (package:flutter/src/rendering/stack.dart:581:43)
RenderStack.performLayout (package:flutter/src/rendering/stack.dart:608:12)
RenderObject.layout (package:flutter/src/rendering/object.dart:2546:7)
RenderBox.layout (package:flutter/src/rendering/box.dart:2389:11)
RenderPadding.performLayout (package:flutter/src/rendering/shifted_box.dart:238:12)
RenderObject.layout (package:flutter/src/rendering/object.dart:2546:7)
RenderBox.layout (package:flutter/src/rendering/box.dart:2389:11)
RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:104:21)
RenderObject.layout (package:flutter/src/rendering/object.dart:2546:7)
RenderBox.layout (package:flutter/src/rendering/box.dart:2389:11)
RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:104:21)
RenderObject.layout (package:flutter/src/rendering/object.dart:2546:7)
RenderBox.layout (package:flutter/src/rendering/box.dart:2389:11)
ChildLayoutHelper.layoutChild (package:flutter/src/rendering/layout_helper.dart:52:11)
RenderFlex._computeSizes (package:flutter/src/rendering/flex.dart:868:45)
RenderFlex.performLayout (package:flutter/src/rendering/flex.dart:903:32)
RenderObject.layout (package:flutter/src/rendering/object.dart:2546:7)
RenderBox.layout (package:flutter/src/rendering/box.dart:2389:11)
MultiChildLayoutDelegate.layoutChild (package:flutter/src/rendering/custom_layout.dart:173:12)
_ScaffoldLayout.performLayout (package:flutter/src/material/scaffold.dart:1062:7)
MultiChildLayoutDelegate._callPerformLayout (package:flutter/src/rendering/custom_layout.dart:237:7)
RenderCustomMultiChildLayoutBox.performLayout (package:flutter/src/rendering/custom_layout.dart:403:14)
RenderObject._layoutWithoutResize (package:flutter/src/rendering/object.dart:2385:7)
PipelineOwner.flushLayout (package:flutter/src/rendering/object.dart:1025:18)
PipelineOwner.flushLayout (package:flutter/src/rendering/object.dart:1038:15)
RendererBinding.drawFrame (package:flutter/src/rendering/binding.dart:591:23)
WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:986:13)
RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:457:5)
SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1325:15)
SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1255:9)
SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:1113:5)
_invoke (dart:ui/hooks.dart:312:13)
PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:383:5)
_drawFrame (dart:ui/hooks.dart:283:31)
}

How can we reproduce it?

  1. Create a page with two TabBar
  2. Include FlutterMap in the first TabBar
  3. When navigating between tabs, the FlutterMap component will crash.

Do you have a potential solution?

_interactiveViewerState

Platforms

Android 13

Severity

Minimum: Allows normal functioning

JaffaKetchup commented 7 months ago

Hi @astraube,

I could not reproduce this, using this code (adpated from the Flutter guide for working with tabs). ```dart DefaultTabController( length: 2, child: Scaffold( appBar: AppBar( bottom: const TabBar( tabs: [ Tab(icon: Icon(Icons.directions_car)), Tab(icon: Icon(Icons.directions_transit)), ], ), title: const Text('Tabs Demo'), ), body: TabBarView( children: [ FlutterMap( options: const MapOptions( initialCenter: LatLng(51.5, -0.09), initialZoom: 11, ), children: [ openStreetMapTileLayer, CircleLayer( circles: [ CircleMarker( point: const LatLng(51.5, -0.09), color: Colors.blue.withOpacity(0.7), borderColor: Colors.black, borderStrokeWidth: 2, useRadiusInMeter: true, radius: 2000, // 2000 meters ), CircleMarker( point: const LatLng(51.4937, -0.6638), // Dorney Lake is ~2km long color: Colors.green.withOpacity(0.9), borderColor: Colors.black, borderStrokeWidth: 2, useRadiusInMeter: true, radius: 1000, // 1000 meters ), ], ), ], ), Icon(Icons.directions_transit), ], ), ), ), ```

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.

corepuncher commented 7 months ago

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

JaffaKetchup commented 7 months ago

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?

JaffaKetchup commented 7 months ago

This can apparently be reproduced by providing a UniqueKey to the FlutterMap, then rebuilding.

V3ntus commented 5 months ago

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.

Exception ```dart ======== Exception caught by widgets library ======================================================= The following assertion was thrown building KeyedSubtree-[GlobalKey#05349]: Should not update options unless they change 'package:flutter_map/src/map/controller/map_controller_impl.dart': Failed assertion: line 342 pos 7: 'newOptions != value.options' The relevant error-causing widget was: Scaffold Scaffold:file:///home/joe/repos/bug/lib/main.dart:97:12 When the exception was thrown, this was the stack: #2 MapControllerImpl.options= (package:flutter_map/src/map/controller/map_controller_impl.dart:342:7) #3 _FlutterMapStateContainer._setMapController (package:flutter_map/src/map/widget.dart:188:22) #4 _FlutterMapStateContainer.initState (package:flutter_map/src/map/widget.dart:61:5) #5 StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:5611:55) #6 ComponentElement.mount (package:flutter/src/widgets/framework.dart:5456:5) ... Normal element mounting (28 frames) #34 Element.inflateWidget (package:flutter/src/widgets/framework.dart:4335:16) #35 MultiChildRenderObjectElement.inflateWidget (package:flutter/src/widgets/framework.dart:6893:36) #36 MultiChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:6905:32) ... Normal element mounting (332 frames) #368 Element.inflateWidget (package:flutter/src/widgets/framework.dart:4335:16) #369 MultiChildRenderObjectElement.inflateWidget (package:flutter/src/widgets/framework.dart:6893:36) #370 MultiChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:6905:32) ... Normal element mounting (688 frames) #1058 Element.inflateWidget (package:flutter/src/widgets/framework.dart:4335:16) #1059 MultiChildRenderObjectElement.inflateWidget (package:flutter/src/widgets/framework.dart:6893:36) #1060 Element.updateChild (package:flutter/src/widgets/framework.dart:3846:18) #1061 Element.updateChildren (package:flutter/src/widgets/framework.dart:4033:32) #1062 MultiChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6918:17) #1063 Element.updateChild (package:flutter/src/widgets/framework.dart:3824:15) #1064 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5505:16) #1065 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5643:11) #1066 Element.rebuild (package:flutter/src/widgets/framework.dart:5196:7) #1067 StatefulElement.update (package:flutter/src/widgets/framework.dart:5666:5) #1068 Element.updateChild (package:flutter/src/widgets/framework.dart:3824:15) #1069 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5505:16) #1070 Element.rebuild (package:flutter/src/widgets/framework.dart:5196:7) #1071 ProxyElement.update (package:flutter/src/widgets/framework.dart:5809:5) #1072 Element.updateChild (package:flutter/src/widgets/framework.dart:3824:15) #1073 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5505:16) #1074 Element.rebuild (package:flutter/src/widgets/framework.dart:5196:7) #1075 ProxyElement.update (package:flutter/src/widgets/framework.dart:5809:5) #1076 _InheritedNotifierElement.update (package:flutter/src/widgets/inherited_notifier.dart:105:11) #1077 Element.updateChild (package:flutter/src/widgets/framework.dart:3824:15) #1078 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5505:16) #1079 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5643:11) #1080 Element.rebuild (package:flutter/src/widgets/framework.dart:5196:7) #1081 StatefulElement.update (package:flutter/src/widgets/framework.dart:5666:5) #1082 Element.updateChild (package:flutter/src/widgets/framework.dart:3824:15) #1083 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5505:16) #1084 Element.rebuild (package:flutter/src/widgets/framework.dart:5196:7) #1085 ProxyElement.update (package:flutter/src/widgets/framework.dart:5809:5) #1086 _InheritedNotifierElement.update (package:flutter/src/widgets/inherited_notifier.dart:105:11) #1087 Element.updateChild (package:flutter/src/widgets/framework.dart:3824:15) #1088 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5505:16) #1089 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5643:11) #1090 Element.rebuild (package:flutter/src/widgets/framework.dart:5196:7) #1091 StatefulElement.update (package:flutter/src/widgets/framework.dart:5666:5) #1092 Element.updateChild (package:flutter/src/widgets/framework.dart:3824:15) #1093 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5505:16) #1094 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5643:11) #1095 Element.rebuild (package:flutter/src/widgets/framework.dart:5196:7) #1096 StatefulElement.update (package:flutter/src/widgets/framework.dart:5666:5) #1097 Element.updateChild (package:flutter/src/widgets/framework.dart:3824:15) #1098 SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6765:14) #1099 Element.updateChild (package:flutter/src/widgets/framework.dart:3824:15) #1100 SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6765:14) #1101 Element.updateChild (package:flutter/src/widgets/framework.dart:3824:15) #1102 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5505:16) #1103 Element.rebuild (package:flutter/src/widgets/framework.dart:5196:7) #1104 ProxyElement.update (package:flutter/src/widgets/framework.dart:5809:5) #1105 Element.updateChild (package:flutter/src/widgets/framework.dart:3824:15) #1106 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5505:16) #1107 Element.rebuild (package:flutter/src/widgets/framework.dart:5196:7) #1108 ProxyElement.update (package:flutter/src/widgets/framework.dart:5809:5) #1109 Element.updateChild (package:flutter/src/widgets/framework.dart:3824:15) #1110 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5505:16) #1111 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5643:11) #1112 Element.rebuild (package:flutter/src/widgets/framework.dart:5196:7) #1113 StatefulElement.update (package:flutter/src/widgets/framework.dart:5666:5) #1114 Element.updateChild (package:flutter/src/widgets/framework.dart:3824:15) #1115 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5505:16) #1116 Element.rebuild (package:flutter/src/widgets/framework.dart:5196:7) #1117 ProxyElement.update (package:flutter/src/widgets/framework.dart:5809:5) #1118 Element.updateChild (package:flutter/src/widgets/framework.dart:3824:15) #1119 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5505:16) #1120 Element.rebuild (package:flutter/src/widgets/framework.dart:5196:7) #1121 ProxyElement.update (package:flutter/src/widgets/framework.dart:5809:5) #1122 _InheritedNotifierElement.update (package:flutter/src/widgets/inherited_notifier.dart:105:11) #1123 Element.updateChild (package:flutter/src/widgets/framework.dart:3824:15) #1124 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5505:16) #1125 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5643:11) #1126 Element.rebuild (package:flutter/src/widgets/framework.dart:5196:7) #1127 StatefulElement.update (package:flutter/src/widgets/framework.dart:5666:5) #1128 Element.updateChild (package:flutter/src/widgets/framework.dart:3824:15) #1129 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5505:16) #1130 Element.rebuild (package:flutter/src/widgets/framework.dart:5196:7) #1131 ProxyElement.update (package:flutter/src/widgets/framework.dart:5809:5) #1132 Element.updateChild (package:flutter/src/widgets/framework.dart:3824:15) #1133 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5505:16) #1134 Element.rebuild (package:flutter/src/widgets/framework.dart:5196:7) #1135 StatelessElement.update (package:flutter/src/widgets/framework.dart:5556:5) #1136 Element.updateChild (package:flutter/src/widgets/framework.dart:3824:15) #1137 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5505:16) #1138 Element.rebuild (package:flutter/src/widgets/framework.dart:5196:7) #1139 ProxyElement.update (package:flutter/src/widgets/framework.dart:5809:5) #1140 Element.updateChild (package:flutter/src/widgets/framework.dart:3824:15) #1141 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5505:16) #1142 Element.rebuild (package:flutter/src/widgets/framework.dart:5196:7) #1143 ProxyElement.update (package:flutter/src/widgets/framework.dart:5809:5) #1144 Element.updateChild (package:flutter/src/widgets/framework.dart:3824:15) #1145 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5505:16) #1146 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5643:11) #1147 Element.rebuild (package:flutter/src/widgets/framework.dart:5196:7) #1148 BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2904:19) #1149 WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:989:21) #1150 RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:448:5) #1151 SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1386:15) #1152 SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1311:9) #1153 SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:1169:5) #1154 _invoke (dart:ui/hooks.dart:312:13) #1155 PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:399:5) #1156 _drawFrame (dart:ui/hooks.dart:283:31) ```
main.dart ```dart # main.dart import 'package:flutter/material.dart'; import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map_tile_caching/flutter_map_tile_caching.dart'; import 'package:go_router/go_router.dart'; import 'package:path/path.dart' as p; import 'package:path_provider/path_provider.dart'; final MapController mapController = MapController(); final router = GoRouter( initialLocation: "/", routes: [ GoRoute(path: "/", builder: (context, state) => const MyApp()), GoRoute( path: "/test", builder: (context, state) => Container( color: Colors.white, child: Center( child: TextButton( onPressed: () => context.go("/"), child: const Text("Go Home"), ), ), ), ), ], ); void main() async { await FMTCObjectBoxBackend().initialise( rootDirectory: p.join((await getApplicationSupportDirectory()).absolute.path, "flutter_map_tile_cache"), ); runApp(MaterialApp.router( routerConfig: router, )); } class MyApp extends StatelessWidget { const MyApp({super.key}); // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), useMaterial3: true, ), home: const MyHomePage(title: 'Flutter Demo Home Page'), ); } } class MyHomePage extends StatefulWidget { const MyHomePage({super.key, required this.title}); final String title; @override State createState() => _MyHomePageState(); } class _MyHomePageState extends State { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: Text(widget.title), ), body: SizedBox.expand( child: FlutterMap( mapController: mapController, children: [ TileLayer( urlTemplate: 'https://api.mapbox.com/styles/v1/mapbox/streets-v12/tiles/256/{z}/{x}/{y}', userAgentPackageName: "com.V3ntus.test", maxZoom: 22, maxNativeZoom: 22, tileProvider: const FMTCStore('mapStore').getTileProvider( settings: FMTCTileProviderSettings( cachedValidDuration: const Duration(days: 60), errorHandler: (error) { if (error.type == FMTCBrowsingErrorType.negativeFetchResponse) { // do nothing - indicates cache miss and HTTP error } }, ), ), ), Center( child: TextButton( onPressed: () => context.go("/test"), child: const Text("Navigate Away"), ), ) ], ), ), ); } } ```
pubspec.yaml ```yaml # pubspec.yaml dependencies: flutter: sdk: flutter cupertino_icons: ^1.0.6 flutter_map: ^6.1.0 flutter_map_tile_caching: ^9.0.0-dev.8 path: ^1.9.0 path_provider: ^2.1.2 go_router: ^13.2.2 dependency_overrides: flutter_map: git: url: https://github.com/fleaflet/flutter_map.git ref: 0fef8d6d36a735132b1b6cbe4624265beb4740bf ```
corepuncher commented 5 months ago

This can apparently be reproduced by providing a UniqueKey to the FlutterMap, then rebuilding.

Just curious if anyone has tried this to reproduce, and, if it had the same effect as for me.

JaffaKetchup commented 5 months ago

If you need to insert your controller into some state management, this is the way I do it:

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

  2. Attach the controller to the map as normal.

  3. Use the onMapReady callback, and set the controller to whichever state you use.

  4. 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?

V3ntus commented 5 months ago

In my case, I'm trying to control the map outside of FlutterMap, thus attempting to define the controller outside of the widget class

JaffaKetchup commented 5 months ago

Ok, so my suggestion above should work. That's what I needed to do as well.

V3ntus commented 5 months ago

Just wanted to confirm that the method JaffaKetchup suggested worked. This was my implementation:

  1. Define MapController in your map widget and pass it to the FlutterMap widget.
  2. Define a nullable MapController variable somewhere globally accessible. Ex. GlobalHandler.mapController
  3. MapOptions.onMapReady callback assigns the map controller to GlobalHandler.mapController
  4. In my 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.
JaffaKetchup commented 5 months ago

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.

corepuncher commented 5 months ago

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!

JaffaKetchup commented 5 months ago

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;
}
JaffaKetchup commented 5 months ago

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