我已经创建了一个基本示例来模拟我的问题,以下是代码:

import 'dart:async';
import 'dart:isolate';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:image/image.dart' as img;

class Home extends StatefulWidget {
  const Home({super.key});

  @override
  State<Home> createState() => _HomeState();
}

class _HomeState extends State<Home> {
  Uint8List? foto;

  Future<Widget> loadImage() async {
    final ByteData data = await rootBundle.load('assets/images/IMG_4510.HEIC');

    foto = data.buffer.asUint8List();

    return Image.memory(foto!);
  }

  void _rotateIsolate(SendPort sendPort) {
    final ReceivePort receivePort = ReceivePort();

    sendPort.send(receivePort.sendPort);

    receivePort.listen((dynamic data) {
      if (data is Uint8List) {
        final rotatedImageBytes = _doRotate(data);

        sendPort.send(rotatedImageBytes);
      }
    });
  }

  Uint8List _doRotate(Uint8List foto) {
    img.Image image = img.decodeImage(foto)!;
    img.Image rotatedImage = img.copyRotate(image, angle: 90);

    return Uint8List.fromList(img.encodePng(rotatedImage));
  }

  Future<void> rotateImage() async {
    final receivePort = ReceivePort();

    await Isolate.spawn(_rotateIsolate, receivePort.sendPort);

    final completer = Completer<Uint8List>();

    receivePort.listen((message) {
      completer.complete(message);

      receivePort.close();
    });

    foto = await completer.future;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Column(
          children: [
            FutureBuilder(
              future: loadImage(),
              builder: (context, snapshot) {
                if (snapshot.hasData) {
                  return snapshot.data as Widget;
                } else {
                  return const CircularProgressIndicator();
                }
              },
            ),
            ElevatedButton(
              onPressed: () => rotateImage(),
              child: const Text('Rotate'),
            )
          ],
        ),
      ),
    );
  }
}

当我按下按钮Rotate flutter抛出这个异常:

Unhandled Exception: Invalid argument(s): Illegal argument in isolate message: object is unsendable - Library:'dart:async' Class: _AsyncCompleter@4048458 (see restrictions listed at `SendPort.send()` documentation for more information)

这很奇怪,因为我的代码基于this guy's work,它从网络上下载一张图片,所以我认为旋转一张图片应该是几乎相同的事情.

我还try 了一些基本的并行计算,如计算斐波纳契数列,如第this medium post节所述,但抛出了完全相同的异常.

现在我不知道是我做错了什么,还是Flutter 引擎出了什么问题.

我正在使用Flutter引擎的稳定通道的最新版本(截至今天): Flutter (Channel stable, 3.16.8)

有人知道为什么在不同的(和测试的)场景中抛出相同的异常吗?

PS:本地资源可以是任何图像

推荐答案

TL;DR:您不能将值为_HomeState的方法作为Isolate的回调函数传递(通过使该方法成为顶级函数来修复它),但是状态和小部件也有一些其他问题.


首先,让我们使用Ffltter的compute函数来简化隔离工作.

(为什么?因为我们的主要关注点是修复Illegal argument in isolate message错误,而且无论如何,对于简单的隔离任务,使用compute是首选的.请参见:Using isolates.基本上,Isolate.runIsolate.spawn更受欢迎,而computeIsolate.run的Flutter 翼版本.)

导入package:flutter/foundation.dart并将rotateImage方法替换为以下代码:

Future<void> rotateImage() async {
  foto = await compute(_doRotate, foto!);
}

现在,当单击旋转按钮时,您将看到完全相同的错误消息.这是因为发送的回调函数是_doRotate,是_HomeState类的方法.要修复它,请将_doRotate移到_HomeState类之外,并使其成为顶级函数.完成此操作后,您必须停止应用程序并重新运行(热重启不起作用).

现在,当您单击旋转时,不会显示错误,但也不会发生任何事情.发生这种情况是因为:

  1. 如果我们只像这样重新分配状态,小部件将不会注意到状态变化:foto = await compute(_doRotate, foto!);
  2. FutureBuilder将始终从返回值loadImage中获取数据,这是资源中的原始图像.

要解决第一个问题,我们必须使用setState:

Future<void> rotateImage() async {
  final rotatedFoto = await compute(_doRotate, foto!);
  setState(() {
    foto = rotatedFoto;
  });
}

要解决第二个问题,我们只需使用一个简单的三元运算符来显示图像,而不是使用FutureBuilder:

Column(
  children: [
    foto != null ? Image.memory(foto!) : CircularProgressIndicator(),
    ElevatedButton(
      onPressed: () => rotateImage(),
      child: const Text('Rotate'),
    )
  ],
),

最后,我们必须将值初始化为foto.在_HomeState中添加以下方法:

@override
void initState() {
  super.initState();
  initFoto();
}

Future<void> initFoto() async {
  final ByteData data = await rootBundle.load('assets/images/profile.png');
  setState(() {
    foto = data.buffer.asUint8List();
  });
}

现在它应该可以像预期的那样工作了,我们已经成功地使用了分离隔离来旋转图像.我们可以删除loadImage_rotateIsolate个方法,因为它们不再被使用.


FULL CODE:

import 'dart:async';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:image/image.dart' as img;

Uint8List _doRotate(Uint8List foto) {
  img.Image image = img.decodeImage(foto)!;
  img.Image rotatedImage = img.copyRotate(image, angle: 90);

  return Uint8List.fromList(img.encodePng(rotatedImage));
}

class Home extends StatefulWidget {
  const Home({super.key});

  @override
  State<Home> createState() => _HomeState();
}

class _HomeState extends State<Home> {
  Uint8List? foto;

  Future<void> rotateImage() async {
    final rotatedFoto = await compute(_doRotate, foto!);
    setState(() {
      foto = rotatedFoto;
    });
  }

  @override
  void initState() {
    super.initState();
    initFoto();
  }

  Future<void> initFoto() async {
    final ByteData data = await rootBundle.load('assets/images/IMG_4510.HEIC');

    setState(() {
      foto = data.buffer.asUint8List();
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Column(
          children: [
            foto != null ? Image.memory(foto!) : CircularProgressIndicator(),
            ElevatedButton(
              onPressed: () => rotateImage(),
              child: const Text('Rotate'),
            )
          ],
        ),
      ),
    );
  }
}

Flutter相关问答推荐

更改BottomNavigationBar onTap?的大小

关闭模式,但再次打开时微件条件相同

将记录/元组用作SplayTreeMap中的键

Flutter Xcode 15 错误(Xcode):DT_TOOLCHAIN_DIR 无法用于判断 LIBRARY_SEARCH_PATHS

Flutter:我的应用程序是否需要包含退款购买?

Select 文本时如何更改 Flutter 中的光标 colored颜色 ?

如何修改来自合并 list 的 Android list 权限?

更新番茄计时器值不会触发 Flutter Riverpod 中的alert 声音

网页浏览器不支持鼠标滚动的行项目

Flutter底部面板局部显示

如何解决需要一个标识符,但得到的是:.try 在:之前插入一个标识符.dart 错误

Flutter Dart 功能弹出错误的小部件?

减少 Flutter 中 API 的连接等待时间

当我使用 Confetti Widget 时,Flutter Android 和 IOS 应用程序崩溃

访问 AsyncSnapshot 中的嵌套属性

在不丢失游戏状态的情况下从 Flame 游戏导航到 Flutter 小部件

为什么 setState 不改变我的变量 Flutter?

如何在手势检测器中获得涟漪效应

如何在Flutter 中根据其父小部件高度设置图标按钮飞溅半径

如何从 MemoryFileSystem 字节创建假 dart:io 文件?