How to update the countdown timer repeatedly in flutter

Issue

SCENARIO

In this app there is a countdown timer, whenever I open the app the countdown timer starts at 5 automatically and stops at 0.

QUESTION

How do I update/change the timer number when it reaches 0 and automatically goes back to 5 and start once again(loop). Below are the dart and bloc code.

Ticker.dart

class Ticker {
      Stream<int> tick({int ticks}) {
        return Stream.periodic(Duration(seconds: 1), (x) => ticks - x - 1)
            .take(ticks);
      }}

TimerBloc.dart

class TimerBloc extends Bloc<TimerEvent, TimerState> {
      final Ticker _ticker;
      final int _duration = 5;
      StreamSubscription<int> _tickerSubscription;
      TimerBloc({@required Ticker ticker})
          : assert(ticker != null),
            _ticker = ticker;
      @override
      TimerState get initialState => Ready(_duration);
      @override
      void onTransition(Transition<TimerEvent, TimerState> transition) {
        super.onTransition(transition);
        print(transition);
      }
      @override
      Stream<TimerState> mapEventToState(
          TimerEvent event,
          ) async* {
        if (event is Start) {
          yield* _mapStartToState(event);
        }else if (event is Tick) {
          yield* _mapTickToState(event);
        }
      }
      @override
      Future<void> close() {
        _tickerSubscription?.cancel();
        return super.close();
      }
      Stream<TimerState> _mapStartToState(Start start) async* {
        yield Running(start.duration);
        _tickerSubscription?.cancel();
        _tickerSubscription = _ticker
            .tick(ticks: start.duration)
            .listen((duration) => add(Tick(duration: duration)));
      }
      Stream<TimerState> _mapTickToState(Tick tick) async* {
        yield tick.duration > 0 ? Running(tick.duration) : Finished();
      }
    }

TimerEvent.dart

abstract class TimerEvent extends Equatable {
  const TimerEvent();
  @override
  List<Object> get props => [];
}
class Start extends TimerEvent {
  final int duration;
  const Start({@required this.duration});
  @override
  String toString() => "Start { duration: $duration }";
}
class Tick extends TimerEvent {
  final int duration;
  const Tick({@required this.duration});
  @override
  List<Object> get props => [duration];
  @override
  String toString() => "Tick { duration: $duration }";
}

TimerState.dart

abstract class TimerState extends Equatable {
  final int duration;
  const TimerState(this.duration);
  @override
  List<Object> get props => [duration];
}
class Ready extends TimerState {
  const Ready(int duration) : super(duration);
  @override
  String toString() => 'Ready { duration: $duration }';
}
class Running extends TimerState {
  const Running(int duration) : super(duration);
  @override
  String toString() => 'Running { duration: $duration }';
}
class Finished extends TimerState {
  const Finished() : super(0);
}

main.dart

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Timer',
      home: BlocProvider(
        create: (context) => TimerBloc(ticker: Ticker()),
        child: Timer(),
      ),);}}
class Timer extends StatefulWidget {
  static const TextStyle timerTextStyle = TextStyle(
    fontSize: 60,
    fontWeight: FontWeight.bold,
    color: Colors.black
  );
  @override
  _TimerState createState() => _TimerState();
}
class _TimerState extends State<Timer> {
  TimerBloc timerBloc;
  @override
  void initState() {
    timerBloc = BlocProvider.of<TimerBloc>(context);
    timerBloc.add(Start(duration: 5));
    super.initState();
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Flutter Timer')),
      body: Stack(
        children: [
          Column(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: <Widget>[
              Padding(
                padding: EdgeInsets.symmetric(vertical: 100.0),
                child: Center(
                  child: BlocBuilder<TimerBloc, TimerState>(
                    builder: (context, state) {
                      final String minutesStr = ((state.duration / 60) % 60)
                          .floor()
                          .toString()
                          .padLeft(2, '0');
                      final String secondsStr = (state.duration % 60)
                          .floor()
                          .toString()
                          .padLeft(2, '0');
                      return Text(
                        '$minutesStr:$secondsStr',
                        style: Timer.timerTextStyle,
                      );},),),),
              BlocBuilder<TimerBloc, TimerState>(
                condition: (previousState, state) =>
                state.runtimeType != previousState.runtimeType,
                builder: (context, state) => Actions(),
              ),],),],),);}}
class Actions extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      children: _mapStateToActionButtons(
        timerBloc: BlocProvider.of<TimerBloc>(context),
      ),);}
List<Widget> _mapStateToActionButtons({
    TimerBloc timerBloc,
  }) {
    final TimerState currentState = timerBloc.state;
    if (currentState is Ready) {
 return [];}}

Solution

You can create a stream that counts from 5 to 0 forever like this:

const int ticks = 5;
Stream<int>.periodic(
        const Duration(seconds: 1), (x) => ticks - x % (ticks + 1))
    .listen((value) => print(value));

You should be able to integrate it in your ticker class like this:

class Ticker {
    Stream<int> tick({int ticks}) {
        return Stream<int>.periodic(
            const Duration(seconds: 1), (x) => ticks - x % (ticks + 1));
    }
}

Answered By – Josh

Answer Checked By – Marie Seifert (FlutterFixes Admin)

Leave a Reply

Your email address will not be published. Required fields are marked *