0

我遵循了这个 优秀的 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);
});

我尝试使用_buttonStateand_timeLeftProvider 并且据我所见,该应用程序可以正常工作。所以,我的问题是:

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

非常感谢!

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(),
          SizedBox(width: 20),
          ResetButton(),
        ],
        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 秒过去,最后我在 2 种情况下得到相同的结果:

输出

2020-10-27 更新(main.dart不使用buttonProviderand的代码timeLeftProvider

即使没有使用,这也是输出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 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(_buttonState);

    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(),
          SizedBox(width: 20),
          ResetButton(),
        ],
        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),
    );
  }
}

我究竟做错了什么?

4

1 回答 1

2

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

在链接的文章中,作者正在利用包作者推荐的解决方法来防止在侦听 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);
});

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

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

于 2020-10-23T18:18:43.280 回答