Flutter中StatefulWidget使用Dart Isolate报错的问题及解决
问题描述
在Flutter中使用Dart Isolate时,StatelessWidget环境下运行正常,但改为StatefulWidget后出现如下错误:
E/flutter ( 3154): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: Invalid argument(s): Illegal argument in isolate message: (object extends NativeWrapper - Library:'dart:ui' Class: EngineLayer) E/flutter ( 3154): #0 Isolate._spawnFunction (dart:isolate-patch/isolate_patch.dart:399:25) E/flutter ( 3154): #1 Isolate.spawn (dart:isolate-patch/isolate_patch.dart:379:7) E/flutter ( 3154): #2 _TestScreenState.createIsolate test_screen.dart:18 E/flutter ( 3154): #3 _TestScreenState.build.<anonymous closure> test_screen.dart:38 E/flutter ( 3154): #4 _InkResponseState.handleTap ink_well.dart:1096 E/flutter ( 3154): #5 GestureRecognizer.invokeCallback recognizer.dart:253 E/flutter ( 3154): #6 TapGestureRecognizer.handleTapUp tap.dart:627 E/flutter ( 3154): #7 BaseTapGestureRecognizer._checkUp tap.dart:306 E/flutter ( 3154): #8 BaseTapGestureRecognizer.handlePrimaryPointer tap.dart:239 E/flutter ( 3154): #9 PrimaryPointerGestureRecognizer.handleEvent recognizer.dart:615 E/flutter ( 3154): #10 PointerRouter._dispatch pointer_router.dart:98 E/flutter ( 3154): #11 PointerRouter._dispatchEventToRoutes.<anonymous closure> pointer_router.dart:143 E/flutter ( 3154): #12 _LinkedHashMapMixin.forEach (dart:collection patch/compact_hash.dart:625:13) E/flutter ( 3154): #13 PointerRouter._dispatchEventToRoutes E/flutter ( 3154): #14 PointerRouter.route pointer_router.dart:127 E/flutter ( 3154): #15 GestureBinding.handleEvent binding.dart:460 E/flutter ( 3154): #16 GestureBinding.dispatchEvent binding.dart:440 E/flutter ( 3154): #17 RendererBinding.dispatchEvent binding.dart:336 E/flutter ( 3154): #18 GestureBinding._handlePointerEventImmediately binding.dart:395 E/flutter ( 3154): #19 GestureBinding.handlePointerEvent binding.dart:357 E/flutter ( 3154): #20 GestureBinding._flushPointerEventQueue binding.dart:314 E/flutter ( 3154): #21 GestureBinding._handlePointerDataPacket binding.dart:295 E/flutter ( 3154): #22 _invoke1 (dart:ui/hooks.dart:164:13) E/flutter ( 3154): #23 PlatformDispatcher._dispatchPointerDataPacket (dart:ui/platform_dispatcher.dart:361:7) E/flutter ( 3154): #24 _dispatchPointerDataPacket (dart:ui/hooks.dart:91:31)
对应的StatefulWidget代码如下:
import 'dart:async'; import 'dart:isolate'; import 'package:flutter/material.dart'; class TestScreen extends StatefulWidget { const TestScreen({super.key}); @override State<TestScreen> createState() => _TestScreenState(); } class _TestScreenState extends State<TestScreen> { void createIsolate() async { print('Inside createIsolate'); final p = ReceivePort(); await Isolate.spawn(_createIsolate, p.sendPort); print(await p .takeWhile((element) => element is String) .take(1) .cast<String>() .first); } Future<void> _createIsolate(SendPort p) async { print('Spawned isolate started.'); p.send('This is the message!'); Isolate.exit(); } @override Widget build(BuildContext context) { return Scaffold( body: Center( child: ElevatedButton( onPressed: () => createIsolate(), child: const Text('Create isolate'), ), ), ); } }
错误指向await Isolate.spawn(_createIsolate, p.sendPort);一行,但改为StatelessWidget时能正常输出:
I/flutter ( 3154): Inside createIsolate I/flutter ( 3154): Spawned isolate started. I/flutter ( 3154): This is the message!
需求是保留StatefulWidget,并要在initState方法中使用Isolate。
原因分析
问题出在_createIsolate是_TestScreenState类的成员方法:当调用Isolate.spawn(_createIsolate, p.sendPort)时,Dart会隐式地把当前State实例的引用传递给新Isolate,而State对象内部持有和Flutter UI相关的原生包装对象(比如错误日志里的EngineLayer),这类对象属于NativeWrapper,无法跨Isolate传递——Isolate之间只能传递可序列化的对象或SendPort这类允许的通信对象。
而在StatelessWidget中,对应的方法要么是顶层函数,要么是Widget的成员方法,不会绑定包含UI相关原生对象的State实例,因此不会触发这个错误。
解决方案
把_createIsolate改为顶层函数,或者改为State类的静态方法,切断它和State实例的绑定,避免传递不可跨Isolate的对象。
修改后的代码示例(静态方法版本)
import 'dart:async'; import 'dart:isolate'; import 'package:flutter/material.dart'; class TestScreen extends StatefulWidget { const TestScreen({super.key}); @override State<TestScreen> createState() => _TestScreenState(); } class _TestScreenState extends State<TestScreen> { @override void initState() { super.initState(); createIsolate(); // 在initState中调用Isolate } void createIsolate() async { print('Inside createIsolate'); final p = ReceivePort(); await Isolate.spawn(_createIsolate, p.sendPort); final message = await p .takeWhile((element) => element is String) .take(1) .cast<String>() .first; print(message); p.close(); // 记得关闭ReceivePort,避免内存泄漏 } // 改为静态方法,不再绑定State实例 static Future<void> _createIsolate(SendPort p) async { print('Spawned isolate started.'); p.send('This is the message!'); Isolate.exit(); } @override Widget build(BuildContext context) { return Scaffold( body: Center( child: ElevatedButton( onPressed: () => createIsolate(), child: const Text('Create isolate'), ), ), ); } }
修改后的代码示例(顶层函数版本)
import 'dart:async'; import 'dart:isolate'; import 'package:flutter/material.dart'; // 顶层函数,脱离State类 Future<void> _createIsolate(SendPort p) async { print('Spawned isolate started.'); p.send('This is the message!'); Isolate.exit(); } class TestScreen extends StatefulWidget { const TestScreen({super.key}); @override State<TestScreen> createState() => _TestScreenState(); } class _TestScreenState extends State<TestScreen> { @override void initState() { super.initState(); createIsolate(); } void createIsolate() async { print('Inside createIsolate'); final p = ReceivePort(); await Isolate.spawn(_createIsolate, p.sendPort); final message = await p .takeWhile((element) => element is String) .take(1) .cast<String>() .first; print(message); p.close(); } @override Widget build(BuildContext context) { return Scaffold( body: Center( child: ElevatedButton( onPressed: () => createIsolate(), child: const Text('Create isolate'), ), ), ); } }
这样修改后,Isolate就能正常在StatefulWidget中运行,包括initState方法里的调用。
内容的提问来源于stack exchange,提问作者sm-sayedi

