I want to show CircularProgressIndicator while waiting for future to resolve using flutter_riverpod, here is my code snippet. But it's not showing, I am using ConsumerStatefulWidget is this right way to do it?
ElevatedButton(
onPressed: () {
rejectResponse = ref
.read(notificationRepositoryProvider)
.approveDocument(1);
Navigator.of(context).pop();
setState(() {});
},
child: FutureBuilder(
future: rejectResponse,
builder: (context, snapshot) {
if (snapshot.connectionState ==
ConnectionState.done) {
if (snapshot.hasData) {
return Text('Yes');
} else if (snapshot.hasError) {
return Text('Error');
}
} else if (snapshot.connectionState ==
ConnectionState.waiting) {
return CircularProgressIndicator();
}
return Text('Yes');
}),
),
CodePudding user response:
You can use ConnectionState
, but I think it can also be in none
state. So you can check if its done, and if not, you should probably show the loading indicator anyway.
builder: (context, snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
return CircularProgressIndicator();
}
else
return Text('Yes');
}
}
I usually use the hasData
property instead. This lets me reduce the number of states I have to deal with, and safely assume snapshot.data
is not null
. I use the below pattern.
// strongly type the builder so snapshot.data is typed as well.
FutureBuilder<MyType>(
future: myFuture
builder: (context, snapshot) {
if (!snapshot.hasData) return Loading();
final data = snapshot.data!; // '!' is safe since hasData is true
return DisplayData(data);
}
)
CodePudding user response:
The preferred way of doing this in Riverpod is using a FutureProvider and AsyncValue:
final notificationRepositoryProvider = FutureProvider<bool?>((ref) async {
Future<bool> approveDocument() => Future.delayed(Duration(seconds: 2), () => Future.value(Random().nextBool()));
return approveDocument();
});
class HomeView extends ConsumerStatefulWidget {
const HomeView({Key? key}) : super(key: key);
@override
HomeViewState createState() => HomeViewState();
}
class HomeViewState extends ConsumerState<HomeView> {
@override
Widget build(BuildContext context) {
AsyncValue<bool?> rejectResponse = ref.watch(notificationRepositoryProvider);
return ElevatedButton(
onPressed: () {
ref.refresh(notificationRepositoryProvider.future);
},
child: rejectResponse.when(
loading: () => const CircularProgressIndicator(
color: Colors.white,
),
skipLoadingOnRefresh: false,
error: (err, stack) => Text('Error'),
data: (data) => Text('Yes: $data'),
));
}
}
Note that after the initial loading, the FutureProvider will return the previous value but will set AsyncValue.isRefreshing to true. To avoid this and always show the loader on refresh, you can set skipLoadingOnRefresh
to false.