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

# App Start Instrumentation | Sentry for Flutter

References to "transactions" on this page apply to the default transaction mode. In stream mode, this integration creates service spans instead, and Sentry sends them as they finish. See [Streamed Spans](https://docs.sentry.io/platforms/dart/guides/flutter/tracing/streamed-spans.md) for more information.

Sentry's app start instrumentation provides insight into how long your application takes to launch.

App start instrumentation is available on **iOS** and **Android**.

## [Instrumentation Behaviour](https://docs.sentry.io/platforms/dart/guides/flutter/integrations/app-start-instrumentation.md#instrumentation-behaviour)

Before diving into the configuration, it's important to understand how app start instrumentation behaves:

App start instrumentation tracks the duration between the earliest native process initialization and the first frame rendered (as reported by [addTimingsCallback](https://api.flutter.dev/flutter/scheduler/SchedulerBinding/addTimingsCallback.html)). Once the app start is processed, the callback is removed to avoid additional overhead.

When the SDK receives the start and end times of the app launch, the SDK:

* Creates a transaction named `ui.load`
* Attaches a span with either `app.start.cold` or `app.start.warm` operation
* Adds app start metrics to the transaction

If you'd rather have the app start reported as its own transaction, see [Standalone App Start Tracing](https://docs.sentry.io/platforms/dart/guides/flutter/integrations/app-start-instrumentation.md#standalone-app-start-tracing).

Sentry's App Start instrumentation aims to be as comprehensive and representative of the user experience as possible, and adheres to guidelines by the platform vendors. For this reason, App Starts reported by Sentry might be longer than what you see in other tools.

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

Before starting, ensure:

1. The Sentry Flutter SDK is initialized. Learn more [here](https://docs.sentry.io/platforms/dart/guides/flutter.md#configure)
2. 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/app-start-instrumentation.md#configure)

This instrumentation is automatically enabled. There is no need for further configuration.

App start instrumentation is designed specifically for pure Flutter applications and requires UI rendering to function properly. If you're using Flutter in an add-to-app integration scenario, the app start metrics will not provide accurate measurements. In such cases, we recommend disabling this instrumentation.

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

### [1. Launch Your App:](https://docs.sentry.io/platforms/dart/guides/flutter/integrations/app-start-instrumentation.md#1-launch-your-app)

Launch your Sentry configured app.

### [2. Locate Your Transaction:](https://docs.sentry.io/platforms/dart/guides/flutter/integrations/app-start-instrumentation.md#2-locate-your-transaction)

Open the [sentry.io performance page](https://sentry.io/performance), find, and select the 'root /' transaction and navigate to the trace view of a sampled event.

### [3. View App Start Metrics:](https://docs.sentry.io/platforms/dart/guides/flutter/integrations/app-start-instrumentation.md#3-view-app-start-metrics)

Select the event within your transaction. Sentry displays the app start metrics on the right side of the screen in the **Mobile Vitals** section.

## [Standalone App Start Tracing](https://docs.sentry.io/platforms/dart/guides/flutter/integrations/app-start-instrumentation.md#standalone-app-start-tracing)

This feature is experimental and available since [version 9.26.0](https://github.com/getsentry/sentry-dart/blob/main/CHANGELOG.md#9260). The API is subject to change and may introduce breaking changes in future releases.

By default, app start data is attached to the first `ui.load` transaction in your app, which mixes startup timing with screen-display timing. Standalone app start tracing sends the app start as its own `App Start` transaction with the `app.start` operation instead. This gives you more accurate measurements, because they no longer depend on a screen transaction being started, and it lets you sample app starts independently.

To enable it:

```dart
await SentryFlutter.init((options) {
  options.tracesSampleRate = 1.0;
  options.enableStandaloneAppStartTracing = true;
});
```

Standalone app start tracing requires tracing to be enabled and is only supported on Android and iOS. On every other platform the SDK keeps attaching app start data to the first `ui.load` transaction.

Because the app start uses the `app.start` operation, you can use `tracesSampler` to give app starts a dedicated sample rate without raising your overall sample rate. Where you read that operation depends on your trace lifecycle: transaction mode exposes it on the transaction context, while [stream mode](https://docs.sentry.io/platforms/dart/guides/flutter/tracing/streamed-spans.md) carries it as the span's `sentry.op` attribute.

**Transaction Mode (Default)**

```dart
await SentryFlutter.init((options) {
  options.enableStandaloneAppStartTracing = true;
  options.tracesSampler = (samplingContext) {
    if (samplingContext.transactionContext.operation == 'app.start') {
      return 1.0;
    }
    return 0.1;
  };
});
```

**Stream Mode**

```dart
await SentryFlutter.init((options) {
  options.enableStandaloneAppStartTracing = true;
  options.tracesSampler = (samplingContext) {
    final operation = samplingContext.spanContext.attributes['sentry.op']?.value;
    if (operation == 'app.start') {
      return 1.0;
    }
    return 0.1;
  };
});
```

### [Extending the App Start](https://docs.sentry.io/platforms/dart/guides/flutter/integrations/app-start-instrumentation.md#extending-the-app-start)

The app start ends when the first frame renders. If your app does startup work that runs past that point — loading initial data from a server or a database, for example — call `SentryFlutter.extendAppStart()` to include that work in the reported duration, then `SentryFlutter.finishExtendedAppStart()` once it's done.

The only requirement is that `extendAppStart()` runs before the first frame renders, so both the `appRunner` callback of `SentryFlutter.init` and your root widget's `initState` work. Reach for `initState` when the startup work belongs to your widget tree, and `appRunner` when it doesn't. Either way, pair the two calls in a `try`/`finally` so an early return or a thrown exception can't leave the app start open.

**appRunner**

```dart
await SentryFlutter.init(
  (options) {
    options.tracesSampleRate = 1.0;
    options.enableStandaloneAppStartTracing = true;
  },
  appRunner: () async {
    SentryFlutter.extendAppStart();

    try {
      runApp(const MyApp());
      await loadStartupConfiguration();
    } finally {
      await SentryFlutter.finishExtendedAppStart();
    }
  },
);
```

**initState**

```dart
class _MyAppState extends State<MyApp> {
  @override
  void initState() {
    super.initState();
    _loadStartupConfiguration();
  }

  Future<void> _loadStartupConfiguration() async {
    // Extend first, before any await, so the call can't slip past the first frame.
    SentryFlutter.extendAppStart();

    try {
      await loadStartupConfiguration();
    } finally {
      await SentryFlutter.finishExtendedAppStart();
    }
  }

  // ...
}
```

`initState` runs during the first build, which happens before the first frame is rasterized. Call `extendAppStart()` before the first `await` in that method, though — awaiting first gives the frame a chance to render, and the extension is then refused.

This adds an `Extended App Start` span with the `app.start.extended` operation, covering the time between the two calls.

#### [Breaking Down the Extension](https://docs.sentry.io/platforms/dart/guides/flutter/integrations/app-start-instrumentation.md#breaking-down-the-extension)

To see which part of your startup work took the time, retrieve the extension span and attach children to it. Use the getter that matches your trace lifecycle: `getExtendedAppStartSpan()` in transaction mode, `getExtendedAppStartSpanV2()` in stream mode. The other one returns `null`.

**Transaction Mode (Default)**

```dart
SentryFlutter.extendAppStart();

ISentrySpan? child;
try {
  child = SentryFlutter.getExtendedAppStartSpan()?.startChild(
    'http.client',
    description: 'Fetch remote config',
  );
  await fetchRemoteConfig();
} finally {
  await child?.finish();
  await SentryFlutter.finishExtendedAppStart();
}
```

**Stream Mode**

```dart
SentryFlutter.extendAppStart();

try {
  await Sentry.startSpan(
    'Fetch remote config',
    (span) => fetchRemoteConfig(),
    parentSpan: SentryFlutter.getExtendedAppStartSpanV2(),
  );
} finally {
  await SentryFlutter.finishExtendedAppStart();
}
```

In stream mode, `startSpan` ends the child for you once the callback completes, so it only needs the parent. The extension span isn't the active span, which is why you have to pass it as `parentSpan` rather than relying on automatic nesting.

Both getters return `null` when the app start isn't extended. In transaction mode the null-aware calls take care of that. In stream mode, passing `parentSpan: null` means "start a root span", so guard the call if a stray root would be a problem.

Spans you start under the extension keep the app start open until they finish, so finish them too if they shouldn't delay it.

Always finish what you extend. While an extension is open the app start is still in progress, and if it hits its 30-second deadline first, the extension is dropped and the reported duration falls back to the first frame.

Extending requires standalone app start tracing to be enabled. `extendAppStart()` does nothing when standalone app start tracing is off, when the first frame has already rendered, or when the app start is already extended. Each of those cases is logged rather than reported back to the caller.

## [Disable App Start Instrumentation](https://docs.sentry.io/platforms/dart/guides/flutter/integrations/app-start-instrumentation.md#disable-app-start-instrumentation)

App start ships as two integrations: `NativeAppStartIntegration` for the default `ui.load`-attached path, and `StandaloneAppStartIntegration` for standalone app start tracing. The SDK registers both and each one stands down at runtime depending on your configuration, so remove both to turn app start off no matter how it's configured.

```dart
// ignore_for_file: implementation_imports
import 'package:sentry_flutter/src/app_start/standalone/standalone_app_start_integration.dart';
import 'package:sentry_flutter/src/app_start/ui_load_attached/native_app_start_integration.dart';

await SentryFlutter.init((options) {
  for (final integration in options.integrations) {
    if (integration is NativeAppStartIntegration ||
        integration is StandaloneAppStartIntegration) {
      options.removeIntegration(integration);
    }
  }
});
```

App start integrations are only registered on the platforms that support them, so don't assume either one is present — looking them up with `firstWhere` throws when they aren't.
