Flutter Maps动态标记onTap调用showModalBottomSheet报No MediaQuery错误咨询
错误原因
报错的核心是你调用
showModalBottomSheet时传入的上下文不正确:_myContext是MyApp组件自身的上下文,而MediaQuery是MaterialApp内部才会初始化的能力,你的上下文处在MaterialApp的上层,自然找不到对应的MediaQuery祖先节点。
同时你还存在两个隐性问题:
- 向markers集合添加新标记后没有调用
setState,新标记不会触发页面重绘,无法显示在地图上 - 数据库监听器的调用时机早于Firebase初始化完成的时机,可能出现空引用异常
解决方案
推荐采用组件拆分的方案,将地图相关逻辑拆分到MaterialApp的子组件中,确保使用的上下文位于MaterialApp子树内,可正常访问MediaQuery。
完整修改后的代码
import 'package:flutter/material.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_database/firebase_database.dart'; import 'package:petrol_finder/data/PetrolStation.dart'; void main() { WidgetsFlutterBinding.ensureInitialized(); runApp(const MyApp()); } class MyApp extends StatefulWidget { const MyApp({Key? key}) : super(key: key); @override _MyAppState createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { bool _initialized = false; bool _error = false; void initializeFlutterFire() async { try { await Firebase.initializeApp(); setState(() { _initialized = true; }); } catch(e) { setState(() { _error = true; }); } } @override void initState() { initializeFlutterFire(); super.initState(); } Widget SomethingWentWrong() { return const ErrorWidget("Firebase初始化出错"); } Widget Loading() { return const SizedBox.shrink(); } @override Widget build(BuildContext context) { if(_error) { return SomethingWentWrong(); } if (!_initialized) { return Loading(); } return MaterialApp( home: const MapPage(), ); } } class MapPage extends StatefulWidget { const MapPage({Key? key}) : super(key: key); @override State<MapPage> createState() => _MapPageState(); } class _MapPageState extends State<MapPage> { late GoogleMapController mapController; final petrolStationsDatabase = FirebaseDatabase(databaseURL:"[替换为你的数据库地址]").reference().child('petrolstations'); final Set<Marker> markers = {}; final LatLng _center = const LatLng(51.5074, -0.1272); void _onMapCreated(GoogleMapController controller) { mapController = controller; } @override void initState() { super.initState(); _activateListeners(); } void _activateListeners() { petrolStationsDatabase.onChildAdded.listen((event) { if(event.snapshot.key==null) { return; } final PetrolStation petrolStation = PetrolStation.fromJson(event.snapshot.value); final newMarker = Marker( markerId: MarkerId(event.snapshot.key!), position: LatLng(petrolStation.latitude, petrolStation.longitude), onTap: () { showModalBottomSheet<void>( context: context, builder: (context) { return Container( height: 200, color: Colors.amber, child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: <Widget>[ const Text('Modal BottomSheet'), ElevatedButton( child: const Text('关闭底部弹窗'), onPressed: () => Navigator.pop(context), ) ], ), ), ); }, ); }, icon: BitmapDescriptor.defaultMarker, ); setState(() { markers.add(newMarker); }); }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('加油站查询'), backgroundColor: Colors.green[700], ), body: GoogleMap( onMapCreated: _onMapCreated, initialCameraPosition: CameraPosition( target: _center, zoom: 11.0, ), markers: markers, ), ); } }
改动说明
- 拆分出独立的
MapPage组件作为MaterialApp的主页,该组件的上下文位于MaterialApp子树内,可正常访问MediaQuery - 移除了原有的
_myContext变量,直接使用MapPage的上下文调用showModalBottomSheet - 新增
setState调用,添加marker后触发页面重绘,保证新标记能显示在地图上 - 调整了数据库监听器的调用时机,确保Firebase初始化完成后才开启监听
内容的提问来源于stack exchange,提问作者haltendehand
相关产品推荐
相关产品推荐

