Flutter Isolate定位报Binding has not yet been initialized错误
问题背景
开发谷歌地图定位应用时,创建Isolate实现获取位置功能时程序抛出错误,自行排查未修复。
项目使用依赖
google_maps_flutter: ^2.1.8geocoding: ^2.0.4geolocator: ^8.2.1flutter_bloc: ^8.0.1
报错信息
Restarted application in 2,738ms. D/MapsInitializer( 6872): preferredRenderer: null D/zzca ( 6872): preferredRenderer: null I/Google Maps Android API( 6872): Google Play services package version: 221215028 I/Google Maps Android API( 6872): Google Play services maps renderer version(legacy): 2031150007 I/Counters( 6872): exceeded sample count in FrameTime I/m.example.g_ma( 6872): NativeAlloc concurrent copying GC freed 17476(988KB) AllocSpace objects, 0(0B) LOS objects, 50% free, 7MB/14MB, paused 172us total 133.433ms10 I/Counters( 6872): exceeded sample count in FrameTime I/m.example.g_ma( 6872): NativeAlloc concurrent copying GC freed 4201(193KB) AllocSpace objects, 0(0B) LOS objects, 49% free, 7MB/14MB, paused 138us total 149.308ms7 I/Counters( 6872): exceeded sample count in FrameTime I/chatty ( 6872): uid=10498(com.example.g_map) androidmapsapi- identical 1 line3 I/Counters( 6872): exceeded sample count in FrameTime E/flutter ( 6872): [ERROR:flutter/runtime/dart_isolate.cc(1098)] Unhandled exception: E/flutter ( 6872): Binding has not yet been initialized. E/flutter ( 6872): The "instance" getter on the ServicesBinding binding mixin is only available once that binding has been initialized. E/flutter ( 6872): Typically, this is done by calling "WidgetsFlutterBinding.ensureInitialized()" or "runApp()" (the latter calls the former). Typically this call is done in the "void main()" method. The "ensureInitialized" method is idempotent; calling it multiple times is not harmful. After calling that method, the "instance" getter will return the binding. E/flutter ( 6872): In a test, one can call "TestWidgetsFlutterBinding.ensureInitialized()" as the first line in the test's "main()" method to initialize the binding. E/flutter ( 6872): If ServicesBinding is a custom binding mixin, there must also be a custom binding class, like WidgetsFlutterBinding, but that mixes in the selected binding, and that is the class that must be constructed before using the "instance" getter. E/flutter ( 6872): #0 BindingBase.checkInstance.<anonymous closure> package:flutter/…/foundation/binding.dart:281 E/flutter ( 6872): #1 BindingBase.checkInstance package:flutter/…/foundation/binding.dart:363 E/flutter ( 6872): #2 ServicesBinding.instance package:flutter/…/services/binding.dart:48 E/flutter ( 6872): #3 MethodChannel.binaryMessenger package:flutter/…/services/platform_channel.dart:132
相关代码
main.dart
import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:g_map/cubit/googlemap_cubit.dart'; import 'screens/home_page.dart'; void main() async{ WidgetsFlutterBinding.ensureInitialized(); runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return BlocProvider<GooglemapCubit>( create: (context) => GooglemapCubit(), child: MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: const MyHomePage(title: 'Flutter Map'), ), ); } }
位置权限工具类
import 'dart:developer'; import 'package:geolocator/geolocator.dart'; class GLocationPermission{ Future<bool> checkPermission()async{ bool serviceEnabled ; LocationPermission permission; serviceEnabled = await Geolocator.isLocationServiceEnabled(); if(!serviceEnabled){ log("",error: "Location service is not enabled"); } permission = await Geolocator.checkPermission(); if(permission == LocationPermission.denied){ permission = await Geolocator.requestPermission(); if(permission == LocationPermission.denied){ log("Permission is denied again "); } } if(permission == LocationPermission.deniedForever){ log("",error: "Location permission denied forever"); return false; } if(permission == LocationPermission.whileInUse || permission == LocationPermission.always){ return true; }else{ return false; } } }
首页实现
import 'dart:developer'; import 'dart:isolate'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:g_map/isolates/current_location_in_background.dart'; import 'package:g_map/services/location_permission.dart'; import 'package:geocoding/geocoding.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; class MyHomePage extends StatefulWidget { const MyHomePage({Key? key, required this.title}) : super(key: key); final String title; @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage>{ late GoogleMapController _mapController; final TextEditingController _searchAddressController = TextEditingController(); final _locationPermission = GLocationPermission(); String searchedAddress = ""; late ReceivePort _receivePort; late Isolate _isolate; @override void initState() { _checkPermission(); super.initState(); } Future<void> _checkPermission()async{ if(await _locationPermission.checkPermission() == true){ createIsloate(); } } Future<void> createIsloate()async{ _receivePort = ReceivePort(); _isolate = await Isolate.spawn(CurrentLocationInBackGround.getLocation, _receivePort.sendPort); _receivePort.listen((message) { log("message $message"); }, onError: (error){ log("Error $error",error: error); }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Stack( children: [ _googleMap( inititalCameraPosition: const CameraPosition( target: LatLng(37.42796133580664, -122.085749655962), )), _searchAddressField(context: context) ], )); } Widget _googleMap({required CameraPosition inititalCameraPosition}) { return GoogleMap( zoomControlsEnabled: true, onMapCreated: _onMapCreated, myLocationButtonEnabled: true, initialCameraPosition: inititalCameraPosition, ); } Widget _searchAddressField({required BuildContext context}){ return Card( child: SizedBox( width: double.infinity, height: MediaQuery.of(context).size.height*.07, child: Center( child: TextField( onChanged: (value){}, controller: _searchAddressController, decoration: InputDecoration(border: InputBorder.none, suffix: IconButton(onPressed:() => _searchAndNavigate(context: context), icon: const Icon(Icons.search)) ), ), ), ), ); } Future<void> _searchAndNavigate({required BuildContext context})async{ final location = GeocodingPlatform.instance.locationFromAddress(_searchAddressController.text); location.then((value){ debugPrint(value[0].latitude.toString()); debugPrint(value[0].longitude.toString()); _mapController.animateCamera(CameraUpdate.newCameraPosition(CameraPosition( zoom: 12, target:LatLng(value[0].latitude, value[0].longitude)))); }); } void _onMapCreated(GoogleMapController controller) { _mapController = controller; } }
后台定位Isolate代码
import 'dart:developer'; import 'dart:isolate'; import 'package:geolocator/geolocator.dart'; class CurrentLocationInBackGround{ static void getLocation(SendPort sendPort)async{ final pos = await Geolocator.getCurrentPosition(); log("pos stream $pos"); sendPort.send(pos); } }
问题原因
通过Isolate.spawn创建的是独立工作线程,不会自动继承主Isolate的Flutter引擎绑定初始化状态。geolocator这类基于MethodChannel实现的插件,底层通信依赖ServicesBinding提供的二进制信使,新创建的普通Isolate没有初始化该绑定,直接调用插件就会抛出Binding has not yet been initialized错误。
修复方案
- 单次获取位置不需要额外创建Isolate:
Geolocator.getCurrentPosition()本身是异步非阻塞调用,不会阻塞UI线程,直接在主Isolate中调用即可,删除自定义Isolate创建、监听相关的冗余代码就能解决问题,这是最简单稳妥的方案。 - 如果需要实现后台持续定位,不要自行创建普通Isolate:直接使用
geolocator自带的后台位置能力,或使用专门的后台服务插件处理跨Isolate的引擎初始化逻辑。 - 若必须在自定义Isolate中调用插件,需要先把主Isolate的token传给新Isolate,再初始化后台信使:
- 主Isolate创建Isolate时传入token:
_isolate = await Isolate.spawn( CurrentLocationInBackGround.getLocation, [_receivePort.sendPort, RootIsolateToken.instance!] );- Isolate入口处初始化绑定:
注意普通Isolate无法执行UI相关操作,所有UI更新逻辑必须把数据通过SendPort传回主Isolate处理。static void getLocation(List<dynamic> args)async{ SendPort sendPort = args[0]; RootIsolateToken token = args[1]; BackgroundIsolateBinaryMessenger.ensureInitialized(token); DartPluginRegistrant.ensureInitialized(); final pos = await Geolocator.getCurrentPosition(); log("pos stream $pos"); sendPort.send(pos); }
内容的提问来源于stack exchange,提问作者Vikash Kumar
相关产品推荐
相关产品推荐

