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:
Add an instance of SentryNavigationObserver
to your application's navigatorObservers
.
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.
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:
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:
- Enable the
enableTimeToFullDisplayTracing
option in the SDK configuration:
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()),
);
}
- Report the span manually:
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.
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.
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.
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.
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.
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.
import 'package:sentry_flutter/sentry_flutter.dart';
/// Override transaction name with route name
SentryNavigatorObserver(
setRouteNameAsTransaction: true,
)
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").