Flutter自动绑定插件auto_binding的使用

Flutter自动绑定插件AutoBinding的使用

Setup

首先,确保在你的 pubspec.yaml 文件中添加 auto_binding 插件。

flutter pub add auto_binding

数据提供

系统提供了常见的四种数据提供方式:ModelStatefulWidget, ModelStatelessWidget, DataStatefulWidget, DataStatelessWidget

ModelStatefulWidget

ModelStatefulWidget 提供了一个 model 参数,并给与泛型,可以直接在 build() 中作为 WidgetTree 使用,便于使用已有的数据类型。

/// 组织WidgetTree
@override
Widget build(BuildContext context) {
  return ModelStatefulWidget<LoginForm>(
    model: LoginForm("", ""),
    child: CallModelState(),
  );
}

注意:

  • LoginFormModelStatefulWidget 的模型数据类型。
  • child 是一个普通的 Widget。
ModelStatelessWidget

ModelStatelessWidget 类似于 ModelStatefulWidget,也提供了 model 参数,可以直接作为 WidgetTree 使用。

@override
Widget build(BuildContext context) {
  return ModelStatelessWidget<LoginForm>(
    model: LoginForm("", ""),
    child: CallModelStatelessWidget(),
  );
}

注意:

  • ModelStatelessWidget 是无状态的,所以每次刷新时 model 会被重新创建。
  • ModelStatefulWidget 在刷新后仍然可以保留数据。
DataStatefulWidget

DataStatefulWidget 提供了一个 StatefulWidget 的抽象类,开发者需要编写子类来继承它。自定义的子类中可以自由地添加共享的数据作为成员变量。

/// ExampleForDataStatefulWidget是DataStatefulWidget的子类
class ExampleForDataStatefulWidget extends DataStatefulWidget {
  ExampleForDataStatefulWidget({super.key});

  @override
  ExampleForDataState createState() => ExampleForDataState();
}

/// ExampleForDataState是DataState的子类;
class ExampleForDataState
    extends DataState<ExampleForDataStatefulWidget> {
  
  //// 定义共享数据
  String username = '';
  String password = '';
  ////
  
  
  /// CallCallDataStatefulWidget调用方函数
  @override
  Widget builder(BuildContext context) => const CallDataStatefulWidget();
}

注意:

  • DataState 提供了自由定义共享数据的代码区域。
DataStatelessWidget

DataStatelessWidget 是一个无状态的抽象类,开发者需要编写其继承类,扩展共享数据项。

/// ModelProviderWidget的继承类
class ExampleForDataStatelessWidget extends DataStatelessWidget {
  
  /// 定义共享数据
  final loginForm = LoginForm('', '');
  ///
  
  /// CallModelProviderWidget是数据调用方
  ExampleForDataStatelessWidget()
      : super(child: CallDataStatelessWidget());
}

注意:

  • 用法与 DataState 相似,也可以自由地定义共享数据。

数据调用

数据调用分为三个步骤:创建构造器,绑定引用,捆绑视图。

创建构造器

通过 context 来创建构造器。

@override
Widget build(BuildContext context) {
  var node = Binding.mount(context);
}

注意:

  • 绑定的 context 应当遵守范围越小越好,context 即发生变化是刷新的范围。
绑定引用

有两种绑定方式:直接绑定和引用绑定。

final usernameRef = Ref.fromData(
  getter: (ModelStatelessWidget<LoginForm> widget) => widget.model.username,
  setter: (ModelStatelessWidget<LoginForm> widget, String username) => 
    widget.model.username = username,
);

@override
Widget build(BuildContext context) {
  var node = Binding.mount(context);
  /// 引用绑定: 使用已定义的Ref变量
  var username = usernameRef(node);

  /// 直接绑定: 提供getter/setter
  var password = Ref.fromData(
      getter: (ModelStatelessWidget<LoginForm> widget) => widget.model.password,
      setter: (ModelStatelessWidget<LoginForm> widget, String password) => 
      widget.model.password = password,
    ).call(node);
  ...
}

注意:

  • 多个上下文使用时,应考虑引用绑定的方式,这样 Ref 变量可以复用。
捆绑视图

使用 binding 填充到某个 WidgetTree 上。

class ExampleForModelStatelessWidget extends StatelessWidget {
  
  final usernameRef = Ref.fromData(
    getter: (ModelStatelessWidget<LoginForm> widget) => widget.model.username,
    setter: (ModelStatelessWidget<LoginForm> widget, String username) => 
    widget.model.username = username,
  );

  Widget build(BuildContext context) {
    /// connecting context
    var node = Binding.mount(context);

    var username = usernameRef(node);

    /// no bind
    username.raw;

    var password = Ref.fromData(
        getter: (ModelStatelessWidget<LoginForm> widget) => widget.model.password,
        setter: (ModelStatelessWidget<LoginForm> widget, String password) => 
        widget.model.password = password,
      ).call(node);
    return Column(
      children: [
        const Text('AutoBinding example for ModelStatelessWidget.',
            style: TextStyle(
                fontSize: 36,
                color: Colors.deepOrange,
                fontWeight: FontWeight.bold)),
        const SizedBox(height: 20),
        const Text('轻便的MVVM双向绑定的框架',
            style: TextStyle(fontSize: 16, color: Colors.black38)),
        const SizedBox(height: 30),

        /// binding TextField
        BindingTextField(
          usernameRef.ref,

          /// 传入binding
          decoration: const InputDecoration(
            labelText: '用户名',
            hintText: '请输入用户名',
          ),
          style: const TextStyle(
              color: Colors.indigo, fontWeight: FontWeight.bold),
        ),
        const SizedBox(height: 20),

        /// binding TextField
        BindingTextField(
          Ref.fromData(
            getter: (ModelStatelessWidget<LoginForm> widget) => widget.model.password,
            setter: (ModelStatelessWidget<LoginForm> widget, String password) => 
            widget.model.password = password,
          ).ref,

          /// 传入binding
          decoration: const InputDecoration(
            labelText: '密码',
            hintText: '请输入密码',
          ),
          style: const TextStyle(
              color: Colors.indigo, fontWeight: FontWeight.bold),
        ),
        const SizedBox(height: 20),
      ],
      const SizedBox(height: 30),
      ElevatedButton(
        onPressed: () async {
          /// write data, to refresh view
          username.notifyChange('来自指定值的修改');
          password.notifyChange('来自指定值的修改');
        },
        style: const ButtonStyle(
          backgroundColor:
          MaterialStatePropertyAll<Color>(Colors.lightBlue),
          foregroundColor:
          MaterialStatePropertyAll<Color>(Colors.white),
        ),
        child: const Text('更改当前值'),
      ),
      const SizedBox(height: 30),
      Container(
          width: double.infinity,
          padding: const EdgeInsets.symmetric(
              vertical: 4, horizontal: 10),
          color: Colors.blueGrey,

          /// binding value
          child: Text(
            'username = ${username.bindChange()}\npassword = ${password.bindChange()}',
          )),
    );
  }
}

注意:

  • 通过 BindingTextField 绑定到 TextField 输入框,通过 bindChange() 绑定值到 Text 视图,value 不会产生绑定。
  • 通过 username.notifyChange('来自指定值的修改'); 赋值立刻产生页面的刷新。
  • BindingTextField 还提供了 valueToStringstringToValue 用于更多的格式化输入输出,更多捆绑方式请参见绑定控件篇。

使用macros生成Ref

由于 macros 仍处于实验阶段,暂时无法支持跨包的 Dart Analysis Server 语法解析。

开启macros模式

配置 sdk 版本,3.5.0-152以上。

# pubspec.yaml
environment:
  sdk: '>=3.5.0-152 <4.0.0'

启动参数 --enable-experiment=macros

flutter run --enable-experiment=macros
dart run --enable-experiment=macros

编写本地宏

// lib/macros/auto_binding.dart
import 'package:auto_binding/auto_binding.dart' as auto_binding;

macro class RefCodable extends auto_binding.RefCodable {
  const RefCodable();
}

macro class IgnoreRefCodable extends auto_binding.IgnoreRefCodable {
  const IgnoreRefCodable();
}
使用RefCodable注解
/// define annotation
[@RefCodable](/user/RefCodable)()
class LoginForm {
  var username = '123';
  String password;

  Info info = Info(nickName: '', gender: 'man');

  LoginForm(this.username, this.password);
  
}

[@RefCodable](/user/RefCodable)()
class Info {
  String nickName;
  String gender;

  Info({required this.nickName, required this.gender});

  Map<String, dynamic> toJson() => {
    'nickName': nickName,
    'gender': gender,
  };

}

/// call ref
var loginForm = LoginForm('username', 'man');
var ref = loginForm.info.nickNameRef;

var nickName = ref.value; // get value
ref.value = '123'; // set value

var node = Binding.mount(context); // old dependentExecutor dispose

var binding = dataRef(node); // dataRef to binding
var binding = loginForm.info.nickNameRef(node); // ref to binding

var nickName = binding.value; // get value but no bind
binding.value = '123'; // set value but do not update page
binding.bindChange(); // get value and add dependentExecutor
binding.notifyChange('123'); // set value and update page

loginForm.info.nickNameRef(node).bindChange(); // get value and add dependentExecutor
loginForm.info.nickNameRef(node).notifyChange('123'); // set value and update page
loginForm.info.nickNameRef(node).value // get value but no bind
loginForm.info.nickNameRef(node).value = '123'; // set value but do not update page

var nickName = loginForm.info.nickNameRef.bindChange(node: node) // get value and add dependentExecutor
loginForm.Info.nickNameRef.notifyChange(node: node, value: '123'); // set value and update page

bindChangeNotifier(node: node, ref: loginForm.info.nickName, changeNotifier: changeNotifier, notifyListener: notifyListener, onChange: onChange);
bindValueNotifier(node: node, ref: loginForm.info.nickName, valueNotifier: valueNotifier);

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

1 回复

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


auto_binding 是一个用于 Flutter 的自动绑定插件,它可以帮助开发者简化在 Flutter 应用中的依赖注入和状态管理。通过使用 auto_binding,你可以减少手动绑定依赖项的工作量,提高代码的可维护性和可读性。

1. 安装插件

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

dependencies:
  flutter:
    sdk: flutter
  auto_binding: ^1.0.0  # 请根据实际情况使用最新版本

然后运行 flutter pub get 来安装依赖。

2. 基本用法

auto_binding 的核心概念是自动绑定依赖项。你可以在应用中定义依赖项,然后通过注解或配置来自动绑定它们。

2.1 创建一个依赖项

假设你有一个 UserService 类,它是一个依赖项:

class UserService {
  String getUserName() {
    return "John Doe";
  }
}

2.2 使用 auto_binding 绑定依赖项

你可以使用 [@Bind](/user/Bind) 注解来标记需要绑定的依赖项:

import 'package:auto_binding/auto_binding.dart';

[@Bind](/user/Bind).singleton
class UserService {
  String getUserName() {
    return "John Doe";
  }
}

2.3 获取绑定的依赖项

在需要使用 UserService 的地方,你可以通过 AutoBinding 来获取它:

import 'package:auto_binding/auto_binding.dart';

void main() {
  // 初始化 AutoBinding
  AutoBinding.init();

  // 获取 UserService 实例
  UserService userService = AutoBinding.get<UserService>();

  // 使用 UserService
  print(userService.getUserName());  // 输出: John Doe
}

3. 高级用法

3.1 依赖注入

auto_binding 支持依赖注入。你可以在构造函数中注入依赖项:

[@Bind](/user/Bind).singleton
class AuthService {
  final UserService userService;

  AuthService(this.userService);

  String getAuthInfo() {
    return "Auth Info: ${userService.getUserName()}";
  }
}

在使用 AuthService 时,auto_binding 会自动注入 UserService

void main() {
  AutoBinding.init();

  AuthService authService = AutoBinding.get<AuthService>();

  print(authService.getAuthInfo());  // 输出: Auth Info: John Doe
}

3.2 Scope 和生命周期管理

auto_binding 支持不同的生命周期管理策略,例如 singleton(单例)、factory(每次获取新实例)、lazy(懒加载)等。

[@Bind](/user/Bind).singleton  // 单例模式
class ConfigService {
  String getConfig() {
    return "Config";
  }
}

[@Bind](/user/Bind).factory  // 每次获取新实例
class LogService {
  String getLog() {
    return "Log";
  }
}

[@Bind](/user/Bind).lazy  // 懒加载
class CacheService {
  String getCache() {
    return "Cache";
  }
}
回到顶部