Flutter通信插件meowchannel的使用

Flutter通信插件meowchannel的使用

meowchannel v2.0.0

轻量级的Flutter Redux实现,带有工作线程
… 还有猫咪! 😼

从v1.3.0迁移

package:meowchannel/worker/* -> package:meowchannel/extensions/worker/*
package:meowchannel/computed/* -> package:meowchannel/extensions/computed/*
迁移到null-safety

从v1.2.0迁移

await initializeMeowChannel(); 应该被添加以初始化meowchannel

从v1.1.0迁移

新版本引入了非阻塞的Reducer和Dispatcher以提高应用程序性能。
所有阻塞的Reducer必须用syncedReducer包装。
所有工作者必须await任何在上下文中发布的操作,以确保其被发布。
所有中间件必须返回Future<void>

概述

概述图

在哪里?

动作(Actions) - 包含数据的有效负载,从视图发送到Store或从中间件发送到Store,描述发生了某件事。可以通过dispatch(action)发送它们。
每个动作都必须扩展自Action类。

状态(State) - 描述应用程序所需的一切的单一结构,以便向用户展示其视图

Reducer - 纯函数,它接受最新分发的动作和以前的状态,并创建(而不是修改!)一个新的状态。伪代码:

Reducer stateReducer = (Action action, State previousState) => newState

以下是重要的理解点:

  • Reducer 是一个函数:不应存在副作用,如调用API或其他外部非纯函数。
  • 尽管Reducer 在异步线程中被调用,但不应执行长时间复杂任务(使用Worker来处理)。
  • State 是不可变的:每个动作的Reducer 应生成以前状态的修改副本或返回旧状态而不进行修改。

中间件(Middleware) - 在动作到达Reducer之前拦截它们的功能。它不是纯的——它可以产生副作用,包括分发其他动作。 伪代码:

Middleware<State> example = (Dispatcher dispatcher, Function<State> getState, Dispatcher next) => (Action action) async {
  doSomething();
  await next(action);
}
  • 中间件 在异步线程中被调用。
  • WorkerMiddleware 是对Store的扩展,引入了异步Worker来消费动作、执行一些工作并分发带有结果的新动作。 伪代码:
Worker<Action, State> example = extensions.worker((context, action) async {
  await doSomething();
  context.put(ResultAction());
})

Store - 组合了动作Dispatcher(即中间件->Reducer),并且还:

  • 持有State及其通道,应用程序可以监听这些通道。
  • 允许通过dispatch(action)分发动作
  • 允许通过getState()直接访问当前状态
  • 分发与生命周期相关的动作(如MeowChannelInit和MeowChannelClose)。 重要点:
  • 允许创建多个具有唯一StateStore
  • 不允许有两个或更多具有相同State类型的Store

示例:构建一个待办事项应用!

首先,我们需要定义我们的TODO模型。

// 使用 https://pub.dev/packages/dataclass 定义实体类 (类似于 Kotlin 的数据类)
@DataClass()
class Todo with _$Todo {
  final int id;
  final String title;
  final String text;

  Todo({
    this.id,
    this.title,
    this.text,
  });
}

接下来,我们需要定义一些动作(Actions)

// 每个动作都必须继承自 Action

// 这些动作是数据相关的
class TodoAction extends Action {}

class TodoListAction extends TodoAction {}

class TodoGetAction extends TodoAction {
  final int id;

  TodoGetAction({
    this.id,
  }) : assert(id != null);
}

class TodoAddAction extends TodoAction {
  final String title;
  final String text;

  TodoAddAction({
    this.title,
    this.text,
  }) : assert(title != null),
       assert(text != null);
}

class TodoEditAction extends TodoAction {
  final int id;
  final String title;
  final String text;

  TodoEditAction({
    this.id,
    this.title,
    this.text,
  }) : assert(id != null);
}

class TodoRemoveAction extends TodoAction {
  final int id;

  TodoRemoveAction({
    this.id,
  }) : assert(id != null);
}

// 这些动作是UI相关的
class TodoUiAction extends Action {}

class TodoUpdateUiAction extends TodoUiAction {
  final List<Todo> todos;

  TodoUpdateUiAction({
    this.todos,
  }) : assert(todos != null);
}

class TodoAddUiAction extends TodoUiAction {
  final Todo todo;

  TodoAddUiAction({
    this.todo,
  }) : assert(todo != null);
}

class TodoEditUiAction extends TodoUiAction {
  final int id;
  final Todo todo;

  TodoEditUiAction({
    this.id,
    this.todo,
  }) : assert(id != null),
       assert(todo != null);
}

class TodoRemoveUiAction extends TodoUiAction {
  final int id;

  TodoRemoveUiAction({
    this.id,
  }) : assert(id != null);
}

然后,我们需要考虑状态。在这种情况下,它将只是一个包含待办事项列表的数据类。

@dataClass
class TodoState {
  final List<Todo> todos;

  TodoState({
    this.todos = const [],
  });
}

现在,我们需要创建三样东西:

  • Reducer
  • WatcherWorker
/// Reducer 是一组 typedReducers<ActionType, State> 的堆栈(基本等同于那些说:“如果这是ActionType,则生成新状态,否则跳过”的Reducer)
/// 这将取代if-else地狱

final Reducer<TodoState> TodoReducer = combinedReducer<TodoState>([
  typedReducer<TodoUpdateUiAction, TodoState>(
    syncedReducer(
      (action, previousState) => previousState.copyWith(
        todos: action.todos,
      ),
    ),
  ),
  typedReducer<TodoAddUiAction, TodoState>(
    syncedReducer(
      (action, previousState) => previousState.copyWith(
        todos: [action.todo] + previousState.todos,
      ),
    ),
  ),
  typedReducer<TodoEditUiAction, TodoState>(
    syncedReducer(
      (action, previousState) => previousState.copyWith(
        todos: previousState.todos.map((todo) =>
            todo.id == action.id ? action.todo : todo)
            .toList(),
      ),
    ),
  ),
  typedReducer<TodoRemoveUiAction, TodoState>(
    syncedReducer(
      (action, previousState) => previousState.copyWith(
        todos: previousState.todos.where((todo) => todo.id != action.id)
            .toList(),
      ),
    ),
  ),
]);
/// Watcher 是一个独立的流操作器,然后会传递给Worker
/// 它的任务是过滤和类型转换动作流,使其匹配特定的Worker
Watcher<TodoAction, TodoState> TodoWatcher(
  Worker<TodoAction, TodoState> extensions.worker,
) =>
    watcher(extensions.worker, (actionStream, context) {
      return actionStream.where((action) => action is TodoAction).cast<TodoAction>();
    });

/// Worker 可以有副作用并处理复杂任务
/// 因为它是异步的!
/// combinedWorker 在逻辑上等同于combinedReducer,但适用于workers
Worker<TodoAction, TodoState> TodoWorker(
  TodoRepository todoRepository,
) =>
    combinedWorker([
      typedWorker<TodoAction, TodoListAction, TodoState>(
        extensions.worker((context, action) async {
          final todos = await todoRepository.list();

          await context.put(TodoUpdateUiAction(
            todos: todos,
          ));
        }),
      ),
      typedWorker<TodoAction, TodoAddAction, TodoState>(
        extensions.worker((context, action) async {
          final todo = await todoRepository.add(
            title: action.title,
            text: action.text,
          );

          await context.put(TodoAddUiAction(
            todo: todo,
          ));
        }),
      ),
      typedWorker<TodoAction, TodoEditAction, TodoState>(
        extensions.worker((context, action) async {
          final todo = await todoRepository.edit(
            id: action.id,
            title: action.title,
            text: action.text,
          );

          await context.put(TodoEditUiAction(
            id: action.id,
            todo: todo,
          ));
        }),
      ),
      typedWorker<TodoAction, TodoRemoveAction, TodoState>(
        extensions.worker((context, action) async {
          await todoRepository.remove(
            id: action.id,
          );

          await context.put(TodoRemoveUiAction(
            id: action.id,
          ));
        }),
      ),
    ]);

最后,让我们创建Store和StoreProvider。

// 别忘了初始化meowchannel
await initializeMeowChannel();

runApp(
  StoreProvider<TodoState>(
    create: (context) =>
      // 为了向子组件注入多个Store,使用MultiStoreProvider
      Store<TodoState>(
        reducer: TodoReducer,
        initialState: TodoState(),
        middleware: [
          // 为了让Worker能够拦截动作,必须创建WorkerMiddleware
          WorkerMiddleware([
            TodoWatcher(TodoWorker(
              TodoRepository(),
            )),
          ]),
        ],
      ),
    child: MaterialApp(
      home: ...,
    ),
  ),
);

如何在小部件中访问Store和State?

/// 对于任何小部件,都可以使用 StoreProvider.of<Store<StateType>>()
/// 来获取所需的State的Store
StoreProvider.of<Store<TodoState>>(context);

/// 对于任何需要根据每次新状态到达而重建的StatefulWidget
class TodoApplicationWidget extends StatefulWidget {
  [@override](/user/override)
  State<StatefulWidget> createState() =>
    _TodoApplicationWidgetState();
}

/// 应该扩展StoreState以自动订阅Store
class _TodoApplicationWidgetState extends StoreState<TodoApplicationWidget> {
  /// 在这里指定Widget所需的Store
  [@override](/user/override)
  List<Store> requireStores(BuildContext context) => [
    StoreProvider.of<Store<TodoState>>(context),
  ];

  [@override](/user/override)
  Widget build(BuildContext context) {
    /// 每次指定的Store更新时,小部件都会重建
    final store = getStore<TodoState>(); /// 获取Store
    final state = getState<TodoState>(); /// 获取当前状态

    ...
    /// 这里发挥你的创造力!
  }

  [@override](/user/override)
  void initState() {
    super.initState();

    /// Store钩子可以在小部件上下文之外跟踪状态更新
    hookTo<TodoState>((store, state) {

    });
  }
}

还有其他方法可以在StoreState之外访问状态更改吗?
当然可以!

class SomeWidget extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    final store = StoreProvider.of<SomeState>(context); /// 获取Store

    return StoreBuilder<SomeState>(
      store: store,
      condition: (previous, current) => previous?.value != current?.value, /// 如果需要,可以应用自定义更改检查器
      builder: (context, state) {
        return Scaffold(
          body: Column(
            children: <Widget>[
              ...
              /// 发挥你的创造力!
            ],
          ),
        );
      },
    );
  }
}

更多关于Flutter通信插件meowchannel的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

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


MeowChannel 是一个用于 Flutter 和原生平台(如 Android 和 iOS)之间进行通信的插件。它提供了一种简单的方式来实现 Flutter 和原生代码之间的双向通信。以下是使用 MeowChannel 的基本步骤:

1. 添加依赖

首先,你需要在 pubspec.yaml 文件中添加 meowchannel 插件的依赖:

dependencies:
  flutter:
    sdk: flutter
  meowchannel: ^1.0.0  # 请使用最新版本

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

2. 创建 MeowChannel 实例

在 Flutter 中,你可以通过 MeowChannel 类来创建一个通信通道。

import 'package:meowchannel/meowchannel.dart';

final MeowChannel channel = MeowChannel('my_channel_name');

3. 在 Flutter 中发送消息

你可以使用 sendMessage 方法从 Flutter 向原生平台发送消息:

channel.sendMessage({'key': 'value'}).then((response) {
  print('Received response from native: $response');
});

4. 在原生平台中接收消息

在原生平台(Android 或 iOS)中,你需要设置一个方法来接收来自 Flutter 的消息,并返回响应。

Android:

MainActivity.kt 中:

import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel

class MainActivity: FlutterActivity() {
    private val CHANNEL = "my_channel_name"

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
            if (call.method == "myMethod") {
                val data = call.argument<String>("key")
                // 处理数据
                result.success("Response from Android")
            } else {
                result.notImplemented()
            }
        }
    }
}

iOS:

AppDelegate.swift 中:

import UIKit
import Flutter

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
        let channel = FlutterMethodChannel(name: "my_channel_name",
                                           binaryMessenger: controller.binaryMessenger)
        channel.setMethodCallHandler({
            (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
            if call.method == "myMethod" {
                let data = call.arguments as? [String: Any]
                // 处理数据
                result("Response from iOS")
            } else {
                result(FlutterMethodNotImplemented)
            }
        })

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

5. 在 Flutter 中接收原生平台的响应

在 Flutter 中,你可以通过 sendMessage 方法的 then 回调来接收原生平台的响应。

6. 在原生平台中发送消息到 Flutter

你也可以从原生平台向 Flutter 发送消息。例如,在 Android 中:

MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).invokeMethod("methodName", "data")

在 iOS 中:

channel.invokeMethod("methodName", arguments: "data")

在 Flutter 中,你可以通过 channel.setMessageHandler 来接收原生平台的消息:

channel.setMessageHandler((message) {
  print('Received message from native: $message');
  return Future.value('Response from Flutter');
});

7. 处理错误

在通信过程中,可能会出现错误。你可以在 sendMessage 方法中使用 catchError 来处理错误:

channel.sendMessage({'key': 'value'}).then((response) {
  print('Received response from native: $response');
}).catchError((error) {
  print('Error: $error');
});
回到顶部