sentry_flutter 8.14.0 copy "sentry_flutter: ^8.14.0" to clipboard
sentry_flutter: ^8.14.0 copied to clipboard

Sentry SDK for Flutter. This package aims to support different Flutter targets by relying on the many platforms supported by Sentry with native SDKs.

example/lib/main.dart

// ignore_for_file: library_private_types_in_public_api

import 'dart:async';
import 'dart:convert';

import 'package:dio/dio.dart';
import 'package:feedback/feedback.dart' as feedback;
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:logging/logging.dart';
import 'package:path_provider/path_provider.dart';
import 'package:provider/provider.dart';
import 'package:sentry_dio/sentry_dio.dart';
import 'package:sentry_drift/sentry_drift.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:sentry_hive/sentry_hive.dart';
import 'package:sentry_isar/sentry_isar.dart';
import 'package:sentry_logging/sentry_logging.dart';
import 'package:sentry_sqflite/sentry_sqflite.dart';
import 'package:sqflite/sqflite.dart';
// import 'package:sqflite_common_ffi/sqflite_ffi.dart';
// import 'package:sqflite_common_ffi_web/sqflite_ffi_web.dart';
import 'package:universal_platform/universal_platform.dart';

import 'auto_close_screen.dart';
import 'drift/connection/connection.dart';
import 'drift/database.dart';
import 'isar/user.dart';

// ATTENTION: Change the DSN below with your own to see the events in Sentry. Get one at sentry.io
const String exampleDsn =
    'https://e85b375ffb9f43cf8bdf9787768149e0@o447951.ingest.sentry.io/5428562';

/// This is an exampleUrl that will be used to demonstrate how http requests are captured.
const String exampleUrl = 'https://jsonplaceholder.typicode.com/todos/';

const _channel = MethodChannel('example.flutter.sentry.io');
var _isIntegrationTest = false;

final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();

Future<void> main() async {
  await setupSentry(
    () => runApp(
      SentryWidget(
        child: DefaultAssetBundle(
          bundle: SentryAssetBundle(),
          child: const MyApp(),
        ),
      ),
    ),
    exampleDsn,
  );
}

Future<void> setupSentry(
  AppRunner appRunner,
  String dsn, {
  bool isIntegrationTest = false,
  BeforeSendCallback? beforeSendCallback,
}) async {
  await SentryFlutter.init(
    (options) {
      options.dsn = exampleDsn;
      options.tracesSampleRate = 1.0;
      options.profilesSampleRate = 1.0;
      options.reportPackages = false;
      options.addInAppInclude('sentry_flutter_example');
      options.considerInAppFramesByDefault = false;
      options.attachThreads = true;
      options.enableWindowMetricBreadcrumbs = true;
      options.addIntegration(LoggingIntegration(minEventLevel: Level.INFO));
      options.sendDefaultPii = true;
      options.reportSilentFlutterErrors = true;
      options.attachScreenshot = true;
      options.attachViewHierarchy = true;
      // We can enable Sentry debug logging during development. This is likely
      // going to log too much for your app, but can be useful when figuring out
      // configuration issues, e.g. finding out why your events are not uploaded.
      options.debug = kDebugMode;
      options.spotlight = Spotlight(enabled: true);
      options.enableTimeToFullDisplayTracing = true;
      options.enableSentryJs = true;

      options.maxRequestBodySize = MaxRequestBodySize.always;
      options.maxResponseBodySize = MaxResponseBodySize.always;
      options.navigatorKey = navigatorKey;

      options.experimental.replay.sessionSampleRate = 1.0;
      options.experimental.replay.onErrorSampleRate = 1.0;

      // This has a side-effect of creating the default privacy configuration,
      // thus enabling Screenshot masking. No need to actually change it.
      options.experimental.privacy;

      _isIntegrationTest = isIntegrationTest;
      if (_isIntegrationTest) {
        options.dist = '1';
        options.environment = 'integration';
        options.beforeSend = beforeSendCallback;
      }
    },
    // Init your App.
    appRunner: appRunner,
  );
}

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

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

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    Future.delayed(const Duration(seconds: 3), () {
      SentryFlutter.reportFullyDisplayed();
    });
    return feedback.BetterFeedback(
      child: ChangeNotifierProvider<ThemeProvider>(
        create: (_) => ThemeProvider(),
        child: Builder(
          builder: (context) => MaterialApp(
            navigatorKey: navigatorKey,
            navigatorObservers: [
              SentryNavigatorObserver(),
            ],
            theme: Provider.of<ThemeProvider>(context).theme,
            home: const MainScaffold(),
          ),
        ),
      ),
    );
  }
}

class TooltipButton extends StatelessWidget {
  final String text;
  final String buttonTitle;
  final void Function()? onPressed;

  const TooltipButton({
    required this.onPressed,
    required this.buttonTitle,
    required this.text,
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    return Tooltip(
      message: text,
      child: ElevatedButton(
        onPressed: onPressed,
        key: key,
        child: Text(buttonTitle),
      ),
    );
  }
}

class MainScaffold extends StatelessWidget {
  const MainScaffold({
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    final themeProvider = Provider.of<ThemeProvider>(context);
    var icon = Icons.light_mode;
    var theme = ThemeData.light();
    if (themeProvider.theme.brightness == Brightness.light) {
      icon = Icons.dark_mode;
      theme = ThemeData.dark();
    }
    return Scaffold(
      appBar: AppBar(
        title: const Text('Sentry Flutter Example'),
        actions: [
          IconButton(
            onPressed: () {
              themeProvider.theme = theme;
            },
            icon: Icon(icon),
          ),
          IconButton(
            onPressed: () {
              themeProvider.updatePrimaryColor(Colors.orange);
            },
            icon: const Icon(Icons.circle, color: Colors.orange),
          ),
          IconButton(
            onPressed: () {
              themeProvider.updatePrimaryColor(Colors.green);
            },
            icon: const Icon(Icons.circle, color: Colors.lime),
          ),
        ],
      ),
      body: SingleChildScrollView(
        child: Column(
          children: [
            if (_isIntegrationTest) const IntegrationTestWidget(),
            const Center(child: Text('Trigger an action.\n')),
            const Padding(
              padding: EdgeInsets.all(15), //apply padding to all four sides
              child: Center(
                child: Text(
                    'Long press a button to see more information. (hover on web)'),
              ),
            ),
            TooltipButton(
              onPressed: () => navigateToAutoCloseScreen(context),
              text:
                  'Pushes a screen and creates a transaction named \'AutoCloseScreen\' with a child span that finishes after 3 seconds. \nAfter the screen has popped the transaction can then be seen on the performance page.',
              buttonTitle: 'Route Navigation Observer',
            ),
            if (!UniversalPlatform.isWeb)
              TooltipButton(
                onPressed: driftTest,
                text:
                    'Executes CRUD operations on an in-memory with Drift and sends the created transaction to Sentry.',
                buttonTitle: 'drift',
              ),
            if (!UniversalPlatform.isWeb)
              TooltipButton(
                onPressed: hiveTest,
                text:
                    'Executes CRUD operations on an in-memory with Hive and sends the created transaction to Sentry.',
                buttonTitle: 'hive',
              ),
            if (!UniversalPlatform.isWeb)
              TooltipButton(
                onPressed: isarTest,
                text:
                    'Executes CRUD operations on an in-memory with Isar and sends the created transaction to Sentry.',
                buttonTitle: 'isar',
              ),
            TooltipButton(
              onPressed: sqfliteTest,
              text:
                  'Executes CRUD operations on an in-memory with Hive and sends the created transaction to Sentry.',
              buttonTitle: 'sqflite',
            ),
            TooltipButton(
              onPressed: () => SecondaryScaffold.openSecondaryScaffold(context),
              text:
                  'Demonstrates how the router integration adds a navigation event to the breadcrumbs that can be seen when throwing an exception for example.',
              buttonTitle: 'Open another Scaffold',
            ),
            const TooltipButton(
              onPressed: tryCatch,
              key: Key('dart_try_catch'),
              text: 'Creates a caught exception and sends it to Sentry.',
              buttonTitle: 'Dart: try catch',
            ),
            TooltipButton(
              onPressed: () => Scaffold.of(context)
                  .showBottomSheet((context) => const Text('Scaffold error')),
              text:
                  'Creates an uncaught exception and sends it to Sentry. This demonstrates how our flutter error integration catches unhandled exceptions.',
              buttonTitle: 'Flutter error : Scaffold.of()',
            ),
            TooltipButton(
              // Warning : not captured if a debugger is attached
              // https://github.com/flutter/flutter/issues/48972
              onPressed: () => throw Exception('Throws onPressed'),
              text:
                  'Creates an uncaught exception and sends it to Sentry. This demonstrates how our flutter error integration catches unhandled exceptions.',
              buttonTitle: 'Dart: throw onPressed',
            ),
            TooltipButton(
              // Warning : not captured if a debugger is attached
              // https://github.com/flutter/flutter/issues/48972
              onPressed: () {
                assert(false, 'assert failure');
              },
              text:
                  'Creates an uncaught exception and sends it to Sentry. This demonstrates how our flutter error integration catches unhandled exceptions.',
              buttonTitle: 'Dart: assert',
            ),
            // Calling the SDK with an appRunner will handle errors from Futures
            // in SDKs runZonedGuarded onError handler
            TooltipButton(
              onPressed: () async => asyncThrows(),
              text:
                  'Creates an async uncaught exception and sends it to Sentry. This demonstrates how our flutter error integration catches unhandled exceptions.',
              buttonTitle: 'Dart: async throws',
            ),
            TooltipButton(
              onPressed: () async => {
                await Future.microtask(
                  () => throw StateError('Failure in a microtask'),
                )
              },
              text:
                  'Creates an uncaught exception in a microtask and sends it to Sentry. This demonstrates how our flutter error integration catches unhandled exceptions.',
              buttonTitle: 'Dart: Fail in microtask',
            ),
            TooltipButton(
              onPressed: () async => {
                await compute(loop, 10),
              },
              text:
                  'Creates an uncaught exception in a compute isolate and sends it to Sentry. This demonstrates how our flutter error integration catches unhandled exceptions.',
              buttonTitle: 'Dart: Fail in compute',
            ),
            TooltipButton(
              onPressed: () async => {
                await Future.delayed(
                  const Duration(milliseconds: 100),
                  () => throw StateError('Failure in a Future.delayed'),
                ),
              },
              text:
                  'Creates an uncaught exception in a Future.delayed and sends it to Sentry. This demonstrates how our flutter error integration catches unhandled exceptions.',
              buttonTitle: 'Throws in Future.delayed',
            ),
            TooltipButton(
              onPressed: () {
                // modeled after a real exception
                FlutterError.onError?.call(
                  FlutterErrorDetails(
                    exception: Exception('A really bad exception'),
                    silent: false,
                    context:
                        DiagnosticsNode.message('while handling a gesture'),
                    library: 'gesture',
                    informationCollector: () => [
                      DiagnosticsNode.message(
                          'Handler: "onTap" Recognizer: TapGestureRecognizer'),
                      DiagnosticsNode.message(
                          'Handler: "onTap" Recognizer: TapGestureRecognizer'),
                      DiagnosticsNode.message(
                          'Handler: "onTap" Recognizer: TapGestureRecognizer'),
                    ],
                  ),
                );
              },
              text:
                  'Creates a FlutterError and passes it to FlutterError.onError callback. This demonstrates how our flutter error integration catches unhandled exceptions.',
              buttonTitle: 'Capture from FlutterError.onError',
            ),
            TooltipButton(
              onPressed: () {
                // Only usable on Flutter >= 3.3
                // and needs the following additional setup:
                // options.addIntegration(OnErrorIntegration());
                (WidgetsBinding.instance.platformDispatcher as dynamic)
                    .onError
                    ?.call(
                      Exception('PlatformDispatcher.onError'),
                      StackTrace.current,
                    );
              },
              text:
                  'This is only usable on Flutter >= 3.3 and requires additional setup: options.addIntegration(OnErrorIntegration());',
              buttonTitle: 'Capture from PlatformDispatcher.onError',
            ),
            TooltipButton(
              onPressed: () => makeWebRequest(context),
              text:
                  'Attaches web request related spans to the transaction and send it to Sentry.',
              buttonTitle: 'Dart: Web request',
            ),
            TooltipButton(
              onPressed: () => makeWebRequestWithDio(context),
              key: const Key('dio_web_request'),
              text:
                  'Attaches web request related spans to the transaction and send it to Sentry.',
              buttonTitle: 'Dio: Web request',
            ),

            TooltipButton(
              onPressed: () => showDialogWithTextAndImage(context),
              text:
                  'Attaches asset bundle related spans to the transaction and send it to Sentry.',
              buttonTitle: 'Flutter: Load assets',
            ),
            TooltipButton(
              onPressed: () {
                // ignore: avoid_print
                print('A print breadcrumb');
                Sentry.captureMessage('A message with a print() Breadcrumb');
              },
              text:
                  'Sends a captureMessage to Sentry with a breadcrumb created by a print() statement.',
              buttonTitle: 'Record print() as breadcrumb',
            ),
            TooltipButton(
              onPressed: () {
                Sentry.captureMessage(
                  'This event has an extra tag',
                  withScope: (scope) {
                    scope.setTag('foo', 'bar');
                  },
                );
              },
              text:
                  'Sends the capture message event with additional Tag to Sentry.',
              buttonTitle: 'Capture message with scope with additional tag',
            ),
            TooltipButton(
              onPressed: () async {
                final transaction = Sentry.getSpan() ??
                    Sentry.startTransaction(
                      'myNewTrWithError3',
                      'myNewOp',
                      description: 'myTr myOp',
                    );
                transaction.setTag('myTag', 'myValue');
                transaction.setData('myExtra', 'myExtraValue');

                await Future.delayed(const Duration(milliseconds: 50));

                final span = transaction.startChild(
                  'childOfMyOp',
                  description: 'childOfMyOp span',
                );
                span.setTag('myNewTag', 'myNewValue');
                span.setData('myNewData', 'myNewDataValue');

                await Future.delayed(const Duration(milliseconds: 70));

                await span.finish(status: const SpanStatus.resourceExhausted());

                await Future.delayed(const Duration(milliseconds: 90));

                final spanChild = span.startChild(
                  'childOfChildOfMyOp',
                  description: 'childOfChildOfMyOp span',
                );

                await Future.delayed(const Duration(milliseconds: 110));

                spanChild.startChild(
                  'unfinishedChild',
                  description: 'I wont finish',
                );

                await spanChild.finish(
                    status: const SpanStatus.internalError());

                await Future.delayed(const Duration(milliseconds: 50));
                // findPrimeNumber(1000000); // Uncomment to see it with profiling
                await transaction.finish(status: const SpanStatus.ok());
              },
              text:
                  'Creates a custom transaction, adds child spans and send them to Sentry.',
              buttonTitle: 'Capture transaction',
            ),
            TooltipButton(
              onPressed: () {
                Sentry.captureMessage(
                  'This message has an attachment',
                  withScope: (scope) {
                    const txt = 'Lorem Ipsum dolor sit amet';
                    scope.addAttachment(
                      SentryAttachment.fromIntList(
                        utf8.encode(txt),
                        'foobar.txt',
                        contentType: 'text/plain',
                      ),
                    );
                  },
                );
              },
              text: 'Sends the capture message with an attachment to Sentry.',
              buttonTitle: 'Capture message with attachment',
            ),
            TooltipButton(
              onPressed: () {
                feedback.BetterFeedback.of(context).show(
                  (feedback.UserFeedback feedback) {
                    Sentry.captureMessage(
                      feedback.text,
                      withScope: (scope) {
                        final entries = feedback.extra?.entries;
                        if (entries != null) {
                          for (final extra in entries) {
                            // ignore: deprecated_member_use
                            scope.setExtra(extra.key, extra.value);
                          }
                        }
                        scope.addAttachment(
                          SentryAttachment.fromUint8List(
                            feedback.screenshot,
                            'feedback.png',
                            contentType: 'image/png',
                          ),
                        );
                      },
                    );
                  },
                );
              },
              text:
                  'Sends the capture message with an image attachment to Sentry.',
              buttonTitle: 'Capture message with image attachment',
            ),
            TooltipButton(
              onPressed: () async {
                final id = await Sentry.captureMessage('UserFeedback');
                final screenshot = await SentryFlutter.captureScreenshot();

                if (!context.mounted) return;
                Navigator.push(
                  context,
                  MaterialPageRoute(
                    builder: (context) => SentryFeedbackWidget(
                        associatedEventId: id, screenshot: screenshot),
                    fullscreenDialog: true,
                  ),
                );
              },
              text:
                  'Shows a custom feedback dialog without an ongoing event that captures and sends user feedback data to Sentry.',
              buttonTitle: 'Capture Feedback',
            ),
            TooltipButton(
              onPressed: () {
                final log = Logger('Logging');
                log.info('My Logging test');
              },
              text:
                  'Demonstrates the logging integration. log.info() will create an info event send it to Sentry.',
              buttonTitle: 'Logging',
            ),
            if (UniversalPlatform.isIOS || UniversalPlatform.isMacOS)
              const CocoaExample(),
            if (UniversalPlatform.isAndroid) const AndroidExample(),
            // ignore: invalid_use_of_internal_member
            if (SentryFlutter.native != null)
              ElevatedButton(
                onPressed: () async {
                  SentryFlutter.nativeCrash();
                },
                child: const Text('Sentry.nativeCrash'),
              ),
          ].map((widget) {
            if (kIsWeb) {
              // Add vertical padding to web so the tooltip doesn't obstruct the clicking of the button below.
              return Padding(
                padding: const EdgeInsets.only(top: 18.0, bottom: 18.0),
                child: widget,
              );
            }
            return widget;
          }).toList(),
        ),
      ),
    );
  }

  Future<void> isarTest() async {
    final tr = Sentry.startTransaction(
      'isarTest',
      'db',
      bindToScope: true,
    );

    final dir = await getApplicationDocumentsDirectory();

    final isar = await SentryIsar.open(
      [UserSchema],
      directory: dir.path,
    );

    final newUser = User()
      ..name = 'Joe Dirt'
      ..age = 36;

    await isar.writeTxn(() async {
      await isar.users.put(newUser); // insert & update
    });

    final existingUser = await isar.users.get(newUser.id); // get

    await isar.writeTxn(() async {
      await isar.users.delete(existingUser!.id); // delete
    });

    await tr.finish(status: const SpanStatus.ok());
  }

  Future<void> hiveTest() async {
    final tr = Sentry.startTransaction(
      'hiveTest',
      'db',
      bindToScope: true,
    );

    final appDir = await getApplicationDocumentsDirectory();
    SentryHive.init(appDir.path);

    final catsBox = await SentryHive.openBox<Map>('cats');
    await catsBox.put('fluffy', {'name': 'Fluffy', 'age': 4});
    await catsBox.put('loki', {'name': 'Loki', 'age': 2});
    await catsBox.clear();
    await catsBox.close();

    SentryHive.close();

    await tr.finish(status: const SpanStatus.ok());
  }

  Future<void> sqfliteTest() async {
    final tr = Sentry.startTransaction(
      'sqfliteTest',
      'db',
      bindToScope: true,
    );

    // databaseFactory = databaseFactoryFfiWeb; // or databaseFactoryFfi // or SentrySqfliteDatabaseFactory()

    // final sqfDb = await openDatabase(inMemoryDatabasePath);
    final db = await openDatabaseWithSentry(inMemoryDatabasePath);
    // final db = SentryDatabase(sqfDb);
    // final batch = db.batch();
    await db.execute('''
      CREATE TABLE Product (
        id INTEGER PRIMARY KEY,
        title TEXT
      )
  ''');
    final dbTitles = <String>[];
    for (int i = 1; i <= 20; i++) {
      final title = 'Product $i';
      dbTitles.add(title);
      await db.insert('Product', <String, Object?>{'title': title});
    }

    await db.query('Product');

    await db.transaction((txn) async {
      await txn
          .insert('Product', <String, Object?>{'title': 'Product Another one'});
      await txn.delete('Product',
          where: 'title = ?', whereArgs: ['Product Another one']);
    });

    await db.delete('Product', where: 'title = ?', whereArgs: ['Product 1']);

    // final batch = db.batch();
    // batch.delete('Product', where: 'title = ?', whereArgs: dbTitles);
    // await batch.commit();

    await db.close();

    await tr.finish(status: const SpanStatus.ok());
  }

  Future<void> driftTest() async {
    final tr = Sentry.startTransaction(
      'driftTest',
      'db',
      bindToScope: true,
    );

    // ignore: deprecated_member_use
    final executor = SentryQueryExecutor(
      () async => inMemoryExecutor(),
      databaseName: 'sentry_in_memory_db',
    );

    final db = AppDatabase(executor);

    await db.into(db.todoItems).insert(
          TodoItemsCompanion.insert(
            title: 'This is a test thing',
            content: 'test',
          ),
        );

    await db.select(db.todoItems).get();

    await db.close();

    await tr.finish(status: const SpanStatus.ok());
  }
}

extension BuildContextExtension on BuildContext {
  bool get isMounted {
    try {
      return (this as dynamic).mounted;
    } on NoSuchMethodError catch (_) {
      // ignore, only available in newer Flutter versions
    }
    return true;
  }
}

class AndroidExample extends StatelessWidget {
  const AndroidExample({super.key});

  @override
  Widget build(BuildContext context) {
    return Column(children: [
      ElevatedButton(
        onPressed: () async {
          await execute('throw');
        },
        child: const Text('Kotlin Throw unhandled exception'),
      ),
      ElevatedButton(
        onPressed: () async {
          await execute('capture');
        },
        child: const Text('Kotlin Capture Exception'),
      ),
      ElevatedButton(
        // ANR is disabled by default, enable it to test it
        onPressed: () async {
          await execute('anr');
        },
        child: const Text('ANR: UI blocked 6 seconds'),
      ),
      ElevatedButton(
        onPressed: () async {
          await execute('cpp_capture_message');
        },
        child: const Text('C++ Capture message'),
      ),
      ElevatedButton(
        onPressed: () async {
          await execute('crash');
        },
        child: const Text('C++ SEGFAULT'),
      ),
      ElevatedButton(
        onPressed: () async {
          await execute('platform_exception');
        },
        child: const Text('Platform exception'),
      ),
    ]);
  }
}

void navigateToAutoCloseScreen(BuildContext context) {
  Navigator.push(
    context,
    MaterialPageRoute(
      settings: const RouteSettings(name: 'AutoCloseScreen'),
      // ignore: deprecated_member_use
      builder: (context) => SentryDisplayWidget(child: const AutoCloseScreen()),
    ),
  );
}

Future<void> tryCatch() async {
  try {
    throw StateError('try catch');
  } catch (error, stackTrace) {
    await Sentry.captureException(error, stackTrace: stackTrace);
  }
}

Future<void> asyncThrows() async {
  throw StateError('async throws');
}

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

  @override
  State<StatefulWidget> createState() {
    return _IntegrationTestWidgetState();
  }
}

class _IntegrationTestWidgetState extends State<IntegrationTestWidget> {
  _IntegrationTestWidgetState();

  var _output = "--";
  var _isLoading = false;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text(
          _output,
          key: const Key('output'),
        ),
        _isLoading
            ? const CircularProgressIndicator()
            : ElevatedButton(
                onPressed: () async => await _captureException(),
                child: const Text('captureException'),
              )
      ],
    );
  }

  Future<void> _captureException() async {
    setState(() {
      _isLoading = true;
    });
    try {
      throw Exception('captureException');
    } catch (error, stackTrace) {
      final id = await Sentry.captureException(error, stackTrace: stackTrace);
      setState(() {
        _output = id.toString();
        _isLoading = false;
      });
    }
  }
}

class CocoaExample extends StatelessWidget {
  const CocoaExample({super.key});

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        ElevatedButton(
          onPressed: () async {
            await execute('fatalError');
          },
          child: const Text('Swift fatalError'),
        ),
        ElevatedButton(
          onPressed: () async {
            await execute('capture');
          },
          child: const Text('Swift Capture NSException'),
        ),
        ElevatedButton(
          onPressed: () async {
            await execute('capture_message');
          },
          child: const Text('Swift Capture message'),
        ),
        ElevatedButton(
          onPressed: () async {
            await execute('throw');
          },
          child: const Text('Objective-C Throw unhandled exception'),
        ),
        ElevatedButton(
          onPressed: () async {
            await execute('crash');
          },
          child: const Text('Objective-C SEGFAULT'),
        ),
      ],
    );
  }
}

/// compute can only take a top-level function, but not instance or static methods.
// Top-level functions are functions declared not inside a class and not inside another function
int loop(int val) {
  var count = 0;
  for (var i = 1; i <= val; i++) {
    count += i;
  }

  throw StateError('from a compute isolate $count');
}

class SecondaryScaffold extends StatelessWidget {
  const SecondaryScaffold({super.key});

  static Future<void> openSecondaryScaffold(BuildContext context) {
    return Navigator.push(
      context,
      MaterialPageRoute<void>(
        settings:
            const RouteSettings(name: 'SecondaryScaffold', arguments: 'foobar'),
        builder: (context) {
          return const SecondaryScaffold();
        },
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('SecondaryScaffold'),
      ),
      body: Center(
        child: Column(
          children: [
            const Text(
              'You have added a navigation event '
              'to the crash reports breadcrumbs.',
            ),
            MaterialButton(
              onPressed: () {
                Navigator.pop(context);
              },
              child: const Text('Go back'),
            ),
            MaterialButton(
              onPressed: () {
                throw Exception('Exception from SecondaryScaffold');
              },
              child: const Text('throw uncaught exception'),
            ),
          ],
        ),
      ),
    );
  }
}

Future<void> makeWebRequest(BuildContext context) async {
  final transaction = Sentry.getSpan() ??
      Sentry.startTransaction(
        'flutterwebrequest',
        'request',
        bindToScope: true,
      );

  final client = SentryHttpClient(
    failedRequestStatusCodes: [SentryStatusCode.range(400, 500)],
  );
  // We don't do any exception handling here.
  // In case of an exception, let it get caught and reported to Sentry
  final response = await client.get(Uri.parse(exampleUrl));

  await transaction.finish(status: const SpanStatus.ok());

  if (!context.mounted) return;
  await showDialog<void>(
    context: context,
    builder: (context) {
      return AlertDialog(
        title: Text('Response ${response.statusCode}'),
        content: SingleChildScrollView(
          child: Text(response.body),
        ),
        actions: [
          MaterialButton(
            onPressed: () => Navigator.pop(context),
            child: const Text('Close'),
          )
        ],
      );
    },
  );
}

Future<void> makeWebRequestWithDio(BuildContext context) async {
  final dio = Dio();
  dio.addSentry();

  final transaction = Sentry.getSpan() ??
      Sentry.startTransaction(
        'dio-web-request',
        'request',
        bindToScope: true,
      );
  final span = transaction.startChild(
    'dio',
    description: 'desc',
  );
  Response<String>? response;
  try {
    response = await dio.get<String>(exampleUrl);
    span.status = const SpanStatus.ok();
  } catch (exception, stackTrace) {
    span.throwable = exception;
    span.status = const SpanStatus.internalError();
    await Sentry.captureException(exception, stackTrace: stackTrace);
  } finally {
    await span.finish();
  }

  if (!context.mounted) return;
  await showDialog<void>(
    context: context,
    builder: (context) {
      return AlertDialog(
        title: Text('Response ${response?.statusCode}'),
        content: SingleChildScrollView(
          child: Text(response?.data ?? 'failed request'),
        ),
        actions: [
          MaterialButton(
            onPressed: () => Navigator.pop(context),
            child: const Text('Close'),
          )
        ],
      );
    },
  );
}

Future<void> showDialogWithTextAndImage(BuildContext context) async {
  final transaction = Sentry.getSpan() ??
      Sentry.startTransaction(
        'asset-bundle-transaction',
        'load',
        bindToScope: true,
      );
  final text =
      await DefaultAssetBundle.of(context).loadString('assets/lorem-ipsum.txt');

  if (!context.mounted) return;
  final imageBytes =
      await DefaultAssetBundle.of(context).load('assets/sentry-wordmark.png');
  await showDialog<void>(
    // ignore: use_build_context_synchronously
    context: context,
    // gets tracked if using SentryNavigatorObserver
    routeSettings: const RouteSettings(
      name: 'AssetBundle dialog',
    ),
    builder: (context) {
      return AlertDialog(
        title: const Text('Asset Example'),
        content: SingleChildScrollView(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              // Use various ways an image is included in the app.
              // Local asset images are not obscured in replay recording.
              Image.asset('assets/sentry-wordmark.png'),
              Image.asset('assets/sentry-wordmark.png', bundle: rootBundle),
              Image.asset('assets/sentry-wordmark.png',
                  bundle: DefaultAssetBundle.of(context)),
              Image.network(
                  'https://www.gstatic.com/recaptcha/api2/logo_48.png'),
              Image.memory(imageBytes.buffer.asUint8List()),
              Text(text),
            ],
          ),
        ),
        actions: [
          MaterialButton(
            onPressed: () => Navigator.pop(context),
            child: const Text('Close'),
          )
        ],
      );
    },
  );
  await transaction.finish(status: const SpanStatus.ok());
}

class ThemeProvider extends ChangeNotifier {
  ThemeData _theme = ThemeData.light();

  ThemeData get theme => _theme;

  set theme(ThemeData theme) {
    _theme = theme;
    notifyListeners();
  }

  void updatePrimaryColor(MaterialColor color) {
    if (theme.brightness == Brightness.light) {
      theme = ThemeData(primarySwatch: color, brightness: theme.brightness);
    } else {
      theme = ThemeData(primarySwatch: color, brightness: theme.brightness);
    }
  }
}

Future<void> execute(String method) async {
  await _channel.invokeMethod(method);
}

// Don't inline this one or it shows up as an anonymous closure in profiles.
@pragma("vm:never-inline")
int findPrimeNumber(int n) {
  int count = 0;
  int a = 2;
  while (count < n) {
    int b = 2;
    bool prime = true; // to check if found a prime
    while (b * b <= a) {
      if (a % b == 0) {
        prime = false;
        break;
      }
      b++;
    }
    if (prime) {
      count++;
    }
    a++;
  }
  return a - 1;
}
copied to clipboard
978
likes
0
points
656k
downloads

Publisher

verified publishersentry.io

Weekly Downloads

2024.05.31 - 2025.04.25

Sentry SDK for Flutter. This package aims to support different Flutter targets by relying on the many platforms supported by Sentry with native SDKs.

Homepage
Repository (GitHub)
View/report issues

Documentation

Documentation

License

unknown (license)

Dependencies

collection, ffi, file, flutter, flutter_web_plugins, meta, package_info_plus, sentry

More

Packages that depend on sentry_flutter