Skip to main content
The try-finally statement is a control flow construct that guarantees the execution of a designated block of code (finally), irrespective of whether an exception is thrown, caught, or left unhandled within the associated try block. It provides a deterministic execution path for state finalization.
try {
  // Protected code block
} finally {
  // Unconditional execution block
}

Execution Mechanics

The Dart runtime enforces strict rules regarding the evaluation order and control flow transfer within a try-finally structure:
  1. Standard Execution: If the try block completes normally (meaning it reaches the end of the block without completing abruptly via a throw or control transfer), control flow immediately transfers to the finally block. This applies regardless of whether the block’s execution is synchronous or asynchronous.
  2. Exception Propagation: If an exception is thrown within the try block and is not caught by an intervening on or catch clause, the try block terminates immediately. The finally block then executes. Once the finally block completes, the unhandled exception resumes propagating up the call stack.
  3. Control Flow Interception: If the try or catch block encounters a control transfer statement (return, break, continue, throw, or rethrow), the finally block intercepts the transfer. The finally block executes completely before the control transfer is finalized.

Syntax with Catch Clauses

While try and finally can exist exclusively together, finally is often positioned as the terminal clause in a broader exception-handling chain. The finally block will always execute after all try, on, and catch blocks have resolved.
try {
  // Protected code block
} on FormatException {
  // Type-specific exception handler
} catch (e, stackTrace) {
  // General exception handler
} finally {
  // Unconditional execution block
}

Return Value Evaluation

When a return statement is invoked inside a try block, the expression being returned is evaluated before the finally block executes, but the actual return to the caller is deferred until after the finally block completes.
int evaluateFlow() {
  try {
    return 1; // Expression '1' is evaluated, but control flow is suspended
  } finally {
    print('Executes prior to the caller receiving the return value');
  }
}
If the finally block itself contains a control transfer statement (such as return or throw), it overrides any pending control transfer from the try or catch blocks.
Tired of Poor Dart Skills? Fix That With Deep Grasping!Learn More