Flutter动画效果插件actors的使用

发布于 1周前 作者 nodeper 来自 Flutter

Flutter动画效果插件actors的使用

概述

actors 是一个允许在Dart中使用Actor模型的库。它是一个轻量级的包装器,围绕着Dart的 Isolate(在Flutter和Dart VM上)和Web Workers(在Web上 - 待办事项),使它们更易于使用。

Actor

创建和启动一个Actor非常简单。你只需要创建一个实现了处理消息逻辑的 Handler,然后使用它来创建一个 Actor

class Accumulator with Handler<int, int> {
  int _value;

  Accumulator([int initialValue = 0]): _value = initialValue;

  int handle(int n) => _value += n;
}

main() async {
  final actor = Actor.create(() => Accumulator(6));
  print(await actor.send(5)); // 11
  await actor.close();
}

如果你的actor不需要维护内部状态,也可以从函数或lambda表达式创建:

int two(int n) => n * 2;

main() async {
  final actor = Actor.of(two);
  print(await actor.send(5)); // 10
  await actor.close();
}

StreamActor

如果需要返回多个消息,可以使用 StreamActor,它可以从 Handler 返回 Stream

class StreamGenerator with Handler<int, Stream<int>> {
  @override
  Stream<int> handle(int message) {
    return Stream.fromIterable(Iterable.generate(message, (i) => i));
  }
}

main() async {
  final actor = StreamActor.create(StreamGenerator.new);
  final stream = actor.send(2);
  await for (final item in stream) {
    print(item); // 0, 1
  }
  await actor.close();
}

Actor状态

Actor可以在自己的Dart Isolate中安全地维护内部状态,这些状态不能被其他actor访问。状态可以包括任何内容,甚至是 Stream 和打开的socket。初始状态可以通过 Handlerinit 方法进行异步初始化。

class HttpServerActor with Handler<Message, Answer> {
  late final HttpServer _server;
  final int port;

  HttpServerActor(this.port);

  @override
  Future<void> init() async {
    _server = await HttpServer.bind(InternetAddress.loopbackIPv4, port, shared: true);
    unawaited(_serveRequests());
  }

  // ...
}

ActorGroup

ActorGroup 允许将多个 Actor 实例分组在一起,所有实例都基于相同的 Handler 实现,但根据可用策略执行:

  • RoundRobin:轮流发送消息给单个 Actor
  • MultiHandler:发送消息给 mActor,等待至少 n 个成功的响应。
// 创建一个包含4个actor的组
final group = ActorGroup(Two(), size: 4);
print(await group.send(5)); // 10
group.close();

Messenger

Messenger 混入由 ActorActorGroupLocalMessenger 实现,后者在其本地 Isolate 中运行其 Handler

Messenger<int, int> messenger;

// 可以是本地的
messenger = LocalMessenger(Two());
print(await messenger.send(2)); // 4

// 或者是一个Actor
messenger = Actor(Two());
print(await messenger.send(3)); // 6
messenger.close();

// 或者是一个ActorGroup
messenger = ActorGroup(Two(), size: 2);
print(await messenger.send(4)); // 8
print(await messenger.send(5)); // 10
messenger.close();

更多示例

完整示例代码

import 'dart:async';
import 'dart:core';
import 'dart:io';
import 'dart:isolate';

import 'package:actors/actors.dart';

/// Example actor that keeps a counter as its state.
///
/// All it takes for a Dart class to become an Actor is for it to
/// mixin [Handler] and be instantiated like:
///
/// ```dart
/// final actor = Actor(Counter());
/// ```
class Counter with Handler<int, int> {
  int _count = 0;

  @override
  int handle(int n) => _count += n;
}

/// Expected printed output from the main function.
Iterator<String> _expectedLines = [
  // Actors example
  '1', '2', '8', '16', //
  // ActorGroup example
  '10', '12', '14', '16', '18', '20', //
  // StreamActor example
  '0', '1', //
  // LocalMessenger example
  '2', '3', '8', '10', //
].iterator;

// This function overrides Dart's "print" so we can verify the printed output
void printAndCheck(Zone self, ZoneDelegate parent, Zone zone, String line) {
  _expectedLines.moveNext();
  if (line == _expectedLines.current) {
    stdout.writeln(line);
  } else {
    throw Exception('Unexpected line: $line, not ${_expectedLines.current}');
  }
}

void main() async {
  await runZoned(() async {
    await actorExample();
    await actorGroupExample();
    await streamActorExample();
    await localMessengerExample();
  }, zoneSpecification: ZoneSpecification(print: printAndCheck));
}

Future actorExample() async {
  stdout.writeln('Actor example');

  // Create an Actor from a Handler
  final actor = Actor.create(Counter.new);
  print(await actor.send(1)); // 1
  print(await actor.send(1)); // 2
  print(await actor.send(6)); // 8
  print(await actor.send(8)); // 16

  // Close the actor to stop its Isolate
  await actor.close();
}

int times2(int n) {
  // print the name of the current Isolate for debugging purposes
  stdout.write(
      '${Isolate.current.debugName?.padRight(8) ?? 'Isolate'} - times2($n)\n');
  return n * 2;
}

Future actorGroupExample() async {
  stdout.writeln('ActorGroup example');

  // create a group of 4 actors from a simple top-level function...
  // in this example, any of the actors in the group could handle a
  // particular message (default behaviour is to use round-robin),
  // and as we don't wait before sending the next message,
  // messages are handled concurrently!
  final group = ActorGroup.of(times2, size: 4);

  // send a bunch of messages and remember the Futures with answers
  final answers =
      Iterable.generate(6, (index) => index + 5).map(group.send).toList();

  // print each response (type shown explicitly for clarity)
  for (FutureOr<int> answer in answers) {
    print(await answer); // prints 10, then 12, 14, 16, 18, 20
  }

  // closing the group will cause any pending message deliveries to fail!
  await group.close();
}

// A Handler that returns a Stream must use a StreamActor, not an Actor.
class StreamGenerator with Handler<int, Stream<int>> {
  @override
  Stream<int> handle(int message) {
    return Stream.fromIterable(Iterable.generate(message, (i) => i));
  }
}

Future streamActorExample() async {
  stdout.writeln('StreamActor example');

  // Create an StreamActor from a Handler that returns Stream.
  final actor = StreamActor.create(StreamGenerator.new);
  final stream = actor.send(2);
  await for (final item in stream) {
    print(item); // 0, 1
  }
  await actor.close();
}

Future localMessengerExample() async {
  stdout.writeln('LocalMessenger example');

  Messenger<int, int> messenger;

  // a Messenger can be local
  messenger = LocalMessenger(Counter());
  print(await messenger.send(2)); // 2
  await messenger.close();

  // or it can be an Actor
  messenger = Actor.create(Counter.new);
  print(await messenger.send(3)); // 3
  await messenger.close();

  // or an ActorGroup
  messenger = ActorGroup.of(times2, size: 2);
  print(await messenger.send(4)); // 8
  print(await messenger.send(5)); // 10
  await messenger.close();
}

以上就是关于 actors 插件的详细介绍和使用方法,希望对你有所帮助。


更多关于Flutter动画效果插件actors的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter动画效果插件actors的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter中,actors 包通常指的是用于播放Flutter动画效果(如Lottie动画)的插件。尽管Flutter本身提供了丰富的动画支持,但actors 包(或更常见的Lottie插件)可以让开发者轻松地在Flutter应用中集成复杂的动画。这里,我将展示如何使用 lottie 包来播放Lottie动画。

首先,确保你的 pubspec.yaml 文件中已经添加了 lottie 依赖:

dependencies:
  flutter:
    sdk: flutter
  lottie: ^1.2.1  # 请检查最新版本号

然后运行 flutter pub get 来获取依赖。

接下来是一个简单的例子,展示如何在Flutter应用中使用Lottie动画:

import 'package:flutter/material.dart';
import 'package:lottie/lottie.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Lottie Animation Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  // 假设你有一个名为 'animation.json' 的Lottie动画文件放在assets目录下
  final String assetName = 'assets/animation.json';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Lottie Animation Demo'),
      ),
      body: Center(
        child: Lottie.asset(assetName,
          // 可选的参数,控制动画的播放
          width: 300, // 设置动画宽度
          height: 300, // 设置动画高度
          repeat: false, // 是否循环播放
          reverse: false, // 是否反向播放
          animate: true, // 是否自动播放动画
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          // 你可以在这里添加代码来控制动画的播放、暂停等
          // 例如,如果你有一个LottieController,你可以调用它的play()或pause()方法
        },
        tooltip: 'Play/Pause',
        child: Icon(Icons.play_arrow),
      ),
    );
  }
}

在这个例子中,我们创建了一个简单的Flutter应用,其中包含一个Lottie动画。Lottie.asset 方法用于从应用的assets目录加载Lottie动画文件。你可以通过传递不同的参数来控制动画的播放行为,如宽度、高度、是否循环、是否反向以及是否自动播放。

注意,你需要将Lottie动画文件(例如 animation.json)放在项目的 assets 目录下,并在 pubspec.yaml 文件中声明它:

flutter:
  assets:
    - assets/animation.json

这样,你的Flutter应用就可以成功加载并播放Lottie动画了。如果你需要更复杂的动画控制,比如播放、暂停、跳转到特定帧等,你可以使用 LottieController,这需要一些额外的设置和状态管理。

回到顶部