简体   繁体   English

在供应商内部构建倒计时时钟 package 更改通知程序

[英]building a countdown clock inside a provider package change notifier

I have built a working countdown clock that displays the time remaining in the format, hours:minutes:senconds.我建立了一个工作倒计时时钟,以小时:分钟:秒的格式显示剩余时间。 inside a stateful widget.在有状态的小部件中。 that uses a fullscreen inkwell to start and stop it.使用全屏墨水池来启动和停止它。 What I want to do is transfer this code to a change notifier.我想要做的是将此代码传输到更改通知程序。 (I already a change notifier setup ready to go called CountdownTimers) so i can see this countdown running from multiple pages on my app. (我已经准备好更改通知程序设置为 go,称为 CountdownTimers)所以我可以看到这个倒计时从我的应用程序的多个页面运行。

Here is the code for the working countdown clock in the stateful widget:这是有状态小部件中工作倒计时时钟的代码:

class ChessClock2 extends StatefulWidget {
  const ChessClock2({Key? key}) : super(key: key);

  @override
  State<ChessClock2> createState() => _ChessClock2State();
}

class _ChessClock2State extends State<ChessClock2> {
  static const countdownDuration = Duration(hours: 5, minutes: 10, seconds: 10);
  Duration duration = Duration();
  Timer? timer;

  bool beenPressed = false;

  @override
  void initState() {
    super.initState();
    Reset();
  }

  void Reset() {
    setState(() => duration = countdownDuration);
  }

  void AddTime() {
    final minusSeconds = -1;
    setState(() {
      final seconds = duration.inSeconds + minusSeconds;
      if (seconds < 0) {
        timer?.cancel();
      } else {
        duration = Duration(seconds: seconds);
      }
    });
  }

  void StartTimer() {
    timer = Timer.periodic(
      Duration(seconds: 1),
      (_) => AddTime(),
    );
  }

  void StopTimer() {
    setState(() => timer?.cancel());
  }

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Scaffold(
        body: InkWell(
          onTap: () {
            setState(() {
              beenPressed = !beenPressed;
            });
            beenPressed ? StartTimer() : StopTimer();
          },
          child: Container(
            width: double.infinity,
            height: double.infinity,
            decoration: BoxDecoration(
              color: beenPressed ? kOrange : kBlueGrey900,
              borderRadius: BorderRadius.circular(30),
            ),
            child: Center(
              child: TimeDisplay(),
            ),
          ),
        ),
      ),
    );
  }

  Widget TimeDisplay() {
    String twoDigits(int n) => n.toString().padLeft(2, '0');
    final hours = twoDigits(
      duration.inHours.remainder(60),
    );
    final minutes = twoDigits(
      duration.inMinutes.remainder(60),
    );
    final seconds = twoDigits(
      duration.inSeconds.remainder(60),
    );
    return Text(
      '$hours:$minutes:$seconds',
      style: TextStyle(fontSize: 50),
    );
  }
}

When I transfer the code over, I'm running into trouble because I can't use setState in the change notifier and I'm unsure how to translate the code to get it to work.当我转移代码时,我遇到了麻烦,因为我不能在更改通知程序中使用 setState,而且我不确定如何转换代码以使其工作。 so far, by moving the individual variables over as well as the widget TimDisplay, I'm able to get the timer to display correctly from the change notifier but am not sure how to get it to work from the change notifier.到目前为止,通过移动单个变量以及小部件 TimDisplay,我能够让计时器从更改通知器中正确显示,但我不确定如何让它从更改通知器中工作。 here is where I am now:这是我现在所在的位置:

type hereclass ChessClock3 extends StatefulWidget {
  const ChessClock3({Key? key}) : super(key: key);

  @override
  State<ChessClock3> createState() => _ChessClock3State();
}

class _ChessClock3State extends State<ChessClock3> {
  @override
  void initState() {
    super.initState();
    Reset();
  }

  void Reset() {
    setState(() => duration = countdownDuration);
  }

  void AddTime() {
    final minusSeconds = -1;

    setState(() {
      final seconds = duration.inSeconds + minusSeconds;
      if (seconds < 0) {
        timer?.cancel();
      } else {
        duration = Duration(seconds: seconds);
      }
    });
  }

  void StartTimer() {
    timer = Timer.periodic(
      Duration(seconds: 1),
      (_) => AddTime(),
    );
  }

  void StopTimer() {
    setState(() => timer?.cancel());
  }

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Scaffold(
        body: InkWell(
          onTap: () {
            setState(() {
              context.read<CountdownTimers>().BeenPressed();
            });
            context.read<CountdownTimers>().beenPressed
                ? StartTimer()
                : StopTimer();
          },
          child: Container(
            width: double.infinity,
            height: double.infinity,
            decoration: BoxDecoration(
              color: context.watch<CountdownTimers>().beenPressed
                  ? kKillTeamOrange
                  : kBlueGrey900,
              borderRadius: BorderRadius.circular(30),
            ),
            child: Center(
              child: context.read<CountdownTimers>().TimeDisplay(),
            ),
          ),
        ),
      ),
    );
  }
}

class CountdownTimers with ChangeNotifier {
  Duration _countdownDuration = Duration(hours: 5, minutes: 10, seconds: 10);
  Duration _duration = Duration();
  Timer? timer;
  bool _beenPressed = false;

  Duration get countdownDuration => _countdownDuration;
  Duration get duration => _duration;
  bool get beenPressed => _beenPressed;

  void BeenPressed() {
    _beenPressed = !_beenPressed;
  }

  Widget TimeDisplay() {
    String twoDigits(int n) => n.toString().padLeft(2, '0');
    final hours = twoDigits(
      _duration.inHours.remainder(60),
    );
    final minutes = twoDigits(
      _duration.inMinutes.remainder(60),
    );
    final seconds = twoDigits(
      _duration.inSeconds.remainder(60),
    );
    return Text(
      '$hours:$minutes:$seconds',
      style: TextStyle(fontSize: 50),
    );
  }
}

If anyone can show me how to translate the code over It would be very much appreciated.如果有人能告诉我如何翻译代码,我将不胜感激。 thanks so much!非常感谢!

I would use Flutter Riverpod instead like the code below.我会像下面的代码那样使用 Flutter Riverpod。 In Riverpod it's not recommended to use Change Notifier.在 Riverpod 中,不推荐使用 Change Notifier。 Even in complexe application.即使在复杂的应用程序中。 To change the state of Change Notifier you must call notifyListeners.要更改 Change Notifier 的 state,您必须调用 notifyListeners。

import 'dart:async';

import 'package:flutter_riverpod/flutter_riverpod.dart';

final countDownControllerProvider = StateNotifierProvider.family
    .autoDispose<CountdownController, Duration, Duration>((ref, initialDuration) {
  return CountdownController(initialDuration);
});

class CountdownController extends StateNotifier<Duration> {
  Timer? timer;
  final Duration initialDuration;

  CountdownController(this.initialDuration) : super(initialDuration) {
    startTimer();
  }

  void startTimer() {
    timer = Timer.periodic(const Duration(seconds: 1), (timer) {
      if (state == Duration.zero) {
        timer.cancel();
      } else {
        if (mounted) {
          state = state - const Duration(seconds: 1);
        } else {
          timer.cancel();
        }
      }
    });
  }

  void stopTimer() {
    timer?.cancel();
  }

  void resetTimer({required Duration initialDuration}) {
    stopTimer();
    state = initialDuration;
    startTimer();
  }

  void addTime({required Duration duration}) {
    state = state + duration;
  }

  void subtractTime({required Duration duration}) {
    state = state - duration;
  }

  @override
  void dispose() {
    timer?.cancel();
    super.dispose();
  }
}

Then in the widget然后在小部件中

Consumer(builder: (context, ref, _) {
                          return Text(
                            ref
                                .watch(countDownControllerProvider(
                                    const Duration(
                                        days: 25,
                                        hours: 15,
                                        minutes: 36,
                                        seconds: 45)))
                                .formatDuration(),
                            style: context.theme.textTheme.bodyText1!
                                .copyWith(color: Colors.white),
                          );
                        })

And finally, don't hesitate to put your conversion logic Duration to String, into a extension最后,不要犹豫,将 Duration 到 String 的转换逻辑放入扩展中

extension DurationExtensions on Duration {
  String formatDuration() {
    int seconds = inSeconds;
    final days = seconds~/Duration.secondsPerDay;
    seconds -= days*Duration.secondsPerDay;
    final hours = seconds~/Duration.secondsPerHour;
    seconds -= hours*Duration.secondsPerHour;
    final minutes = seconds~/Duration.secondsPerMinute;
    seconds -= minutes*Duration.secondsPerMinute;

    final List<String> tokens = [];
    if (days != 0) {
      tokens.add('${days}d');
    }
    if (tokens.isNotEmpty || hours != 0){
      tokens.add('${hours}h');
    }
    if (tokens.isNotEmpty || minutes != 0) {
      tokens.add('${minutes}min');
    }
    if(tokens.isNotEmpty || seconds != 0) {
      tokens.add('${seconds}s');
    }

    return tokens.join('');
  }
}

The trick you are looking for is the function notifyListeners() of a ChangeNotifier .您正在寻找的技巧是ChangeNotifier的 function notifyListeners() If you used context.watch() inside your widget to watch the ChangeNotifier, you will be updated and the widget is rebuild if necessary.如果您在小部件内使用context.watch()来监视 ChangeNotifier,您将被更新并且小部件将在必要时重建。

A short example might look like this一个简短的例子可能看起来像这样


ConsumingWidget.dart ConsumingWidget.dart

@override
Widget build(BuildContext context) {
  var timeProvider = context.watch<TimeProvider>();
  int counter = timeProvider.counter;

  return Text("${counter} seconds have passed");
}

In this case, you would need to provide an instance of TimeProvider above your main widget in the widget tree via ChangeNotifierProvider.在这种情况下,您需要通过ChangeNotifierProvider.在小部件树中的主小部件上方提供一个TimeProvider实例。 You can then let the widget rebuild (if .counter changed) via notifyListeners()然后,您可以通过notifyListeners()让小部件重建(如果.counter更改)


ParentWidget.dart ParentWidget.dart

@override
Widget build(BuildContext context) {
  ChangeNotifierProvider(
    create: (_) => TimeProvider(),
    child: ConsumingWidget()
  );
}

Provider.dart提供商.dart

class TimeProvider extends ChangeNotifier {
  Timer? timer;
  final Duration initialDuration;
  int counter = 0;

  CountdownController(this.initialDuration) : super(initialDuration) {
    startTimer();
  }

  void startTimer() {
    timer = Timer.periodic(const Duration(seconds: 1), (timer) {
      counter += 1; // Change some data based on the timer. Just an example here
      notifyListeners(); // Then update listeners!
    });
  }

  @override
  void dispose() {
    timer?.cancel();
    super.dispose();
  }

This is the built-in solution of the Provider package. Starting from this basic pattern, you can then look into more sophisticated state management solutions like Riverpod, Bloc etc.这是 Provider package 的内置解决方案。从这个基本模式开始,您可以研究更复杂的 state 管理解决方案,如 Riverpod、Bloc 等。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Flutter Provider Change Notifier 内的数据未更新 - Data inside Flutter Provider Change Notifier is not updated 使用异步 function 更改通知提供程序 - Change Notifier Provider with async function 更改通知提供程序以将小部件添加到收藏屏幕 - Change Notifier Provider to add widget to Favourite Screen 使用更改通知程序提供程序的提供程序未在另一个屏幕中显示更新状态 - Provider not displaying updated state in another screen usng change notifier provider 如何在另一个变更通知程序 class 提供程序中使用来自一个变更通知程序 class 的方法 - How to use method from one Change notifier class in another change notifier class provider Flutter 线图小部件未使用更改通知程序在提供者的数据更改上重建 - Flutter Linechart Widget not rebuilding on Data Changes with Provider using Change Notifier “更改通知程序提供程序”不是函数错误消息 - 'Change Notifier Provider' isn't a function error message Flutter:如何从颤振/提供者更改通知器中获取值 - Flutter:How to get a value from flutter/provider change notifier 具有提供程序/范围 Model 的更改通知程序是一个错误的 State Flutter 管理解决方案吗? - Is Change Notifier with Provider/Scoped Model a bad State Management solution for Flutter? Null 检查运算符用于 Stream 构建器中的 null 值更改通知提供程序错误 - Null check operator used on a null value in Stream builder in Change Notifier Provider error
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM