diff --git a/example/lib/main.dart b/example/lib/main.dart index 257bdde5b..e3edb3c09 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -22,6 +22,8 @@ part 'src/screens/crashes_page.dart'; part 'src/screens/bug_reporting.dart'; +part 'src/screens/session_replay_page.dart'; + part 'src/screens/complex_page.dart'; part 'src/screens/apm_page.dart'; diff --git a/example/lib/src/app_routes.dart b/example/lib/src/app_routes.dart index f52636cde..bfc429dab 100644 --- a/example/lib/src/app_routes.dart +++ b/example/lib/src/app_routes.dart @@ -1,4 +1,5 @@ import 'package:flutter/widgets.dart' show BuildContext; +import 'package:instabug_flutter/instabug_flutter.dart'; import 'package:instabug_flutter_example/main.dart'; final appRoutes = { @@ -12,6 +13,10 @@ final appRoutes = { BugReportingPage.screenName: (BuildContext context) => const BugReportingPage(), ComplexPage.screenName: (BuildContext context) => const ComplexPage(), + SessionReplayPage.screenName: (BuildContext context) => + const SessionReplayPage(), + TopTabBarScreen.route: (BuildContext context) => const TopTabBarScreen(), + ApmPage.screenName: (BuildContext context) => const ApmPage(), ScreenLoadingPage.screenName: (BuildContext context) => const ScreenLoadingPage(), diff --git a/example/lib/src/components/network_content.dart b/example/lib/src/components/network_content.dart index 23eaa4dd2..e5380a85c 100644 --- a/example/lib/src/components/network_content.dart +++ b/example/lib/src/components/network_content.dart @@ -36,6 +36,42 @@ class _NetworkContentState extends State { text: 'Send Request Without Custom traceparent header', onPressed: () => _sendRequestToUrl(endpointUrlController.text), ), + InstabugButton( + text: 'obfuscateLog', + onPressed: () { + NetworkLogger.obfuscateLog((networkData) async { + return networkData.copyWith(url: 'fake url'); + }); + }, + ), + InstabugButton( + text: 'omitLog', + onPressed: () { + NetworkLogger.omitLog((networkData) async { + return networkData.url.contains('google.com'); + }); + }, + ), + InstabugButton( + text: 'obfuscateLogWithException', + onPressed: () { + NetworkLogger.obfuscateLog((networkData) async { + throw Exception("obfuscateLogWithException"); + + return networkData.copyWith(url: 'fake url'); + }); + }, + ), + InstabugButton( + text: 'omitLogWithException', + onPressed: () { + NetworkLogger.omitLog((networkData) async { + throw Exception("OmitLog with exception"); + + return networkData.url.contains('google.com'); + }); + }, + ), ], ); } diff --git a/example/lib/src/components/non_fatal_crashes_content.dart b/example/lib/src/components/non_fatal_crashes_content.dart index 8d3bb7fb3..25e5ab790 100644 --- a/example/lib/src/components/non_fatal_crashes_content.dart +++ b/example/lib/src/components/non_fatal_crashes_content.dart @@ -55,28 +55,24 @@ class _NonFatalCrashesContentState extends State { InstabugButton( text: 'Throw ArgumentError', key: const Key('non_fatal_argument_exception'), - onPressed: () => throwHandledException(ArgumentError('This is an ArgumentError.')), ), InstabugButton( text: 'Throw RangeError', key: const Key('non_fatal_range_exception'), - onPressed: () => throwHandledException( RangeError.range(5, 0, 3, 'Index out of range')), ), InstabugButton( text: 'Throw FormatException', key: const Key('non_fatal_format_exception'), - onPressed: () => throwHandledException(UnsupportedError('Invalid format.')), ), InstabugButton( text: 'Throw NoSuchMethodError', key: const Key('non_fatal_no_such_method_exception'), - onPressed: () { dynamic obj; throwHandledException(obj.methodThatDoesNotExist()); @@ -85,7 +81,6 @@ class _NonFatalCrashesContentState extends State { const InstabugButton( text: 'Throw Handled Native Exception', key: Key('non_fatal_native_exception'), - onPressed: InstabugFlutterExampleMethodChannel.sendNativeNonFatalCrash, ), @@ -113,9 +108,8 @@ class _NonFatalCrashesContentState extends State { Expanded( child: InstabugTextField( label: "User Attribute key", - key: const Key("non_fatal_user_attribute_key_textfield"), - - controller: crashUserAttributeKeyController, + key: const Key("non_fatal_user_attribute_key_textfield"), + controller: crashUserAttributeKeyController, validator: (value) { if (crashUserAttributeValueController.text.isNotEmpty) { if (value?.trim().isNotEmpty == true) return null; @@ -128,9 +122,8 @@ class _NonFatalCrashesContentState extends State { Expanded( child: InstabugTextField( label: "User Attribute Value", - key: const Key("non_fatal_user_attribute_value_textfield"), - - controller: crashUserAttributeValueController, + key: const Key("non_fatal_user_attribute_value_textfield"), + controller: crashUserAttributeValueController, validator: (value) { if (crashUserAttributeKeyController.text.isNotEmpty) { if (value?.trim().isNotEmpty == true) return null; @@ -147,9 +140,9 @@ class _NonFatalCrashesContentState extends State { Expanded( child: InstabugTextField( label: "Fingerprint", - key: const Key("non_fatal_user_attribute_fingerprint_textfield"), - - controller: crashfingerPrintController, + key: const Key( + "non_fatal_user_attribute_fingerprint_textfield"), + controller: crashfingerPrintController, )), ], ), @@ -161,7 +154,6 @@ class _NonFatalCrashesContentState extends State { flex: 5, child: DropdownButtonHideUnderline( key: const Key("non_fatal_crash_level_dropdown"), - child: DropdownButtonFormField( value: crashType, diff --git a/example/lib/src/screens/bug_reporting.dart b/example/lib/src/screens/bug_reporting.dart index 121e24292..6581a5345 100644 --- a/example/lib/src/screens/bug_reporting.dart +++ b/example/lib/src/screens/bug_reporting.dart @@ -10,7 +10,11 @@ class BugReportingPage extends StatefulWidget { } class _BugReportingPageState extends State { - List reportTypes = [ReportType.bug,ReportType.feedback,ReportType.question]; + List reportTypes = [ + ReportType.bug, + ReportType.feedback, + ReportType.question + ]; List invocationOptions = []; final disclaimerTextController = TextEditingController(); @@ -63,9 +67,7 @@ class _BugReportingPageState extends State { } else { reportTypes.add(reportType); } - setState(() { - - }); + setState(() {}); BugReporting.setReportTypes(reportTypes); } @@ -107,6 +109,34 @@ class _BugReportingPageState extends State { }); } + void setOnDismissCallbackWithException() { + BugReporting.setOnDismissCallback((dismissType, reportType) { + throw Exception("Test crash from dismiss callback"); + }); + } + + void setOnInvokeCallbackWithException() { + BugReporting.setOnInvokeCallback(() { + throw Exception("Test crash from invoke callback"); + }); + } + + void setOnInvoiceCallback() { + BugReporting.setOnInvokeCallback(() { + showDialog( + context: context, + builder: (context) { + return const AlertDialog( + title: Text('On Invoke'), + content: Text( + 'onInvoke callback called', + ), + ); + }, + ); + }); + } + void setDisclaimerText() { BugReporting.setDisclaimerText(disclaimerTextController.text); } @@ -296,7 +326,6 @@ class _BugReportingPageState extends State { title: const Text("Screenshot"), subtitle: const Text('Enable attachment for screenShot'), key: const Key('attachment_option_screenshot'), - ), CheckboxListTile( value: attachmentsOptionsExtraScreenshot, @@ -305,12 +334,10 @@ class _BugReportingPageState extends State { attachmentsOptionsExtraScreenshot = value ?? false; }); addAttachmentOptions(); - }, title: const Text("Extra Screenshot"), subtitle: const Text('Enable attachment for extra screenShot'), key: const Key('attachment_option_extra_screenshot'), - ), CheckboxListTile( value: attachmentsOptionsGalleryImage, @@ -319,12 +346,10 @@ class _BugReportingPageState extends State { attachmentsOptionsGalleryImage = value ?? false; }); addAttachmentOptions(); - }, title: const Text("Gallery"), subtitle: const Text('Enable attachment for gallery'), key: const Key('attachment_option_gallery'), - ), CheckboxListTile( value: attachmentsOptionsScreenRecording, @@ -333,17 +358,14 @@ class _BugReportingPageState extends State { attachmentsOptionsScreenRecording = value ?? false; }); addAttachmentOptions(); - }, title: const Text("Screen Recording"), subtitle: const Text('Enable attachment for screen Recording'), key: const Key('attachment_option_screen_recording'), - ), ], ), const SectionTitle('Bug reporting type'), - ButtonBar( mainAxisSize: MainAxisSize.min, alignment: MainAxisAlignment.start, @@ -351,8 +373,9 @@ class _BugReportingPageState extends State { ElevatedButton( key: const Key('bug_report_type_bug'), style: ElevatedButton.styleFrom( - backgroundColor: reportTypes.contains(ReportType.bug)?Colors.grey.shade400:null - ), + backgroundColor: reportTypes.contains(ReportType.bug) + ? Colors.grey.shade400 + : null), onPressed: () => toggleReportType(ReportType.bug), child: const Text('Bug'), ), @@ -360,16 +383,18 @@ class _BugReportingPageState extends State { key: const Key('bug_report_type_feedback'), onPressed: () => toggleReportType(ReportType.feedback), style: ElevatedButton.styleFrom( - backgroundColor: reportTypes.contains(ReportType.feedback)?Colors.grey.shade400:null - ), + backgroundColor: reportTypes.contains(ReportType.feedback) + ? Colors.grey.shade400 + : null), child: const Text('Feedback'), ), ElevatedButton( key: const Key('bug_report_type_question'), onPressed: () => toggleReportType(ReportType.question), style: ElevatedButton.styleFrom( - backgroundColor: reportTypes.contains(ReportType.question)?Colors.grey.shade400:null - ), + backgroundColor: reportTypes.contains(ReportType.question) + ? Colors.grey.shade400 + : null), child: const Text('Question'), ), ], @@ -392,7 +417,6 @@ class _BugReportingPageState extends State { onPressed: () => setDisclaimerText, child: const Text('set disclaimer text'), ), - const SectionTitle('Extended Bug Reporting'), ButtonBar( mainAxisSize: MainAxisSize.min, @@ -431,6 +455,18 @@ class _BugReportingPageState extends State { onPressed: setOnDismissCallback, text: 'Set On Dismiss Callback', ), + InstabugButton( + onPressed: setOnInvoiceCallback, + text: 'Set On Invoice Callback', + ), + InstabugButton( + onPressed: setOnDismissCallbackWithException, + text: 'Set On Dismiss Callback with Exception', + ), + InstabugButton( + onPressed: setOnInvokeCallbackWithException, + text: 'Set On Invoice Callback with Exception', + ), ], // This trailing comma makes auto-formatting nicer for build methods. ); } diff --git a/example/lib/src/screens/my_home_page.dart b/example/lib/src/screens/my_home_page.dart index 293e2fa18..29e2f66d7 100644 --- a/example/lib/src/screens/my_home_page.dart +++ b/example/lib/src/screens/my_home_page.dart @@ -169,6 +169,16 @@ class _MyHomePageState extends State { ); } + void _navigateToSessionReplay() { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const SessionReplayPage(), + settings: const RouteSettings(name: SessionReplayPage.screenName), + ), + ); + } + final _formUserAttributeKey = GlobalKey(); @override @@ -320,6 +330,10 @@ class _MyHomePageState extends State { onPressed: _navigateToComplex, text: 'Complex', ), + InstabugButton( + onPressed: _navigateToSessionReplay, + text: 'Session Replay', + ), const SectionTitle('Sessions Replay'), InstabugButton( onPressed: getCurrentSessionReplaylink, @@ -365,9 +379,7 @@ class _MyHomePageState extends State { onPressed: () => removeAllFeatureFlags(), text: 'RemoveAllFeatureFlags', ), - const SectionTitle('Set User Attribute'), - Form( key: _formUserAttributeKey, child: Column( @@ -397,7 +409,9 @@ class _MyHomePageState extends State { )), ], ), - SizedBox(height: 8,), + const SizedBox( + height: 8, + ), InstabugButton( text: 'Set User attribute', key: const Key('set_user_data_btn'), @@ -413,12 +427,89 @@ class _MyHomePageState extends State { key: const Key('remove_user_data_btn'), onPressed: () { if (_formUserAttributeKey.currentState?.validate() == true) { - Instabug.removeUserAttribute(userAttributeKeyController.text); + Instabug.removeUserAttribute( + userAttributeKeyController.text); } }, ), - SizedBox(height: 10,), - + const SizedBox( + height: 10, + ), + const SectionTitle('Log'), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Row( + children: [ + Expanded( + child: InstabugButton( + margin: const EdgeInsets.symmetric(horizontal: 2), + key: const ValueKey('log_hello_debug_btn'), + onPressed: () { + InstabugLog.logDebug("hello Debug"); + }, + text: 'Log Hello Debug', + ), + ), + Expanded( + child: InstabugButton( + margin: const EdgeInsets.symmetric(horizontal: 2), + key: const ValueKey('log_hello_error_btn'), + onPressed: () { + InstabugLog.logError("hello Error"); + }, + text: 'Log Hello Error', + ), + ), + Expanded( + child: InstabugButton( + margin: const EdgeInsets.symmetric(horizontal: 2), + key: const ValueKey('hello_warning_btn'), + onPressed: () { + InstabugLog.logWarn("hello Warning"); + }, + text: 'Log Hello Warn', + ), + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Row( + children: [ + Expanded( + child: InstabugButton( + margin: const EdgeInsets.symmetric(horizontal: 2), + key: const ValueKey('log_hello_info_btn'), + onPressed: () { + InstabugLog.logInfo("hello Info"); + }, + text: 'Log Hello Info', + ), + ), + Expanded( + child: InstabugButton( + margin: const EdgeInsets.symmetric(horizontal: 2), + key: const ValueKey('log_hello_verbose_btn'), + onPressed: () { + InstabugLog.logVerbose("hello Verbose"); + }, + text: 'Log Hello Verbose', + ), + ), + Expanded( + child: InstabugButton( + margin: const EdgeInsets.symmetric(horizontal: 2), + key: const ValueKey('clear_logs_btn'), + onPressed: () { + InstabugLog.clearAllLogs(); + }, + text: 'Clear All logs', + ), + ), + ], + ), + ), ], ), ), diff --git a/example/lib/src/screens/session_replay_page.dart b/example/lib/src/screens/session_replay_page.dart new file mode 100644 index 000000000..34d0d6d71 --- /dev/null +++ b/example/lib/src/screens/session_replay_page.dart @@ -0,0 +1,114 @@ +part of '../../main.dart'; + +class SessionReplayPage extends StatefulWidget { + static const screenName = 'SessionReplay'; + + const SessionReplayPage({Key? key}) : super(key: key); + + @override + State createState() => _SessionReplayPageState(); +} + +class _SessionReplayPageState extends State { + @override + Widget build(BuildContext context) { + return Page(title: 'Session Replay', children: [ + const SectionTitle('Enabling Session Replay'), + InstabugButton( + key: const Key('instabug_sesssion_replay_disable'), + onPressed: () => SessionReplay.setEnabled(false), + text: "Disable Session Replay", + ), + InstabugButton( + key: const Key('instabug_sesssion_replay_enable'), + onPressed: () => SessionReplay.setEnabled(true), + text: "Enable Session Replay", + ), + const SectionTitle('Enabling Session Replay Network'), + InstabugButton( + key: const Key('instabug_sesssion_replay_network_disable'), + onPressed: () => SessionReplay.setNetworkLogsEnabled(false), + text: "Disable Session Replay Network", + ), + InstabugButton( + key: const Key('instabug_sesssion_replay_network_enable'), + onPressed: () => SessionReplay.setNetworkLogsEnabled(true), + text: "Enable Session Replay Network", + ), + const SectionTitle('Enabling Session Replay User Steps'), + InstabugButton( + key: const Key('instabug_sesssion_replay_user_steps_disable'), + onPressed: () => SessionReplay.setUserStepsEnabled(false), + text: "Disable Session Replay User Steps", + ), + InstabugButton( + key: const Key('instabug_sesssion_replay_user_steps_enable'), + onPressed: () => SessionReplay.setUserStepsEnabled(true), + text: "Enable Session Replay User Steps", + ), + const SectionTitle('Enabling Session Replay Logs'), + InstabugButton( + key: const Key('instabug_sesssion_replay_logs_disable'), + onPressed: () => SessionReplay.setInstabugLogsEnabled(false), + text: "Disable Session Replay Logs", + ), + InstabugButton( + key: const Key('instabug_sesssion_replay_logs_enable'), + onPressed: () => SessionReplay.setInstabugLogsEnabled(true), + text: "Enable Session Replay Logs", + ), + const SectionTitle('Enabling Session Replay Repro steps'), + InstabugButton( + key: const Key('instabug_sesssion_replay_repro_steps_disable'), + onPressed: () => Instabug.setReproStepsConfig( + sessionReplay: ReproStepsMode.disabled), + text: "Disable Session Replay Repro steps", + ), + InstabugButton( + key: const Key('instabug_sesssion_replay_repro_steps_enable'), + onPressed: () => + Instabug.setReproStepsConfig(sessionReplay: ReproStepsMode.enabled), + text: "Enable Session Replay Repro steps", + ), + InstabugButton( + key: const Key('instabug_sesssion_replay_tab_screen'), + onPressed: () => Navigator.of(context).pushNamed(TopTabBarScreen.route), + text: 'Open Tab Screen', + ), + ]); + } +} + +class TopTabBarScreen extends StatelessWidget { + static const String route = "/tap"; + + const TopTabBarScreen({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return DefaultTabController( + length: 4, // Number of tabs + child: Scaffold( + appBar: AppBar( + title: const Text('Top TabBar with 4 Tabs'), + bottom: const TabBar( + tabs: [ + Tab(text: 'Home', icon: Icon(Icons.home)), + Tab(text: 'Search', icon: Icon(Icons.search)), + Tab(text: 'Alerts', icon: Icon(Icons.notifications)), + Tab(text: 'Profile', icon: Icon(Icons.person)), + ], + ), + ), + body: const TabBarView( + children: [ + Center(child: Text('Home Screen')), + Center(child: Text('Search Screen')), + Center(child: Text('Alerts Screen')), + Center(child: Text('Profile Screen')), + ], + ), + ), + ); + } +} diff --git a/example/lib/src/widget/instabug_button.dart b/example/lib/src/widget/instabug_button.dart index 97e434061..5bdb31eef 100644 --- a/example/lib/src/widget/instabug_button.dart +++ b/example/lib/src/widget/instabug_button.dart @@ -7,6 +7,7 @@ class InstabugButton extends StatelessWidget { this.onPressed, this.fontSize, this.margin, + this.backgroundColor, }) : super(key: key); const InstabugButton.smallFontSize({ @@ -15,12 +16,13 @@ class InstabugButton extends StatelessWidget { this.onPressed, this.fontSize = 10.0, this.margin, + this.backgroundColor, }) : super(key: key); final String text; final Function()? onPressed; final double? fontSize; - + final Color? backgroundColor; final EdgeInsetsGeometry? margin; @override @@ -34,7 +36,7 @@ class InstabugButton extends StatelessWidget { child: ElevatedButton( onPressed: onPressed, style: ElevatedButton.styleFrom( - backgroundColor: Colors.lightBlue, + backgroundColor: backgroundColor ?? Colors.lightBlue, foregroundColor: Colors.white, textStyle: Theme.of(context) .textTheme