You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 04:05:23