Error Handling Basics


Error Handling Basics Interview with follow-up questions

1. What is error handling in Flutter and why is it important?

Error handling is anticipating, catching, and recovering from failures — Dart exceptions, async errors from Futures/Streams, framework build errors, and platform-channel failures — so the app degrades gracefully instead of crashing or freezing on a red error screen.

Why it matters (and what a senior answer covers):

  • Stability & UX — a caught network failure becomes a retry button; an uncaught one becomes a crash. You keep the user in control.
  • Layered handling — Flutter has distinct hooks for distinct failures, and you should name them:
    • try/catch/finally (with on clauses) for synchronous code and awaited futures.
    • Future.catchError / Stream's onError for callback-style async.
    • FlutterError.onError for errors thrown during build/layout/paint inside the framework.
    • PlatformDispatcher.instance.onError for uncaught async errors — this largely replaced the old runZonedGuarded boilerplate.
    • ErrorWidget.builder to replace the default red error screen with something user-friendly in release.
  • Observability — wire those handlers into Sentry or Firebase Crashlytics so production failures are reported with stack traces, not lost.
  • Platform/plugin errors surface as PlatformException (and MissingPluginException); handle them where you call native code.

Gotcha interviewers probe: the default red ErrorWidget only shows in debug — in release it's a grey box, so you should set ErrorWidget.builder and report via FlutterError.onError, or users see a blank screen with no telemetry.

↑ Back to top

Follow-up 1

Can you give an example of a common error that might occur in Flutter?

One common error that might occur in Flutter is the 'NoSuchMethodError'. This error occurs when a method is called on an object that does not have that method defined. For example, if you try to call a method on a null object reference, you will encounter this error.

Follow-up 2

How does error handling improve the user experience?

Error handling improves the user experience by providing meaningful error messages and handling errors gracefully. Instead of crashing or freezing, the application can display an error message to the user, explaining what went wrong and suggesting possible solutions. This helps users understand and recover from errors, leading to a better overall experience.

Follow-up 3

What are some best practices for error handling in Flutter?

Some best practices for error handling in Flutter include:

  1. Use try-catch blocks to catch and handle exceptions.
  2. Display meaningful error messages to the user.
  3. Provide options for the user to retry or recover from errors.
  4. Log errors for debugging purposes.
  5. Use error monitoring tools to track and analyze errors in production.
  6. Test error scenarios to ensure proper handling.
  7. Follow the Flutter community guidelines and recommendations for error handling.

2. What is the difference between compile-time and runtime errors in Flutter?

Compile-time errors are caught by the Dart analyzer/compiler before the app runs — the build simply fails. With Dart 3's sound null safety and strong static typing, a lot of would-be crashes are now compile-time errors: type mismatches, calling a method on a possibly-null value, missing required parameters, using an undefined identifier. You fix them in the editor; the app never ships with them.

Runtime errors happen while the app is executing, when something the type system couldn't prove ahead of time goes wrong: a failed network call, a bad as cast (TypeError), an out-of-range list index (RangeError), parsing malformed JSON, a null assertion (!) on a value that turned out null, or a thrown Exception. These you handle with try/catch, async error hooks, and recovery UI.

// Compile-time error — won't build (null safety):
String name = null; // Error: a value of type 'Null' can't be assigned...

// Runtime error — compiles fine, throws when executed:
final n = int.parse('abc'); // FormatException at runtime

The framing interviewers want: Dart 3's sound null safety shifts many classic "null pointer" runtime crashes to compile time, which is a core selling point. What remains at runtime is genuinely dynamic — I/O, casts of dynamic/Object?, external data — and that's where your error-handling strategy (try/catch, FlutterError.onError, PlatformDispatcher.onError, crash reporting) lives.

Gotcha: the null-assertion operator ! and unchecked as casts deliberately defer a check to runtime — overusing them reintroduces the very crashes null safety was meant to prevent.

↑ Back to top

Follow-up 1

Can you give an example of each?

Sure! Here's an example of a compile-time error in Flutter:

void main() {
  print('Hello, World!')
}

In this example, a semicolon is missing at the end of the print statement, which results in a compile-time error.

And here's an example of a runtime error in Flutter:

void main() {
  int result = 10 ~/ 0;
  print('Result: $result');
}

In this example, we are trying to divide a number by zero, which is not allowed and results in a runtime error.

Follow-up 2

How can you prevent compile-time errors?

To prevent compile-time errors in Flutter, it is important to write correct and valid code. Here are some tips to avoid compile-time errors:

  1. Double-check your syntax: Make sure all the required semicolons, parentheses, and curly braces are in place.
  2. Use proper data types: Ensure that you are using the correct data types for variables and function parameters.
  3. Import necessary packages: If you are using external packages or libraries, make sure to import them correctly.
  4. Follow Flutter coding conventions: Adhere to the recommended coding style and conventions to minimize errors.
  5. Test your code: Regularly test your code during development to catch any potential errors early on.

Follow-up 3

How can you handle runtime errors?

Handling runtime errors in Flutter involves implementing error handling mechanisms to gracefully handle unexpected conditions. Here are some approaches to handle runtime errors:

  1. Use try-catch blocks: Wrap the code that may potentially throw an error in a try block and catch the specific type of error in a catch block. This allows you to handle the error gracefully and provide appropriate feedback to the user.
try {
  // Code that may throw an error
} catch (e) {
  // Handle the error
}
  1. Use error handling functions: Flutter provides various error handling functions like onError for streams and FlutterError.onError for global errors. These functions allow you to define custom error handling logic.

  2. Provide fallback or default values: When dealing with potential errors, you can provide fallback values or default behavior to prevent crashes or incorrect behavior.

  3. Log errors: Logging errors can help in debugging and identifying the cause of runtime errors. Use logging libraries like logger to log errors and relevant information for analysis.

It is important to handle runtime errors properly to ensure a smooth user experience and prevent crashes or unexpected behavior in your Flutter applications.

3. What is an exception in Flutter?

An exception is an error signal thrown during execution that interrupts normal control flow. If nothing catches it, it propagates up the call stack; an uncaught exception on the main path crashes that operation (and, if on the framework's path, shows the error screen).

In Dart there are two throwable hierarchies, and knowing the distinction is a common follow-up:

  • Exception — conditions you're expected to anticipate and recover from: FormatException, TimeoutException, HttpException, PlatformException. Catch these and handle them.
  • Error — programming bugs that signal you did something wrong: StateError, RangeError, TypeError, the assertion failures behind !. You generally shouldn't catch these to "recover" — you should fix the code. Catching Error hides bugs.

Dart lets you throw any object, but idiomatic code throws Exception subtypes (or custom ones):

class PaymentDeclinedException implements Exception {
  final String reason;
  PaymentDeclinedException(this.reason);
  @override
  String toString() => 'PaymentDeclined: $reason';
}

try {
  if (balance < amount) throw PaymentDeclinedException('insufficient funds');
} on PaymentDeclinedException catch (e) {
  showError(e.reason);
}

Gotchas: prefer on SpecificException catch (e) over a bare catch (e) so you don't accidentally swallow Errors (real bugs). And async exceptions thrown inside an un-awaited Future won't be caught by a surrounding try/catch — they surface through PlatformDispatcher.instance.onError instead.

↑ Back to top

Follow-up 1

What is the difference between an error and an exception?

In Flutter, an error is a problem that occurs during the compilation or execution of a program and prevents it from running correctly. It is usually caused by a mistake in the code or a problem with the environment. On the other hand, an exception is an event that occurs during the execution of a program and disrupts the normal flow of instructions. It is usually caused by an unexpected condition or an error condition that the program is designed to handle.

Follow-up 2

How can you catch and handle exceptions in Flutter?

In Flutter, you can catch and handle exceptions using a try-catch block. The try block contains the code that may throw an exception, and the catch block contains the code that handles the exception. If an exception is thrown in the try block, the program jumps to the catch block and executes the code inside it. You can also use multiple catch blocks to handle different types of exceptions.

Follow-up 3

Can you give an example of a common exception in Flutter?

One common exception in Flutter is the NoSuchMethodError. This exception occurs when you try to call a method on an object that does not have that method. For example, if you try to call a non-existent method on a null object, a NoSuchMethodError will be thrown. To handle this exception, you can use a try-catch block and provide a fallback behavior or display an error message to the user.

4. What is the 'try-catch' block in Flutter?

try/catch is Dart's construct for catching exceptions thrown in a block of code and handling them instead of letting them crash the operation. The full shape is try / on / catch / finally:

try {
  final response = await http.get(uri);   // also catches awaited async errors
  return parse(response.body);
} on FormatException catch (e) {
  // specific type — bad JSON
  return fallback();
} on TimeoutException {
  showRetry();
  rethrow;                                 // re-raise to let a caller also react
} catch (e, stackTrace) {
  // anything else; capture the stack trace as the 2nd parameter
  reportError(e, stackTrace);
} finally {
  hideSpinner();                           // always runs, error or not
}

Points interviewers want:

  • on Type catches a specific exception class; catch (e) catches anything; catch (e, st) also gives you the StackTrace. Order specific clauses before the generic one.
  • try/catch works with await — awaiting a failing Future throws into the surrounding try, so you don't need .catchError for async/await code.
  • rethrow preserves the original stack trace when you partially handle then re-throw; throw e would reset it.

Gotchas:

  • A bare catch (e) also catches Errors (real bugs) — prefer on clauses so you don't mask them.
  • try/catch cannot catch errors from a Future you forgot to await or from a Stream — those need await, .catchError, Stream.onError, or the global PlatformDispatcher.instance.onError.
↑ Back to top

Follow-up 1

How does it work?

The 'try-catch' block works by enclosing a section of code that may potentially throw an exception within a 'try' block. If an exception is thrown within the 'try' block, it is caught by the corresponding 'catch' block. The 'catch' block contains the code that handles the exception, allowing you to gracefully handle errors and prevent your application from crashing.

Follow-up 2

When should you use it?

You should use the 'try-catch' block in Flutter when you anticipate that a certain section of your code may throw an exception. This is particularly useful when dealing with external dependencies, network requests, or any other operation that may result in an error. By using a 'try-catch' block, you can handle these errors in a controlled manner and provide appropriate feedback to the user.

Follow-up 3

Can you give an example of how to use a 'try-catch' block in Flutter?

Certainly! Here's an example of how to use a 'try-catch' block in Flutter:

try {
  // Code that may throw an exception
  int result = 10 ~/ 0; // Division by zero
  print('Result: $result');
} catch (e) {
  // Exception handling
  print('An error occurred: $e');
}

In this example, we are attempting to divide 10 by 0, which will result in a division by zero exception. The 'try' block contains the code that may throw the exception, and the 'catch' block handles the exception by printing an error message. This prevents the application from crashing and allows you to handle the error gracefully.

5. What is the 'finally' block in Flutter?

A finally block runs after the try (and any matching catch), regardless of how the try exited — normal completion, a caught exception, an uncaught/re-thrown exception, or even a return. It's where you put cleanup that must always happen.

final connection = await db.open();
try {
  await connection.run(query);
  return result;          // finally still runs before the value is returned
} catch (e) {
  log(e);
  rethrow;                // finally still runs before the exception propagates
} finally {
  await connection.close(); // guaranteed cleanup: close resources, hide spinner
}

Typical uses: closing files/database connections, releasing locks, hiding a loading indicator, disposing controllers — anything that must not leak whether or not the operation succeeded.

Subtleties interviewers like:

  • finally runs even when there's a return or rethrow in the try/catch. Its job is cleanup, not control flow.
  • Avoid returning from inside finally — it overrides the value or exception from the try/catch, silently swallowing errors. That's a classic bug.
  • With await, the finally itself can be async (await connection.close()), and the surrounding async function waits for it before completing.

Gotcha: finally is for cleanup that always runs; it is not a substitute for catch. If you only have try/finally with no catch, the exception still propagates after the cleanup — which is often exactly what you want.

↑ Back to top

Follow-up 1

When should you use it?

You should use the 'finally' block in Flutter when you want to ensure that certain code is always executed, regardless of whether an exception occurs or not. It is commonly used for cleanup tasks, such as closing files or releasing resources, that need to be performed regardless of the outcome of the try-catch block.

Follow-up 2

How does it work?

The 'finally' block is placed after the 'try' and 'catch' blocks in a try-catch-finally statement. If an exception is thrown within the 'try' block, the 'catch' block is executed to handle the exception. After the 'catch' block, the 'finally' block is executed, regardless of whether an exception was thrown or not. If no exception is thrown, the 'finally' block is still executed.

Follow-up 3

Can you give an example of how to use a 'finally' block in Flutter?

Sure! Here's an example of how to use a 'finally' block in Flutter:

try {
  // Code that may throw an exception
} catch (e) {
  // Code to handle the exception
} finally {
  // Code that will always be executed
}

In this example, if an exception is thrown within the 'try' block, the 'catch' block will be executed to handle the exception. After the 'catch' block, the 'finally' block will be executed, regardless of whether an exception was thrown or not. If no exception is thrown, the 'finally' block is still executed.

Live mock interview

Mock interview: Error Handling Basics

Intermediate ~5 min Your own free AI key

Your voice and your AI key never touch our servers; the key stays in this browser and is sent only to Google. Only your round scores are saved to track progress.