203 lines
6.2 KiB
Dart

import 'dart:async';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'app/injection_container.dart';
import 'app/app.dart';
import 'core/constants/app_constants.dart';
import 'shared/widgets/walkguide_loading_screen.dart';
Future<void> main() async {
await runZonedGuarded(
() async {
WidgetsFlutterBinding.ensureInitialized();
_installGlobalErrorUi();
await AppConstants.clearServerUrl();
runApp(const WalkGuideBootApp());
try {
await initDependencies();
} catch (error, stackTrace) {
debugPrint('WalkGuide startup failed: $error\n$stackTrace');
runApp(WalkGuideFatalApp(error: error, stackTrace: stackTrace));
return;
}
runApp(const WalkGuideApp());
},
(error, stackTrace) {
debugPrint('WalkGuide uncaught error: $error\n$stackTrace');
runApp(WalkGuideFatalApp(error: error, stackTrace: stackTrace));
},
);
}
class WalkGuideBootApp extends StatelessWidget {
const WalkGuideBootApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: WalkGuideLoadingScreen(
subtitle: 'Preparing connection setup',
),
);
}
}
void _installGlobalErrorUi() {
FlutterError.onError = (details) {
FlutterError.presentError(details);
debugPrint('WalkGuide Flutter error: ${details.exceptionAsString()}');
};
PlatformDispatcher.instance.onError = (error, stackTrace) {
debugPrint('WalkGuide platform error: $error\n$stackTrace');
return true;
};
ErrorWidget.builder = (details) {
return WalkGuideErrorPanel(
title: 'WalkGuide UI Error',
message:
'A screen failed to render. Please report this message to the developer.',
details: details.exceptionAsString(),
);
};
}
class WalkGuideFatalApp extends StatelessWidget {
final Object error;
final StackTrace? stackTrace;
const WalkGuideFatalApp({
super.key,
required this.error,
this.stackTrace,
});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: WalkGuideErrorPanel(
title: 'WalkGuide Startup Error',
message:
'The app could not finish startup. Please report this screen to the developer.',
details: error.toString(),
stackTrace: stackTrace?.toString(),
),
);
}
}
class WalkGuideErrorPanel extends StatelessWidget {
final String title;
final String message;
final String details;
final String? stackTrace;
const WalkGuideErrorPanel({
super.key,
required this.title,
required this.message,
required this.details,
this.stackTrace,
});
@override
Widget build(BuildContext context) {
final textTheme = Theme.of(context).textTheme;
return Scaffold(
backgroundColor: const Color(0xFFF8FAFC),
body: SafeArea(
child: Center(
child: SingleChildScrollView(
padding: const EdgeInsets.all(24),
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 520),
child: DecoratedBox(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
border: Border.all(color: const Color(0xFFE2E8F0)),
boxShadow: const [
BoxShadow(
blurRadius: 24,
offset: Offset(0, 12),
color: Color(0x1A0F172A),
),
],
),
child: Padding(
padding: const EdgeInsets.all(22),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Icon(
Icons.error_outline,
color: Color(0xFFDC2626),
size: 42,
),
const SizedBox(height: 16),
Text(
title,
style: textTheme.headlineSmall?.copyWith(
fontWeight: FontWeight.w800,
color: const Color(0xFF0F172A),
),
),
const SizedBox(height: 8),
Text(
message,
style: textTheme.bodyMedium?.copyWith(
color: const Color(0xFF475569),
),
),
const SizedBox(height: 18),
Container(
width: double.infinity,
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: const Color(0xFFF1F5F9),
borderRadius: BorderRadius.circular(10),
border: Border.all(color: const Color(0xFFE2E8F0)),
),
child: SelectableText(
_formatDetails(),
style: const TextStyle(
fontFamily: 'monospace',
fontSize: 12,
color: Color(0xFF334155),
),
),
),
const SizedBox(height: 16),
const Text(
'Tip: close the app and open it again after fixing the configuration.',
style: TextStyle(
color: Color(0xFF64748B),
fontSize: 12,
),
),
],
),
),
),
),
),
),
),
);
}
String _formatDetails() {
final stack = stackTrace;
if (stack == null || stack.isEmpty) return details;
final shortStack = stack.split('\n').take(8).join('\n');
return '$details\n\n$shortStack';
}
}