From 2a666056c259235981d7eaa89d3f21ddf4991ddf Mon Sep 17 00:00:00 2001 From: dab246 Date: Tue, 7 Apr 2026 16:09:49 +0700 Subject: [PATCH] TF-4268 Refactor app runner: split web/mobile runners, add platform error handlers --- integration_test/base/test_base.dart | 2 + lib/main.dart | 6 +-- lib/main/app_runner.dart | 49 ------------------------- lib/main/main_entry.dart | 2 - lib/main/runner/app_error_handlers.dart | 30 +++++++++++++++ lib/main/runner/app_runner_base.dart | 12 ++++++ lib/main/runner/app_runner_mobile.dart | 10 +++++ lib/main/runner/app_runner_web.dart | 21 +++++++++++ 8 files changed, 78 insertions(+), 54 deletions(-) delete mode 100644 lib/main/app_runner.dart create mode 100644 lib/main/runner/app_error_handlers.dart create mode 100644 lib/main/runner/app_runner_base.dart create mode 100644 lib/main/runner/app_runner_mobile.dart create mode 100644 lib/main/runner/app_runner_web.dart diff --git a/integration_test/base/test_base.dart b/integration_test/base/test_base.dart index 3797830d6..924ef0b6a 100644 --- a/integration_test/base/test_base.dart +++ b/integration_test/base/test_base.dart @@ -1,3 +1,4 @@ +import 'package:core/utils/config/env_loader.dart'; import 'package:core/utils/platform_info.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -39,6 +40,7 @@ class TestBase { } Future setupTest() async { + await EnvLoader.loadEnvFile(); await runTmail(); final originalOnError = FlutterError.onError!; diff --git a/lib/main.dart b/lib/main.dart index b5f4ff9dc..a88535943 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -10,7 +10,8 @@ import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations_delegate.dart'; import 'package:tmail_ui_user/main/localizations/localization_service.dart'; import 'package:tmail_ui_user/main/main_entry.dart'; -import 'package:tmail_ui_user/main/app_runner.dart'; +import 'package:tmail_ui_user/main/runner/app_runner_mobile.dart' + if (dart.library.html) 'package:tmail_ui_user/main/runner/app_runner_web.dart'; import 'package:tmail_ui_user/main/pages/app_pages.dart'; import 'package:tmail_ui_user/main/routes/app_routes.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart'; @@ -27,7 +28,6 @@ class TMailApp extends StatefulWidget { } class _TMailAppState extends State { - DeepLinksManager? _deepLinksManager; @override @@ -85,4 +85,4 @@ class _TMailAppState extends State { } super.dispose(); } -} \ No newline at end of file +} diff --git a/lib/main/app_runner.dart b/lib/main/app_runner.dart deleted file mode 100644 index 02b6b88f0..000000000 --- a/lib/main/app_runner.dart +++ /dev/null @@ -1,49 +0,0 @@ -import 'dart:async'; - -import 'package:core/utils/app_logger.dart'; -import 'package:core/utils/sentry/sentry_manager.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:sentry_flutter/sentry_flutter.dart'; -import 'package:tmail_ui_user/main.dart'; -import 'package:tmail_ui_user/main/main_entry.dart'; - -Future runAppWithMonitoring(Future Function() runTmail) async { - await runZonedGuarded(() async { - WidgetsFlutterBinding.ensureInitialized(); - - // Handling Flutter UI and Build Errors - FlutterError.onError = (details) async { - logError( - 'FlutterError: ${details.exception}', - exception: details.exception, - stackTrace: details.stack, - ); - FlutterError.presentError(details); - }; - - // Handling Uncaught and Platform-Specific Errors - PlatformDispatcher.instance.onError = (error, stack) { - logError( - 'PlatformDispatcher: Error: $error', - exception: error, - stackTrace: stack, - ); - return true; - }; - - await SentryManager.instance.initialize( - appRunner: () async { - await runTmailPreload(); - runApp(SentryWidget(child: const TMailApp())); - }, - fallBackRunner: runTmail, - ); - }, (error, stack) async { - logError( - 'Uncaught zone error: $error', - exception: error, - stackTrace: stack, - ); - }); -} diff --git a/lib/main/main_entry.dart b/lib/main/main_entry.dart index c251152f4..a8a31c167 100644 --- a/lib/main/main_entry.dart +++ b/lib/main/main_entry.dart @@ -1,6 +1,5 @@ import 'package:core/presentation/utils/theme_utils.dart'; import 'package:core/utils/build_utils.dart'; -import 'package:core/utils/config/env_loader.dart'; import 'package:core/utils/platform_info.dart'; import 'package:flutter/widgets.dart'; import 'package:get/get.dart'; @@ -23,7 +22,6 @@ Future runTmailPreload() async { await Future.wait([ MainBindings().dependencies(), HiveCacheConfig.instance.setUp(), - EnvLoader.loadEnvFile(), if (PlatformInfo.isWeb) AssetPreloader.preloadHtmlEditorAssets(), ], eagerError: false); diff --git a/lib/main/runner/app_error_handlers.dart b/lib/main/runner/app_error_handlers.dart new file mode 100644 index 000000000..9d666b26d --- /dev/null +++ b/lib/main/runner/app_error_handlers.dart @@ -0,0 +1,30 @@ +import 'dart:ui'; + +import 'package:core/utils/app_logger.dart'; +import 'package:core/utils/build_utils.dart'; +import 'package:flutter/material.dart'; + +/// Configures global error handlers for both Flutter framework and Platform dispatcher. +void setupErrorHooks() { + // Handle Flutter Framework Errors (Rendering, Build, Layout, etc.) + FlutterError.onError = (details) { + logError( + 'FlutterError: ${details.exception}', + exception: details.exception, + stackTrace: details.stack, + ); + // Show the "Red Screen of Death" in debug mode. + FlutterError.presentError(details); + }; + + // Handle Asynchronous Errors (Futures, Streams, Platform Channels) + PlatformDispatcher.instance.onError = (error, stack) { + logError( + 'PlatformDispatcherError: $error', + exception: error, + stackTrace: stack, + ); + // Return true in release to prevent crashes; false in debug to surface errors. + return BuildUtils.isReleaseMode; + }; +} diff --git a/lib/main/runner/app_runner_base.dart b/lib/main/runner/app_runner_base.dart new file mode 100644 index 000000000..fdc246453 --- /dev/null +++ b/lib/main/runner/app_runner_base.dart @@ -0,0 +1,12 @@ +import 'dart:async'; + +import 'package:sentry_flutter/sentry_flutter.dart'; +import 'package:tmail_ui_user/main/runner/app_error_handlers.dart'; + +Future runAppGuarded(Future Function() runner) async { + SentryWidgetsFlutterBinding.ensureInitialized(); + + setupErrorHooks(); + + await runner(); +} diff --git a/lib/main/runner/app_runner_mobile.dart b/lib/main/runner/app_runner_mobile.dart new file mode 100644 index 000000000..454dcb8b2 --- /dev/null +++ b/lib/main/runner/app_runner_mobile.dart @@ -0,0 +1,10 @@ +import 'package:core/utils/config/env_loader.dart'; +import 'package:tmail_ui_user/main/runner/app_runner_base.dart'; + +Future runAppWithMonitoring(Future Function() runTmail) async { + await runAppGuarded(() async { + await EnvLoader.loadEnvFile(); + + await runTmail(); + }); +} diff --git a/lib/main/runner/app_runner_web.dart b/lib/main/runner/app_runner_web.dart new file mode 100644 index 000000000..f5b1263fd --- /dev/null +++ b/lib/main/runner/app_runner_web.dart @@ -0,0 +1,21 @@ +import 'package:core/utils/config/env_loader.dart'; +import 'package:flutter/material.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; +import 'package:tmail_ui_user/main.dart'; +import 'package:tmail_ui_user/main/main_entry.dart'; +import 'package:core/utils/sentry/sentry_manager.dart'; +import 'package:tmail_ui_user/main/runner/app_runner_base.dart'; + +Future runAppWithMonitoring(Future Function() runTmail) async { + await runAppGuarded(() async { + await EnvLoader.loadEnvFile(); + + await SentryManager.instance.initialize( + appRunner: () async { + await runTmailPreload(); + runApp(SentryWidget(child: const TMailApp())); + }, + fallBackRunner: runTmail, + ); + }); +}