Flutter插件udf的使用

udf是一个允许你使用单向数据流(Unidirectional Data Flow)的 Flutter 包,灵感来源于 Elm 和 Erlang/OTP 语言及平台。


初始化设置

在运行应用之前,初始化你的提供者和模型非常重要,否则可能会导致空指针异常。

示例:

void main() {
  initAppState();
  runApp(ScannerApp());
}

void initAppState() {
  LoginModelProvider.init();
  MenuModelProvider.init();
}

路由器 (Router)

路由器允许你在屏幕之间导航。所有视图都需要一个 routeName 字符串来标识它们。为了启用路由功能,你需要在 widget 树中放置一个启用路由的 widget。

示例:

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
        title: '我的精美应用',
        home: RouterWidget(),
        routes: {
          Login.routeName: (context) => Login(), // 所有视图都需要添加到这里
          Menu.routeName: (context) => Menu(),
        });
  }
}

class RouterWidget extends StatelessWidget {
  final Router router = Router();

  RouterWidget({
    Key key,
  }) : super(key: key);

  [@override](/user/override)
  Widget build(BuildContext context) {
    router.init(Navigator.of(context));
    return Login(); // 返回你想要开始的视图
  }
}

这样你就可以在应用中像这样进行导航:

RaisedButton(
  child: Text("登录"),
  onPressed: () => {
    Router().navigateTo(routeName: Menu.routeName),
  },
),

路由器是缓存的,因此再次调用构造函数时会检索第一次创建的相同实例。


包结构

模型 (Model)

模型是一个不可变类,用于存储你的数据。确保使用 final 关键字以保证数据一致性。通过 copyWith 方法创建新副本来更新模型数据。数据一致性是防止应用程序中出现错误的重要因素之一。类型系统是防止某些错误发生的良好工具。通过智能地使用类型系统,可以禁止尽可能多的不一致状态。

示例:

class LoginModel extends Model<LoginModel> {
  final String email;
  final String password;

  LoginModel._(this.email, this.password);

  static LoginModel init() {
    return LoginModel._("", "");
  }

  LoginModel copyWith({String email, String password}) {
    return LoginModel._(email ?? this.email, password ?? this.password);
  }

  [@override](/user/override)
  String toString() {
    return 'LoginModel{email: $email, password: $password}';
  }
}

小提示: 添加适当的 toString 覆盖方法将使调试应用程序变得更加容易。


视图通知器 (ViewNotifier)

对模型所做的更改通常需要应用程序重新渲染这些更改。为此,你需要在 widget 树中放置一个 ViewNotifier

示例:

[@override](/user/override)
Widget build(BuildContext context) {
  return ViewNotifier<MenuModelProvider>(
    stateProvider: StateProvider.providerOf(MenuModelProvider),
    child: MenuView(),
  );
}

这使得在视图中使用数据变得简单:

示例:

class MenuView extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    var provider = StateProvider.providerOf(MenuModelProvider);
    MenuModel model = provider.model();

    return Scaffold(
      appBar: AppBar(
        title: Text("菜单视图"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(model.user.email),
            RaisedButton(child: Text("菜单"), onPressed: () => {}),
          ],
        ),
      ),
    );
  }
}

消息 (Message)

消息是一个不可变类,用于传递应用程序的任何功能。其 handle 方法定义了如何更新应用程序的状态。

示例:

class EmailChangedMessage extends Message<LoginModel, EmailChangedMessage> {

  final String email;

  EmailChangedMessage(this.email);

  [@override](/user/override)
  LoginModel handle(StateProvider<LoginModel> provider, EmailChangedMessage msg, LoginModel model) {
    return model.copyWith(email: msg.email);
  }

  [@override](/user/override)
  String toString() {
    return 'EmailChangedMessage{email: $email}';
  }
}

小提示: 添加适当的 toString 覆盖方法将使调试应用程序变得更加容易。


状态提供者 (StateProvider)

状态提供者是一个管理类,负责处理消息并更新模型。

实例化提供者时,它基本上只需要知道自己的名称和将使用的模型。

建议添加以下内容:

  • 一个工厂方法 init,用于实例化模型的初始状态。
  • 一个静态方法 getModel,用于获取当前模型的状态。
  • 一个静态方法 send,用于向提供者发送消息。

这些不是必需的,但它们隐藏了一些样板代码,并使业务逻辑更清晰。

示例:

class LoginModelProvider extends StateProvider<LoginModel> {

  @Protected
  LoginModelProvider(LoginModel model) : super(model);

  static send(Message msg) => StateProvider.providerOf(LoginModelProvider).receive(msg);

  static getModel() => StateProvider.providerOf(LoginModelProvider).model();

  factory LoginModelProvider.init() => LoginModelProvider(model: LoginModel(email: "", password: ""));
}

发送消息

接收 (receive)

这是通过调用 stateProviderreceive 方法完成的。receive 方法接受一个 Message 对象,并执行消息提供的 handle 方法。消息按接收顺序解析。

提供者需要从 ViewNotifier 中检索出来,以便触发重新渲染。

示例:

class LoginView extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    var provider = ViewNotifier.of(LoginModelProvider);
    var model = provider.model();

    return Scaffold(
      appBar: AppBar(
        title: Text("登录"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            MyWidgets.textField(
              "邮箱",
              "输入您的邮箱",
              (email) => {provider.receive(EmailChangedMessage(email))},
            ),
            RaisedButton(
              child: Text("登录"),
              onPressed: () => {
                Router().navigateTo(routeName: Menu.routeName),
              },
            ),
          ],
        ),
      ),
    );
  }
}

// 小提示:
static Widget textField(String label, String hint, ValueChanged<String> onChanged) {
  return Padding(
    padding: EdgeInsets.fromLTRB(20, 20, 20, 20),
    child: TextField(
        decoration: InputDecoration(labelText: label, hintText: hint, fillColor: Colors.white), onChanged: onChanged),
  );
}

sendWhenCompletes

有时你希望在某个刺激下触发应用程序中的某些操作。例如,当从 API 接收到响应时。此方法通过执行未来(第 1 个参数),然后要么向提供者发送消息,要么记录消息(可选)来处理这种情况。

示例:

stateProvider.sendWhenCompletes(
  API.getEmployees(),
  (employees) => EmployeesReceivedMessage(employees),
  errorMessage: "获取员工失败",
);

在视图之间传递数据

每个视图应仅有一个与该视图相关的模型和提供者。在视图之间传递数据很简单,只需检索要传递数据的目标视图的提供者并发送消息即可。这永远不会导致当前视图重新渲染,因为其他提供者不会添加到当前 widget 树的通知器中。

示例:

class MenuView extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {

    ...

    var provider = StateProvider.providerOf(PaymentModelProvider);
    provider.receive(PaymentMessage(amount: 10, currency: EURO));

    ...
  }
}

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

1 回复

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


在Flutter中,UDF(User Defined Function)通常指的是用户自定义函数。然而,Flutter本身并没有直接提供名为UDF的插件或功能。如果你提到的UDF是指某种特定的插件或库,可能需要进一步的上下文来理解。

假设你是指如何在Flutter中使用自定义函数或插件,以下是一些相关的步骤和示例:

1. 自定义函数

在Dart中,你可以轻松地定义自己的函数。例如:

int add(int a, int b) {
  return a + b;
}

void main() {
  print(add(2, 3)); // 输出: 5
}

2. 使用插件

如果你需要使用某个特定的功能,通常可以通过在pubspec.yaml文件中添加依赖来使用现有的Flutter插件。例如,使用http插件进行网络请求:

步骤 1: 添加依赖

pubspec.yaml文件中添加依赖:

dependencies:
  flutter:
    sdk: flutter
  http: ^0.13.3

步骤 2: 导入并使用

在Dart文件中导入并使用插件:

import 'package:http/http.dart' as http;

void fetchData() async {
  var url = Uri.parse('https://jsonplaceholder.typicode.com/posts');
  var response = await http.get(url);
  print('Response status: ${response.statusCode}');
  print('Response body: ${response.body}');
}

void main() {
  fetchData();
}

3. 创建自定义插件

如果你需要创建一个自定义插件,可以按照以下步骤进行:

步骤 1: 创建插件项目

使用Flutter命令行工具创建一个新的插件项目:

flutter create --template=plugin my_custom_plugin

步骤 2: 实现插件功能

在生成的插件项目中,你可以在lib目录下实现Dart代码,在androidios目录下实现平台特定的代码。

步骤 3: 发布和使用插件

你可以在本地使用插件,或者将其发布到pub.dev上供他人使用。

4. 使用MethodChannel调用平台代码

如果你需要在Flutter中调用平台特定的代码(如Android或iOS),可以使用MethodChannel

示例:调用Android代码

android目录下实现平台代码:

package com.example.my_custom_plugin;

import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.plugin.common.PluginRegistry.Registrar;

public class MyCustomPlugin implements MethodCallHandler {
  public static void registerWith(Registrar registrar) {
    final MethodChannel channel = new MethodChannel(registrar.messenger(), "my_custom_plugin");
    channel.setMethodCallHandler(new MyCustomPlugin());
  }

  @Override
  public void onMethodCall(MethodCall call, Result result) {
    if (call.method.equals("getPlatformVersion")) {
      result.success("Android " + android.os.Build.VERSION.RELEASE);
    } else {
      result.notImplemented();
    }
  }
}

在Flutter中调用:

import 'package:flutter/services.dart';

class MyCustomPlugin {
  static const MethodChannel _channel = MethodChannel('my_custom_plugin');

  static Future<String> get platformVersion async {
    final String version = await _channel.invokeMethod('getPlatformVersion');
    return version;
  }
}

void main() async {
  String platformVersion = await MyCustomPlugin.platformVersion;
  print('Platform Version: $platformVersion');
}
回到顶部