Reputation: 77
When trying to read a collection from cloud_firestore
, I'm using riverpod provider.
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import '../dataStructs.dart';
part 'ChargesProvider.g.dart';
@riverpod
class GetCharges extends _$GetCharges {
@override
Future<Charges> build({required Layers layer}) async {
final layerNumber = layer.toDBName();
final layerDoc = 'Layers-$layerNumber';
final chargeSnapshot =
await FirebaseFirestore.instance.collection('charge').doc(layerDoc).get();
final chargeMap = chargeSnapshot.data()!;
return Charges.fromMap(chargeMap);
}
}
The code inside above build function works, tested by writing it directly in onPressed of a button. So it gets the data without any errors.
But when called like below in onPressed of a button using ref.read the provider immediately disposes.
Align(
alignment: Alignment.centerRight,
child: CustomOutlinedButton(
text: 'Continue',
onPressed: () {
if (chosenProduct != null) {
Charges charges;
ref.read(GetChargesProvider(layer: chosenProduct.layers))
.when(data: (chrg) {
charges = chrg;
print(charges);
return AsyncValue.data(chrg);
}, error: (err, stTrc) {
print('ERROR: $err');
return AsyncValue.data(defaultValue);
}, loading: () {
print('isLOADING');
return AsyncValue.data(defaultValue);
});
}
},
),
),
Below is the output on terminal.
Provider getChargesProvider:GetChargesProvider#53198(null) was initialized with AsyncLoading<Charges>()
isLOADING
Provider getChargesProvider:GetChargesProvider#53198(null) was disposed
I have tried saving my ref.read statement in a variable and then use it but got same results, it disposes. I am not using this provider anywhere else. Please suggest how to use the value of this provider. I'm using riverpod here because in case on the same page if this provider is required again, then it does not re-pull the data from firestore.
Upvotes: 0
Views: 210
Reputation: 509
The problem Is that your provider was never been registered before on any widget. Perhaps you have never made a ref.watch(...) on It. Thus when you call the read(), It will be created and at the end of the method It Will be disposed soon. A provider Is disposed when no listeners are related to It. Thus you should register It on a widget/Page before to use It in the call back
Upvotes: 0
Reputation: 77
Below solution works for me but better solutions are welcome.
ref.read as per docs read the current state of the provider. So, directly reading a new provider (not watched before/having no state) in a onPressed function was causing it to dispose the provider (correct me if I'm wrong). I changed my function to -
onPressed: () async {
if (chosenProduct != null) {
Charges? charges;
charges = await ref
.read(GetChargesProvider(layer:chosenProduct.layers).future)
.then((Charges chrg) {
// DO ALL Operations using chrg here
return chrg;
});
Above function works on single click of button, i.e. waits for provider to return the value and perform required operations post that. Also provider is updated as below -
@riverpod
class GetCharges extends _$GetCharges {
@override
Future<Charges> build({required Layers layer}) async {
ref.cacheFor(Duration(minutes: 5)); // <<<<< added this
final layerNumber = layer.toDBName();
final layerDoc = 'Layers-$layerNumber';
final chargeSnapshot =
await FirebaseFirestore.instance.collection('charge').doc(layerDoc).get();
final chargeMap = chargeSnapshot.data()!;
return Charges.fromMap(chargeMap);
}
}
Also, used below directly from riverpod docs
extension CacheForExtension on AutoDisposeRef<Object?> {
/// Keeps the provider alive for [duration].
void cacheFor(Duration duration) {
// Immediately prevent the state from getting destroyed.
final link = keepAlive();
// After duration has elapsed, we re-enable automatic disposal.
final timer = Timer(duration, link.close);
// Optional: when the provider is recomputed (such as with ref.watch),
// we cancel the pending timer.
onDispose(timer.cancel);
}
}
Upvotes: 0
Reputation: 895
To prevent auto dispose, call ref.keepAlive();
in Provider.build
Or set keepAlive
to true in riverpod annotation.
@Riverpod(keepAlive: true)
class GetCharges extends _$GetCharges {
...
}
Upvotes: 0