Flutter数据持久化插件rxstore的使用

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

Flutter数据持久化插件rxstore的使用

RxStore 是一个基于流的 Redux 实现,支持副作用处理。

概念

Store 包含了应用的所有状态,这些状态不能直接被修改。要改变状态,必须分发一个动作(Action),它只是一个数据对象。你可以将动作视为解释某些更改原因的面包屑。Reducer 是一个函数,它接受一个动作和当前状态,并返回下一个状态。然后,Store 将新的状态发射给其订阅者。

Reducer 是纯函数且是同步的。异步任务和副作用应在 Epic 中处理。Epic 是一个函数,它接受一个动作和当前状态,并返回一个动作的流。Epic 不能改变状态,但可以返回其他动作,这些动作由可以改变状态的 Reducer 处理。这使得调试变得更加容易。

状态

首先,定义应用的状态。建议使用不可变状态,可以使用像 built_valuefreezed 这样的库来实现,但为了简单起见,我们这里使用普通的 Dart 类:

class AppState {
  AppState({required this.toDoList, required this.showCompleted});

  final List<ToDo> toDoList;
  final bool showCompleted;
}

class ToDo {
  ToDo({required this.text, required this.completed});

  final String text;
  final bool completed;
}

动作

动作是一个描述发生了什么的对象。它只是一个数据,没有逻辑。以下是一个简单的动作,用于向列表中添加一个新的待办事项:

class AddToDo implements Action {
  AddToDo({required this.text});

  final String text;
}

减少器

减少器是唯一更新状态的方法。它接受当前状态和动作作为参数,并返回下一个状态。由于减少器是纯函数,因此它们易于测试和调试。

AppState reducer(AppState state, Action action) {
  if (action is AddToDo) {
    return AppState(
        toDoList: [...state.toDoList, ToDo(text: action.text, completed: false)], showCompleted: state.showCompleted);
  }

  return state;
}

Epic

Epic 用于处理异步任务,如调用 REST 端点或读取文件。与减少器一样,它接受当前状态和动作作为参数,但它返回的是动作的流。这些动作会被再次传递给所有减少器和 Epic,因此确保只处理你感兴趣的事件,以避免编写循环。

// 创建一个 Epic,仅响应 FetchToDoList 动作并调用 _fetchToDoList
Stream<Action> epic(Stream<Action> actions, ValueStream<AppState> state) =>
  actions.whereType<FetchToDoList>() // 过滤 FetchToDoList 动作
         .switchMap((FetchToDoList action) => _fetchToDoList(action));

Stream<Action> _fetchToDoList(FetchToDoList action) async* {
  yield const FetchingToDoList(); // 减少器可以设置加载标志为 true

  // 异步调用服务器
  final response = await client.fetchToDoList();
  if (response.statusCode == HttpStatus.ok) {
      // 所有正常,返回带有列表的动作
      yield FetchedToDoList(list: response.items, error: null);
    } else {
      // 发生错误,返回详细说明错误的动作
      yield FetchedToDoList(list: null, error: 'Something went wrong');
    }
}

Store

Store 将状态、减少器和可选的 Epic 绑定在一起。

创建 Store 时,至少需要传递一个减少器和初始状态:

final store = Store<AppState>(
  reducer,
  initialState: AppState(toDoList: [], showCompleted: true),
);

// 如果有 Epic,可以在构造函数中传递
final store = Store<AppState>(
  reducer,
  initialState: AppState(toDoList: [], showCompleted: true),
  epic: epic,
);

监听 Store

Store 暴露了一个流,始终发出最新的状态。你可以使用它来对状态变化做出反应。

订阅时,即使状态在那之前没有改变,你也会立即得到当前状态。

store.state.listen(print);

如何定义多个减少器/Epic?

该库提供两个帮助函数来完成此操作,combineReducerscombineEpics

final rootReducer = combineReducers([reducerOne, reducerTwo, reducerThree]);
final rootEpic = combineEpics([epicOne, epicTwo, epicThree]);

这两个函数分别返回一个普通的减少器和 Epic,你可以再次使用它们进行组合调用或将它们传递给 Store。

帮助!我写了一个循环…

每个由 Epic 返回的动作也会被相同的 Epic 接收。只要你不返回 Epic 正在处理的动作类型,就不会引入任何循环。

// 无限循环:处理并返回所有动作
Stream<Action> epic(Stream<Action> actions, ValueStream<AppState> state) => actions;

// 无限循环:过滤某种类型但仍然返回相同类型
Stream<Action> epic(Stream<Action> actions, ValueStream<AppState> state) => 
  actions.whereType<FetchToDoList>();

// 无循环:处理 FetchToDoList 类型的动作,但返回 FetchingToDoList
Stream<Action> epic(Stream<Action> actions, ValueStream<AppState> state) =>
  actions.whereType<FetchToDoList>()
         .switchMap((FetchToDoList action) => const FetchingToDoList());

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

1 回复

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


当然,下面是一个关于如何在Flutter中使用rxstore插件进行数据持久化的代码案例。rxstore是一个轻量级的、响应式的Flutter本地存储库,适用于存储和管理应用状态。

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

dependencies:
  flutter:
    sdk: flutter
  rxstore: ^最新版本号  # 请替换为实际的最新版本号

然后,运行flutter pub get来获取依赖。

接下来,我们将展示如何使用rxstore进行简单的数据持久化操作。

1. 初始化RxStore

首先,我们需要创建一个RxStore实例。在这个例子中,我们将存储一个简单的用户对象。

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

class User {
  String name;
  int age;

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

  User.fromJson(Map<String, dynamic> json)
      : name = json['name'],
        age = json['age'];

  Map<String, dynamic> toJson() => {'name': name, 'age': age};
}

class UserStore extends RxStore<User> {
  UserStore() : super(initialState: User(name: '', age: 0));

  void setName(String name) {
    update((state) => User(name: name, age: state.age));
  }

  void setAge(int age) {
    update((state) => User(name: state.name, age: age));
  }
}

2. 使用Provider管理状态

我们可以使用Provider包来管理我们的UserStore实例,这样可以在整个应用中轻松访问和更新用户数据。

main.dart中设置Provider:

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'user_store.dart';

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

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

3. 在UI中使用RxStore

现在,我们可以在UI组件中使用ConsumerSelector来访问和更新用户数据。

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'user_store.dart';

class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final userStore = Provider.of<UserStore>(context);

    return Scaffold(
      appBar: AppBar(
        title: Text('RxStore Demo'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            TextField(
              decoration: InputDecoration(labelText: 'Name'),
              onChanged: (value) {
                userStore.setName(value);
              },
            ),
            SizedBox(height: 16),
            TextField(
              decoration: InputDecoration(labelText: 'Age'),
              keyboardType: TextInputType.number,
              onChanged: (value) {
                if (value.isNotEmpty) {
                  userStore.setAge(int.parse(value));
                }
              },
            ),
            SizedBox(height: 16),
            Text('User Data: ${userStore.state.toJson()}'),
          ],
        ),
      ),
    );
  }
}

4. 数据持久化

rxstore本身并不直接提供持久化功能,但你可以结合其他持久化方案(如shared_preferenceshivesqflite)来实现。在这个例子中,我们将展示如何在应用启动时从持久化存储加载数据,以及在数据更新时保存到持久化存储。

这里我们使用shared_preferences作为示例:

首先,添加shared_preferences依赖:

dependencies:
  shared_preferences: ^最新版本号  # 请替换为实际的最新版本号

然后,修改UserStore以包含持久化逻辑:

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:rxstore/rxstore.dart';
import 'package:shared_preferences/shared_preferences.dart';

class UserStore extends RxStore<User> {
  UserStore() {
    _loadUser();
  }

  late SharedPreferences _preferences;

  Future<void> _loadUser() async {
    _preferences = await SharedPreferences.getInstance();
    final userJson = _preferences.getString('user');
    User? user;
    if (userJson != null) {
      user = User.fromJson(jsonDecode(userJson));
    }
    super.initialState = user ?? User(name: '', age: 0);
  }

  @override
  void notifyListeners() {
    super.notifyListeners();
    _saveUser();
  }

  void _saveUser() {
    _preferences.setString('user', jsonEncode(state.toJson()));
  }

  void setName(String name) {
    update((state) => User(name: name, age: state.age));
  }

  void setAge(int age) {
    update((state) => User(name: state.name, age: age));
  }
}

在这个修改后的UserStore中,我们在构造函数中加载用户数据,并在每次状态更新时保存用户数据。

现在,你的Flutter应用已经能够使用rxstore进行响应式状态管理,并结合shared_preferences实现数据持久化。

回到顶部