Flutter状态管理插件riverpod_hook_mutation的使用

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

Flutter状态管理插件riverpod_hook_mutation的使用

Riverpod and hook mutation package

License Pub GitHub stars

这是一个提供简单方式在Riverpod中管理mutations的包。

Inspiration

解决Riverpod中的mutation问题:https://github.com/rrousselGit/riverpod/issues/1660

Features

  • ✅ 使用hooks进行mutation
  • ✅ 使用provider进行mutation
  • ✅ 使用Flutter的ValueNotifierAsyncSnapshot来通知状态变化

Getting Started

为了使用这个包,你需要在你的pubspec.yaml文件中添加riverpod_hook_mutation作为依赖项。

dependencies:
  riverpod_hook_mutation: ^0.0.1

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

Usage

以下是一个完整的示例代码,展示了如何使用riverpod_hook_mutation来管理状态。

示例代码

主入口文件 main.dart

import 'dart:math';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:riverpod_hook_mutation/riverpod_hook_mutation.dart';

part 'main.g.dart';

Future<void> main() async {
  runApp(
    const ProviderScope(
      child: MaterialApp(
        home: TodosScreen(),
      ),
    ),
  );
}

class TodoRepository {
  static final TodoRepository _instance = TodoRepository._internal();

  factory TodoRepository() => _instance;

  TodoRepository._internal();

  final _todos = [
    TodoModel(
      title: 'Buy milk',
      completed: false,
    ),
    TodoModel(
      title: 'Buy eggs',
      completed: true,
    ),
    TodoModel(
      title: 'Buy bread',
      completed: false,
    ),
  ];

  Future<List<TodoModel>> fetchTodos() async {
    await Future.delayed(const Duration(seconds: 3));
    return _todos;
  }

  Future<TodoModel> createTodo() async {
    return Future.delayed(const Duration(seconds: 3), () {
      final todo = TodoModel(
        title: 'Buy cheese ${Random().nextInt(1000000)}',
        completed: false,
      );
      _todos.add(todo);
      return todo;
    });
  }
}

class TodoModel {
  final String title;
  final bool completed;

  TodoModel({
    required this.title,
    required this.completed,
  });

  Map<String, dynamic> toJson() {
    return {
      'title': title,
      'completed': completed,
    };
  }
}

@riverpod
class Todos extends _$Todos {
  TodoRepository get _repository => TodoRepository();

  @override
  Future<List<TodoModel>> build() {
    return _repository.fetchTodos();
  }

  Future<TodoModel> addTodo() async {
    final result = await _repository.createTodo();
    ref.invalidateSelf();

    if (kDebugMode) print(result.toJson());
    return result;
  }
}

class TodosScreen extends HookConsumerWidget {
  const TodosScreen({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final provider = todosProvider;
    final todos = ref.watch(provider);

    return Scaffold(
      appBar: AppBar(
        actions: [
          FloatingActionButton.small(
            heroTag: null,
            child: const Icon(Icons.add),
            onPressed: () {
              Navigator.of(context).push(
                MaterialPageRoute(
                  builder: (_) {
                    return const ItemAddScreen();
                  },
                ),
              );
            },
          )
        ],
      ),
      body: todos.when(
        data: (data) {
          return RefreshIndicator(
            onRefresh: () => ref.read(provider.future),
            child: ListView.builder(
              itemCount: data.length,
              itemBuilder: (context, index) {
                final todo = data[index];
                return ListTile(
                  title: Text(todo.title),
                );
              },
            ),
          );
        },
        error: (error, stackTrace) {
          return Center(
            child: Text('Error: $error'),
          );
        },
        loading: () {
          return const Center(
            child: CircularProgressIndicator(),
          );
        },
      ),
    );
  }
}

class ItemAddScreen extends HookConsumerWidget {
  const ItemAddScreen({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final addTodo = useMutation<TodoModel>();

    return Scaffold(
      appBar: AppBar(),
      body: Column(
        children: [
          addTodo.when(
            idle: () {
              return const Icon(Icons.add);
            },
            data: (data) {
              return const Icon(Icons.add);
            },
            error: (error, stackTrace) {
              return const Icon(Icons.error);
            },
            loading: CircularProgressIndicator.new,
          ),
          FilledButton(
            child: const Text('Add .call'),
            onPressed: () {
              final notifier = ref.read(todosProvider.notifier);
              addTodo(
                notifier.addTodo(),
                mounted: () => context.mounted,
              );
            },
          ),
          FilledButton(
            child: const Text('Add .mutate'),
            onPressed: () async {
              final notifier = ref.read(todosProvider.notifier);

              await addTodo.mutate(
                notifier.addTodo(),
                mounted: () => context.mounted,
                loading: Navigator.of(context).pop,
                data: (data) {
                  ScaffoldMessenger.of(context).showSnackBar(
                    SnackBar(
                      content: Text('Todo added: ${data.title}'),
                    ),
                  );
                },
                error: (error, stackTrace) {
                  ScaffoldMessenger.of(context).showSnackBar(
                    SnackBar(
                      content: Text('Error: $error'),
                    ),
                  );
                },
              );
            },
          ),
        ],
      ),
    );
  }
}

Documentation

更多详细信息,请查看文档

Contributing

欢迎贡献!如果你发现任何问题或有建议,请创建一个新的issue或提交一个pull request。

License

本项目采用MIT License授权。


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

1 回复

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


当然,以下是一个关于如何使用 riverpod_hook_mutation 进行状态管理的 Flutter 代码示例。riverpod_hook_mutation 是一个结合 Riverpod 和 Flutter Hooks 的状态管理插件,它使得状态管理更加简洁和直观。

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

dependencies:
  flutter:
    sdk: flutter
  flutter_hooks: ^0.18.0 # 请检查最新版本
  flutter_riverpod: ^1.0.0 # 请检查最新版本
  riverpod_hook_mutation: ^0.1.0 # 请检查最新版本

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

接下来,我们来看一个完整的示例代码:

import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_hook_mutation/riverpod_hook_mutation.dart';

void main() {
  runApp(
    ProviderScope(
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends HookWidget {
  final counterProvider = StateProvider<int>((ref) => 0);

  @override
  Widget build(BuildContext context) {
    final counter = useProvider(counterProvider.state);
    final mutation = useMutation(counterProvider);

    return Scaffold(
      appBar: AppBar(
        title: Text('Riverpod Hook Mutation Demo'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          mutation.mutate((state) => state! + 1);
        },
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

在这个示例中,我们做了以下几件事:

  1. 定义状态提供者:我们使用 StateProvider 来创建一个管理整数值的提供者 counterProvider

  2. 创建主应用:在 MyApp 中,我们使用 ProviderScope 来包裹整个应用,这是使用 Riverpod 所必需的。

  3. 构建主页面:在 MyHomePage 中,我们使用 HookWidget 作为基类,这使得我们可以使用 Flutter Hooks。

  4. 使用状态提供者:通过 useProvider 钩子获取 counterProvider 的当前状态。

  5. 定义并使用 mutation:通过 useMutation 钩子创建一个 mutation,这个 mutation 可以用来修改状态。在按钮点击事件中,我们调用 mutate 方法,并传入一个更新状态的函数。

这个示例展示了如何使用 riverpod_hook_mutation 来管理一个简单的计数器状态。你可以根据需要扩展这个示例,以管理更复杂的状态。

回到顶部