使用background_locator_2插件遇类型错误:Null无法转为LocationDto
解决background_locator_2空安全适配后的类型转换错误
问题情况
使用background_locator_2插件时,将原本不支持空安全的代码修改后,运行出现类型转换错误,启动或停止插件时触发该异常。
错误信息
[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: type 'Null' is not a subtype of type 'LocationDto'
错误触发位置
错误出现在initState方法的port.listen回调逻辑中:
port.listen( (dynamic data) async { await updateUI(data); }, );
相关代码片段
void main() => runApp(MyApp()); class MyApp extends StatefulWidget { @override _MyAppState createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { ReceivePort port = ReceivePort(); String logStr = ''; bool? isRunning; LocationDto? lastLocation; @override void initState() { super.initState(); if (IsolateNameServer.lookupPortByName( LocationServiceRepository.isolateName) != null) { IsolateNameServer.removePortNameMapping( LocationServiceRepository.isolateName); } IsolateNameServer.registerPortWithName( port.sendPort, LocationServiceRepository.isolateName); port.listen( (dynamic data) async { await updateUI(data); }, ); initPlatformState(); } @override void dispose() { super.dispose(); } Future<void> updateUI(LocationDto data) async { final log = await FileManager.readLogFile(); await _updateNotificationText(data); setState(() { lastLocation = data; logStr = log; }); } Future<void> _updateNotificationText(LocationDto data) async { if (data == null) { return; } await BackgroundLocator.updateNotificationText( title: "new location received", msg: "${DateTime.now()}", bigMsg: "${data.latitude}, ${data.longitude}"); } Future<void> initPlatformState() async { print('Initializing...'); await BackgroundLocator.initialize(); logStr = await FileManager.readLogFile(); print('Initialization done'); final _isRunning = await BackgroundLocator.isServiceRunning(); setState(() { isRunning = _isRunning; }); print('Running ${isRunning.toString()}'); } @override Widget build(BuildContext context) { final start = SizedBox( width: double.maxFinite, child: ElevatedButton( child: Text('Start'), onPressed: () { _onStart(); }, ), ); final stop = SizedBox( width: double.maxFinite, child: ElevatedButton( child: Text('Stop'), onPressed: () { onStop(); }, ), ); final clear = SizedBox( width: double.maxFinite, child: ElevatedButton( child: Text('Clear Log'), onPressed: () { FileManager.clearLogFile(); setState(() { logStr = ''; }); }, ), ); String msgStatus = "-"; if (isRunning != null) { if (isRunning!) { msgStatus = 'Is running'; } else { msgStatus = 'Is not running'; } } final status = Text("Status: $msgStatus"); final log = Text( logStr, ); return MaterialApp( home: Scaffold( appBar: AppBar( title: const Text('Flutter background Locator'), ), body: Container( width: double.maxFinite, padding: const EdgeInsets.all(22), child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: <Widget>[start, stop, clear, status, log], ), ), ), ), ); } void onStop() async { await BackgroundLocator.unRegisterLocationUpdate(); final _isRunning = await BackgroundLocator.isServiceRunning(); setState(() { isRunning = _isRunning; }); } void _onStart() async { await _startLocator(); final _isRunning = await BackgroundLocator.isServiceRunning(); setState(() { isRunning = _isRunning; lastLocation = null; }); } } Future<bool> isLocationAlwaysGranted() async => await Permission.locationAlways.isGranted; Future<bool> askForLocationAlwaysPermission() async { bool granted = await Permission.locationAlways.isGranted; if (!granted) { granted = await Permission.locationAlways.request() == PermissionStatus.granted; } return granted; } Future<void> _startLocator() async { Map<String, dynamic> data = {'countInit': 1}; return await BackgroundLocator.registerLocationUpdate( LocationCallbackHandler.callback, initCallback: LocationCallbackHandler.initCallback, initDataCallback: data, disposeCallback: LocationCallbackHandler.disposeCallback, iosSettings: IOSSettings( accuracy: LocationAccuracy.NAVIGATION, distanceFilter: 0, stopWithTerminate: true), autoStop: false, androidSettings: AndroidSettings( accuracy: LocationAccuracy.NAVIGATION, interval: 5, distanceFilter: 0, client: LocationClient.google, androidNotificationSettings: AndroidNotificationSettings( notificationChannelName: 'Location tracking', notificationTitle: 'Start Location Tracking', notificationMsg: 'Track location in background', notificationBigMsg: 'Background location is on to keep the app up-tp-date with your location. This is required for main features to work properly when the app is not running.', notificationIconColor: Colors.grey, notificationTapCallback: LocationCallbackHandler.notificationCallback))); }
解决方法
错误核心是port收到的数据可能为Null,但updateUI要求传入非空的LocationDto,空安全下类型不匹配导致报错,按以下步骤修改:
1. 调整updateUI方法的参数与逻辑
将参数改为可空类型,并在方法开头判断空值:
Future<void> updateUI(LocationDto? data) async { if (data == null) { return; } final log = await FileManager.readLogFile(); await _updateNotificationText(data); setState(() { lastLocation = data; logStr = log; }); }
2. 优化port.listen的回调判断
在回调中先校验数据类型与非空状态:
port.listen( (dynamic data) async { if (data is LocationDto) { await updateUI(data); } }, );
3. 简化_updateNotificationText方法
因为updateUI已确保数据非空,可移除冗余判断并将参数改为非空类型:
Future<void> _updateNotificationText(LocationDto data) async { await BackgroundLocator.updateNotificationText( title: "new location received", msg: "${DateTime.now()}", bigMsg: "${data.latitude}, ${data.longitude}"); }
完成以上修改后,即可处理port可能收到空值或非预期类型数据的情况,避免空安全下的类型转换错误。
内容的提问来源于stack exchange,提问作者Pannam
相关产品推荐
相关产品推荐

