河波德 | 真正需要多少个 Provider 才能仅监视类的单个状态

Mar*_*rco 1 state-management flutter flutter-provider riverpod

我遵循了这个 优秀的Riverpod 教程。在最后的步骤中,作者使用了以下代码:

\n
final _buttonState = Provider<ButtonState>((ref) {\n  return ref.watch(timerProvider.state).buttonState;\n});\nfinal buttonProvider = Provider<ButtonState>((ref) {\n  return ref.watch(_buttonState);\n});\n
Run Code Online (Sandbox Code Playgroud)\n

\n
final _timeLeftProvider = Provider<String>((ref) {\n  return ref.watch(timerProvider.state).timeLeft;\n});\nfinal timeLeftProvider = Provider<String>((ref) {\n  return ref.watch(_timeLeftProvider);\n});\n
Run Code Online (Sandbox Code Playgroud)\n

我尝试使用_buttonStateand _timeLeftProvider ,据我所知,该应用程序运行正常。所以,我的问题是:

\n
    \n
  • 有什么需要创建和使用buttonProviderand timeLeftProvider
  • \n
  • 真正需要多少个提供商?
  • \n
\n

非常感谢!

\n

2020-10-26 更新(main.dart代码和输出图像)

\n

我的main.dart代码是:

\n
import \'package:flutter/material.dart\';\nimport \'package:flutter_hooks/flutter_hooks.dart\';\nimport \'package:hooks_riverpod/hooks_riverpod.dart\';\nimport \'package:riverpod_timer_app/timer.dart\';\n\nfinal timerProvider = StateNotifierProvider<TimerNotifier>(\n  (ref) => TimerNotifier(),\n);\n\nfinal _buttonState = Provider<ButtonState>((ref) {\n  return ref.watch(timerProvider.state).buttonState;\n});\n\nfinal buttonProvider = Provider<ButtonState>((ref) {\n  return ref.watch(_buttonState);\n});\n\nfinal _timeLeftProvider = Provider<String>((ref) {\n  return ref.watch(timerProvider.state).timeLeft;\n});\nfinal timeLeftProvider = Provider<String>((ref) {\n  return ref.watch(_timeLeftProvider);\n});\n\nvoid main() {\n  runApp(\n    const ProviderScope(child: MyApp()),\n  );\n}\n\nclass MyApp extends StatelessWidget {\n  const MyApp({Key key}) : super(key: key);\n\n  @override\n  Widget build(BuildContext context) {\n    return MaterialApp(\n      debugShowCheckedModeBanner: false,\n      home: MyHomePage(),\n    );\n  }\n}\n\nclass MyHomePage extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    print(\'building MyHomePage\');\n\n    return Scaffold(\n      appBar: AppBar(title: Text(\'My Timer App\')),\n      body: Center(\n        child: Column(\n          mainAxisAlignment: MainAxisAlignment.center,\n          children: <Widget>[\n            TimerTextWidget(),\n            SizedBox(height: 20),\n            ButtonsContainer(),\n          ],\n        ),\n      ),\n    );\n  }\n}\n\nclass TimerTextWidget extends HookWidget {\n  const TimerTextWidget({Key key}) : super(key: key);\n\n  @override\n  Widget build(BuildContext context) {\n    final timeLeft = useProvider(timeLeftProvider);\n\n    print(\'building TimerTextWidget $timeLeft\');\n\n    return Text(\n      timeLeft,\n      style: Theme.of(context).textTheme.headline2,\n    );\n  }\n}\n\nclass ButtonsContainer extends HookWidget {\n  const ButtonsContainer({Key key}) : super(key: key);\n\n  @override\n  Widget build(BuildContext context) {\n    print(\'building ButtonsContainer\');\n\n    final state = useProvider(buttonProvider);\n\n    return Row(\n      mainAxisAlignment: MainAxisAlignment.center,\n      children: [\n        if (state == ButtonState.initial) ...[\n          StartButton(),\n        ],\n        if (state == ButtonState.started) ...[\n          PauseButton(),\n          SizedBox(width: 20),\n          ResetButton(),\n        ],\n        if (state == ButtonState.paused) ...[\n          StartButton(),\n          SizedBox(width: 20),\n          ResetButton(),\n        ],\n        if (state == ButtonState.finished) ...[\n          ResetButton(),\n        ],\n      ],\n    );\n  }\n}\n\nclass StartButton extends StatelessWidget {\n  const StartButton({Key key}) : super(key: key);\n\n  @override\n  Widget build(BuildContext context) {\n    print(\'building StartButton\');\n    return FloatingActionButton(\n      onPressed: context.read(timerProvider).start,\n      child: Icon(Icons.play_arrow),\n    );\n  }\n}\n\nclass PauseButton extends StatelessWidget {\n  const PauseButton({Key key}) : super(key: key);\n\n  @override\n  Widget build(BuildContext context) {\n    print(\'building PauseButton\');\n\n    return FloatingActionButton(\n      onPressed: context.read(timerProvider).pause,\n      child: Icon(Icons.pause),\n    );\n  }\n}\n\nclass ResetButton extends StatelessWidget {\n  const ResetButton({Key key}) : super(key: key);\n\n  @override\n  Widget build(BuildContext context) {\n    print(\'building ResetButton\');\n\n    return FloatingActionButton(\n      onPressed: context.read(timerProvider).reset,\n      child: Icon(Icons.replay),\n    );\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

如果我点击 \xe2\x80\x98Play\xe2\x80\x99 按钮,然后等 10 秒过去,最后我在两种情况下得到相同的结果:

\n

输出

\n

2020-10-27 更新(不使用and 的main.dart代码)buttonProvidertimeLeftProvider

\n

buttonProvider即使未使用和,这也是输出timeLeftProvider,如下所示main.dart

\n
import \'package:flutter/material.dart\';\nimport \'package:flutter_hooks/flutter_hooks.dart\';\nimport \'package:hooks_riverpod/hooks_riverpod.dart\';\nimport \'package:riverpod_timer_app/timer.dart\';\n\nfinal timerProvider = StateNotifierProvider<TimerNotifier>(\n  (ref) => TimerNotifier(),\n);\n\nfinal _buttonState = Provider<ButtonState>((ref) {\n  return ref.watch(timerProvider.state).buttonState;\n});\n\n// final buttonProvider = Provider<ButtonState>((ref) {\n//   return ref.watch(_buttonState);\n// });\n\nfinal _timeLeftProvider = Provider<String>((ref) {\n  return ref.watch(timerProvider.state).timeLeft;\n});\n\n// final timeLeftProvider = Provider<String>((ref) {\n//   return ref.watch(_timeLeftProvider);\n// });\n\nvoid main() {\n  runApp(\n    const ProviderScope(child: MyApp()),\n  );\n}\n\nclass MyApp extends StatelessWidget {\n  const MyApp({Key key}) : super(key: key);\n\n  @override\n  Widget build(BuildContext context) {\n    return MaterialApp(\n      debugShowCheckedModeBanner: false,\n      home: MyHomePage(),\n    );\n  }\n}\n\nclass MyHomePage extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    print(\'building MyHomePage\');\n\n    return Scaffold(\n      appBar: AppBar(title: Text(\'My Timer App\')),\n      body: Center(\n        child: Column(\n          mainAxisAlignment: MainAxisAlignment.center,\n          children: <Widget>[\n            TimerTextWidget(),\n            SizedBox(height: 20),\n            ButtonsContainer(),\n          ],\n        ),\n      ),\n    );\n  }\n}\n\nclass TimerTextWidget extends HookWidget {\n  const TimerTextWidget({Key key}) : super(key: key);\n\n  @override\n  Widget build(BuildContext context) {\n    final timeLeft = useProvider(_timeLeftProvider);\n\n    print(\'building TimerTextWidget $timeLeft\');\n\n    return Text(\n      timeLeft,\n      style: Theme.of(context).textTheme.headline2,\n    );\n  }\n}\n\nclass ButtonsContainer extends HookWidget {\n  const ButtonsContainer({Key key}) : super(key: key);\n\n  @override\n  Widget build(BuildContext context) {\n    print(\'building ButtonsContainer\');\n\n    final state = useProvider(_buttonState);\n\n    return Row(\n      mainAxisAlignment: MainAxisAlignment.center,\n      children: [\n        if (state == ButtonState.initial) ...[\n          StartButton(),\n        ],\n        if (state == ButtonState.started) ...[\n          PauseButton(),\n          SizedBox(width: 20),\n          ResetButton(),\n        ],\n        if (state == ButtonState.paused) ...[\n          StartButton(),\n          SizedBox(width: 20),\n          ResetButton(),\n        ],\n        if (state == ButtonState.finished) ...[\n          ResetButton(),\n        ],\n      ],\n    );\n  }\n}\n\nclass StartButton extends StatelessWidget {\n  const StartButton({Key key}) : super(key: key);\n\n  @override\n  Widget build(BuildContext context) {\n    print(\'building StartButton\');\n    return FloatingActionButton(\n      onPressed: context.read(timerProvider).start,\n      child: Icon(Icons.play_arrow),\n    );\n  }\n}\n\nclass PauseButton extends StatelessWidget {\n  const PauseButton({Key key}) : super(key: key);\n\n  @override\n  Widget build(BuildContext context) {\n    print(\'building PauseButton\');\n\n    return FloatingActionButton(\n      onPressed: context.read(timerProvider).pause,\n      child: Icon(Icons.pause),\n    );\n  }\n}\n\nclass ResetButton extends StatelessWidget {\n  const ResetButton({Key key}) : super(key: key);\n\n  @override\n  Widget build(BuildContext context) {\n    print(\'building ResetButton\');\n\n    return FloatingActionButton(\n      onPressed: context.read(timerProvider).reset,\n      child: Icon(Icons.replay),\n    );\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

我究竟做错了什么?

\n

Ale*_*ord 5

这些提供程序用于防止不必要的重建,但从根本上来说并不是必需的。仅创建您需要的提供程序 - 特别是这些提供程序在应用程序生命周期中永远不会被丢弃,它们只是浪费空间。然而,防止不必要的重建应该是首要任务。

在链接的文章中,作者正在利用包作者推荐的解决方法来防止在侦听 StateNotifier 的特定属性时进行重建。因此,就目前而言,这是完成任务的最有效方法。如果引入新功能来解决这个问题,我将尝试更新这个答案。

我会参考包创建者的示例以获取更多上下文。

下面是一个简单示例,说明了为什么您可以使用多个提供程序来缓存来自外部 API 的响应:

class ExampleApiRepository {
  ExampleApiRepository(this._read);

  static final provider = Provider((ref) => ExampleApiRepository(ref.read));

  final Reader _read;

  Future<Example> search(String query) async {
    final response = await _call('api/example/$query');
    return Example.fromJson(response.data);
  }
}

final searchExample = FutureProvider.family<Example, String>((ref, query) async {
  return ref.watch(ExampleApiRepository.provider).search(query);
});
Run Code Online (Sandbox Code Playgroud)

在此示例中,如果将相同的查询传递给searchExample提供者,它将返回先前获取的结果。如果没有多个提供商,这可以实现吗?是的——对于大多数情况来说这是正确的。创建提供商关乎便利和效率。因此,不要害怕使用许多提供程序,但不要为了创建它们而创建它们。

也就是说,您链接的文章内容丰富且值得赞赏。