Riverpod |真正需要多少个提供者来观看一个类的单个状态

问题描述

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

final _buttonState = Provider<ButtonState>((ref) {
  return ref.watch(timerProvider.state).buttonState;
});
final buttonProvider = Provider<ButtonState>((ref) {
  return ref.watch(_buttonState);
});

final _timeLeftProvider = Provider<String>((ref) {
  return ref.watch(timerProvider.state).timeLeft;
});
final timeLeftProvider = Provider<String>((ref) {
  return ref.watch(_timeLeftProvider);
});

我尝试使用_buttonState_timeLeftProvider ,从我所见,该应用程序可以正常运行。所以,我的问题是:

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

非常感谢您!

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

我的main.dart代码是:

import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:riverpod_timer_app/timer.dart';

final timerProvider = StateNotifierProvider<TimerNotifier>(
  (ref) => TimerNotifier(),);

final _buttonState = Provider<ButtonState>((ref) {
  return ref.watch(timerProvider.state).buttonState;
});

final buttonProvider = Provider<ButtonState>((ref) {
  return ref.watch(_buttonState);
});

final _timeLeftProvider = Provider<String>((ref) {
  return ref.watch(timerProvider.state).timeLeft;
});
final timeLeftProvider = Provider<String>((ref) {
  return ref.watch(_timeLeftProvider);
});

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,home: MyHomePage(),);
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print('building MyHomePage');

    return Scaffold(
      appBar: AppBar(title: Text('My Timer App')),body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[
            TimerTextWidget(),SizedBox(height: 20),ButtonsContainer(),],),);
  }
}

class TimerTextWidget extends HookWidget {
  const TimerTextWidget({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final timeLeft = useProvider(timeLeftProvider);

    print('building TimerTextWidget $timeLeft');

    return Text(
      timeLeft,style: Theme.of(context).textTheme.headline2,);
  }
}

class ButtonsContainer extends HookWidget {
  const ButtonsContainer({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    print('building ButtonsContainer');

    final state = useProvider(buttonProvider);

    return Row(
      mainAxisAlignment: MainAxisAlignment.center,children: [
        if (state == ButtonState.initial) ...[
          StartButton(),if (state == ButtonState.started) ...[
          PauseButton(),SizedBox(width: 20),ResetButton(),if (state == ButtonState.paused) ...[
          StartButton(),if (state == ButtonState.finished) ...[
          ResetButton(),);
  }
}

class StartButton extends StatelessWidget {
  const StartButton({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    print('building StartButton');
    return FloatingActionButton(
      onPressed: context.read(timerProvider).start,child: Icon(Icons.play_arrow),);
  }
}

class PauseButton extends StatelessWidget {
  const PauseButton({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    print('building PauseButton');

    return FloatingActionButton(
      onPressed: context.read(timerProvider).pause,child: Icon(Icons.pause),);
  }
}

class ResetButton extends StatelessWidget {
  const ResetButton({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    print('building ResetButton');

    return FloatingActionButton(
      onPressed: context.read(timerProvider).reset,child: Icon(Icons.replay),);
  }
}

如果我点击“播放”按钮,然后经过10秒钟,最后在两种情况下我会得到相同的结果:

Output

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

即使未使用buttonProvidertimeLeftProvider,这也是输出,如以下main.dart所示:

import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:riverpod_timer_app/timer.dart';

final timerProvider = StateNotifierProvider<TimerNotifier>(
  (ref) => TimerNotifier(),);

final _buttonState = Provider<ButtonState>((ref) {
  return ref.watch(timerProvider.state).buttonState;
});

// final buttonProvider = Provider<ButtonState>((ref) {
//   return ref.watch(_buttonState);
// });

final _timeLeftProvider = Provider<String>((ref) {
  return ref.watch(timerProvider.state).timeLeft;
});

// final timeLeftProvider = Provider<String>((ref) {
//   return ref.watch(_timeLeftProvider);
// });

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

class TimerTextWidget extends HookWidget {
  const TimerTextWidget({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final timeLeft = useProvider(_timeLeftProvider);

    print('building TimerTextWidget $timeLeft');

    return Text(
      timeLeft,);
  }
}

class ButtonsContainer extends HookWidget {
  const ButtonsContainer({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    print('building ButtonsContainer');

    final state = useProvider(_buttonState);

    return Row(
      mainAxisAlignment: MainAxisAlignment.center,);
  }
}

我在做什么错了?

解决方法

这些提供程序用于防止不必要的重建,但从根本上讲不是必需的。仅创建您需要的提供程序-尤其是因为这些提供程序永远不会在应用程序生命周期中被丢弃,因此它们只是浪费空间。但是,防止不必要的重建应该是当务之急。

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

我会参考package creator's examples以获得更多背景信息。

这是一个为什么您可能会使用多个提供程序来缓存来自外部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);
});

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

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

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...