Flutter build方法先于变量初始化导致崩溃 如何提前初始化参数
Flutter 异步初始化会话变量解决build执行早于initState异步逻辑崩溃问题
问题根因
initState是同步生命周期方法,内部调用的异步setGps方法不会阻塞后续build执行,你从FlutterSession取的经纬度可能为空,或未等赋值完成就被使用,传给LatLng空值就会触发崩溃。- 不存在让
build等待initState异步执行完的方法,正确解法是用FutureBuilder控制渲染逻辑:异步加载完成前显示加载占位,加载完成再渲染地图页面。
修改步骤说明
- 调整
setGps方法,直接返回处理后的LatLng对象,同时新增空值兜底逻辑,避免会话无值时传入空参数崩溃 - 用
FutureBuilder包裹原有页面逻辑,根据异步执行状态返回对应组件:加载中显示加载提示,加载完成再渲染地图 - 移除冗余的状态更新逻辑,直接用Future返回的经纬度初始化地图参数
完整可运行代码
import 'dart:async'; import 'package:flutter/material.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:permission/permission.dart'; import 'package:flutter_session/flutter_session.dart'; class GmyApp extends StatefulWidget { const GmyApp({Key? key}) : super(key: key); @override _GmyApp createState() => _GmyApp(); } class _GmyApp extends State<GmyApp> { Completer<GoogleMapController> _controller = Completer(); static const LatLng _defaultCenter = const LatLng(10.8505, 76.2711); final Set<Marker> _markers = {}; late LatLng _lastMapPosition; MapType _currentMapType = MapType.normal; // 声明异步加载的Future late Future<LatLng> _loadPositionFuture; void _onMapTypeButtonPressed() { setState(() { _currentMapType = _currentMapType == MapType.normal ? MapType.satellite : MapType.normal; }); } void _onAddMarkerButtonPressed() { setState(() { _markers.add(Marker( markerId: MarkerId(_lastMapPosition.toString()), position: _lastMapPosition, infoWindow: InfoWindow( title: 'Your last location', snippet: 'Location Refreshed every 1 hour', ), icon: BitmapDescriptor.defaultMarker, )); }); } void _onCameraMove(CameraPosition position) { _lastMapPosition = position.target; } void _onMapCreated(GoogleMapController controller) { _controller.complete(controller); } @override void initState() { super.initState(); // 初始化时就启动异步加载,赋值给Future变量 _loadPositionFuture = setGps(); } // 修改setGps为返回LatLng的Future,新增空值兜底 Future<LatLng> setGps() async { print('I am in GPS setter*'); var latitude_data = await FlutterSession().get('lat'); var longitude_data = await FlutterSession().get('longi'); print('User logi Id is in GPS* $longitude_data'); print('User lat is in GPS* $latitude_data'); // 空值判断,会话取不到就用默认坐标 if (latitude_data == null || longitude_data == null) { _lastMapPosition = _defaultCenter; return _defaultCenter; } LatLng center1 = LatLng(latitude_data, longitude_data); print('Lat .lat is in GPS $center1'); _lastMapPosition = center1; print('Lat .lat lastMap Position is $_lastMapPosition'); return center1; } @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar( title: Text('Your Current Location is'), backgroundColor: Colors.green[700], ), // 用FutureBuilder包裹原有body逻辑 body: FutureBuilder<LatLng>( future: _loadPositionFuture, builder: (context, snapshot) { // 加载中显示居中加载提示 if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } // 加载出错显示错误提示 if (snapshot.hasError) { return Center(child: Text('加载位置信息失败:${snapshot.error}')); } // 加载完成,正常渲染地图 return Stack( children: <Widget>[ GoogleMap( onMapCreated: _onMapCreated, initialCameraPosition: CameraPosition( target: _lastMapPosition, zoom: 11.0, ), mapType: _currentMapType, markers: _markers, onCameraMove: _onCameraMove, ), Padding( padding: const EdgeInsets.all(16.0), child: Align( alignment: Alignment.topRight, child: Column( children: <Widget> [ FloatingActionButton( onPressed: _onMapTypeButtonPressed, heroTag: "btn1", materialTapTargetSize: MaterialTapTargetSize.padded, backgroundColor: Colors.green, child: const Icon(Icons.map, size: 36.0), ), SizedBox(height: 16.0), FloatingActionButton( onPressed: _onAddMarkerButtonPressed, heroTag: "btn2", materialTapTargetSize: MaterialTapTargetSize.padded, backgroundColor: Colors.green, child: const Icon(Icons.add_location, size: 36.0), ), ], ), ), ), ], ); }, ), ), ); } }
额外注意点
- 代码适配了Flutter 2.0+的空安全语法,如果你的项目是旧版本非空安全,把变量声明里的
?和late关键字去掉即可 - 已经新增了空值兜底逻辑,就算会话里没有存储经纬度,也会用默认坐标渲染,不会崩溃
- 加载过程中会显示圆形加载指示器,用户感知更友好
内容的提问来源于stack exchange,提问作者Ashin ashin
相关产品推荐
相关产品推荐

