搜尋套件、儲存庫與 APP 分類

套件詳情v2.1.0

flutter_isolate

FlutterIsolate 提供了一種在 Flutter 中啟動 Dart Isolate 的方式,可與 Flutter 插件一起使用。

AndroidiOS
近 30 天下載
1.8萬
喜歡數
482
Pub 分數
150

安裝套件

flutter pub add flutter_isolate

套件介紹

根據套件公開資料產生的選型評估,僅供參考。

FlutterIsolate 允許在 Flutter 應用中建立和管理 Dart 隔離,從而能在不阻塞主要 UI 線程的情況下並行執行 CPU 負載型任務。它特別設計為能與 Flutter 外掛無縫配合,確保基於隔離的操作可安全地與平台特定功能互動。此套件透過利用 Dart 的隔離模型簡化了 Flutter 應用中的背景處理,同時維持與現有外掛生態系統的相容性。

適用情境

  • 在背景中執行重型計算
  • 處理大型資料集而不凍結使用者介面
  • 在隔離環境中執行平台特定程式碼
  • 在密集任務期間提高應用程式回應速度
  • 在並行工作流中與現有的 Flutter 插件整合

主要特色

  • 與 Flutter 插件無縫整合
  • 支援 Android 與 iOS 平台
  • 用於啟動隔離區的簡單 API
  • 主線程與隔離執行緒之間的安全通訊
  • 用於隔離執行的內建錯誤處理

適合對象

  • 需要在Flutter中進行背景處理的開發者
  • CPU密集型操作的應用程式,如影像或音訊處理
  • 使用需要隔離的 Flutter 插件的團隊
  • 旨在提升UI性能的項目
  • 需要安全、隔離執行環境的應用程式

使用建議

  • 需要理解 Dart 的 isolate 和訊息傳遞
  • 僅限 Android 與 iOS 平台
  • 由於隔離區開銷,可能導致記憶體使用量增加
  • 不適用於 I/O 緊密型任務,這些任務使用 async/await 即可
  • 跨隔離區通訊在除錯中可能具有複雜性

原始 README

以下為關聯原始碼儲存庫的 README 原文快照,最新內容請造訪原始碼儲存庫。

FlutterIsolate

A Dart isolate is roughly equivalent to a single, independent execution thread. In a Flutter context, creating ("spawning") an isolate allows code to execute outside the main thread, which is important for running expensive/long-running tasks that would otherwise block the UI.

However, code in a spawned isolate will generally not be able to interact with Flutter plugins. This is due to the tight integration between the platform plugin scaffolding and the main application isolate.

The FlutterIsolate plugin fixes this with the introduction of a FlutterIsolate class, which is a wrapper around the platform APIs for creating isolates and setting up the bindings necessary for code running in spawned isolates to communicate with Flutter plugins.

FlutterIsolate API
Android iOS Description
FlutterIsolate.spawn(entryPoint,message) :white_check_mark: :white_check_mark: spawns a new FlutterIsolate
FlutterIsolate.pause() :white_check_mark: :white_check_mark: pauses a running isolate
FlutterIsolate.resume() :white_check_mark: :white_check_mark: resumed a paused isolate
FlutterIsolate.kill() :white_check_mark: :white_check_mark: kills a an isolate
FlutterIsolate.killAll() :white_check_mark: :white_check_mark: kills all currently running isolates
FlutterIsolate.runningIsolates :white_check_mark: :white_check_mark: returns the IDs associated with all currently running isolates
flutterCompute(callback,message) :white_check_mark: :white_check_mark: spawns a new FlutterIsolate, runs callback and returns the returned value
Usage

To spawn a FlutterIsolate, call the spawn method with a top-level or static function that has been annotated with the @pragma('vm:entry-point') decorator:

import 'package:flutter_isolate/flutter_isolate.dart';

@pragma('vm:entry-point')
void someFunction(String arg) { 
  print("Running in an isolate with argument : $arg");
}
...

class SomeWidget() {
...

@override
  Widget build(BuildContext context) {
    return ElevatedButton(
            child: Text('Run'),
            onPressed: () {
                FlutterIsolate.spawn(someFunction, "hello world");
            },
    );
}

If you just want to spawn an isolate to perform a single task (like the Flutter compute method), call flutterCompute:

@pragma('vm:entry-point')
Future<int> expensiveWork(int arg) async {
  int result;
  // lots of calculations
  return result;
}

Future<int> doExpensiveWorkInBackground() async {
  return await flutterCompute(expensiveWork, arg);
}

Isolates can also be spawned from other isolates:

import 'package:flutter_startup/flutter_startup.dart';
import 'package:flutter_isolate/flutter_isolate.dart';

@pragma('vm:entry-point')
void isolate2(String arg) {
  FlutterStartup.startupReason.then((reason){
    print("Isolate2 $reason");
  });
  Timer.periodic(Duration(seconds:1),(timer)=>print("Timer Running From Isolate 2"));
}

@pragma('vm:entry-point')
void isolate1(String arg) async  {

  final isolate = await FlutterIsolate.spawn(isolate2, "hello2");

  FlutterStartup.startupReason.then((reason){
    print("Isolate1 $reason");
  });
  Timer.periodic(Duration(seconds:1),(timer)=>print("Timer Running From Isolate 1"));
}

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  final isolate = await FlutterIsolate.spawn(isolate1, "hello");
  Timer(Duration(seconds:5), (){print("Pausing Isolate 1");isolate.pause();});
  Timer(Duration(seconds:10),(){print("Resuming Isolate 1");isolate.resume();});
  Timer(Duration(seconds:20),(){print("Killing Isolate 1");isolate.kill();});

  runApp(MyApp());
}
...

See example/lib/main.dart for example usage with the flutter_downloader plugin.

It is important to note that the entrypoint must be a top-level function, decorated with the `@pragma('vm:entry-point') annotation:

@pragma('vm:entry-point')
void topLevelFunction(Map<String, dynamic> args) {
  // performs work in an isolate
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {

  @override
  void initState() {
    FlutterIsolate.spawn(topLevelFunction, {});
    super.initState();
  }
  Widget build(BuildContext context) {
    return Container();
  }
}

or a static method:

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  
  @pragma('vm:entry-point')
  static void topLevelFunction(Map<String, dynamic> args) {
    // performs work in an isolate
  }

  @override
  void initState() {
    FlutterIsolate.spawn(_MyAppState.staticMethod, {});
    super.initState();
  }
  Widget build(BuildContext context) {
    return Container();
  }
}

A class-level method will not work and will throw an Exception:

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {

  
  void classMethod(Map<String, dynamic> args) {
    // don't do this!
  }

  @override
  void initState() {
    
    FlutterIsolate.spawn(classMethod, {}); // this will throw NoSuchMethodError: The method 'toRawHandle' was called on null.
    super.initState();
  }
  Widget build(BuildContext context) {
    return Container();
  }
}

Failure to add the @pragma('vm:entry-point') annotation will cause the app to crash in release mode.

Notes

Due to a FlutterIsolate being backed by a platform specific 'view', the event loop will not terminate when there is no more 'user' work left to do and FlutterIsolates will require explict termination with kill().

Additionally this plugin has not been tested with a large range of plugins, only a small subset I have been using such as flutter_notification, flutter_blue and flutter_startup.

Communicating between isolates

To pass data between isolates, a ReceivePort should be created on your (parent) isolate with the corresponding SendPort sent via the spawn method:

@pragma('vm:entry-point')
void spawnIsolate(SendPort port) {
  port.send("Hello!");
}

void main() {
  var port = ReceivePort();
  port.listen((msg) {
    print("Received message from isolate $msg");
  });
  var isolate = await FlutterIsolate.spawn(spawnIsolate, port.sendPort);

}

Only primitives can be sent via a SendPort - see the SendPort documentation for further details.

Custom plugin registrant

See the example project for a sample implementation using a custom plugin registrant.

iOS

By default, flutter_isolate will register all plugins provided by Flutter's automatically generated GeneratedPluginRegistrant.m file.

If you want to register, e.g. some custom FlutterMethodChannels, you can define a custom registrant:

// Defines a custom plugin registrant, to be used specifically together with FlutterIsolatePlugin
@objc(IsolatePluginRegistrant) class IsolatePluginRegistrant: NSObject {
    @objc static func register(withRegistry registry: FlutterPluginRegistry) {
        // Register channels for Flutter Isolate
        registerMethodChannelABC(bm: registry.registrar(forPlugin: "net.myapp.myChannelABC").messenger())

        // Register default plugins
        GeneratedPluginRegistrant.register(with: registry)
    }
}

// In AppDelegate.swift
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        let controller = window.rootViewController as! FlutterViewController

        // Register custom channels for Flutter
        registerMethodChannelABC(bm: controller.binaryMessenger) // <-- the custom method channel

        // Point FlutterIsolatePlugin to use our previously defined custom registrant.
        // The string content must be equal to the plugin registrant class annotation 
        // value: @objc(IsolatePluginRegistrant)
        FlutterIsolatePlugin.isolatePluginRegistrantClassName = "IsolatePluginRegistrant" // <--

        GeneratedPluginRegistrant.register(with: self)
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }
}
Android

Define a custom plugin registrant, to be used specifically together with FlutterIsolatePlugin:

public final class CustomPluginRegistrant {
  public static void registerWith(@NonNull FlutterEngine flutterEngine) {
    flutterEngine.getPlugins().add(... [your plugin goes here]);
  }
}

Create a MainApplication class that sets this custom isolate registrant:

public class MainApplication extends FlutterApplication {
  public MainApplication() {
    FlutterIsolatePlugin.setCustomIsolateRegistrant(CustomPluginRegistrant.class);
  }
}

所屬分類

依賴

依賴清單 1 項

原始碼儲存庫

使用此套件的專案