Routing Instrumentation

Learn more about the Sentry Routing Instrumentation for the Flutter SDK.

Sentry's routing instrumentation for Flutter automatically tracks and reports page navigation events in your app. It supports both traditional Flutter routing and the GoRouter package.

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

Before starting, ensure:

  1. The Sentry Flutter SDK is initialized. Learn more here.
  2. Tracing is set up. Learn more here.

Add an instance of SentryNavigationObserver to your application's navigatorObservers.

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

return MaterialApp(
  title: 'My Widget',
  home: MyWidget(),
  navigatorObservers: [
    SentryNavigatorObserver()
  ],
);

By default the application's main route name is "/". The instrumentation sets the span operation to navigation and the span name to the provided route name. For transactions to be created when navigation changes, you need to provide route names:

  • Flutter routing: use RouteSettings and set the name in the constructor.
  • GoRouter: use the name parameter to set the route name.
Copied
import 'package:flutter/material.dart';
import 'package:sentry_flutter/sentry_flutter.dart';

/// Setting the route name is required
MaterialPageRoute(
  builder: (BuildContext context) => MyWidget(),
  settings: RouteSettings(name: 'My Widget'),
)

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). There are two ways to set up TTID:

The default setup is enabled automatically, but only provides an approximation of TTID. To set a more accurate TTID, manually wrap the desired widget with SentryDisplayWidget, as shown below:

Copied
import 'package:sentry_flutter/sentry_flutter.dart';

SentryDisplayWidget(
  child: MyWidget(),
)

If you have some Widgets that need accurate measurements while the rest can be automatically approximated, you can use a combination of both the default and the manual method.

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:
Copied
import 'package:flutter/widgets.dart';
import 'package:sentry_flutter/sentry_flutter.dart';

Future<void> main() async {
  await SentryFlutter.init(
    (options) => options.enableTimeToFullDisplayTracing = true,
    appRunner: () => runApp(MyApp()),
  );
}
  1. Report the span manually:
Copied
import 'package:sentry_flutter/sentry_flutter.dart';

SentryFlutter.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.

Set up a new widget that executes an expensive operation.

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

/// Widget that executes an expensive operation
class MyWidget extends StatefulWidget {
  const MyWidget({super.key});

  
  MyWidgetState createState() => MyWidgetState();
}

class MyWidgetState extends State<MyWidget> {
  static const delayInSeconds = 5;

  
  void initState() {
    super.initState();
    _doComplexOperation();
  }

  /// Attach child spans to the routing transaction
  /// or the transaction will not be sent to Sentry.
  Future<void> _doComplexOperation() async {
    final activeTransaction = Sentry.getSpan();
    final childSpan = activeTransaction?.startChild('complex operation',
    description: 'running a $delayInSeconds seconds operation');
    await Future.delayed(const Duration(seconds: delayInSeconds));
    childSpan?.finish();
  }

  
  Widget build(BuildContext context) {
    return ...
  }
}

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

Copied
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) => MyWidget(),
  ),
);

Log into sentry.io and open your project's performance page to see the transaction MyWidget.

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

Copied
import 'package:sentry_flutter/sentry_flutter.dart';

/// Change how long navigation transactions idle before being finished
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.

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

Copied
import 'package:sentry_flutter/sentry_flutter.dart';

/// Only track navigation breadcrumbs
SentryNavigatorObserver(
  enableAutoTransactions: false,
)

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

Copied
import 'package:sentry_flutter/sentry_flutter.dart';

/// Ignore matching routes
SentryNavigatorObserver(
  ignoreRoutes: ["/ignoreThisRoute", "/my/ignored/route"],
)

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.

Copied
import 'package:sentry_flutter/sentry_flutter.dart';

/// Override transaction name with route name
SentryNavigatorObserver(
  setRouteNameAsTransaction: true,
)
Help improve this content
Our documentation is open source and available on GitHub. Your contributions are welcome, whether fixing a typo (drat!) or suggesting an update ("yeah, this would be better").