乐趣区

关于flutter:基于-Riverpod-的-Flutter-状态管理

原文

https://itnext.io/flutter-sta…

代码

https://github.com/iisprey/ri…

参考

  • https://itnext.io/a-minimalis…
  • https://pub.dev/packages/stat…
  • https://iisprey.medium.com/ge…
  • https://iisprey.medium.com/ho…

注释

正如我上周所承诺的,我将向您展现我本人的最终国家治理解决方案门路

Riverpod + StateNotifier + Hooks + Freezed

Riverpod 太棒了!然而好的例子并不多。只有最根本的,就这样。这一次,我试图使一个例子既可了解又简单。我的目标是通过这个例子教你什么时候应用 Riverpod,以及如何应用它。只管我简化了过程。心愿你喜爱!

动机

在这个例子中咱们要做什么?

咱们只须要从 API 中获取一些数据,而后在 UI 中对它们进行排序和过滤

基本上,咱们会;

  1. Create simple and complex providers and combine them
  2. 创立简略和简单的提供程序并将它们组合起来
  3. Use AsyncValue object and show async value in the UI using when method
  4. 应用 AsyncValue 对象并在 UI 中应用 when 办法显示 async 值
  5. Also, create freezed objects for immutable object solution
  6. 同时,为不可变物件 / 解决方案创立解冻对象

咱们开始吧!

创立 API 服务

留神: 我没有找到一个好的 API 模型来应用过滤性能,因为我本人增加了这些角色。原谅我这么说

final userService = Provider((ref) => UserService());

class UserService {final _dio = Dio(BaseOptions(baseUrl: 'https://reqres.in/api/'));

  Future<List<User>> getUsers() async {final res = await _dio.get('users');
    final List list = res.data['data'];
    // API didn't have user roles I just added by hand (it looks ugly but never mind)
    list[0]['role'] = 'normal';
    list[1]['role'] = 'normal';
    list[2]['role'] = 'normal';
    list[3]['role'] = 'admin';
    list[4]['role'] = 'admin';
    list[5]['role'] = 'normal';
    return list.map((e) => User.fromJson(e)).toList();}
}

应用 freezedjson_serializable 创立不可变模型

咱们只须要创立一个

part 'user.freezed.dart';
part 'user.g.dart';

@freezed
class User with _$User {@JsonSerializable(fieldRename: FieldRename.snake)
  const factory User({
    required int id,
    required String email,
    required String firstName,
    required String lastName,
    required String avatar,
    @JsonKey(unknownEnumValue: Role.normal) required Role role,
  }) = _User;

  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
}

如果你想理解更多对于 freezed 的信息,请查看这篇文章。

https://iisprey.medium.com/ho…

从服务中取得 value

你能够想想,AsyncValue 是什么? 它只是一个联结类,帮忙咱们解决咱们的值的状态。它提供了一个现成的提供者包。

我将在接下来的文章中具体解释,但就目前而言,仅此而已。

final usersProvider = StateNotifierProvider.autoDispose<UserNotifier, AsyncValue<List<User>>>((ref) {return UserNotifier(ref);
});

class UserNotifier extends StateNotifier<AsyncValue<List<User>>> {
  final AutoDisposeStateNotifierProviderRef _ref;

  late final UserService _service;

  UserNotifier(this._ref) : super(const AsyncValue.data(<User>[])) {_service = _ref.watch(userService);
    getUsers();}

  Future<void> getUsers() async {state = const AsyncValue.loading();
    final res = await AsyncValue.guard(() async => await _service.getUsers());
    state = AsyncValue.data(res.asData!.value);
  }
}

创立排序和过滤器提供程序

enum Role {none, normal, admin}
enum Sort {normal, reversed}

final filterProvider = StateProvider.autoDispose<Role>((_) => Role.none);
final sortProvider = StateProvider.autoDispose<Sort>((_) => Sort.normal);

从提供程序获取获取的列表并进行筛选,而后应用其余提供程序对它们进行排序

final filteredAndSortedUsersProvider = Provider.autoDispose.family<List<User>, List<User>>((ref, users) {final filter = ref.watch(filterProvider);
  final sort = ref.watch(sortProvider);

  late final List<User> filteredList;

  switch (filter) {
    case Role.admin:
      filteredList = users.where((e) => e.role == Role.admin).toList();
      break;
    case Role.normal:
      filteredList = users.where((e) => e.role == Role.normal).toList();
      break;
    default:
      filteredList = users;
  }

  switch (sort) {
    case Sort.normal:
      return filteredList;
    case Sort.reversed:
      return filteredList.reversed.toList();
    default:
      return filteredList;
  }
});

在用户界面中显示所有内容

class HomePage extends ConsumerWidget {const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {final users = ref.watch(usersProvider);
    return Scaffold(
      appBar: AppBar(
        centerTitle: true,
        title: const Text('Users'),
      ),
      body: RefreshIndicator(onRefresh: () async => await ref.refresh(usersProvider),
        child: users.when(data: (list) {final newList = ref.watch(filteredAndSortedUsersProvider(list));
            if (newList.isEmpty) {return const Center(child: Text('There is no user'));
            }
            return ListView.builder(
              itemCount: newList.length,
              itemBuilder: (_, i) {final user = newList[i];
                return ListTile(
                  minVerticalPadding: 25,
                  leading: Image.network(user.avatar),
                  title: Text('${user.firstName} ${user.lastName}'),
                  trailing: Text(user.role.name),
                );
              },
            );
          },
          error: (_, __) => const Center(child: Text('err')),
          loading: () => const Center(child: CircularProgressIndicator()),
        ),
      ),
      floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
      floatingActionButton: Row(
        mainAxisSize: MainAxisSize.min,
        children: [
          Expanded(
            child: Padding(padding: const EdgeInsets.all(8.0),
              child: Consumer(builder: (_, ref, __) {final sort = ref.watch(sortProvider.state);
                  return ElevatedButton(onPressed: () {if (sort.state == Sort.reversed) {sort.state = Sort.normal;} else {sort.state = Sort.reversed;}
                    },
                    child: Text(
                      sort.state == Sort.normal
                          ? 'sort reversed'
                          : 'sort normal',
                    ),
                  );
                },
              ),
            ),
          ),
          Expanded(
            child: Padding(padding: const EdgeInsets.all(8.0),
              child: Consumer(builder: (_, ref, __) {final filter = ref.watch(filterProvider.state);
                  return ElevatedButton(onPressed: () {if (filter.state == Role.admin) {filter.state = Role.none;} else {filter.state = Role.admin;}
                    },
                    child: Text(
                      filter.state == Role.admin
                          ? 'remove filter'
                          : 'filter admins',
                    ),
                  );
                },
              ),
            ),
          ),
        ],
      ),
    );
  }
}

完结

如果你把这个例子看作一个电子商务应用程序,那么这个例子就更有意义了

我不是 riverpod 的宗师。只是学习和分享我的教训,所以请如果你晓得一个更好的形式应用 riverpod 请让咱们晓得!

示例 Github 我的项目

这是源代码。

https://github.com/iisprey/ri…

谢谢你的浏览


© 猫哥

  • ducafecat.tech
  • github
  • bilibili

退出移动版