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

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

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

特性

inherited_rxdart 是一个简单的状态管理解决方案,它结合了继承小部件(Inherited Widget)和RxDart的功能。通过使用多个Bloc(业务逻辑组件),你可以为你的应用程序创建状态管理逻辑,这些Bloc本质上只是流和RxDart。

入门

首先,导入 inherited_rxdart 包:

import 'package:inherited_rxdart/inherited_rxdart.dart';

接下来,定义你的状态类和Bloc:

[@immutable](/user/immutable)
class MyState {
  final int number;
  final String text;

  const MyState({required this.number, required this.text});

  MyState copyWith({int? number, String? text}) {
    return MyState(number: number ?? this.number, text: text ?? this.text);
  }
}

class CounterBloc extends RxBloc<MyState, String> {
  CounterBloc(MyState initialState) : super(initialState);

  void showDialog() {
    notify("showDialog");
  }

  void changeText(String newText) {
    state = state.copyWith(text: newText);
  }

  void increase() {
    state = state.copyWith(number: state.number + 1);
  }

  void decrease() {
    state = state.copyWith(number: state.number - 1);
  }
}

然后,在你的应用中使用它们:

class App extends StatelessWidget {
  const App({Key? key}) : super(key: key);

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
        home: RxProvider<CounterBloc>(
          create: () => CounterBloc(const MyState(text: "hi", number: 0)),
          child: MyHomePage(),
        )
    );
  }
}

在子树中的任何地方访问它们:

final bloc = RxProvider.of<CounterBloc>(context);

使用

查看每个API的文档以获取更多详细信息。该库支持Bloc模式和ViewModel模式来管理你的应用程序状态。

Bloc的小部件

对于Bloc,有一些特定的小部件用于不同的目的,包括重建和监听等:

  • RxBuilder: 根据状态构建小部件。
  • RxListener: 监听状态变化和通知。
  • RxStateListener: 只监听状态变化。
  • RxConsumer: 结合了 RxListenerRxBuilder
  • RxStateConsumer: 结合了 RxStateListenerRxBuilder
  • RxSelector: 根据状态的特定属性选择性地重建小部件。
RxCubit

RxCubit 是一个简单的小部件,它在整个生命周期中发出状态,并在必要时重建小部件。它与以下小部件一起工作:

  • RxBuilder
  • RxSelector
  • RxStateConsumer
  • RxStateListener
RxBloc

RxBloc 是一个带有通知的状态小部件,可以被监听并相应地作出反应。它与以下小部件一起工作:

  • RxBuilder
  • RxListener
  • RxStateListener
  • RxConsumer
  • RxStateConsumer
  • RxSelector
RxViewModel

RxViewModel 是一种基于简单视图模型的状态管理方式,附带了相关的构建器:

  • RxViewModelBuilder: 处理状态发出时重建小部件。
  • RxViewModelSelector: 当状态改变时选择性地重建。
  • RxViewModelListener: 状态改变时的监听回调。
  • RxViewModelConsumer: 结合了构建器和监听器。
RxValue

RxValue 是一种用于响应式状态管理的值,当其值设置时,将导致其依赖项重建。

  • RxValueBuilder: 与 RxValue 一起使用。
ServiceProvider

还有一个简单的服务提供商,用于仓库或简单地通过小部件子树注入实例。

  • ServiceProvider
注册全局单例

尽管不完全是继承,但此库确实提供了通过GetIt注册实例的方法,并在构建器和监听器中使用它们。此功能可以通过 RxProvider 的静态方法访问。

EventDispatcherMixin

这是一个混入,用于向 RxCubit/RxBloc 添加事件分发能力,并注册对这些事件的回调。

默认情况下,RxCubit/RxBloc 有一个流,即 RxBase.stateStream,该流将状态输出到UI以重建UI。

这个混入引入了另一个从UI到 RxCubit/RxBloc 的流,可用于处理事件类型及其子类型。每个都可以使用 StreamTransformer 进行转换。

额外信息

为了提供多个Bloc/视图模型/服务实例,鼓励使用以下小部件:

  • RxMultiProvider
  • MultiServiceProvider

为了快速访问Bloc/服务,而不是使用这些函数:

RxProvider.of<MyBloc>(context);
ServiceProvider.of<MyService>(context);

可以使用:

context.watch<MyBloc>(); // 获取Bloc/视图模型的实例并订阅其更改。
context.read<MyBloc>(); // 获取Bloc/视图模型的实例。
context.get<MyService>(); // 获取服务的实例。

并且:

final text = RxValue<String>("hello");

等价于:

final text = "hello".rx;

完整示例代码

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

void main() => runApp(const App());

[@immutable](/user/immutable)
class MyState {
  final int number;
  final String text;

  const MyState({required this.number, required this.text});

  MyState copyWith({int? number, String? text}) {
    return MyState(number: number ?? this.number, text: text ?? this.text);
  }
}

class CounterBloc extends RxBloc<MyState, String> {
  CounterBloc(MyState initialState) : super(initialState);

  void showDialog() {
    notify("showDialog");
  }

  void changeText(String newText) {
    state = state.copyWith(text: newText);
  }

  void increase() {
    state = state.copyWith(number: state.number + 1);
  }

  void decrease() {
    state = state.copyWith(number: state.number - 1);
  }
}

class CounterBloc2 extends RxCubit<int> {
  CounterBloc2(int initialState) : super(initialState);

  void increase() {
    state++;
  }

  void decrease() {
    state--;
  }
}

class CounterViewModel extends RxViewModel {
  int num1;
  int num2;
  final num3 = 0.rx;

  CounterViewModel(this.num1, [this.num2 = 0]);

  void increaseNum3() {
    num3.value++;
  }

  void increase() {
    num1++;
    num2++;
    stateChanged();
  }

  void decrease() {
    num1--;
    // num2--;
    stateChanged();
  }
}

class App extends StatelessWidget {
  const App({Key? key}) : super(key: key);

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: RxMultiProvider(
        providers: [
          RxProvider<CounterBloc>(
              create: () => CounterBloc(const MyState(text: "hi", number: 0))),
          RxProvider<CounterBloc2>(create: () => CounterBloc2(10)),
          RxProvider<CounterViewModel>(
            create: () => CounterViewModel(20),
          ),
        ],
        child: const MyHomePage(),
      ),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({Key? key}) : super(key: key);

  [@override](/user/override)
  Widget build(BuildContext context) {
    debugPrint("build MyHomePage");
    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const MyCounter(),
            RxSelector<CounterBloc, MyState, String>(
                stateRebuildSelector: (state) {
              return state.text;
            }, builder: (context, value) {
              debugPrint("build Text");
              return Text("state text: $value");
            }),
            RxViewModelSelector<CounterViewModel, int>(
                stateRebuildSelector: (state) {
              return state.num2;
            }, builder: (context, value) {
              debugPrint("build num2");
              return Text("state num2: $value");
            }),
            TextField(
              onSubmitted: (value) {
                RxProvider.of<CounterBloc>(context, listen: false)
                    .changeText(value);
              },
            ),
            Row(
              mainAxisSize: MainAxisSize.min,
              children: [
                ElevatedButton(
                    onPressed: () {
                      RxProvider.of<CounterBloc>(context, listen: false)
                          .increase();
                      RxProvider.of<CounterBloc2>(context, listen: false)
                          .increase();
                      RxProvider.of<CounterViewModel>(context, listen: false)
                          .increase();
                    },
                    child: const Text("Increase")),
                ElevatedButton(
                    onPressed: () {
                      RxProvider.of<CounterBloc>(context, listen: false)
                          .decrease();
                      RxProvider.of<CounterBloc2>(context, listen: false)
                          .decrease();
                      RxProvider.of<CounterViewModel>(context, listen: false)
                          .decrease();
                    },
                    child: const Text("Decrease")),
              ],
            ),
            ElevatedButton(
                onPressed: () {
                  Navigator.of(context).push(MaterialPageRoute(builder: (_) {
                    return RxProvider.value(
                        value:
                            RxProvider.of<CounterBloc>(context, listen: false),
                        child: const MyNested());
                  }));
                },
                child: const Text("New Screen")),
          ],
        ),
      ),
    );
  }
}

class MyCounter extends StatelessWidget {
  const MyCounter({Key? key}) : super(key: key);

  [@override](/user/override)
  Widget build(BuildContext context) {
    debugPrint("build MyCounter");
    return RxListener<CounterBloc, MyState, String>(
      stateCallback: (context, state) {
        debugPrint(
            "from RxBlocListener: CounterBloc/${state.number}/${state.text}");
      },
      child: RxStateListener<CounterBloc2, int>(
        stateCallback: (context, state) {
          debugPrint("from RxStateListener: CounterBloc2/$state");
        },
        child: RxStateListener<CounterBloc, MyState>(
          stateCallback: (context, state) {
            debugPrint(
                "from RxStateListener: CounterBloc/${state.number}/${state.text}");
          },
          child: RxViewModelListener<CounterViewModel>(
            stateCallback: (context, state) {
              debugPrint(
                  "from RxStateListener: CounterBloc3/${state.num1}/${state.num2}");
            },
            child: Column(
              children: [
                RxBuilder<CounterBloc, MyState>(
                  builder: (context, state) {
                    debugPrint("build Number 1");
                    return Text('counter bloc 1:  ${state.number}');
                  },
                  shouldRebuildWidget: (prev, curr) {
                    return prev.number != curr.number;
                  },
                ),
                RxBuilder<CounterBloc2, int>(
                  builder: (context, state) {
                    debugPrint("build Number 2");
                    return Text('counter bloc 2:  $state');
                  },
                  shouldRebuildWidget: (prev, curr) {
                    return curr < 10;
                  },
                ),
                RxViewModelBuilder<CounterViewModel>(
                  builder: (context, state) {
                    debugPrint("build Number 3");
                    return Text('counter bloc 3:  ${state.num1}');
                  },
                  shouldRebuildWidget: (state) {
                    return state.num1 < 20;
                  },
                ),
                RxValueBuilder<int>(
                    value: context.read<CounterViewModel>().num3,
                    builder: (context, value) {
                      debugPrint(
                          "rebuild elevated button of counter view model");
                      return ElevatedButton(
                          onPressed: () {
                            context.read<CounterViewModel>().increaseNum3();
                          },
                          child: Text("Num3: $value"));
                    }),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

class MyNested extends StatelessWidget {
  const MyNested({Key? key}) : super(key: key);

  [@override](/user/override)
  Widget build(BuildContext context) {
    debugPrint("build MyNested");
    return RxListener<CounterBloc, MyState, String>(
      notificationCallback: (context, notification) {
        if (notification == "showDialog") {
          showDialog(
              context: context,
              builder: (context) {
                return const Dialog(
                  child: Text("This is a dialog"),
                );
              });
        }
      },
      stateCallback: (context, state) {
        debugPrint("this is stateCallback");
      },
      child: Scaffold(
        appBar: AppBar(),
        body: Center(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              RxBuilder<CounterBloc, MyState>(builder: (context, state) {
                debugPrint("build BlocBuilder 2 ");
                return Text(state.number.toString());
              }),
              ElevatedButton(
                  onPressed: () {
                    RxProvider.of<CounterBloc>(context, listen: false)
                        .showDialog();
                  },
                  child: const Text("show notification")),
            ],
          ),
        ),
      ),
    );
  }
}

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

1 回复

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


在Flutter中进行响应式编程时,inherited_rxdart 是一个非常有用的插件,它结合了 InheritedWidgetrxdart 库,以提供响应式数据管理能力。rxdart 是一个基于 Dart 的响应式扩展库,主要用于处理 Dart Streams。结合 InheritedWidgetinherited_rxdart 可以让我们在 Flutter 应用中高效地管理状态,并在状态变化时通知相关的监听器。

以下是一个使用 inherited_rxdart 的简单示例,展示如何创建一个响应式的数据提供者,并在数据变化时通知子组件。

1. 添加依赖

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

dependencies:
  flutter:
    sdk: flutter
  inherited_rxdart: ^x.y.z  # 请替换为最新版本号
  rxdart: ^x.y.z           # 请替换为最新版本号

然后运行 flutter pub get

2. 创建响应式数据提供者

创建一个继承自 InheritedRxDartObject 的类,用于提供响应式数据。这里我们假设有一个简单的计数器示例。

import 'package:flutter/material.dart';
import 'package:inherited_rxdart/inherited_rxdart.dart';
import 'package:rxdart/rxdart.dart';

class CounterProvider extends InheritedRxDartObject<int> {
  CounterProvider({
    required Widget child,
    required ValueStream<int> counterStream,
  }) : super(child: child, stream: counterStream);

  // 获取 CounterProvider 的静态方法
  static CounterProvider of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<CounterProvider>()!;
  }

  // 更新计数器的值
  void increment() {
    // 假设你有一个 BehaviorSubject 来管理计数器状态
    final subject = (stream as BehaviorSubject<int>);
    subject.add(subject.value! + 1);
  }
}

3. 创建计数器逻辑

接下来,我们创建一个包含计数器逻辑的 Widget,并使用 CounterProvider 来提供数据。

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

class CounterApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final counterController = BehaviorSubject<int>.seeded(0);

    return CounterProvider(
      counterStream: counterController.stream,
      child: MaterialApp(
        home: Scaffold(
          appBar: AppBar(
            title: Text('Inherited RxDart Example'),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Text(
                  'You have pushed the button this many times:',
                ),
                StreamBuilder<int>(
                  stream: counterController.stream,
                  builder: (context, snapshot) {
                    return Text(
                      '${snapshot.data ?? 0}',
                      style: Theme.of(context).textTheme.headline4,
                    );
                  },
                ),
              ],
            ),
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: () {
              // 使用 CounterProvider 的 of 方法获取实例并调用 increment
              CounterProvider.of(context).increment();
            },
            tooltip: 'Increment',
            child: Icon(Icons.add),
          ),
        ),
      ),
    );
  }

  @override
  void dispose() {
    // 清理资源
    counterController.close();
    super.dispose();
  }
}

4. 运行应用

CounterApp 作为应用的根 Widget:

void main() {
  runApp(CounterApp());
}

总结

以上代码展示了如何使用 inherited_rxdart 插件结合 rxdart 实现响应式编程。CounterProvider 使用 InheritedRxDartObject 来提供响应式数据,并在数据变化时通知子组件。StreamBuilder 用于监听数据变化并更新 UI。通过这种方式,我们可以轻松地在 Flutter 应用中实现状态管理。

回到顶部