2

Building an app with Flutter and Riverpod, using a lot of:

ref.watch(someProvider).when(data: (someData){
   // render layout with data
}, error: (err, stack) { 
  // do stuff with error
}, loading: (){
  return LoadingScreen(); <----
})

The problem is that in most cases the loading screen only renders for a split second, causing a bad experience where the app feels a little "jumpy". I would like to be able to set a minimum of say 2 seconds for the loading state, is it possible to force a widget to stay rendered for a minimum amount of time some how?

Md. Yeasin Sheikh
  • 54,221
  • 7
  • 29
  • 56
Majoren
  • 983
  • 5
  • 16
  • 36

2 Answers2

2

I have 2 suggestions for you :

Option A - You can put a delay of say 2 seconds in your provider, somewhere between loading and the next state. This way you make the loading screen visible at least 2 seconds.

Option B - You can also make a loadingScreen as an overlay Widget, which dismisses itself after say 2 seconds. This way you make the loading screen visible exactly 2 seconds.

I believe either way is fine in your case, since you say it flashes, I assume no heavy/long tasks are done in between states. In case you do need heavy/long tasks, option A guarantees the tasks are finished before loading screen disappears.

Tom Shen
  • 1,045
  • 3
  • 11
  • 29
2

You can create Stopwatch to track (approximate) operation duration. This example will give you some idea.

final someProvider = FutureProvider<int>((ref) async {
  final operationDuration = Duration(seconds: 1); //random, you can give it random to test
  final stopwatch = Stopwatch()..start();
  final data = await Future.delayed(operationDuration);

  final minOperationTime = const Duration(seconds: 2);
  final extraWaiting = minOperationTime.inMilliseconds - stopwatch.elapsed.inMilliseconds;
  if (stopwatch.elapsed.inSeconds < 2) {
    await Future.delayed(minOperationTime - stopwatch.elapsed);
  }
  return extraWaiting;
});

And widget

class LoadingTestOnFutureBuilder extends ConsumerWidget {
  const LoadingTestOnFutureBuilder({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return ref.watch(someProvider).when(data: (someData) {
      return Text("got data  ${someData}");
    }, error: (err, stack) {
      return Text("Err");
    }, loading: () {
      return CircularProgressIndicator();
    });
  }
}
Md. Yeasin Sheikh
  • 54,221
  • 7
  • 29
  • 56
  • Interesting, I will experiment with this, my only worry is when I combine multiple providers. But maybe I'll find a way around that issue as well. Thank's for the example! – Majoren Dec 16 '22 at 09:12