I am new to Flutter and Firebase. I am stuck on how to handle exceptions. For example, if there’s no internet or if something went wrong, I want to show a Snackbar
displaying that error message. What I am trying to do is:
1. get the user to input some data and pictures
2. Upload that picture to Firebase Storage and retrieve the URL
3. Upload data input by user and URL to Firestore
4.Catch any errors and display on a Snackbar
To begin,
When I click on the save button it will call this function:
void _saveItem() async {
if (_formKey.currentState!.validate()) {
setState(() {
_isSaving = true;
});
_formKey.currentState!.save();
final urlLink = await uploadImage(_selectedImage);
createListing(
itemName: _itemName!,
urlLink: urlLink!,
mainCat: _chosenMainCategory!,
subCat: _chosenSubCategory!,
dietaryInfo: _chosenDietaryOption!,
addInfo: _additionalInfo!,
expDate: _chosenDate!,
lat: _lat!,
lng: _lng!,
address: _address!,
);
Navigator.pop(context);
}
}
Which will then call uploadImage to retrieve the image URL:
Future<String> uploadImage(File file) async {
String fileName = DateTime.now().millisecondsSinceEpoch.toString();
firebase_storage.Reference ref = firebase_storage.FirebaseStorage.instance
.ref()
.child('listingImages/$fileName');
await ref.putFile(file);
String imageUrl = await ref.getDownloadURL();
return imageUrl;
}
Then it will call createListing to upload the document onto Firestore:
Future<void> createListing({
required String itemName,
required String urlLink,
required String mainCat,
required String subCat,
required String dietaryInfo,
required String addInfo,
required DateTime expDate,
required double lat,
required double lng,
required String address,
}) async {
final docListing = FirebaseFirestore.instance.collection('Listings').doc();
final user = FirebaseAuth.instance.currentUser!;
final listing = Listing(
id: docListing.id,
itemName: itemName,
image: urlLink,
mainCategory: mainCat,
subCategory: subCat,
dietaryNeeds: dietaryInfo,
additionalNotes: addInfo,
expiryDate: expDate,
lat: lat,
lng: lng,
address: address,
isAvailable: true,
userId: user.email.toString(),
userName: user.displayName.toString(),
);
final json = listing.toJson();
await docListing.set(json);
}
I don’t know where or how I am supposed to handle errors or exceptions.
I have tried using try catch on everything that has ‘await’ but it doesnt seem to timeout and the snackbar was never shown on screen.
This is my utils class i use for displaying errors on snackbars.
class Utils {
static final messengerKey = GlobalKey<ScaffoldMessengerState>();
static showSnackBar(String? text) {
if (text == null) return;
final snackBar = SnackBar(content: Text(text), backgroundColor: Colors.red);
messengerKey.currentState!
..removeCurrentSnackBar()
..showSnackBar(snackBar);
}
}
The way i use it to display an error e on screen:
Utils.showSnackBar(e.message);
Would like to get some comments on my edit, i am not sure whether this would suffice just by adding a try catch when calling saveItem function:
void _saveItem() async {
if (_selectedImage == null) {
showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: const Text(
'Picture missing',
textAlign: TextAlign.center,
),
content: const SizedBox(
height: 16,
width: 32,
child: Center(
child: Text('Add a picture'),
),
),
actions: [
TextButton(
onPressed: () {
Navigator.pop(ctx);
},
child: const Text('ok'),
),
],
),
);
return;
}
if (_address == null) {
showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: const Text(
'Address missing',
textAlign: TextAlign.center,
),
content: const SizedBox(
height: 16,
width: 32,
child: Center(
child: Text('Click on get address'),
),
),
actions: [
TextButton(
onPressed: () {
Navigator.pop(ctx);
},
child: const Text('ok'),
),
],
),
);
return;
}
if (_formKey.currentState!.validate()) {
setState(() {
_isSaving = true;
});
_formKey.currentState!.save();
try {
final urlLink = await uploadImage(_selectedImage);
createListing(
itemName: _itemName!,
urlLink: urlLink!,
mainCat: _chosenMainCategory!,
subCat: _chosenSubCategory!,
dietaryInfo: _chosenDietaryOption!,
addInfo: _additionalInfo!,
expDate: _chosenDate!,
lat: _lat!,
lng: _lng!,
address: _address!,
);
} catch (e) {
Utils.showSnackBar('error: $e');
}
Navigator.pop(context);
}
}
2
Answers
Usually, when we are calling something to the network add a try-catch clause to catch any errors,
also you can check your network connection using this package https://pub.dev/packages/connectivity_plus
in your case, you can add it here
The loss of the internet will never rise an exception. And this is because the Firebase products you work with are designed to work offline. So if you lose connectivity, your app will continue to work.
That’s the expected behavior. The Firebase SDK will try to reconnect until the device regains connectivity.
So if you need to take some action only when the device is connected to the internet, then you have to check that yourself.
Please note that @MrShakila’s solution will help only to know if the device is connected to a network. However, you can be connected to a network, to a WiFi network, for example, that doesn’t have an internet connection. So you have to check two things, if you’re connected to a network and if the internet is working properly. This means that you can try to ping a server in your application code, and see if you get a response. This can be done by pinging Google servers using: