Flutter响应式编程插件reactives的使用

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

Flutter响应式编程插件reactives的使用

reactives 是一种新的方式,用于在 Flutter 中重用和组合常见的逻辑。你可以将它们视为 React 的 Hook,但适用于 Flutter。

动机

你是否曾经写过类似这样的代码?

class AwesomeWidget extends StatefulWidget {
  const AwesomeWidget({Key? key}) : super(key: key);

  [@override](/user/override)
  _AwesomeWidgetState createState() => _AwesomeWidgetState();
}

class _AwesomeWidgetState extends State<AwesomeWidget> with TickerProviderStateMixin {
  late final TextEditingController emailCtrl;
  late final TextEditingController passwordCtrl;
  late final AnimationController entryAnimation;
  late final AnimationController highLightAnimation;

  [@override](/user/override)
  void initState() {
    super.initState();
    emailCtrl = TextEditingController();
    passwordCtrl = TextEditingController();
    entryAnimation = AnimationController(vsync: this);
    highLightAnimation = AnimationController(vsync: this);
  }

  [@override](/user/override)
  void dispose() {
    emailCtrl.dispose();
    entryAnimation.dispose();
    highLightAnimation.dispose();
    passwordCtrl.dispose();
    super.dispose();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Container();
  }
}

这段代码存在几个问题:

  • 大部分对象管理逻辑是通用的,可以被重用。
  • 容易忘记调用 dispose 方法(坦白地说)。
  • 将逻辑耦合到小部件中,使得测试变得困难。

使用 reactives 可以将这段代码转换为:

class AwesomeReactiveWidget extends StatefulWidget {
  const AwesomeReactiveWidget({Key? key}) : super(key: key);

  [@override](/user/override)
  _AwesomeReactiveWidgetState createState() => _AwesomeReactiveWidgetState();
}

class _AwesomeReactiveWidgetState extends State<AwesomeReactiveWidget> with ReactiveHostMixin {
  late final emailCtrl = ReactiveTextEditingController(this);
  late final passwordCtrl = ReactiveTextEditingController(this);
  late final entryAnimation = ReactiveAnimationController(this);
  late final exitAnimation = ReactiveAnimationController(this);

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Container(
        // ....
    );
  }
}

查看 示例 获取更多关于编写 reactive 的示例,或者直接浏览 源码,它非常简洁。

flutter_hooks 的比较

从用户的角度来看,flutter_hooks 是对 StatefulWidget 的替代。而 reactives 并不是。如果你查看 ReactiveHostMixin 的代码,你会发现它只有大约 60 行(包括空行)。reactives 并不试图替代 StatefulWidget,它只是解决了由于 mixin 的“is-a”关系而导致的可重用性问题。reactives 采用的是“has-a”关系。

编写 reactives 没有新的规则。查看现有的 reactives 示例即可。基本上就是将相同的逻辑隔离到不同的类中。 因此,它不需要像 useState 这样的大量钩子,因为如果尝试替代 StatefulWidget,则需要这些钩子。

学习 reactives 几乎不需要什么成本。hooks 需要你学习一些概念以及如何或不如何做某些事情。它还要求你完全改变整个小部件。reactives 即使对于单个小部件也可以逐步适应。

示例代码

以下是 reactives 的一个简单示例,展示了如何在 Flutter 中使用 reactives

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

/// 简单的展示 reactive 的小部件
class LoginWidget extends StatefulWidget {
  const LoginWidget({Key? key}) : super(key: key);

  [@override](/user/override)
  _LoginWidgetState createState() => _LoginWidgetState();
}

// 添加 ReactiveHostMixin
class _LoginWidgetState extends State<LoginWidget> with ReactiveHostMixin {
  // 创建所需的 reactive
  late final emailCtrl = ReactiveTextEditingController(this).ctrl;
  late final passwordCtrl = ReactiveTextEditingController(this).ctrl;

  var passwordVisible = false;

  void submit() {
    // 登录逻辑
  }

  void toggeVisible() {
    setState(() {
      passwordVisible = !passwordVisible;
    });
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: [
        // 像任何字段一样使用 reactive
        TextField(controller: emailCtrl),
        TextField(
          controller: passwordCtrl,
          decoration: InputDecoration(
            suffix: IconButton(
              icon: const Icon(Icons.visibility),
              onPressed: toggeVisible,
            ),
          ),
          obscureText: !passwordVisible,
        ),
        ElevatedButton(
          child: const Text('登录'),
          onPressed: submit,
        ),
      ],
    );
  }
}

/// ReactiveLogin 是一种方法,可以将登录逻辑从小部件中提取出来,以便于测试和复用
class ReactiveLogin extends Reactive {
  final ReactiveTextEditingController _email;
  final ReactiveTextEditingController _password;

  TextEditingController get emailCtrl => _email.ctrl;
  TextEditingController get passwordCtrl => _password.ctrl;

  bool _passwordVisible = false;
  bool get passwordVisible => _passwordVisible;

  ReactiveLogin(ReactiveHost host)
      : _email = ReactiveTextEditingController(host),
        _password = ReactiveTextEditingController(host),
        super(host);

  void submit() {
    // 登录逻辑
  }

  void toggleVisible() {
    _passwordVisible = !_passwordVisible;
    host.requestUpdate(); // 调用 setState
  }
}

/// 上面的示例可以通过提取的登录逻辑重新编写
/// 这减少了小部件的 UI 逻辑
class LoginView extends StatefulWidget {
  const LoginView({Key? key}) : super(key: key);

  [@override](/user/override)
  _LoginViewState createState() => _LoginViewState();
}

// 添加 ReactiveHostMixin
class _LoginViewState extends State<LoginView> with ReactiveHostMixin {
  // 创建所需的登录 Reactive
  late final login = ReactiveLogin(this);

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: [
        // 像任何字段一样使用 reactive
        TextField(controller: login.emailCtrl),
        TextField(
          controller: login.passwordCtrl,
          decoration: InputDecoration(
            suffix: IconButton(
              icon: const Icon(Icons.visibility),
              onPressed: login.toggleVisible,
            ),
          ),
          obscureText: !login.passwordVisible,
        ),
        ElevatedButton(child: const Text('登录'), onPressed: login.submit),
      ],
    );
  }
}

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

1 回复

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


在Flutter中,响应式编程是一种强大的范式,用于处理应用状态的变化。reactives 插件(虽然这不是一个官方或广泛认知的插件名,我假设你指的是一种响应式编程的实现或类似 providerriverpodget_it 等状态管理库的替代方案)可以帮助你更有效地管理这些状态变化。不过,由于 reactives 并非一个标准的 Flutter 库名,我将基于 Flutter 社区中常见的响应式编程实践,给出一个使用 provider 库进行响应式编程的代码案例。

provider 库是 Flutter 社区中非常流行的状态管理解决方案之一,它允许你在 widget 树中共享和监听数据,当数据变化时,相关的 UI 会自动重建。

以下是一个使用 provider 库进行响应式编程的简单示例:

  1. 添加依赖: 首先,在你的 pubspec.yaml 文件中添加 provider 依赖:

    dependencies:
      flutter:
        sdk: flutter
      provider: ^6.0.0  # 请检查最新版本号
    
  2. 创建一个 ChangeNotifier 类: 这个类将持有我们的应用状态,并在状态改变时通知监听者。

    import 'package:flutter/material.dart';
    import 'package:provider/provider.dart';
    
    class Counter with ChangeNotifier {
      int _count = 0;
    
      int get count => _count;
    
      void increment() {
        _count++;
        notifyListeners();
      }
    }
    
  3. 设置 MultiProvider: 在 main.dart 中,使用 MultiProvider 将我们的 Counter 提供给应用。

    import 'package:flutter/material.dart';
    import 'package:provider/provider.dart';
    import 'counter.dart';  // 假设我们的 ChangeNotifier 类在 counter.dart 文件中
    
    void main() {
      runApp(
        MultiProvider(
          providers: [
            ChangeNotifierProvider(create: (_) => Counter()),
          ],
          child: MyApp(),
        ),
      );
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: MyHomePage(),
        );
      }
    }
    
  4. 在 UI 中消费状态: 使用 ConsumerSelector widget 来监听 Counter 的变化,并更新 UI。

    import 'package:flutter/material.dart';
    import 'package:provider/provider.dart';
    
    class MyHomePage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('Flutter Demo Home Page'),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Text(
                  'You have pushed the button this many times:',
                ),
                Consumer<Counter>(
                  builder: (context, counter, child) => Text(
                    '${counter.count}',
                    style: Theme.of(context).textTheme.headline4,
                  ),
                ),
              ],
            ),
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: () {
              final counter = Provider.of<Counter>(context, listen: false);
              counter.increment();
            },
            tooltip: 'Increment',
            child: Icon(Icons.add),
          ),
        );
      }
    }
    

在这个示例中,我们创建了一个 Counter 类来管理计数器的状态,使用 ChangeNotifierProvider 将其提供给整个应用。然后,在 MyHomePage 中,我们使用 Consumer widget 来监听 Counter 的变化,并在按钮点击时更新状态。

尽管 reactives 不是一个标准的 Flutter 库名,但上述代码示例展示了如何在 Flutter 中使用响应式编程原则,通过 provider 库来管理应用状态。如果你指的是一个特定的第三方库,请确保你提供了正确的库名,以便给出更精确的代码示例。

回到顶部