Flutter响应式状态管理插件reactive_notifier的使用

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

Flutter响应式状态管理插件reactive_notifier的使用

介绍

ReactiveNotifier 是一个灵活、优雅且安全的Flutter状态管理工具。它设计用于细粒度的状态控制,轻松集成MVVM等架构模式,保证完全独立于BuildContext,适用于任何规模的项目。

reactive_notifier

特性

  • 🚀 简单直观的API
  • 🏗️ 适合MVVM架构
  • 🔄 独立于BuildContext
  • 🎯 类型安全的状态管理
  • 📡 内置异步和流支持
  • 🔗 智能相关状态系统
  • 🛠️ 仓库/服务层集成
  • ⚡ 高性能,最小化重建
  • 🐛 强大的调试工具
  • 📊 详细的错误报告

安装

在你的 pubspec.yaml 文件中添加以下依赖:

dependencies:
  reactive_notifier: ^2.4.2

快速开始

使用ReactiveNotifier

与类、ViewModel等一起使用

mixin ConnectionService {
  static final ReactiveNotifier<ConnectionManager> instance = ReactiveNotifier<ConnectionManager>(() => ConnectionManager());
}

ReactiveBuilder<ConnectionManager>(
  notifier: ConnectionService.instance,
  builder: (service, keep) {
    final state = service.notifier;
    return Card(
      elevation: 4,
      child: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          mainAxisSize: MainAxisSize.min,
          spacing: 10,
          children: [
            CircleAvatar(
              radius: 30,
              backgroundColor: state.color.withValues(alpha: 255 * 0.2),
              child: Icon(
                state.icon,
                color: state.color,
                size: 35,
              ),
            ),
            Text(
              state.message,
              style: Theme.of(context).textTheme.titleMedium,
            ),
            if (state.isError || state == ConnectionState.disconnected)
              keep(
                ElevatedButton.icon(
                  onPressed: () => service.manualReconnect(),
                  icon: const Icon(Icons.refresh),
                  label: const Text('Retry Connection'),
                ),
              ),
            if (state.isSyncing) const LinearProgressIndicator(),
          ],
        ),
      ),
    );
  },
);

与简单值一起使用

mixin ConnectionService {
  static final ReactiveNotifier<String> instance = ReactiveNotifier<String>(() => "N/A");
}

// 声明一个简单的状态
ReactiveBuilder<String>(
  notifier: ConnectionService.instance,
  builder: (state, keep) => Text(state),
);

/// 修改来自其他小部件。
class OtherWidget extends StatelessWidget {
  const OtherWidget({super.key});

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        OutlinedButton(
          onPressed: () {
            ConnectionService.instance.updateState("New value");
          },
          child: Text('Edit String'),
        ),
      ],
    );
  }
}

/// 修改来自任何类等。
class OtherViewModel {
  void otherFunction() {
    /// 一些代码.....

    ConnectionService.instance.updateState("Value from other viewmodel");
  }
}

ViewModelStateImpl(无仓库)

对于不需要仓库层的直接状态管理,可以使用 ViewModelStateImpl。这种做法直接在ViewModel中管理状态:

class CartViewModel extends ViewModelStateImpl<CartModel> {
  CartViewModel() : super(CartModel());

  // 直接将产品添加到状态
  void addProduct(String item, double price) {
    final currentItems = notifier.items;
    final newItems = [...currentItems, item];
    final newTotal = notifier.total + price;
    
    // 或者使用transformState
    transformState((state) => state.copyWith(items: newItems, total: newTotal));
    
    // 或者创建新实例并替换。
    updateState(newCarInstance);
  }

  // 其他业务逻辑方法...
}

使用库仓库与ViewModelImpl

在这个例子中,我们将使用库提供的内置仓库来获取和更新购物车数据。

1. 使用库定义仓库

import 'package:reactive_notifier/reactive_notifier.dart';

class CartRepository extends RepositoryImpl<CartModel> {
  // 模拟加载购物车
  Future<CartModel> fetchData() async {
    await Future.delayed(Duration(seconds: 2));
    return CartModel(
      items: ['Product A', 'Product B'],
      total: 39.98,
    );
  }

  /// 更多函数 .....
}

2. ViewModelImpl与仓库

class CartViewModel extends ViewModelImpl<CartModel> {
  final CartRepository repository;

  CartViewModel(this.repository) : super(CartModel());

  // 加载购物车的函数
  Future<void> loadShoppingCart() async {
    try {
      // 从仓库获取购物车
      final shoppingCart = await repository.fetchData();
      updateState(shoppingCart); // 更新状态
    } catch (e) {
      // 错误处理
      print("Error: $e");
    }
  }

  // 添加产品的函数
  Future<void> addProduct(String item, double price) async {
    try {
      await repository.addProduct(item, price);

      // 更新状态
      updateState(notifier.copyWith(items: value.items, total: value.total));
    } catch (e) {
      // 错误处理
      print("Error: $e");
    }
  }
}

3. 仓库实例与ViewModelImpl

final cartViewModel = ReactiveNotifier<CartViewModel>(() {
  final cartRepository = CartRepository();
  return CartViewModel(cartRepository);
});

4. 购物车状态小部件

ReactiveBuilder<CartViewModel>(
  notifier: cartViewModel,
  builder: (viewModel, keep) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        if (viewModel.data.isEmpty)
          keep(Text("Loading cart...")),
        if (viewModel.data.isNotEmpty) ...[
          keep(Text("Products in cart:")),
          ...viewModel.data.map((item) => Text(item)).toList(),
          keep(const SizedBox(height: 20)),
          Text("Total: \$${viewModel.total.toStringAsFixed(2)}"),
          keep(const SizedBox(height: 20)),
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              keep(
                ElevatedButton(
                  onPressed: () {
                    // 添加新产品
                    cartViewModel.notifier.addProduct("Product C", 29.99);
                  },
                  child: Text("Add Product C"),
                ),
              ),
              keep(const SizedBox(width: 10)),
              keep(
                ElevatedButton(
                  onPressed: () {
                    // 清空购物车
                    cartViewModel.notifier.clearCart();
                  },
                  child: Text("Clear Cart"),
                ),
              ),
            ],
          ),
        ],
      ],
    );
  },
)

Essential: ReactiveNotifier核心概念

使用related属性

ReactiveNotifierrelated 属性允许你高效地管理互相关联的状态。它们可以根据状态结构和复杂度的不同以不同的方式使用。

建立通知器之间的关系

  • ReactiveNotifier 可以管理任何类型的数据(简单或复杂)。通过 related 属性,你可以建立不同通知器之间的连接,当任何相关通知器发生变化时,会触发 ReactiveBuilder 的更新。

示例场景:管理关联状态

  • 主通知器可能处理一个复杂的 UserInfo 类,而其他通知器管理相关的状态如 SettingsPreferences。使用 related,任何这些互相关联状态的变化都会通过 ReactiveBuilder 触发适当的UI更新。

直接关联简单通知器

在这种方法中,你有多个简单的 ReactiveNotifier,并将它们一起使用以在任何这些通知器发生变化时通知状态变化。ReactiveNotifier 通过 related 属性相互关联,并显示一个组合的 ReactiveBuilder

示例

final timeHoursNotifier = ReactiveNotifier<int>(() => 0);
final routeNotifier = ReactiveNotifier<String>(() => '');
final statusNotifier = ReactiveNotifier<bool>(() => false);

// 一个组合的ReactiveNotifier,监视所有三个通知器的变化
final combinedNotifier = ReactiveNotifier(
  () {},
  related: [timeHoursNotifier, routeNotifier, statusNotifier],
);

ReactiveBuilder(
  notifier: combinedNotifier,
  builder: (state, keep) {
    return Column(
      children: [
        Text("Hours: ${timeHoursNotifier.value}"),
        Text("Route: ${routeNotifier.value}"),
        Text("State: ${statusNotifier.value ? 'Active' : 'Inactive'}"),
      ],
    );
  },
);

主ReactiveNotifier与其他补充通知器的关系

在这种方法中,你有一个主 ReactiveNotifier 处理一个更复杂的类(如 UserInfo 对象),其他补充 ReactiveNotifier 通过 related 属性与其关联。这些补充通知器不需要声明在主对象类中,而是通过 related 属性与其集成。

示例:UserInfo与Settings

假设我们有一个 UserInfo 类表示用户信息,以及一个 Settings 类包含补充设置。这些状态的通知器通过 related 属性关联,以便 SettingsUserInfo 的任何变化都会触发全局更新。

class UserInfo {
  final String name;
  final int age;

  UserInfo({required this.name, required this.age});

  // 默认值构造函数
  UserInfo.empty() : name = '', age = 0;

  // 方法克隆并使用新值
  UserInfo copyWith({String? name, int? age}) {
    return UserInfo(
      name: name ?? this.name,
      age: age ?? this.age,
    );
  }
}

// 补充通知器
final settingsNotifier = ReactiveNotifier<String>(() => 'Dark Mode');
final notificationsEnabledNotifier = ReactiveNotifier<bool>(() => true);

// 组合的ReactiveNotifier,监视所有相关通知器
final userStateNotifier = ReactiveNotifier<UserInfo>(
  () => UserInfo.empty(),
  related: [settingsNotifier, notificationsEnabledNotifier],
);

ReactiveBuilder<UserInfo>(
  notifier: userStateNotifier,
  builder: (userInfo, keep) {
    return Column(
      children: [
        Text("User: ${userInfo.name}, Age: ${userInfo.age}"),
        Text("Configuration: ${settingsNotifier.notifier}"),
        Text("Notifications: ${notificationsEnabledNotifier.notifier ? 'Active' : 'Inactive'}"),
      ],
    );
  },
);

访问ReactiveBuilder中的相关状态

当你有多个相关 ReactiveNotifier 时,可以在 ReactiveBuilder 中以多种方式访问这些状态。

1. 直接访问相关 ReactiveNotifier

class AppDashboard extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ReactiveBuilder<AppState>(
      notifier: appState,
      builder: (state, keep) {
        final user = userState.notifier.data;
        final cart = cartState.notifier.data;
        final settings = settingsState.notifier.data;

        return Column(
          children: [
            Text('Welcome ${user.name}'),
            Text('Cart Items: ${cart.items.length}'),
            Text('Settings: ${settings.theme}'),
            if (user.isLoggedIn) keep(const UserProfile())
          ],
        );
      },
    );
  }
}

2. 使用 from<T>() 方法

class AppDashboard extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ReactiveBuilder<AppState>(
      notifier: appState,
      builder: (state, keep) {
        final user = appState.from<UserState>();
        final cart = appState.from<CartState>();
        final settings = appState.from<SettingsState>();

        return Column(
          children: [
            Text('Welcome ${user.name}'),
            Text('Cart Items: ${cart.items.length}'),
            Text('Settings: ${settings.theme}'),
            if (user.isLoggedIn) keep(const UserProfile())
          ],
        );
      },
    );
  }
}

3. 使用 keyNotifier 访问特定通知器

class AppDashboard extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ReactiveBuilder<AppState>(
      notifier: appState,
      builder: (state, keep) {
        final user = appState.from<UserState>(userState.keyNotifier);
        final cart = appState.from<CartState>(cartState.keyNotifier);
        final settings = appState.from<SettingsState>(settingsState.keyNotifier);

        return Column(
          children: [
            Text('Welcome ${user.name}'),
            Text('Cart Items: ${cart.items.length}'),
            Text('Settings: ${settings.theme}'),
            if (user.isLoggedIn) keep(const UserProfile())
          ],
        );
      },
    );
  }
}

异步与流支持

异步操作

class ProductViewModel extends AsyncViewModelImpl<List<Product>> {
  @override
  Future<List<Product>> fetchData() async {
    return await repository.getProducts();
  }
}

class ProductsScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ReactiveAsyncBuilder<List<Product>>(
      notifier: productViewModel,
      onSuccess: (products) => ProductGrid(products),
      onLoading: () => const LoadingSpinner(),
      onError: (error, stack) => ErrorWidget(error),
      onInitial: () => const InitialView(),
    );
  }
}

流处理

final messagesStream = ReactiveNotifier<Stream<Message>>(
  () => messageRepository.getMessageStream()
);

class ChatScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ReactiveStreamBuilder<Message>(
      notifier: messagesStream,
      onData: (message) => MessageBubble(message),
      onLoading: () => const LoadingIndicator(),
      onError: (error) => ErrorMessage(error),
      onEmpty: () => const NoMessages(),
      onDone: () => const StreamComplete(),
    );
  }
}

调试系统

ReactiveNotifier 包括一个全面的调试系统,具有详细的错误消息:

  • 创建跟踪
  • 无效结构检测
  • 性能监控

最佳实践

  • 状态声明
    • 全局或作为静态mixin成员声明 ReactiveNotifier 实例
    • 从不创建小部件内部的实例
    • 使用mixin更好地组织相关状态
  • 性能优化
    • 使用 keep 用于静态内容
    • 维护扁平的状态层次结构
    • 使用 keyNotifier 访问特定状态
    • 避免不必要的重建
  • 架构指南
    • 遵循MVVM模式
    • 利用仓库/服务模式
    • 让ViewModel自动初始化
    • 保持状态更新与上下文无关
  • 相关状态
    • 维护扁平的关系
    • 避免循环依赖
    • 使用类型安全的访问
    • 保持状态更新可预测

即将推出:实时状态检查器 🔍

我们正在开发一个强大的可视化调试界面,将彻底改变你调试和监控 ReactiveNotifier 状态的方式:

  • 实时状态可视化
  • 实时更新跟踪
  • 性能指标
  • 交互式依赖图
  • 更新时间线
  • 深度状态检查
  • DevTools集成

示例

查看我们的 示例应用 以获取更多综合示例和用例。

贡献

我们欢迎贡献!请先阅读我们的 贡献指南

  1. Fork 项目
  2. 创建功能分支(git checkout -b feature/amazing
  3. 提交更改(git commit -am 'Add amazing feature'
  4. 推送到分支(git push origin feature/amazing
  5. 创建新的Pull Request

支持

  • 星标项目以表示支持
  • 报告bug
  • 提交功能请求
  • 贡献文档

许可证

本项目采用MIT许可证,详情参见 LICENSE 文件。

Made with ❤️ by JhonaCode


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

1 回复

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


当然,以下是一个关于如何使用Flutter中的reactive_notifier插件进行响应式状态管理的代码示例。reactive_notifier是一个轻量级的状态管理库,它结合了providerreactive_dart库的优势,使得状态管理更加简洁和高效。

首先,确保你的pubspec.yaml文件中已经添加了reactive_notifier依赖:

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

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

接下来,我们创建一个简单的计数器应用来演示如何使用reactive_notifier

1. 创建CounterNotifier

首先,我们需要创建一个继承自ReactiveNotifier的类,这个类将管理我们的计数器状态。

import 'package:reactive_notifier/reactive_notifier.dart';

class CounterNotifier extends ReactiveNotifier<int> {
  CounterNotifier() : super(0);

  void increment() {
    value = value! + 1;
  }

  void decrement() {
    value = value! - 1;
  }
}

2. 设置Provider

main.dart中,我们需要使用ReactiveProvider来提供我们的CounterNotifier实例。

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:reactive_notifier/reactive_notifier.dart';
import 'counter_notifier.dart';

void main() {
  runApp(
    MultiProvider(
      providers: [
        ReactiveProvider<CounterNotifier>(
          create: (_) => CounterNotifier(),
        ),
      ],
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: CounterPage(),
    );
  }
}

3. 使用useReactiveNotifier钩子在UI中消费状态

CounterPage中,我们使用useReactiveNotifier钩子来访问和监听CounterNotifier的状态。

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

class CounterPage extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final counterNotifier = useReactiveNotifier<CounterNotifier>();

    return Scaffold(
      appBar: AppBar(
        title: Text('Reactive Notifier Example'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '${counterNotifier.value}',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: counterNotifier.increment,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
      floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
    );
  }
}

4. 添加减少按钮(可选)

如果你还想添加一个减少按钮,可以在CounterPage中添加如下代码:

// 在Column中添加一个新的FloatingActionButton
Column(
  mainAxisAlignment: MainAxisAlignment.center,
  children: <Widget>[
    Text(
      'You have pushed the button this many times:',
    ),
    Text(
      '${counterNotifier.value}',
      style: Theme.of(context).textTheme.headline4,
    ),
    SizedBox(height: 20),  // 添加一些间距
    FloatingActionButton(
      onPressed: counterNotifier.decrement,
      tooltip: 'Decrement',
      child: Icon(Icons.remove),
    ),
  ],
)

这样,你就完成了一个使用reactive_notifier进行响应式状态管理的简单计数器应用。这个示例展示了如何创建和管理状态、在UI中监听状态变化,以及如何在UI中触发状态更新。

回到顶部