---
title: "Routing Instrumentation"
description: "Learn more about the Sentry Routing Instrumentation for the Flutter SDK."
url: https://docs.sentry.io/platforms/dart/guides/flutter/integrations/routing-instrumentation/
---

# Routing Instrumentation | Sentry for Flutter

Sentry's routing instrumentation for Flutter automatically tracks and reports page navigation events in your app. It supports imperative navigation (`Navigator.push()`), the declarative `Router` API (`MaterialApp.router`), and popular routing packages like [GoRouter](https://pub.dev/packages/go_router) and [auto\_route](https://pub.dev/packages/auto_route).

The routing instrumentation feature is shipped with Sentry's Flutter SDK automatically.

## [Prerequisites](https://docs.sentry.io/platforms/dart/guides/flutter/integrations/routing-instrumentation.md#prerequisites)

Before starting, ensure:

1. The Sentry Flutter SDK `9.1.0` or later is installed.
2. The Sentry Flutter SDK is initialized. Learn more [here](https://docs.sentry.io/platforms/dart/guides/flutter.md#configure).
3. Tracing is set up. Learn more [here](https://docs.sentry.io/platforms/dart/guides/flutter/tracing.md).

## [Configure](https://docs.sentry.io/platforms/dart/guides/flutter/integrations/routing-instrumentation.md#configure)

### [1. Add `SentryNavigatorObserver`](https://docs.sentry.io/platforms/dart/guides/flutter/integrations/routing-instrumentation.md#1-add-sentrynavigatorobserver)

How you add `SentryNavigatorObserver` depends on which navigation approach you use.

```dart
import 'package:flutter/material.dart';
import 'package:sentry_flutter/sentry_flutter.dart';

Future<void> main() async {
  await SentryFlutter.init((options) {
    options.dsn = '___DSN___';
  }, appRunner: () => runApp(SentryWidget(child: MyApp())));
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(

        navigatorObservers: [
          SentryNavigatorObserver(),
        ],

        ...
    );
  }
}
```

### [2. Define Route Names](https://docs.sentry.io/platforms/dart/guides/flutter/integrations/routing-instrumentation.md#2-define-route-names)

The instrumentation sets the span `operation` to `ui.load` and the span `name` to the provided route name.

How you define route names depends on your navigation approach:

* **Navigator (imperative)**: Pass a `name` in the `RouteSettings` when pushing routes.
* **Router**: Set the `name` on the `Page` objects you return in your `pages` list (for example, `MaterialPage(name: 'My Widget', child: ...)`).
* **GoRouter**: GoRouter automatically uses the route `path` as the name. You can optionally set the `name` parameter on your `GoRoute` to override it.
* **auto\_route**: Route names are generated automatically from your `@RoutePage()` annotations — no extra configuration needed.

If the SDK cannot determine a route name, it will not create transactions, breadcrumbs, or performance insights such as [TTID](https://docs.sentry.io/platforms/dart/guides/flutter/integrations/routing-instrumentation.md#time-to-initial-display) or [TTFD](https://docs.sentry.io/platforms/dart/guides/flutter/integrations/routing-instrumentation.md#time-to-full-display) for that route.

```dart
MaterialPageRoute(
  builder: (BuildContext context) => MyWidget(),

  settings: RouteSettings(name: 'My Widget'),

)
```

## [Time to Initial Display](https://docs.sentry.io/platforms/dart/guides/flutter/integrations/routing-instrumentation.md#time-to-initial-display)

Time to initial display (TTID) provides insight into how long it takes your Widget to launch and draw their first frame. This is measured by adding a span for navigation to a Widget. The SDK then sets the span operation to `ui.load.initial-display` and the span description to the Widget's route name, followed by initial display (for example, `MyWidget initial display`).

TTID is enabled by default.

## [Time to Full Display](https://docs.sentry.io/platforms/dart/guides/flutter/integrations/routing-instrumentation.md#time-to-full-display)

Time to full display (TTFD) provides insight into how long it would take your Widget to launch and load all of its content. This is measured by adding a span for each navigation to a Widget. The SDK then sets the span operation to `ui.load.full-display` and the span description to the Widget's route name, followed by full display (for example, `MyWidget full display`).

TTFD is disabled by default. To enable TTFD measurements, follow these steps:

### [1. Enable the `enableTimeToFullDisplayTracing` option in the SDK configuration](https://docs.sentry.io/platforms/dart/guides/flutter/integrations/routing-instrumentation.md#1-enable-the-enabletimetofulldisplaytracing-option-in-the-sdk-configuration)

```dart
await SentryFlutter.init(
  (options) {
    options.dsn = '___DSN___';

    options.enableTimeToFullDisplayTracing = true;

  }, appRunner: () => runApp(SentryWidget(child: MyApp())),
);
```

### [2. Report the TTFD span](https://docs.sentry.io/platforms/dart/guides/flutter/integrations/routing-instrumentation.md#2-report-the-ttfd-span)

There are two ways to report when your widget is fully displayed:

#### [1. Wrap with `SentryDisplayWidget`](https://docs.sentry.io/platforms/dart/guides/flutter/integrations/routing-instrumentation.md#1-wrap-with-sentrydisplaywidget)

Embed your target widget in `SentryDisplayWidget` and call `SentryDisplayWidget.of(context).reportFullyDisplayed()`.

#### [2. Call the API directly](https://docs.sentry.io/platforms/dart/guides/flutter/integrations/routing-instrumentation.md#2-call-the-api-directly)

Retrieve the current display span via `SentryFlutter.currentDisplay()` in `initState()` and call `currentDisplay.reportFullyDisplayed()` — no wrapper needed.

**Important for `StatelessWidget`:**

If you're navigating to a `StatelessWidget`, you must use the `SentryDisplayWidget` wrapper. `SentryDisplayWidget` automatically reports TTFD as soon as the build completes. You do not need to call `reportFullyDisplayed()` yourself.

```dart
Navigator.push(
  context,
  MaterialPageRoute(
    settings: const RouteSettings(name: 'MyWidget'),

    builder: (context) => SentryDisplayWidget(child: MyWidget()),

  ),
);

// Inside MyWidget’s State:
@override
void initState() {
  super.initState();

  // Do some long running work...
  Future.delayed(const Duration(seconds: 3), () {
    if (mounted) {
      SentryDisplayWidget.of(context).reportFullyDisplayed();
    }
  });

}
```

If the span finishes through the API, its status will be set to `SpanStatus.OK`.

If the span doesn't finish after 30 seconds, it will be finished by the SDK automatically, and its status will be set to `SpanStatus.DEADLINE_EXCEEDED`. In this case, its duration will match the TTID span.

## [Verify](https://docs.sentry.io/platforms/dart/guides/flutter/integrations/routing-instrumentation.md#verify)

### [1. Implement a Test Widget:](https://docs.sentry.io/platforms/dart/guides/flutter/integrations/routing-instrumentation.md#1-implement-a-test-widget)

Set up a new widget that executes an expensive operation.

```dart
import 'package:flutter/material.dart';
import 'package:sentry/sentry.dart';

class MyWidget extends StatefulWidget {
  const MyWidget({super.key});

  @override
  MyWidgetState createState() => MyWidgetState();
}

class MyWidgetState extends State<MyWidget> {
  @override
  void initState() {
    super.initState();
    Future.delayed(const Duration(seconds: 3), () {
      if (mounted) {
        SentryDisplayWidget.of(context).reportFullyDisplayed();
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return ...
  }
}
```

### [2. Navigate to the Widget:](https://docs.sentry.io/platforms/dart/guides/flutter/integrations/routing-instrumentation.md#2-navigate-to-the-widget)

Use the navigator to transition to your widget. This should create and send a transaction named after the widget's route.

```dart
import 'package:flutter/material.dart';
import 'my_widget.dart';

/// Push to a new screen
Navigator.push(
  context,
  MaterialPageRoute(
    settings: const RouteSettings(name: 'MyWidget'),
    builder: (context) => SentryDisplayWidget(child: MyWidget()),
  ),
);
```

Log into [sentry.io](https://sentry.io) and open your project's performance page to see the transaction `MyWidget`.

## [Additional Configuration](https://docs.sentry.io/platforms/dart/guides/flutter/integrations/routing-instrumentation.md#additional-configuration)

### [Auto Finish Time](https://docs.sentry.io/platforms/dart/guides/flutter/integrations/routing-instrumentation.md#auto-finish-time)

Adjust the duration before a routing transaction automatically finishes. The default is 3 seconds.

```dart
SentryNavigatorObserver(
  autoFinishAfter: Duration(seconds: 5)
)
```

When configuring the `autoFinishAfter` parameter, consider the following behaviours:

* Started child spans will be attached to the navigation transaction - for example the `MyWidget` transaction.
* If child spans finish after the `autoFinishAfter` time, the transaction extends and finishes when all child spans finished.
* If child spans finish before the `autoFinishAfter` time, the transaction's end time will be set to the last child end time.

### [Breadcrumb Tracking Only](https://docs.sentry.io/platforms/dart/guides/flutter/integrations/routing-instrumentation.md#breadcrumb-tracking-only)

Set `enableAutoTransactions` to `false` if you only want to track navigation breadcrumbs. Enabled by default.

```dart
SentryNavigatorObserver(
  enableAutoTransactions: false,
)
```

### [Ignore Routes](https://docs.sentry.io/platforms/dart/guides/flutter/integrations/routing-instrumentation.md#ignore-routes)

Set `ignoreRoutes` if you want routes to be ignored and not processed by the navigation observer. Empty by default.

```dart
SentryNavigatorObserver(
  ignoreRoutes: ["/ignoreThisRoute", "/my/ignored/route"],
)
```

### [Override Transaction Name](https://docs.sentry.io/platforms/dart/guides/flutter/integrations/routing-instrumentation.md#override-transaction-name)

Set `setRouteNameAsTransaction` to `true` to override the transaction name with the route name. An existing transaction in the scope 'CustomTransaction' will be renamed to 'MyWidget' for example. Disabled by default.

```dart
SentryNavigatorObserver(
  setRouteNameAsTransaction: true,
)
```
