From ee4c549959f763957cc42955043a6911b22ad05d Mon Sep 17 00:00:00 2001 From: GIancarlo Buenaflor Date: Thu, 30 Nov 2023 14:56:32 +0100 Subject: [PATCH 1/6] Change http url and add route navigation observer example --- flutter/example/lib/delayed_screen.dart | 41 +++++++++++++++++++ flutter/example/lib/main.dart | 54 ++++++++++++++++++++----- 2 files changed, 84 insertions(+), 11 deletions(-) create mode 100644 flutter/example/lib/delayed_screen.dart diff --git a/flutter/example/lib/delayed_screen.dart b/flutter/example/lib/delayed_screen.dart new file mode 100644 index 0000000000..17ca3c5c7d --- /dev/null +++ b/flutter/example/lib/delayed_screen.dart @@ -0,0 +1,41 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:sentry/sentry.dart'; + +class DelayedScreen extends StatefulWidget { + @override + _DelayedScreenState createState() => _DelayedScreenState(); +} + +class _DelayedScreenState extends State { + static const delayInSeconds = 3; + + @override + void initState() { + super.initState(); + _doComplexOperationThenClose(); + } + + Future _doComplexOperationThenClose() async { + final activeSpan = Sentry.getSpan(); + final childSpan = activeSpan?.startChild('complex operation', description: 'running a $delayInSeconds seconds operation'); + await Future.delayed(const Duration(seconds: delayInSeconds)); + childSpan?.finish(); + Navigator.of(context).pop(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Delayed Screen'), + ), + body: const Center( + child: Text( + 'This screen will automatically close in $delayInSeconds seconds...', + textAlign: TextAlign.center, + ), + ), + ); + } +} diff --git a/flutter/example/lib/main.dart b/flutter/example/lib/main.dart index 8ff567b8cb..350e566cb9 100644 --- a/flutter/example/lib/main.dart +++ b/flutter/example/lib/main.dart @@ -18,6 +18,7 @@ import 'package:sqflite/sqflite.dart'; import 'package:universal_platform/universal_platform.dart'; import 'package:feedback/feedback.dart' as feedback; import 'package:provider/provider.dart'; +import 'delayed_screen.dart'; import 'drift/database.dart'; import 'drift/connection/connection.dart'; import 'user_feedback_dialog.dart'; @@ -30,9 +31,14 @@ import 'package:sentry_hive/sentry_hive.dart'; 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 navigatorKey = GlobalKey(); + Future main() async { await setupSentry( () => runApp( @@ -100,6 +106,7 @@ class _MyAppState extends State { create: (_) => ThemeProvider(), child: Builder( builder: (context) => MaterialApp( + navigatorKey: navigatorKey, navigatorObservers: [ SentryNavigatorObserver(), ], @@ -112,6 +119,23 @@ class _MyAppState extends State { } } +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, Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Tooltip( + message: text, + child: ElevatedButton(onPressed: onPressed, child: Text(buttonTitle)) + ); + } +} + + class MainScaffold extends StatelessWidget { const MainScaffold({ Key? key, @@ -156,6 +180,11 @@ class MainScaffold extends StatelessWidget { if (_isIntegrationTest) const IntegrationTestWidget(), const Center(child: Text('Trigger an action:\n')), // For simplicity sake we skip the web set up for now. + TooltipButton( + onPressed: () => navigateToDelayedScreen(context), + text: 'Pushes a screen and creates a transaction named \'DelayedScreen\' 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) ElevatedButton( onPressed: () => driftTest(), @@ -596,6 +625,16 @@ class AndroidExample extends StatelessWidget { } } +void navigateToDelayedScreen(BuildContext context) { + Navigator.push( + context, + MaterialPageRoute( + settings: const RouteSettings(name: 'DelayedScreen'), + builder: (context) => DelayedScreen(), + ), + ); +} + Future tryCatch() async { try { throw StateError('try catch'); @@ -756,7 +795,8 @@ class SecondaryScaffold extends StatelessWidget { } } -Future makeWebRequest(BuildContext context) async { +void makeWebRequest(BuildContext context) async { + print('span: ${Sentry.getSpan()}'); final transaction = Sentry.getSpan() ?? Sentry.startTransaction( 'flutterwebrequest', @@ -769,7 +809,7 @@ Future makeWebRequest(BuildContext context) async { ); // 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('https://flutter.dev/')); + final response = await client.get(Uri.parse(exampleUrl)); await transaction.finish(status: const SpanStatus.ok()); @@ -781,10 +821,6 @@ Future makeWebRequest(BuildContext context) async { // ignore: use_build_context_synchronously await showDialog( context: context, - // gets tracked if using SentryNavigatorObserver - routeSettings: const RouteSettings( - name: 'flutter.dev dialog', - ), builder: (context) { return AlertDialog( title: Text('Response ${response.statusCode}'), @@ -818,7 +854,7 @@ Future makeWebRequestWithDio(BuildContext context) async { ); Response? response; try { - response = await dio.get('https://flutter.dev/'); + response = await dio.get(exampleUrl); span.status = const SpanStatus.ok(); } catch (exception, stackTrace) { span.throwable = exception; @@ -836,10 +872,6 @@ Future makeWebRequestWithDio(BuildContext context) async { // ignore: use_build_context_synchronously await showDialog( context: context, - // gets tracked if using SentryNavigatorObserver - routeSettings: const RouteSettings( - name: 'flutter.dev dialog', - ), builder: (context) { return AlertDialog( title: Text('Response ${response?.statusCode}'), From dcfc81d9089b6bfb161a53e4fb9cc646089597d5 Mon Sep 17 00:00:00 2001 From: GIancarlo Buenaflor Date: Thu, 30 Nov 2023 14:58:24 +0100 Subject: [PATCH 2/6] Remove comment --- flutter/example/lib/main.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/flutter/example/lib/main.dart b/flutter/example/lib/main.dart index 350e566cb9..859f7e5df7 100644 --- a/flutter/example/lib/main.dart +++ b/flutter/example/lib/main.dart @@ -179,7 +179,6 @@ class MainScaffold extends StatelessWidget { children: [ if (_isIntegrationTest) const IntegrationTestWidget(), const Center(child: Text('Trigger an action:\n')), - // For simplicity sake we skip the web set up for now. TooltipButton( onPressed: () => navigateToDelayedScreen(context), text: 'Pushes a screen and creates a transaction named \'DelayedScreen\' with a child span that finishes after 3 seconds. \nAfter the screen has popped the transaction can then be seen on the performance page.', From f6b0c1fa119ec7697da1e7e7428226bbd0354e8a Mon Sep 17 00:00:00 2001 From: GIancarlo Buenaflor Date: Thu, 30 Nov 2023 16:34:15 +0100 Subject: [PATCH 3/6] Improve sample --- ...yed_screen.dart => auto_close_screen.dart} | 11 +- flutter/example/lib/main.dart | 228 +++++++++++------- 2 files changed, 144 insertions(+), 95 deletions(-) rename flutter/example/lib/{delayed_screen.dart => auto_close_screen.dart} (69%) diff --git a/flutter/example/lib/delayed_screen.dart b/flutter/example/lib/auto_close_screen.dart similarity index 69% rename from flutter/example/lib/delayed_screen.dart rename to flutter/example/lib/auto_close_screen.dart index 17ca3c5c7d..9784d44ae7 100644 --- a/flutter/example/lib/delayed_screen.dart +++ b/flutter/example/lib/auto_close_screen.dart @@ -2,12 +2,17 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:sentry/sentry.dart'; -class DelayedScreen extends StatefulWidget { +/// This screen is only used to demonstrate how route navigation works. +/// Init will create a child span and pop the screen after 3 seconds. +/// Afterwards the transaction should be seen on the performance page. +class AutoCloseScreen extends StatefulWidget { + const AutoCloseScreen({super.key}); + @override - _DelayedScreenState createState() => _DelayedScreenState(); + _AutoCloseScreenState createState() => _AutoCloseScreenState(); } -class _DelayedScreenState extends State { +class _AutoCloseScreenState extends State { static const delayInSeconds = 3; @override diff --git a/flutter/example/lib/main.dart b/flutter/example/lib/main.dart index 859f7e5df7..11a41891ff 100644 --- a/flutter/example/lib/main.dart +++ b/flutter/example/lib/main.dart @@ -18,7 +18,7 @@ import 'package:sqflite/sqflite.dart'; import 'package:universal_platform/universal_platform.dart'; import 'package:feedback/feedback.dart' as feedback; import 'package:provider/provider.dart'; -import 'delayed_screen.dart'; +import 'auto_close_screen.dart'; import 'drift/database.dart'; import 'drift/connection/connection.dart'; import 'user_feedback_dialog.dart'; @@ -124,18 +124,25 @@ class TooltipButton extends StatelessWidget { final String buttonTitle; final void Function()? onPressed; - const TooltipButton({required this.onPressed, required this.buttonTitle, required this.text, Key? key}) : super(key: key); + const TooltipButton( + {required this.onPressed, + required this.buttonTitle, + required this.text, + Key? key}) + : super(key: key); @override Widget build(BuildContext context) { return Tooltip( - message: text, - child: ElevatedButton(onPressed: onPressed, child: Text(buttonTitle)) - ); + message: text, + child: ElevatedButton( + onPressed: onPressed, + key: key, + child: Text(buttonTitle), + )); } } - class MainScaffold extends StatelessWidget { const MainScaffold({ Key? key, @@ -178,81 +185,112 @@ class MainScaffold extends StatelessWidget { child: Column( children: [ if (_isIntegrationTest) const IntegrationTestWidget(), - const Center(child: Text('Trigger an action:\n')), + const Center(child: Text('Trigger an action.\n')), + const Center( + child: Text( + 'Hover over a button to see more information. (long press on mobile) \n')), TooltipButton( - onPressed: () => navigateToDelayedScreen(context), - text: 'Pushes a screen and creates a transaction named \'DelayedScreen\' with a child span that finishes after 3 seconds. \nAfter the screen has popped the transaction can then be seen on the performance page.', + 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) - ElevatedButton( - onPressed: () => driftTest(), - child: const Text('drift'), + TooltipButton( + onPressed: driftTest, + text: + 'Executes CRUD operations on an in-memory with Drift and sends the created transaction to Sentry.', + buttonTitle: 'drift', ), - ElevatedButton( - onPressed: () => hiveTest(), - child: const Text('hive'), - ), - ElevatedButton( - onPressed: () => sqfliteTest(), - child: const Text('sqflite'), + 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', + ), + TooltipButton( + onPressed: sqfliteTest, + text: + 'Executes CRUD operations on an in-memory with Hive and sends the created transaction to Sentry.', + buttonTitle: 'sqflite', ), - ElevatedButton( + TooltipButton( onPressed: () => SecondaryScaffold.openSecondaryScaffold(context), - child: const Text('Open another Scaffold'), + 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', ), - ElevatedButton( - onPressed: () => tryCatch(), - key: const Key('dart_try_catch'), - child: const Text('Dart: try catch'), + const TooltipButton( + onPressed: tryCatch, + key: Key('dart_try_catch'), + text: 'Creates a caught exception and sends it to Sentry.', + buttonTitle: 'Dart: try catch', ), - ElevatedButton( + TooltipButton( onPressed: () => Scaffold.of(context).showBottomSheet( (context) => const Text('Scaffold error'), ), - child: const Text('Flutter error : Scaffold.of()'), + 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()', ), - ElevatedButton( + TooltipButton( // Warning : not captured if a debugger is attached // https://github.com/flutter/flutter/issues/48972 onPressed: () => throw Exception('Throws onPressed'), - child: const Text('Dart: throw 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', ), - ElevatedButton( + TooltipButton( + // Warning : not captured if a debugger is attached + // https://github.com/flutter/flutter/issues/48972 onPressed: () { - // Only relevant in debug builds - // Warning : not captured if a debugger is attached - // https://github.com/flutter/flutter/issues/48972 assert(false, 'assert failure'); }, - child: const Text('Dart: assert'), + 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 - ElevatedButton( + TooltipButton( onPressed: () async => asyncThrows(), - child: const Text('Dart: async throws'), + 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', ), - ElevatedButton( + TooltipButton( onPressed: () async => { await Future.microtask( () => throw StateError('Failure in a microtask'), ) }, - child: const Text('Dart: Fail in 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', ), - ElevatedButton( - onPressed: () async => {await compute(loop, 10)}, - child: const Text('Dart: Fail in compute'), + 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', ), - ElevatedButton( - onPressed: () => Future.delayed( - const Duration(milliseconds: 100), - () => throw Exception('Throws in Future.delayed'), - ), - child: const Text('Throws in Future.delayed'), + 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', ), - ElevatedButton( + TooltipButton( onPressed: () { // modeled after a real exception FlutterError.onError?.call(FlutterErrorDetails( @@ -270,9 +308,11 @@ class MainScaffold extends StatelessWidget { ], )); }, - child: const Text('Capture from FlutterError.onError'), + 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', ), - ElevatedButton( + TooltipButton( onPressed: () { // Only usable on Flutter >= 3.3 // and needs the following additional setup: @@ -284,41 +324,40 @@ class MainScaffold extends StatelessWidget { StackTrace.current, ); }, - child: const Text('Capture from PlatformDispatcher.onError'), - ), - ElevatedButton( - key: const Key('view hierarchy'), - onPressed: () => {}, - child: const Visibility( - visible: true, - child: Opacity( - opacity: 0.5, - child: Text('view hierarchy'), - ), - ), + text: + 'This is only usable on Flutter >= 3.3 and requires additional setup: options.addIntegration(OnErrorIntegration());', + buttonTitle: 'Capture from PlatformDispatcher.onError', ), - ElevatedButton( + TooltipButton( onPressed: () => makeWebRequest(context), - child: const Text('Dart: Web request'), + text: + 'Attaches web request related spans to the transaction and send it to Sentry.', + buttonTitle: 'Dart: Web request', ), - ElevatedButton( - onPressed: () => showDialogWithTextAndImage(context), - child: const Text('Flutter: Load assets'), - ), - ElevatedButton( - key: const Key('dio_web_request'), + TooltipButton( onPressed: () async => await makeWebRequestWithDio(context), - child: const Text('Dio: Web request'), + 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', ), - ElevatedButton( + TooltipButton( onPressed: () { // ignore: avoid_print print('A print breadcrumb'); Sentry.captureMessage('A message with a print() Breadcrumb'); }, - child: const Text('Record print() as breadcrumb'), + text: 'Sends a captureMessage to Sentry with a breadcrumb created by a print() statement.', + buttonTitle: 'Record print() as breadcrumb', ), - ElevatedButton( + TooltipButton( onPressed: () { Sentry.captureMessage( 'This event has an extra tag', @@ -327,10 +366,10 @@ class MainScaffold extends StatelessWidget { }, ); }, - child: - const Text('Capture message with scope with additional tag'), + text: 'Sends the capture message event with additional Tag to Sentry.', + buttonTitle: 'Capture message with scope with additional tag', ), - ElevatedButton( + TooltipButton( onPressed: () async { final transaction = Sentry.getSpan() ?? Sentry.startTransaction( @@ -375,9 +414,10 @@ class MainScaffold extends StatelessWidget { // findPrimeNumber(1000000); // Uncomment to see it with profiling await transaction.finish(status: const SpanStatus.ok()); }, - child: const Text('Capture transaction'), + text: 'Creates a custom transaction, adds child spans and send them to Sentry.', + buttonTitle: 'Capture transaction', ), - ElevatedButton( + TooltipButton( onPressed: () { Sentry.captureMessage( 'This message has an attachment', @@ -393,9 +433,10 @@ class MainScaffold extends StatelessWidget { }, ); }, - child: const Text('Capture message with attachment'), + text: 'Sends the capture message with an attachment to Sentry.', + buttonTitle: 'Capture message with attachment', ), - ElevatedButton( + TooltipButton( onPressed: () { feedback.BetterFeedback.of(context) .show((feedback.UserFeedback feedback) { @@ -419,9 +460,10 @@ class MainScaffold extends StatelessWidget { ); }); }, - child: const Text('Capture message with image attachment'), + text: 'Sends the capture message with an image attachment to Sentry.', + buttonTitle: 'Capture message with image attachment', ), - ElevatedButton( + TooltipButton( onPressed: () async { final id = await Sentry.captureMessage('UserFeedback'); // ignore: use_build_context_synchronously @@ -437,9 +479,10 @@ class MainScaffold extends StatelessWidget { }, ); }, - child: const Text('Capture User Feedback'), + text: 'Shows a custom user feedback dialog without an ongoing event that captures and sends user feedback data to Sentry.', + buttonTitle: 'Capture User Feedback', ), - ElevatedButton( + TooltipButton( onPressed: () async { await showDialog( context: context, @@ -448,14 +491,16 @@ class MainScaffold extends StatelessWidget { }, ); }, - child: const Text('Show UserFeedback Dialog without event'), + text: '', + buttonTitle: 'Show UserFeedback Dialog without event', ), - ElevatedButton( + TooltipButton( onPressed: () { final log = Logger('Logging'); log.info('My Logging test'); }, - child: const Text('Logging'), + 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(), @@ -624,12 +669,12 @@ class AndroidExample extends StatelessWidget { } } -void navigateToDelayedScreen(BuildContext context) { +void navigateToAutoCloseScreen(BuildContext context) { Navigator.push( context, MaterialPageRoute( - settings: const RouteSettings(name: 'DelayedScreen'), - builder: (context) => DelayedScreen(), + settings: const RouteSettings(name: 'AutoCloseScreen'), + builder: (context) => const AutoCloseScreen(), ), ); } @@ -795,7 +840,6 @@ class SecondaryScaffold extends StatelessWidget { } void makeWebRequest(BuildContext context) async { - print('span: ${Sentry.getSpan()}'); final transaction = Sentry.getSpan() ?? Sentry.startTransaction( 'flutterwebrequest', From ba5ec4744ee2e3f302be3479b9e62325f43c1ea3 Mon Sep 17 00:00:00 2001 From: GIancarlo Buenaflor Date: Thu, 30 Nov 2023 16:45:16 +0100 Subject: [PATCH 4/6] Format --- flutter/example/lib/auto_close_screen.dart | 3 ++- flutter/example/lib/main.dart | 31 ++++++++++++++-------- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/flutter/example/lib/auto_close_screen.dart b/flutter/example/lib/auto_close_screen.dart index 9784d44ae7..85496346b6 100644 --- a/flutter/example/lib/auto_close_screen.dart +++ b/flutter/example/lib/auto_close_screen.dart @@ -23,7 +23,8 @@ class _AutoCloseScreenState extends State { Future _doComplexOperationThenClose() async { final activeSpan = Sentry.getSpan(); - final childSpan = activeSpan?.startChild('complex operation', description: 'running a $delayInSeconds seconds operation'); + final childSpan = activeSpan?.startChild('complex operation', + description: 'running a $delayInSeconds seconds operation'); await Future.delayed(const Duration(seconds: delayInSeconds)); childSpan?.finish(); Navigator.of(context).pop(); diff --git a/flutter/example/lib/main.dart b/flutter/example/lib/main.dart index 11a41891ff..b4e14c5a60 100644 --- a/flutter/example/lib/main.dart +++ b/flutter/example/lib/main.dart @@ -186,9 +186,12 @@ class MainScaffold extends StatelessWidget { children: [ if (_isIntegrationTest) const IntegrationTestWidget(), const Center(child: Text('Trigger an action.\n')), - const Center( - child: Text( - 'Hover over a button to see more information. (long press on mobile) \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: @@ -338,14 +341,14 @@ class MainScaffold extends StatelessWidget { onPressed: () async => await makeWebRequestWithDio(context), key: const Key('dio_web_request'), text: - 'Attaches web request related spans to the transaction and send it to Sentry.', + '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.', + 'Attaches asset bundle related spans to the transaction and send it to Sentry.', buttonTitle: 'Flutter: Load assets', ), TooltipButton( @@ -354,7 +357,8 @@ class MainScaffold extends StatelessWidget { 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.', + text: + 'Sends a captureMessage to Sentry with a breadcrumb created by a print() statement.', buttonTitle: 'Record print() as breadcrumb', ), TooltipButton( @@ -366,7 +370,8 @@ class MainScaffold extends StatelessWidget { }, ); }, - text: 'Sends the capture message event with additional Tag to Sentry.', + text: + 'Sends the capture message event with additional Tag to Sentry.', buttonTitle: 'Capture message with scope with additional tag', ), TooltipButton( @@ -414,7 +419,8 @@ class MainScaffold extends StatelessWidget { // 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.', + text: + 'Creates a custom transaction, adds child spans and send them to Sentry.', buttonTitle: 'Capture transaction', ), TooltipButton( @@ -460,7 +466,8 @@ class MainScaffold extends StatelessWidget { ); }); }, - text: 'Sends the capture message with an image attachment to Sentry.', + text: + 'Sends the capture message with an image attachment to Sentry.', buttonTitle: 'Capture message with image attachment', ), TooltipButton( @@ -479,7 +486,8 @@ class MainScaffold extends StatelessWidget { }, ); }, - text: 'Shows a custom user feedback dialog without an ongoing event that captures and sends user feedback data to Sentry.', + text: + 'Shows a custom user feedback dialog without an ongoing event that captures and sends user feedback data to Sentry.', buttonTitle: 'Capture User Feedback', ), TooltipButton( @@ -499,7 +507,8 @@ class MainScaffold extends StatelessWidget { 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.', + text: + 'Demonstrates the logging integration. log.info() will create an info event send it to Sentry.', buttonTitle: 'Logging', ), if (UniversalPlatform.isIOS || UniversalPlatform.isMacOS) From 72de1326467f877a39e6439fb8a92e13e0e96c2a Mon Sep 17 00:00:00 2001 From: GIancarlo Buenaflor Date: Thu, 30 Nov 2023 16:52:54 +0100 Subject: [PATCH 5/6] Add padding --- flutter/example/lib/main.dart | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/flutter/example/lib/main.dart b/flutter/example/lib/main.dart index b4e14c5a60..3d8cefb5e9 100644 --- a/flutter/example/lib/main.dart +++ b/flutter/example/lib/main.dart @@ -514,7 +514,16 @@ class MainScaffold extends StatelessWidget { if (UniversalPlatform.isIOS || UniversalPlatform.isMacOS) const CocoaExample(), if (UniversalPlatform.isAndroid) const AndroidExample(), - ], + ].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(), ), ), ); From 11d2c3ea807634f90e1953c5e871e4cb6f49c4b9 Mon Sep 17 00:00:00 2001 From: GIancarlo Buenaflor Date: Tue, 5 Dec 2023 13:11:00 +0100 Subject: [PATCH 6/6] Fix analyze issues --- flutter/example/lib/auto_close_screen.dart | 6 +++--- flutter/example/lib/main.dart | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/flutter/example/lib/auto_close_screen.dart b/flutter/example/lib/auto_close_screen.dart index 85496346b6..15e8fac1fb 100644 --- a/flutter/example/lib/auto_close_screen.dart +++ b/flutter/example/lib/auto_close_screen.dart @@ -1,4 +1,3 @@ -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:sentry/sentry.dart'; @@ -9,10 +8,10 @@ class AutoCloseScreen extends StatefulWidget { const AutoCloseScreen({super.key}); @override - _AutoCloseScreenState createState() => _AutoCloseScreenState(); + AutoCloseScreenState createState() => AutoCloseScreenState(); } -class _AutoCloseScreenState extends State { +class AutoCloseScreenState extends State { static const delayInSeconds = 3; @override @@ -27,6 +26,7 @@ class _AutoCloseScreenState extends State { description: 'running a $delayInSeconds seconds operation'); await Future.delayed(const Duration(seconds: delayInSeconds)); childSpan?.finish(); + // ignore: use_build_context_synchronously Navigator.of(context).pop(); } diff --git a/flutter/example/lib/main.dart b/flutter/example/lib/main.dart index 3d8cefb5e9..61c05ab741 100644 --- a/flutter/example/lib/main.dart +++ b/flutter/example/lib/main.dart @@ -338,7 +338,7 @@ class MainScaffold extends StatelessWidget { buttonTitle: 'Dart: Web request', ), TooltipButton( - onPressed: () async => await makeWebRequestWithDio(context), + onPressed: () => makeWebRequestWithDio(context), key: const Key('dio_web_request'), text: 'Attaches web request related spans to the transaction and send it to Sentry.', @@ -857,7 +857,7 @@ class SecondaryScaffold extends StatelessWidget { } } -void makeWebRequest(BuildContext context) async { +Future makeWebRequest(BuildContext context) async { final transaction = Sentry.getSpan() ?? Sentry.startTransaction( 'flutterwebrequest',