Skip to main content

Debugging

About 3 min

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() in login.dart
  • _saveTrip() in trip_detail.dart
  • watchTrips() in trip_repository.dart
  • the builder of the StreamBuilder in trip_list.dart

When the app pauses on a breakpoint, inspect the variables in VS Code. You can check values such as:

  • emailController.text
  • passwordController.text
  • widget.id
  • editedTrip
  • snapshot.hasError
  • snapshot.data

Hot reload or restart?

Flutter has different ways to rerun code:

ActionUse when
hot reloadyou changed UI inside a widget
hot restartyou changed initialization, routes, Firebase setup or app-level state
stop and run againthe 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:

ProblemCheck
login does nothingis _submit() called?
validation always failscheck the validators
Firebase returns an errorcheck error.code
app stays loadingcheck 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() return true?
  • What is inside editedTrip?
  • Is isNewTrip true or false?
  • Does the code call createTrip() or updateTrip()?
  • 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

SymptomLikely cause
firebase_options.dart not foundflutterfire configure was not run
DefaultFirebaseOptions not foundmissing import 'firebase_options.dart';
trips never loaduser is not logged in, wrong Firestore path or rules deny read
save button does nothingform validation fails
duplicate savessave button is not disabled while isSaving is true
only one account sees datathis is expected if trips are stored under users/{uid}/trips
permission deniedFirestore 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 editedTrip before it is saved.
  • Add a breakpoint in the StreamBuilder of TripListPage.
  • Check that the new trip appears in snapshot.data.
  • Temporarily print snapshot.error when snapshot.hasError is true.