Flutter更新依赖后出现_lifecycleState != defunct断言错误求助
解决Flutter蓝牙打印机页面切换时的
_lifecycleState != _ElementLifecycle.defunct崩溃问题 更新旧Flutter项目依赖后,测试蓝牙热敏打印机连接页面时,每次打开或关闭页面都会触发崩溃,报错信息如下:
'package:flutter/src/widgets/framework.dart': Failed assertion: line 5004 pos 12: '_lifecycleState != _ElementLifecycle.defunct': is not true.
页面代码示例:
import 'dart:io'; import 'package:flutter/material.dart'; import 'dart:async'; import 'package:blue_thermal_printer/blue_thermal_printer.dart'; import 'package:flutter/services.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:path_provider/path_provider.dart'; import 'package:ploans_mobile/models/settings/printerSettings.dart'; import 'package:ploans_mobile/services/testPrinter.dart'; class PrinterSettingsPage extends StatefulWidget { @override _PrinterSettingsPageState createState() => new _PrinterSettingsPageState(); } class _PrinterSettingsPageState extends State<PrinterSettingsPage> { BlueThermalPrinter bluetooth = BlueThermalPrinter.instance; List<BluetoothDevice> _devices = []; BluetoothDevice? _device; bool _connected = false; bool _pressed = false; String pathImage = ''; late TestPrint testPrint; @override void initState() { super.initState(); initPlatformState(); initSavetoPath(); testPrint = TestPrint(); } @override void dispose() { _disconnect(); _connected = false; super.dispose(); } initSavetoPath() async { //read and write //image max 300px X 300px final filename = 'yourlogo.png'; var bytes = await rootBundle.load("lib/Assets/images/yourlogo.png"); String dir = (await getApplicationDocumentsDirectory()).path; writeToFile(bytes, '$dir/$filename'); setState(() { pathImage = '$dir/$filename'; }); } void _showToast(String text) { Fluttertoast.showToast( msg: text, toastLength: Toast.LENGTH_SHORT, timeInSecForIosWeb: 3); } Future<void> initPlatformState() async { List<BluetoothDevice> devices = []; try { devices = await bluetooth.getBondedDevices(); } on PlatformException { // Ok _showToast('Error getting list of devices'); } bluetooth.onStateChanged().listen((state) { switch (state) { case BlueThermalPrinter.CONNECTED: setState(() { _connected = true; _pressed = false; }); break; case BlueThermalPrinter.DISCONNECTED: setState(() { _connected = false; _pressed = false; }); break; default: print(state); break; } }); if (!mounted) return; setState(() { _devices = devices; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( foregroundColor: Colors.white, title: Text('Thermal Printer'), backgroundColor: Color.fromRGBO(58, 66, 86, 1.0), ), body: Container( child: ListView( children: <Widget>[ Padding( padding: const EdgeInsets.fromLTRB(10.0, 10.0, 10.0, 0.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: <Widget>[ Text( 'Device:', style: TextStyle( fontWeight: FontWeight.bold, ), ), DropdownButton( items: _getDeviceItems(), onChanged: (value) => setState(() { _device = value as BluetoothDevice; }), value: _device, ), ElevatedButton( onPressed: _pressed ? null : _connected ? _disconnect : _connect, child: Text(_connected ? 'Disconnect' : 'Connect'), ), ], ), ), Padding( padding: const EdgeInsets.only(left: 10.0, right: 10.0, top: 50), child: Container( decoration: BoxDecoration( color: Color.fromRGBO(58, 66, 86, 1.0), borderRadius: BorderRadius.only( topRight: Radius.circular(32), bottomRight: Radius.circular(32)), ), height: 55, child: ListTile( contentPadding: EdgeInsets.symmetric( horizontal: 5.0, ), trailing: Container( height: 40, width: 40, decoration: BoxDecoration( color: Color.fromRGBO(253, 253, 253, 1.0), borderRadius: BorderRadius.all(Radius.circular(32)), ), child: Icon( Icons.print, color: Color.fromRGBO(58, 66, 86, 1.0), size: 25, ), ), onTap: () { testPrint.sample(pathImage); }, ), )), Padding( padding: const EdgeInsets.only(left: 10.0, right: 10.0, top: 50), child: Container( decoration: BoxDecoration( color: Color.fromRGBO(58, 66, 86, 1.0), borderRadius: BorderRadius.only( topRight: Radius.circular(32), bottomRight: Radius.circular(32)), ), height: 55, child: ListTile( contentPadding: EdgeInsets.symmetric( horizontal: 5.0, ), trailing: Container( height: 40, width: 40, decoration: BoxDecoration( color: Color.fromRGBO(253, 253, 253, 1.0), borderRadius: BorderRadius.all(Radius.circular(32)), ), child: Icon( Icons.save, color: Color.fromRGBO(58, 66, 86, 1.0), size: 25, ), ), onTap: () { _submit(); }, ), )), ], ), ), ); } List<DropdownMenuItem<BluetoothDevice>> _getDeviceItems() { List<DropdownMenuItem<BluetoothDevice>> items = []; if (_devices.isEmpty) { items.add(DropdownMenuItem( child: Text('NONE'), )); } else { _devices.forEach((device) { items.add(DropdownMenuItem( child: Text(device.name!), value: device, )); }); } return items; } void _connect() { if (_device == null) { show('No device selected.'); } else { bluetooth.isConnected.then((isConnected) { if (!isConnected!) { bluetooth.connect(_device!).catchError((error) { setState(() => _pressed = false); }); setState(() => _pressed = true); } }); } } void _disconnect() { if (_connected) { bluetooth.disconnect(); } setState(() => _pressed = true); } void _submit() { if (_connected == true) { var connection = PrinterSettings(); connection.name = _device!.name!; connection.address = _device!.address!; connection.savePrinterSettings(connection); _disconnect(); Navigator.of(context).pop(); } else { _showToast("Pleased set a printer first!"); return null; } } //write to app path Future<void> writeToFile(ByteData data, String path) { final buffer = data.buffer; return new File(path).writeAsBytes( buffer.asUint8List(data.offsetInBytes, data.lengthInBytes)); } Future show( String message, { Duration duration = const Duration(seconds: 3), }) async { await new Future.delayed(new Duration(milliseconds: 100)); ScaffoldMessenger.of(context).showSnackBar( new SnackBar( content: new Text( message, style: new TextStyle( color: Colors.white, ), ), duration: duration, ), ); } }
崩溃原因分析
核心问题是页面销毁后,蓝牙状态监听的回调仍触发setState:
- 在
initPlatformState中订阅了bluetooth.onStateChanged(),但未在页面销毁时取消订阅 dispose方法仅调用了断开蓝牙,未处理监听的取消- 页面关闭时,蓝牙断开的异步回调可能延迟触发,此时State对象已处于
defunct状态,无法更新UI
修复步骤
保存蓝牙状态监听的Subscription对象
在State类中添加变量:StreamSubscription? _bluetoothStateSubscription;订阅时保存Subscription并添加mounted判断
修改initPlatformState中的监听逻辑:_bluetoothStateSubscription = bluetooth.onStateChanged().listen((state) { switch (state) { case BlueThermalPrinter.CONNECTED: if (mounted) { setState(() { _connected = true; _pressed = false; }); } break; case BlueThermalPrinter.DISCONNECTED: if (mounted) { setState(() { _connected = false; _pressed = false; }); } break; default: print(state); break; } });在dispose中取消订阅
更新dispose方法:@override void dispose() { _bluetoothStateSubscription?.cancel(); _disconnect(); super.dispose(); }全局添加mounted判断
所有异步回调中的setState都需先检查组件状态:// 修改initSavetoPath中的setState if (mounted) { setState(() { pathImage = '$dir/$filename'; }); } // 修改_connect中的错误回调 bluetooth.connect(_device!).catchError((error) { if (mounted) { setState(() => _pressed = false); } });优化_submit中的导航逻辑
等待蓝牙断开完成后再执行页面导航:void _submit() async { if (_connected == true) { var connection = PrinterSettings(); connection.name = _device!.name!; connection.address = _device!.address!; connection.savePrinterSettings(connection); await bluetooth.disconnect(); if (mounted) { Navigator.of(context).pop(); } } else { _showToast("请先设置打印机!"); } }
内容的提问来源于stack exchange,提问作者Fermin Salcedo
相关产品推荐
相关产品推荐

