Debugging
Debugging
Debugging means finding out where something goes wrong and why it happens.
In this course, the most useful app to debug is Travel Notes. It has several moving parts:
- Firebase initialization
- Authentication
- Firestore reads and writes
- forms and validation
- realtime updates with
StreamBuilder
That makes it a good app to practise debugging.
Start with the error message
When Flutter shows a red error screen or VS Code prints an error in the debug console, read the first useful message carefully.
Good questions:
- Which file is mentioned?
- Which line number is mentioned?
- Is it a Flutter error, a Firebase error or a Dart error?
- Did the error happen while building UI, logging in, reading Firestore or saving a trip?
Do not start by changing random code. First find the place where the error starts.
VS Code debugging
Open the Travel Notes Flutter project in VS Code.
- Open the Run and Debug tab.
- Choose a device, for example Chrome or an Android emulator.
- Press Start Debugging.
- Add a breakpoint by clicking left of a line number.
For example, useful breakpoints are:
_submit()inlogin.dart_saveTrip()intrip_detail.dartwatchTrips()intrip_repository.dart- the
builderof theStreamBuilderintrip_list.dart
When the app pauses on a breakpoint, inspect the variables in VS Code. You can check values such as:
emailController.textpasswordController.textwidget.ideditedTripsnapshot.hasErrorsnapshot.data
Hot reload or restart?
Flutter has different ways to rerun code:
| Action | Use when |
|---|---|
| hot reload | you changed UI inside a widget |
| hot restart | you changed initialization, routes, Firebase setup or app-level state |
| stop and run again | the app is stuck, Firebase config changed, or the debugger behaves strangely |
If you change main.dart, Firebase.initializeApp(...), themes or imports, use hot restart or run the app again. Hot reload is not always enough for those changes.
Debug output
Use debugPrint() when you want to inspect what happens while the app runs.
debugPrint("Saving trip: ${editedTrip.title}");
For example, in _saveTrip():
debugPrint("Saving trip");
debugPrint("Title: ${titleController.text}");
debugPrint("Destination: ${destinationController.text}");
Remove temporary debug output when the problem is fixed.
Debug Firebase Authentication
In login.dart, authentication errors are caught here:
} on FirebaseAuthException catch (error) {
setState(() {
errorMessage = error.message;
});
}
While debugging, it can help to print the error code:
} on FirebaseAuthException catch (error) {
debugPrint("Auth error code: ${error.code}");
setState(() {
errorMessage = error.message;
});
}
Common causes:
| Problem | Check |
|---|---|
| login does nothing | is _submit() called? |
| validation always fails | check the validators |
| Firebase returns an error | check error.code |
| app stays loading | check if isLoading is reset in finally |
Debug Firestore reads
In TripListPage, the StreamBuilder tells you what is happening.
if (snapshot.hasError) {
return const Center(child: Text("Could not load trips"));
}
if (!snapshot.hasData) {
return const Center(child: CircularProgressIndicator());
}
final trips = snapshot.data!;
If the list does not show trips, check:
- Is the user logged in?
- Does Firestore contain documents under
users/{uid}/trips? - Does the app use the same Firebase project as the Firebase console?
- Do the Firestore security rules allow this user to read their own trips?
- Is there an error in
snapshot.error?
Temporarily print the error:
if (snapshot.hasError) {
debugPrint("Firestore error: ${snapshot.error}");
return const Center(child: Text("Could not load trips"));
}
Debug Firestore writes
In trip_detail.dart, most write problems happen in _saveTrip().
Useful things to inspect:
- Does
formKey.currentState!.validate()returntrue? - What is inside
editedTrip? - Is
isNewTriptrueorfalse? - Does the code call
createTrip()orupdateTrip()? - Does Firestore reject the write because of security rules?
You can add temporary output:
debugPrint("Is new trip: $isNewTrip");
debugPrint("Trip title: ${editedTrip.title}");
debugPrint("Trip destination: ${editedTrip.destination}");
Common Travel Notes bugs
| Symptom | Likely cause |
|---|---|
firebase_options.dart not found | flutterfire configure was not run |
DefaultFirebaseOptions not found | missing import 'firebase_options.dart'; |
| trips never load | user is not logged in, wrong Firestore path or rules deny read |
| save button does nothing | form validation fails |
| duplicate saves | save button is not disabled while isSaving is true |
| only one account sees data | this is expected if trips are stored under users/{uid}/trips |
| permission denied | Firestore security rules do not match the app path |
Read stack traces
A stack trace shows which functions were called before the error happened. Start near the top and look for files from your own app, such as:
lib/pages/trip_detail.dart
lib/pages/trip_list.dart
lib/repositories/trip_repository.dart
Flutter and Firebase files are useful context, but your own file is usually where you can fix the problem.
Best practices
- Reproduce the bug with clear steps.
- Read the first useful error message.
- Add a breakpoint near the code that should run.
- Inspect variables instead of guessing.
- Use
debugPrint()for temporary clues. - Remove temporary debug output after fixing the bug.
- Fix one problem at a time.
Exercise
Debug the Travel Notes app:
- Add a breakpoint in
_saveTrip(). - Create a new trip.
- Inspect
editedTripbefore it is saved. - Add a breakpoint in the
StreamBuilderofTripListPage. - Check that the new trip appears in
snapshot.data. - Temporarily print
snapshot.errorwhensnapshot.hasErroris true.