我试图设置一个mapController来返回Flutter_map的边界,但是这个“LateInitializationError:Field '_internalController@'尚未初始化”错误不断出现
import 'package:flutter/material.dart';
import 'dart:io';
import 'package:dio_cache_interceptor/dio_cache_interceptor.dart';
import 'package:dio_cache_interceptor_file_store/dio_cache_interceptor_file_store.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map_cache/flutter_map_cache.dart';
import 'package:latlong2/latlong.dart';
import 'package:path_provider/path_provider.dart';
class CityMap extends StatefulWidget {
const CityMap({super.key});
@override
State<CityMap> createState() => _CityMapState();
}
class _CityMapState extends State<CityMap> with TickerProviderStateMixin {
final Future<CacheStore> _cacheStoreFuture = _getCacheStore();
static Future<CacheStore> _getCacheStore() async {
final dir = await getTemporaryDirectory();
return FileCacheStore('${dir.path}${Platform.pathSeparator}MapTiles');
}
var _mapController = MapController();
List<LatLng> markertram = [const LatLng(51.502762, -0.113125)];
List<Marker> buildMarker() {
List<Marker> markers = [];
LatLngBounds bounder = _mapController.camera.visibleBounds;
void createMarkers(
List<LatLng> positions, String metroNumber, List<String> polyLineIds) {
List<Marker> tramMarkers = positions.where((position) {
return bounder.contains(position);
}).map((position) {
return Marker(
width: 20.0,
height: 20.0,
point: position,
rotate: true,
alignment: Alignment.center,
child: GestureDetector(
onTap: () {},
child: Tooltip(
message: " Metro $metroNumber",
child: const Icon(Icons.tram_outlined),
),
),
);
}).toList();
markers.addAll(tramMarkers);
}
createMarkers(markertram, '2', ['tram2']);
return markers;
}
@override
void initState() {
super.initState();
_mapController = MapController();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder<CacheStore>(
future: _cacheStoreFuture,
builder: (context, snapshot) {
if (snapshot.hasData) {
final cacheStore = snapshot.data!;
return Stack(
children: [
FlutterMap(
mapController: MapController(),
options: const MapOptions(
initialCenter: LatLng(51.515635, -0.092354),
initialZoom: 15.5,
maxZoom: 19.5,
),
children: [
TileLayer(
urlTemplate:
'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
tileProvider: CachedTileProvider(
store: cacheStore,
),
),
MarkerLayer(
markers: buildMarker(),
),
],
),
],
);
} else if (snapshot.hasError) {
return Center(child: Text(snapshot.error.toString()));
}
return const Center(child: CircularProgressIndicator());
},
));
}
}
我所看到的解决方案似乎都是在 initstate 中设置 mapController,但这不起作用。但我也使用 futurebuilder 来延迟标记渲染
问题就像错误所说的那样,您在渲染
MapController
之前使用FlutterMap
来管理FlutterMap
。有一个错误可以修复:
_mapController
,但在 MapController()
小部件中使用了 FlutterMap
。FlutterMap(
// Use [_mapController] instead of MapController.
//
// So you can manage the map using [_mapController].
mapController: _mapController,
options: const MapOptions(
initialCenter: LatLng(51.515635, -0.092354),
initialZoom: 15.5,
maxZoom: 19.5,
),
children: [
TileLayer(
urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
tileProvider: CachedTileProvider(
store: cacheStore,
),
),
MarkerLayer(
markers: buildMarker(),
),
],
),
buildMarker()
内呼唤
FlutterMap
这会导致错误,因为
FlutterMap
尚未渲染。 onMapReady
中有一个属性MapOptions
提供了这种情况。我的建议是创建新变量:
List<Marker> _markersList = []
然后在您的
FlutterMap
选项中,在 _markerList
属性中填写 onMapReady
值:
options: MapOptions(
initialCenter: const LatLng(51.502762, -0.113125),
initialZoom: 15.5,
maxZoom: 19.5,
// This is where you can manage Map using _mapController for the first time rendered.
//
// Before use _mapController, make sure that FlutterMap is already rendered,
// this [onMapReady] provided that case.
onMapReady: () {
// This is where the [_markers] value filled with [buildMarker()]
// Then refresh the state using `setState` to effect.
_markerList = buildMarker();
setState(() {});
},
),
通过这两个修复,您的应用程序将会很好。
我提供了额外的最小可重现代码供您尝试此解决方案,我根据您的代码进行了一些修改,但逻辑仍然相同。
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:latlong2/latlong.dart';
class FlutterMapPage extends StatefulWidget {
const FlutterMapPage({super.key});
@override
State<FlutterMapPage> createState() => _FlutterMapPageState();
}
class _FlutterMapPageState extends State<FlutterMapPage> {
/// [mapController] use to programatically control the map camera & access
/// some helper functionality - control camera.
MapController mapController = MapController();
/// Markers list on map
List<Marker> _markers = [];
Marker _buildMarker(LatLng position, String metroNumber) {
const double markerSize = 20.0;
Widget child = GestureDetector(
onTap: () {},
child: Tooltip(
message: "Metro $metroNumber",
child: const Icon(Icons.tram_outlined),
),
);
return Marker(
width: markerSize,
height: markerSize,
point: position,
rotate: true,
alignment: Alignment.center,
child: child,
);
}
List<Marker> _getMarkerList() {
// Initiate tram position
const List<LatLng> tramsPosition = [LatLng(51.502762, -0.113125)];
// Initiate metroNumber
const String metroNumber = '2';
// Initiate markers variable to store list of marker data in this function.
final List<Marker> markers = [];
// Initiate the map camera bounder position.
final LatLngBounds bounder = mapController.camera.visibleBounds;
// Get list of trams position inside [bounder].
List<LatLng> tramInBounder = tramsPosition
.where(
(position) => bounder.contains(position),
)
.toList();
// Iterate [tramInBounder] for add [tramInBounder] value to [markers]
for (LatLng position in tramInBounder) {
markers.add(_buildMarker(position, metroNumber));
}
return markers;
}
@override
Widget build(BuildContext context) {
const String urlTemplate = 'https://tile.openstreetmap.org/{z}/{x}/{y}.png';
return Scaffold(
appBar: AppBar(title: const Text('Flutter Map')),
body: FlutterMap(
mapController: mapController,
options: MapOptions(
initialCenter: const LatLng(51.502762, -0.113125),
initialZoom: 15.5,
maxZoom: 19.5,
// This is where you run a function with [_mapController].
//
// Before use _mapController, make sure that FlutterMap is already rendered,
// this [onMapReady] provided that case.
onMapReady: () {
// This is where the [_markers] value filled with [buildMarker()]
// Then refresh the state using `setState` to effect.
_markers = _getMarkerList();
setState(() {});
},
),
children: [
TileLayer(urlTemplate: urlTemplate),
MarkerLayer(markers: _markers),
],
),
);
}
}