title: Flutter - Provider - Flutter_Countdown_Timer - CountdownTimerController - FlutterError (setState() or markNeedsBuild() called during build
body: <p>I am developing an app using Flutter, managing state with provider, and I keep running into the error:</p>
  <blockquote>
  <p><em>&quot;FlutterError (setState() or markNeedsBuild() called during build. This _InheritedProviderScope&lt;CountdownTimerController?&gt; widget cannot
  be marked as needing to build because the framework is already in the
  process of building widgets. A widget can be marked as needing to be
  built during the build phase only if one of its ancestors is currently
  building. This exception is allowed because the framework builds
  parent widgets before children, which means a dirty descendant will
  always be built. Otherwise, the framework might not visit this widget
  during this build phase.&quot;</em></p>
  </blockquote>
  <p>I'm using the package <a href="https://%20https://pub.dev/packages/flutter_countdown_timer" rel="nofollow noreferrer">flutter countdown timer</a> as a widget, but I am trying to place the associated CountdownTimerController as a provider higher in the widget tree so I can access its state from other widgets. Whenever I attempt to define a countdown timer widget using provider.watch, I receive the above error at compile time. Oddly, the widget will still build, but the error throws every time I try to add a new instance of the widget into a listview with an action button.</p>
  <p>Here is my provider definition within a listview builder:</p>
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
  <div class="snippet-code">
  <pre class="snippet-code-html lang-html prettyprint-override"><code>body: ListView.separated(
    padding: const EdgeInsets.fromLTRB(10, 5, 10, 5),
    itemCount: challenges.length,
    itemBuilder: (BuildContext context, int index) {
      Challenge challenge = challenges[index];
      return MultiProvider(providers: [
        ChangeNotifierProvider(
            create: (context) =&gt; CountdownTimerController(
                endTime: challenge.nextCheckIn.millisecondsSinceEpoch))
      ], child: ChallengeCard(challenge));
    },</code></pre>
  </div>
  </div>
  </p>
  <p>Here is my custom ChallengeCard widget, where I am attempting to access the controller with context.watch:</p>
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
  <div class="snippet-code">
  <pre class="snippet-code-html lang-html prettyprint-override"><code>class ChallengeCard extends StatefulWidget {
    final Challenge challenge;
    const ChallengeCard(this.challenge, {Key? key}) : super(key: key);
  
    @override
    State&lt;StatefulWidget&gt; createState() =&gt; _ChallengeCardState();
  }
  
  class _ChallengeCardState extends State&lt;ChallengeCard&gt; {
    @override
    Widget build(BuildContext context) {
      return Card(
        child: Padding(
          padding: const EdgeInsets.fromLTRB(10, 5, 10, 5),
          child: Column(
            children: [
              Row(
                children: [
                  Expanded(
                    flex: 3,
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Text(
                          widget.challenge.passage.fullRef,
                          style: TextStyles.title,
                        ),
                        Row(
                          children: [
                            const Text(
                              "Next Check In: ",
                              style: TextStyles.subtitle,
                            ),
                            CountdownTimer(
                              textStyle: TextStyles.subtitle,
                              endWidget: const Text(
                                "Now!",
                                style: TextStyles.subtitle,
                              ),
                              controller:
                                  context.watch&lt;CountdownTimerController&gt;(),
                              onEnd: () {
                                print("Timer Finished!");
                              },
                            ),
                          ],
                        ),
                      ],
                    ),
                  ),
                  Expanded(child: DetailsButton(widget.challenge)),
                  Expanded(child: CheckInButton(widget.challenge)),
                ],
              ),
              CounterProgressIndicator(widget.challenge)
            ],
          ),
        ),
      );
    }
  }</code></pre>
  </div>
  </div>
  </p>
  <p>Here is the full flutter_countdown_provider class:</p>
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
  <div class="snippet-code">
  <pre class="snippet-code-html lang-html prettyprint-override"><code>import 'package:flutter/material.dart';
  import 'package:flutter/widgets.dart';
  import 'package:flutter_countdown_timer/index.dart';
  
  typedef CountdownTimerWidgetBuilder = Widget Function(
      BuildContext context, CurrentRemainingTime? time);
  
  /// A Countdown.
  class CountdownTimer extends StatefulWidget {
    ///Widget displayed after the countdown
    final Widget endWidget;
    ///Used to customize the countdown style widget
    final CountdownTimerWidgetBuilder? widgetBuilder;
    ///Countdown controller, can end the countdown event early
    final CountdownTimerController? controller;
    ///Countdown text style
    final TextStyle? textStyle;
    ///Event called after the countdown ends
    final VoidCallback? onEnd;
    ///The end time of the countdown.
    final int? endTime;
  
    CountdownTimer({
      Key? key,
      this.endWidget = const Center(
        child: Text('The current time has expired'),
      ),
      this.widgetBuilder,
      this.controller,
      this.textStyle,
      this.endTime,
      this.onEnd,
    })  : assert(endTime != null || controller != null),
          super(key: key);
  
    @override
    _CountDownState createState() =&gt; _CountDownState();
  }
  
  class _CountDownState extends State&lt;CountdownTimer&gt; {
    late CountdownTimerController controller;
  
    CurrentRemainingTime? get currentRemainingTime =&gt;
        controller.currentRemainingTime;
  
    Widget get endWidget =&gt; widget.endWidget;
  
    CountdownTimerWidgetBuilder get widgetBuilder =&gt;
        widget.widgetBuilder ?? builderCountdownTimer;
  
    TextStyle? get textStyle =&gt; widget.textStyle;
  
    @override
    void initState() {
      super.initState();
      initController();
    }
  
    ///Generate countdown controller.
    initController() {
      controller = widget.controller ??
          CountdownTimerController(endTime: widget.endTime!, onEnd: widget.onEnd);
      if (controller.isRunning == false) {
        controller.start();
      }
      controller.addListener(() {
        if (mounted) {
          setState(() {});
        }
      });
    }
  
    @override
    void didUpdateWidget(CountdownTimer oldWidget) {
      super.didUpdateWidget(oldWidget);
      if (oldWidget.endTime != widget.endTime || widget.controller != oldWidget.controller) {
        controller.dispose();
        initController();
      }
    }
  
    @override
    Widget build(BuildContext context) {
      return widgetBuilder(context, currentRemainingTime);
    }
  
    Widget builderCountdownTimer(
        BuildContext context, CurrentRemainingTime? time) {
      if (time == null) {
        return endWidget;
      }
      String value = '';
      if (time.days != null) {
        var days = time.days!;
        value = '$value$days days ';
      }
      var hours = _getNumberAddZero(time.hours ?? 0);
      value = '$value$hours : ';
      var min = _getNumberAddZero(time.min ?? 0);
      value = '$value$min : ';
      var sec = _getNumberAddZero(time.sec ?? 0);
      value = '$value$sec';
      return Text(
        value,
        style: textStyle,
      );
    }
  
    /// 1 -&gt; 01
    String _getNumberAddZero(int number) {
      if (number &lt; 10) {
        return "0" + number.toString();
      }
      return number.toString();
    }
  }</code></pre>
  </div>
  </div>
  </p>
  <p>Here is the full CountdownTimerController class:</p>
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
  <div class="snippet-code">
  <pre class="snippet-code-html lang-html prettyprint-override"><code>import 'dart:async';
  
  import 'package:flutter/material.dart';
  import 'package:flutter_countdown_timer/index.dart';
  
  ///Countdown timer controller.
  class CountdownTimerController extends ChangeNotifier {
    CountdownTimerController(
        {required int endTime, this.onEnd, TickerProvider? vsync})
        : this._endTime = endTime {
      if (vsync != null) {
        this._animationController =
            AnimationController(vsync: vsync, duration: Duration(seconds: 1));
      }
    }
  
    ///Event called after the countdown ends
    final VoidCallback? onEnd;
  
    ///The end time of the countdown.
    int _endTime;
  
    ///Is the countdown running.
    bool _isRunning = false;
  
    ///Countdown remaining time.
    CurrentRemainingTime? _currentRemainingTime;
  
    ///Countdown timer.
    Timer? _countdownTimer;
  
    ///Intervals.
    Duration intervals = const Duration(seconds: 1);
  
    ///Seconds in a day
    int _daySecond = 60 * 60 * 24;
  
    ///Seconds in an hour
    int _hourSecond = 60 * 60;
  
    ///Seconds in a minute
    int _minuteSecond = 60;
  
    bool get isRunning =&gt; _isRunning;
  
    set endTime(int endTime) =&gt; _endTime = endTime;
  
    ///Get the current remaining time
    CurrentRemainingTime? get currentRemainingTime =&gt; _currentRemainingTime;
  
    AnimationController? _animationController;
  
    ///Start countdown
    start() {
      disposeTimer();
      _isRunning = true;
      _countdownPeriodicEvent();
      if (_isRunning) {
        _countdownTimer = Timer.periodic(intervals, (timer) {
          _countdownPeriodicEvent();
        });
      }
    }
  
    ///Check if the countdown is over and issue a notification.
    _countdownPeriodicEvent() {
      _currentRemainingTime = _calculateCurrentRemainingTime();
      _animationController?.reverse(from: 1);
      notifyListeners();
      if (_currentRemainingTime == null) {
        onEnd?.call();
        disposeTimer();
      }
    }
  
    ///Calculate current remaining time.
    CurrentRemainingTime? _calculateCurrentRemainingTime() {
      int remainingTimeStamp =
          ((_endTime - DateTime.now().millisecondsSinceEpoch) / 1000).floor();
      if (remainingTimeStamp &lt;= 0) {
        return null;
      }
      int? days, hours, min, sec;
  
      ///Calculate the number of days remaining.
      if (remainingTimeStamp &gt;= _daySecond) {
        days = (remainingTimeStamp / _daySecond).floor();
        remainingTimeStamp -= days * _daySecond;
      }
  
      ///Calculate remaining hours.
      if (remainingTimeStamp &gt;= _hourSecond) {
        hours = (remainingTimeStamp / _hourSecond).floor();
        remainingTimeStamp -= hours * _hourSecond;
      } else if (days != null) {
        hours = 0;
      }
  
      ///Calculate remaining minutes.
      if (remainingTimeStamp &gt;= _minuteSecond) {
        min = (remainingTimeStamp / _minuteSecond).floor();
        remainingTimeStamp -= min * _minuteSecond;
      } else if (hours != null) {
        min = 0;
      }
  
      ///Calculate remaining second.
      sec = remainingTimeStamp.toInt();
      return CurrentRemainingTime(
          days: days,
          hours: hours,
          min: min,
          sec: sec,
          milliseconds: _animationController?.view);
    }
  
    disposeTimer() {
      _isRunning = false;
      _countdownTimer?.cancel();
      _countdownTimer = null;
    }
  
    @override
    void dispose() {
      disposeTimer();
      _animationController?.dispose();
      super.dispose();
    }
  }</code></pre>
  </div>
  </div>
  </p>