Flutter状态恢复与依赖管理插件flutter_riverpod_restorable的使用

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

Flutter状态恢复与依赖管理插件flutter_riverpod_restorable的使用

特性

Riverpod 不提供简单的状态恢复方法,当应用被操作系统杀死时,状态将无法恢复。此插件提供了这样的功能。

使用方法

如何注册全局提供者以进行状态恢复
final counterProvider = RestorableProvider(
  // 我们的默认值
  (ref) => RestorableInt(0),
  // 每个提供者都需要一个唯一的恢复ID
  restorationId: 'counterProvider',
);

MaterialApp(
  // 必须恢复状态
  restorationScopeId: 'root',
  // 必须注册全局提供者以进行恢复
  builder: (context, child) => RestorableProviderRegister(
    restorationId: 'app',
    providers: [
      // 在这里列出你的全局可恢复提供者
      counterProvider,
    ],
    child: child ?? const SizedBox.shrink(),
  ),
);
如何注册覆盖提供者以进行状态恢复
RestorableProviderScope(
  restorationId: 'counter_page_scope',
  overrides: [
    // 如果你只使用被覆盖的恢复提供者,则只需在这里添加它。
    counterProvider.overrideWith((ref) => RestorableInt(0)),
  ],
  child: const CounterPage(),
)

其他信息

有关如何恢复导航的信息,请参阅示例或访问 Flutter 文档

示例代码

以下是一个完整的示例,展示了如何使用 flutter_riverpod_restorable 插件来实现状态恢复和依赖管理。

import 'package:flutter/services.dart' show StandardMessageCodec;
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_riverpod_restorable/flutter_riverpod_restorable.dart';

void main() => runApp(const ProviderScope(child: MyApp()));

final materialColors = [
  Colors.red,
  Colors.pink,
  Colors.purple,
  Colors.deepPurple,
  Colors.indigo,
  Colors.blue,
  Colors.lightBlue,
  Colors.cyan,
  Colors.teal,
  Colors.green,
  Colors.lightGreen,
  Colors.lime,
  Colors.yellow,
  Colors.amber,
  Colors.orange,
  Colors.deepOrange,
  Colors.brown,
  Colors.grey,
  Colors.blueGrey,
];

/// 我们只能恢复可以由 [StandardMessageCodec] 序列化的值。
/// 对于任何不能由 [StandardMessageCodec] 序列化的值,我们必须自己进行序列化。
/// 一种简单的方法是将其从/向JSON转换。
/// 有关更多信息,请参阅 [RestorableProperty]。
class RestorableMaterialColor extends RestorableValue<MaterialColor> {
  RestorableMaterialColor(this._defaultValue);

  final MaterialColor _defaultValue;

  [@override](/user/override)
  MaterialColor createDefaultValue() => _defaultValue;

  [@override](/user/override)
  void didUpdateValue(MaterialColor? oldValue) => notifyListeners();

  [@override](/user/override)
  MaterialColor fromPrimitives(Object? data) {
    final value = (data as List).cast<int>();
    return MaterialColor(value[0], {
      50: Color(value[1]),
      100: Color(value[2]),
      200: Color(value[3]),
      300: Color(value[4]),
      400: Color(value[5]),
      500: Color(value[6]),
      600: Color(value[7]),
      700: Color(value[8]),
      800: Color(value[9]),
      900: Color(value[10]),
    });
  }

  [@override](/user/override)
  Object? toPrimitives() => [
        value.value,
        value.shade50.value,
        value.shade100.value,
        value.shade200.value,
        value.shade300.value,
        value.shade400.value,
        value.shade500.value,
        value.shade600.value,
        value.shade700.value,
        value.shade800.value,
        value.shade900.value,
      ];
}

/// 每个 [RestorableProvider] 都必须有一个唯一的 [RestorableProvider.restorationId]
final primaryMaterialColorProvider = 
    RestorableProvider<RestorableMaterialColor>(
  (ref) => RestorableMaterialColor(Colors.blue),
  restorationId: 'primaryMaterialColorProvider',
);

final counterProvider = RestorableProvider<RestorableInt>(
  (ref) => throw UnimplementedError(),
  restorationId: 'counterProvider',
);

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

  [@override](/user/override)
  Widget build(BuildContext context, WidgetRef ref) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(),

      /// 必须恢复状态
      restorationScopeId: 'root',

      /// 必须注册全局提供者以进行恢复
      builder: (context, child) => RestorableProviderRegister(
        restorationId: 'app',
        providers: [
          primaryMaterialColorProvider,
        ],
        child: child ?? const SizedBox.shrink(),
      ),
      home: const HomePage(),
    );
  }
}

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

  [@override](/user/override)
  Widget build(BuildContext context, WidgetRef ref) {
    final color = ref.watch(primaryMaterialColorProvider).value;
    return Theme(
      data: ThemeData(primarySwatch: color),
      child: Scaffold(
        appBar: AppBar(
          title: const Text('Home Page'),
        ),
        body: Center(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              ElevatedButton(
                child: const Text("Open counter screen"),
                onPressed: () => _onCounter(context),
              ),
              ElevatedButton(
                child: const Text("Random color"),
                onPressed: () {
                  final color = ref.read(primaryMaterialColorProvider);
                  color.value = (materialColors..shuffle()).first;
                },
              ),
            ],
          ),
        ),
      ),
    );
  }

  /// 使用可恢复的导航方法来记住当前导航状态。
  /// 传递的任何参数都必须由 [StandardMessageCodec] 序列化。
  String _onCounter(BuildContext context) {
    return Navigator.of(context).restorablePush(_buildRoute, arguments: 0);
  }

  /// 路由必须静态才能被恢复。
  static Route<void> _buildRoute(BuildContext context, Object? params) {
    return MaterialPageRoute(
      builder: (BuildContext context) {
        /// 如果你想覆盖一个 [RestorableProvider],你必须使用 [RestorableProviderScope]。
        return RestorableProviderScope(
          restorationId: 'counter_page_scope',
          overrides: [
            counterProvider.overrideWith((ref) => RestorableInt(params as int)),
          ],
          child: const CounterPage(),
        );
      },
    );
  }
}

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

  void _incrementCounter(WidgetRef ref) {
    final counter = ref.read(counterProvider);
    counter.value++;
  }

  [@override](/user/override)
  Widget build(BuildContext context, WidgetRef ref) {
    final color = ref.watch(primaryMaterialColorProvider).value;
    final counter = ref.watch(counterProvider);
    return Theme(
      data: ThemeData(primarySwatch: color),
      child: Scaffold(
        appBar: AppBar(
          title: const Text("Counter"),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              const Text('You have pushed the button this many times:'),
              Text(
                '${counter.value}',
                style: Theme.of(context).textTheme.headlineMedium,
              ),
            ],
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () => _incrementCounter(ref),
          tooltip: 'Increment',
          child: const Icon(Icons.add),
        ),
      ),
    );
  }
}

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

1 回复

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


当然,下面是一个关于如何在Flutter应用中使用flutter_riverpod_restorable插件进行状态恢复与依赖管理的代码示例。这个插件允许你在应用重新启动时恢复Riverpod的状态。

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

dependencies:
  flutter:
    sdk: flutter
  flutter_hooks: ^0.18.0 # 如果你使用Flutter Hooks
  flutter_riverpod: ^1.0.0 # 请根据最新版本调整
  flutter_riverpod_restorable: ^0.2.0 # 请根据最新版本调整

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

接下来,让我们创建一个简单的Flutter应用,演示如何使用flutter_riverpod_restorable

主应用文件 (main.dart)

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

void main() {
  runApp(
    ProviderScope(
      overrides: [
        restorableStateProvider.overrideWithValue(
          RestorableState(
            storage: FlutterStorage(), // 使用Flutter的本地存储
          ),
        ),
      ],
      child: MyApp(),
    ),
  );
}

class MyApp extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final counterProvider = useRestorableProvider((ref) => 0);
    final increment = useStateProvider((ref) => () {
      ref.value = (ref.value ?? 0) + 1;
    });

    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flutter Riverpod Restorable Example'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Text(
                'You have pushed the button this many times:',
              ),
              Text(
                '${counterProvider.state}',
                style: Theme.of(context).textTheme.headline4,
              ),
            ],
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () => increment.read().call(),
          tooltip: 'Increment',
          child: Icon(Icons.add),
        ),
      ),
    );
  }
}

解释

  1. ProviderScope: 在main函数中,我们使用ProviderScope来包裹整个应用,并覆盖restorableStateProvider,使用FlutterStorage进行本地存储。

  2. useRestorableProvider: 我们使用useRestorableProvider来创建一个可恢复的计数器状态。这个状态在应用重新启动时会从存储中恢复。

  3. useStateProvider: 我们使用useStateProvider来创建一个增加计数器的函数。这个函数通过修改ref.value来增加计数器的值。

  4. UI部分: 在UI中,我们显示当前的计数器值,并添加一个浮动按钮来增加计数器的值。

这个示例展示了如何使用flutter_riverpod_restorable插件来恢复应用的状态。你可以根据需要扩展这个示例,添加更多的状态管理逻辑和复杂的UI。

回到顶部