Flutter中flutter-mapbox-gl初始加载覆盖AppBar与NavBar问题
问题表现
- 地图首次加载时尺寸超出布局约束,直接覆盖在AppBar、底部NavBar上层绘制
- 后续地图组件会自动归位到正确布局位置,但AppBar和NavBar的渲染缓存已被破坏,显示异常

复现环境
Flutter环境信息:
Doctor summary (to see all details, run flutter doctor -v): [√] Flutter (Channel stable, 3.0.1, on Microsoft Windows [Version 10.0.19044.1706], locale en-NZ) [√] Android toolchain - develop for Android devices (Android SDK version 31.0.0) [√] Chrome - develop for the web [√] Visual Studio - develop for Windows (Visual Studio Community 2022 17.2.0) [√] Android Studio (version 2021.2) [√] VS Code (version 1.67.2) [√] Connected device (4 available) [√] HTTP Host Availability
问题对应依赖版本:mapbox_gl: ^0.16.0
最小复现代码:
import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:mapbox_gl/mapbox_gl.dart'; import '../services/item.dart'; class MapControl extends StatefulWidget { const MapControl({Key? key}) : super(key: key); @override State<StatefulWidget> createState() => MapControlState(); } class MapControlState extends State<MapControl> { final itemService = Get.find<ItemService>(); MapboxMapController? mapController; @override void initState() { super.initState(); } _onMapCreated(MapboxMapController controller) { mapController = controller; } _onStyleLoadedCallback() { ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: const Text("Style loaded :)"), backgroundColor: Theme.of(context).primaryColor, duration: const Duration(seconds: 1), )); } @override Widget build(BuildContext context) { return Scaffold( body: MapboxMap( styleString: MapboxStyles.LIGHT, accessToken: 'xxxxxxxxxxxxxxxxx', onMapCreated: _onMapCreated, initialCameraPosition: const CameraPosition(target: LatLng(0.0, 0.0)), onStyleLoadedCallback: _onStyleLoadedCallback, )); } }
问题根因
该问题是mapbox_gl 0.16.x版本适配Flutter 3.0+时的渲染时序缺陷:原生地图PlatformView初始化时未正确获取Scaffold的布局约束与安全区配置,首帧默认按全屏尺寸渲染,触发PlatformView层级错乱;后续布局更新时未同步刷新上层导航组件的绘制缓存,导致导航栏显示损坏。
修复方案
按改造成本从低到高排序:
方案1:显式约束尺寸+延迟初始化(无需改依赖版本)
不要直接将MapboxMap作为Scaffold的body根节点,通过SafeArea显式声明布局边界,同时增加一帧延迟,等Scaffold的AppBar、NavBar布局完成后再初始化地图组件,避免首帧拿到全屏尺寸。
改造后的代码示例:
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text("地图页面")), // 用SafeArea显式约束子组件布局边界,避开状态栏、导航栏区域 body: SafeArea( child: FutureBuilder( // 等待首帧布局完成再渲染地图 future: Future.delayed(Duration.zero), builder: (context, snapshot) { if (snapshot.connectionState != ConnectionState.done) { return const Center(child: CircularProgressIndicator()); } return MapboxMap( styleString: MapboxStyles.LIGHT, accessToken: '你的MapBox accessToken', onMapCreated: _onMapCreated, initialCameraPosition: const CameraPosition(target: LatLng(0.0, 0.0)), onStyleLoadedCallback: _onStyleLoadedCallback, ); }, ), ), bottomNavigationBar: BottomNavigationBar( items: const [ BottomNavigationBarItem(icon: Icon(Icons.home), label: "首页"), BottomNavigationBarItem(icon: Icon(Icons.map), label: "地图"), ], ), ); }
方案2:升级依赖版本
如果不需要锁定0.16.0版本,直接升级到已修复该问题的mapbox_gl: ^0.17.0及以上版本即可,新版本已适配Flutter 3.0的PlatformView渲染逻辑,无需额外加延迟或布局约束。
修改pubspec.yaml中的依赖配置:
dependencies: mapbox_gl: ^0.17.0
执行flutter pub get拉取新版本依赖后,重新编译运行即可。
方案3:修改Android端渲染配置(仅Android端生效)
如果前两种方案不适用,可以单独修改Android端配置,将MapBox的渲染模式切换为Texture层模式,规避PlatformView层级错乱问题。
打开android/app/src/main/AndroidManifest.xml,在地图页对应的Activity标签下添加如下配置:
<meta-data android:name="io.flutter.embedded_views_preview" android:value="true" /> <meta-data android:name="mapbox.render_mode" android:value="texture" />
注意:Texture层渲染模式会略微降低地图的渲染帧率,非必要不优先使用该方案。
内容的提问来源于stack exchange,提问作者Christine

