Skip to content

Commit 540b058

Browse files
committed
✨ 优化错误处理机制,添加 FallbackLogApp 以在应用启动失败时显示错误信息
1 parent da864f0 commit 540b058

1 file changed

Lines changed: 140 additions & 66 deletions

File tree

lib/main.dart

Lines changed: 140 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import 'package:system_fonts/system_fonts.dart';
2828
import 'package:timezone/data/latest.dart' as tz;
2929

3030
void main() {
31-
void catchError([ArcheLogger? logger]) async {
31+
Future<void> catchError([ArcheLogger? logger]) async {
3232
var store =
3333
logger ?? ArcheBus.bus.provideof<ArcheLogger>(instance: ArcheLogger());
3434

@@ -45,79 +45,92 @@ void main() {
4545

4646
runZonedGuarded(
4747
() async {
48-
WidgetsFlutterBinding.ensureInitialized();
49-
// Ensure directory is initialized here for faster load
50-
await ensurePlatDirectoryValue();
51-
52-
await initializeRust(assignRustSignal);
5348
final logger = ArcheLogger();
54-
final platUserData = await platUserDataDirectory.getValue();
55-
56-
final configPath = platUserData.subPath("app.config.json");
57-
final config = ArcheConfig.path(configPath);
58-
logger.info("Application Config Stored in `$configPath`");
59-
if (!kDebugMode) {
60-
FlutterError.onError = (err) async {
61-
logger.error(err.exception);
62-
logger.error(err.stack);
63-
catchError(logger);
64-
};
65-
}
49+
try {
50+
WidgetsFlutterBinding.ensureInitialized();
51+
// Ensure directory is initialized here for faster load
52+
await ensurePlatDirectoryValue();
53+
54+
await initializeRust(assignRustSignal);
55+
final platUserData = await platUserDataDirectory.getValue();
56+
57+
final configPath = platUserData.subPath("app.config.json");
58+
final config = ArcheConfig.path(configPath);
59+
logger.info("Application Config Stored in `$configPath`");
60+
if (!kDebugMode) {
61+
FlutterError.onError = (err) async {
62+
logger.error(err.exception);
63+
logger.error(err.stack);
64+
catchError(logger);
65+
};
66+
}
6667

67-
// Calendar
68-
tz.initializeTimeZones();
69-
ICalendar.registerField(
70-
field: "WEEK",
71-
function: (value, params, event, lastEvent) {
72-
lastEvent['week'] = value;
73-
return lastEvent;
74-
},
75-
);
68+
// Calendar
69+
tz.initializeTimeZones();
70+
ICalendar.registerField(
71+
field: "WEEK",
72+
function: (value, params, event, lastEvent) {
73+
lastEvent['week'] = value;
74+
return lastEvent;
75+
},
76+
);
7677

77-
var bus = ArcheBus();
78-
var configs = ApplicationConfigs(config);
79-
bus.provide(ArcheLogger()).provide(config).provide(configs);
80-
// Cache Background Image in memory for faster load
81-
await calendarBackgroundData.update();
82-
// Parse Data for fast load
83-
await icalendarParsersData.update();
84-
85-
// Custom Font
86-
var customfont = platUserData.subFile("customfont");
87-
if (await customfont.exists()) {
88-
final loader = FontLoader("Custom Font")
89-
..addFont(
90-
Future(() async {
91-
var data = await customfont.readAsBytes();
92-
93-
return data.buffer.asByteData();
94-
}),
95-
);
96-
await loader.load();
97-
configs.sysfont.write("Custom Font");
98-
} else {
99-
// Load SystemFont
100-
if (Platform.isWindows || Platform.isLinux || Platform.isMacOS) {
101-
var font = configs.sysfont.tryGet();
102-
if (font != null) {
103-
await SystemFonts().loadFont(font);
78+
var bus = ArcheBus();
79+
var configs = ApplicationConfigs(config);
80+
bus.provide(logger).provide(config).provide(configs);
81+
// Cache Background Image in memory for faster load
82+
await calendarBackgroundData.update();
83+
// Parse Data for fast load
84+
await icalendarParsersData.update();
85+
86+
// Custom Font
87+
var customfont = platUserData.subFile("customfont");
88+
if (await customfont.exists()) {
89+
final loader = FontLoader("Custom Font")
90+
..addFont(
91+
Future(() async {
92+
var data = await customfont.readAsBytes();
93+
94+
return data.buffer.asByteData();
95+
}),
96+
);
97+
await loader.load();
98+
configs.sysfont.write("Custom Font");
99+
} else {
100+
// Load SystemFont
101+
if (Platform.isWindows || Platform.isLinux || Platform.isMacOS) {
102+
var font = configs.sysfont.tryGet();
103+
if (font != null) {
104+
await SystemFonts().loadFont(font);
105+
}
104106
}
105107
}
106-
}
107108

108-
logger.info("Run Application in `main`...");
109-
logger.info("Try to load `MultiAccountData`");
109+
logger.info("Run Application in `main`...");
110+
logger.info("Try to load `MultiAccountData`");
110111

111-
if (await MultiAccoutData.hasAccountsFile()) {
112-
bus.provide((await MultiAccoutData.readAccounts())!);
113-
} else {
114-
logger.warn("Can't find `accounts.json`");
115-
bus.provide(MultiAccoutData.template);
116-
}
112+
if (await MultiAccoutData.hasAccountsFile()) {
113+
bus.provide((await MultiAccoutData.readAccounts())!);
114+
} else {
115+
logger.warn("Can't find `accounts.json`");
116+
bus.provide(MultiAccoutData.template);
117+
}
117118

118-
runApp(
119-
MainApplication(key: rootKey),
120-
);
119+
runApp(
120+
MainApplication(key: rootKey),
121+
);
122+
} catch (error, stack) {
123+
logger.error(error);
124+
logger.error(stack);
125+
await catchError(logger);
126+
runApp(
127+
FallbackLogApp(
128+
error: error,
129+
stackTrace: stack,
130+
logs: logger.getLogs().map((log) => log.toString()).toList(),
131+
),
132+
);
133+
}
121134
},
122135
(error, stack) async {
123136
var logger = ArcheBus.bus.provideof(instance: ArcheLogger());
@@ -478,3 +491,64 @@ class MainViewState extends State<MainView> with RefreshMountedStateMixin {
478491
);
479492
}
480493
}
494+
495+
class FallbackLogApp extends StatelessWidget {
496+
final Object error;
497+
final StackTrace stackTrace;
498+
final List<String> logs;
499+
500+
const FallbackLogApp({
501+
super.key,
502+
required this.error,
503+
required this.stackTrace,
504+
required this.logs,
505+
});
506+
507+
@override
508+
Widget build(BuildContext context) {
509+
return MaterialApp(
510+
debugShowCheckedModeBanner: false,
511+
home: Scaffold(
512+
appBar: AppBar(
513+
title: const Text("致命错误!应用启动失败😭"),
514+
),
515+
body: SafeArea(
516+
child: Padding(
517+
padding: const EdgeInsets.all(16),
518+
child: Column(
519+
crossAxisAlignment: CrossAxisAlignment.stretch,
520+
children: [
521+
Text(
522+
"错误: ${error.toString()}",
523+
style: Theme.of(context).textTheme.titleMedium,
524+
),
525+
const SizedBox(height: 8),
526+
Expanded(
527+
child: DecoratedBox(
528+
decoration: BoxDecoration(
529+
border: Border.all(color: Colors.redAccent),
530+
borderRadius: BorderRadius.circular(8),
531+
),
532+
child: SingleChildScrollView(
533+
padding: const EdgeInsets.all(12),
534+
child: SelectableText(
535+
([error.toString(), stackTrace.toString(), ...logs])
536+
.join("\n"),
537+
style: const TextStyle(fontFamily: "monospace"),
538+
),
539+
),
540+
),
541+
),
542+
const SizedBox(height: 8),
543+
const Text(
544+
"请将上述日志反馈给开发者以便排查。",
545+
textAlign: TextAlign.center,
546+
),
547+
],
548+
),
549+
),
550+
),
551+
),
552+
);
553+
}
554+
}

0 commit comments

Comments
 (0)