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)。 重要点:
- 允许创建多个具有唯一State的Store。
- 不允许有两个或更多具有相同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
- Watcher 和 Worker 对
/// 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
更多关于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');
});

