When you tap an item in the Drawer, it opens a page called "FirstPage". During the initial call, since the page doesn't exist in the Widget Tree, FutureProvider goes into Loading mode, and the screen switches as soon as data is retrieved.
However, when the FirstPage is displayed and you write code to call the FirstPage from the Drawer, FutureProvider's data retrieval doesn't seem to run, perhaps because FirstPage remains in the Widget Tree and doesn't get autoDisposed.
When I tried to invalidate during transition with the Drawer, if FirstPage is displayed, it doesn't go into loading mode, but the data re-retrieval does execute. No, I want it to start over from the loading of AsyncValue.
Below is the Minimal Reproducible Example. I think wanting to Refresh when calling the same page multiple times from the Drawer is a common requirement, but I wonder what everyone else is doing.
import 'package:drawer_refresh_sample/drawer.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
void main() {
runApp(const ProviderScope(child: MyApp()));
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
Widget build(BuildContext context) {
return Scaffold(
drawer: const SampleDrawer(),
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(title),
),
body: const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Good Morning',
),
],
),
),
);
}
}
import 'package:drawer_refresh_sample/first_page.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
class SampleDrawer extends ConsumerWidget {
const SampleDrawer({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
return Drawer(
child: ListView(
children: <Widget>[
ListTile(
title: const Text("Page 1"),
trailing: const Icon(Icons.arrow_forward),
onTap: () {
Navigator.of(context).pop();
ref.invalidate(helloProvider);
Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (_) => const FirstPage()));
},
),
],
),
);
}
}
import 'package:drawer_refresh_sample/sample_provider.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'drawer.dart';
class FirstPage extends ConsumerWidget {
const FirstPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final asyncHello = ref.watch(helloProvider);
return asyncHello.when(
data: (data) => Scaffold(
appBar: AppBar(
title: const Text("First Page"),
),
drawer: const SampleDrawer(),
body: SafeArea(child: Center(child: Text(data)))),
error: (error, stackTrace) => Text(stackTrace.toString()),
loading: () => const Center(child: CircularProgressIndicator.adaptive()));
}
}
import "dart:math";
import 'package:flutter/material.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'sample_provider.g.dart';
@riverpod
Future<String> hello(HelloRef ref) {
return Future<String>.delayed(const Duration(seconds: 2), () {
return "Hello${Random().nextInt(10)}";
});
}
According to the documentation of Riverpod, when doing refresh, the loading callback is skipped. If you are using new version of Riverpod, you can make skipLoadingOnRefresh false.
return asyncHello.when(
skipLoadingOnRefresh: false,
data: (data) => Scaffold(
appBar: AppBar(
title: const Text("First Page"),
),
drawer: const SampleDrawer(),
body: SafeArea(child: Center(child: Text(data)))),
error: (error, stackTrace) => Text(stackTrace.toString()),
loading: () => const Center(child: CircularProgressIndicator.adaptive()));
There is second approach. You can check if it is refreshing by checking isRefreshing
member variable. For example:
@override
Widget build(BuildContext context, WidgetRef ref) {
final asyncHello = ref.watch(helloProvider);
if(asyncHello.isRefreshing) {
// return <Loading widget>
}
return asyncHello.when(
data: (data) => Scaffold(
appBar: AppBar(
title: const Text("First Page"),
),
drawer: const SampleDrawer(),
body: SafeArea(child: Center(child: Text(data)))),
error: (error, stackTrace) => Text(stackTrace.toString()),
loading: () => const Center(child: CircularProgressIndicator.adaptive()));
}