flutterdartflutter-riverpod

flutter Riverpod moving the parameters from a State Notifier class into a function inside the class


I'm new to flutter and very new to riverpod. I've just been helped with some code to use a countdown clock that can then be viewed on multiple pages using Riverpod. here is the Riverpod State Notifier.

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) {
    stopTimer();
  }

  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();
        }
      }
    });
  }
}

Currently, the input for the time to display on the countdown clock is inputted when you call CountdownController. (the class with startTimer function inside it). the problem I'm having is if I want to call startTimer(), I need to reinput the time to display which is a problem if I'm stopping and starting the clock.

how would I move the time input from a parameter of the CountdownController class, into a function inside the class that I can then call on when needed so I don't have to set it when starting/stopping the clock? and what would that code look like?

thanks so much


Solution

  • I didn't test it. If you need to save duration to state, consider making the state a data class.

    EDIT: tested.

    import 'dart:math';
    
    import 'package:flutter_riverpod/flutter_riverpod.dart';
    import 'package:flutter/material.dart';
    import 'dart:async';
    
    void main() {
      runApp(const ProviderScope(child: App()));
    }
    
    final countDownControllerProvider =
        StateNotifierProvider.autoDispose<CountdownController, Timer?>((ref) {
      return CountdownController(ref);
    });
    
    final counterProvider = StateProvider((_) => 0);
    final intervalProvider = StateProvider((_) => Duration(seconds: 1));
    
    class CountdownController extends StateNotifier<Timer?> {
      CountdownController(this.ref) : super(null);
    
      final Ref ref;
    
      void startTimer() {
        state?.cancel();
        state = Timer.periodic(ref.read(intervalProvider), (timer) {
          ref.read(counterProvider.notifier).state++;
        });
      }
    
      void stopTimer() {
        state?.cancel();
      }
    
      void accelerate(double multiplier) {
        final duration = ref.read(intervalProvider);
        ref.read(intervalProvider.notifier).state = Duration(
          milliseconds: (duration.inMilliseconds * (1 / multiplier)).floor(),
        );
        startTimer();
      }
    
      void speedUp() {
        accelerate(sqrt2);
      }
    
      void speedDown() {
        accelerate(1 / sqrt2);
      }
    }
    
    class App extends ConsumerWidget {
      const App();
    
      @override
      Widget build(BuildContext context, WidgetRef ref) {
        final counter = ref.watch(counterProvider);
        final controller = ref.watch(countDownControllerProvider.notifier);
        final timer = ref.watch(countDownControllerProvider);
    
        return MaterialApp(
          home: Scaffold(
            body: SafeArea(
              child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [
                Text("$counter", style: Theme.of(context).textTheme.headlineLarge),
                SizedBox(height: 24),
                Row(children: [
                  SizedBox(width: 24),
                  Expanded(
                    child: ElevatedButton(
                      onPressed: controller.startTimer,
                      child: Text(timer == null ? "start" : "stop"),
                    ),
                  ),
                  SizedBox(width: 24),
                  Expanded(
                    child: ElevatedButton(
                      onPressed: controller.speedUp,
                      child: Text("+"),
                    ),
                  ),
                  SizedBox(width: 24),
                  Expanded(
                    child: ElevatedButton(
                      onPressed: controller.speedDown,
                      child: Text("-"),
                    ),
                  ),
                  SizedBox(width: 24),
                ]),
              ]),
            ),
          ),
        );
      }
    }