201 lines
6.5 KiB
Dart
201 lines
6.5 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:hive_flutter/hive_flutter.dart';
|
|
import 'package:firebase_core/firebase_core.dart';
|
|
import 'package:firebase_messaging/firebase_messaging.dart';
|
|
|
|
import 'core/config/app_config.dart';
|
|
import 'core/theme/app_theme.dart';
|
|
// import 'services/firebase_service.dart'; // Not needed for direct Firebase initialization
|
|
import 'services/gemini_service.dart';
|
|
import 'services/dialog_service.dart';
|
|
// TODO: Import models when Hive adapters are generated
|
|
import 'models/user_model.dart';
|
|
import 'models/menu_model.dart';
|
|
import 'models/recommendation_model.dart';
|
|
import 'screens/splash_screen.dart';
|
|
import 'core/constants/app_constants.dart';
|
|
import 'firebase_options.dart';
|
|
|
|
// Background message handler
|
|
@pragma('vm:entry-point')
|
|
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
|
|
// Only initialize if not already initialized
|
|
if (Firebase.apps.isEmpty) {
|
|
await Firebase.initializeApp(
|
|
options: DefaultFirebaseOptions.currentPlatform,
|
|
);
|
|
}
|
|
debugPrint('Handling a background message: ${message.messageId}');
|
|
}
|
|
|
|
void main() async {
|
|
WidgetsFlutterBinding.ensureInitialized();
|
|
|
|
// Set system UI overlay style
|
|
SystemChrome.setSystemUIOverlayStyle(
|
|
const SystemUiOverlayStyle(
|
|
statusBarColor: Colors.transparent,
|
|
statusBarIconBrightness: Brightness.dark,
|
|
systemNavigationBarColor: Colors.white,
|
|
systemNavigationBarIconBrightness: Brightness.dark,
|
|
),
|
|
);
|
|
|
|
// Set preferred orientations
|
|
await SystemChrome.setPreferredOrientations([
|
|
DeviceOrientation.portraitUp,
|
|
DeviceOrientation.portraitDown,
|
|
]);
|
|
|
|
try {
|
|
// Initialize Hive
|
|
await Hive.initFlutter();
|
|
|
|
// Register Hive adapters
|
|
Hive.registerAdapter(UserModelAdapter());
|
|
Hive.registerAdapter(UserPreferencesAdapter());
|
|
Hive.registerAdapter(MenuModelAdapter());
|
|
Hive.registerAdapter(RecommendationModelAdapter());
|
|
Hive.registerAdapter(MenuRecommendationAdapter());
|
|
|
|
// Open Hive boxes
|
|
await Future.wait([
|
|
Hive.openBox<UserModel>(AppConstants.userPreferencesBox),
|
|
Hive.openBox<RecommendationModel>(AppConstants.recommendationsBox),
|
|
Hive.openBox<MenuModel>(AppConstants.menusBox),
|
|
]);
|
|
|
|
// Initialize Firebase (handle duplicate app error gracefully)
|
|
try {
|
|
if (Firebase.apps.isEmpty) {
|
|
await Firebase.initializeApp(
|
|
options: DefaultFirebaseOptions.currentPlatform,
|
|
);
|
|
}
|
|
debugPrint('✅ Firebase initialized');
|
|
} catch (e) {
|
|
if (e.toString().contains('duplicate-app')) {
|
|
debugPrint('⚠️ Firebase already initialized, using existing instance');
|
|
} else {
|
|
rethrow;
|
|
}
|
|
}
|
|
|
|
// Set up background message handler
|
|
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
|
|
|
|
// Initialize Gemini service
|
|
try {
|
|
await GeminiService().initialize();
|
|
debugPrint('✅ Gemini service initialized');
|
|
} catch (e) {
|
|
debugPrint('⚠️ Gemini service initialization failed: $e');
|
|
debugPrint('💡 Users can set their own API key in settings');
|
|
}
|
|
|
|
// Print configuration in debug mode
|
|
AppConfig.printConfig();
|
|
|
|
runApp(const ProviderScope(child: AmoriMenuRecommenderApp()));
|
|
} catch (e) {
|
|
debugPrint('Initialization error: $e');
|
|
runApp(
|
|
MaterialApp(
|
|
home: Scaffold(
|
|
body: Center(
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
const Icon(Icons.error_outline, size: 64, color: Colors.red),
|
|
const SizedBox(height: 16),
|
|
const Text(
|
|
'Gagal menginisialisasi aplikasi',
|
|
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
|
),
|
|
const SizedBox(height: 8),
|
|
Text(
|
|
'Error: $e',
|
|
textAlign: TextAlign.center,
|
|
style: const TextStyle(fontSize: 14, color: Colors.grey),
|
|
),
|
|
const SizedBox(height: 24),
|
|
ElevatedButton(
|
|
onPressed: () {
|
|
// Restart app
|
|
main();
|
|
},
|
|
child: const Text('Coba Lagi'),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class AmoriMenuRecommenderApp extends ConsumerWidget {
|
|
const AmoriMenuRecommenderApp({super.key});
|
|
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
return MaterialApp(
|
|
title: AppConstants.appName,
|
|
debugShowCheckedModeBanner: false,
|
|
theme: AppTheme.lightTheme,
|
|
darkTheme: AppTheme.darkTheme,
|
|
themeMode: ThemeMode.system,
|
|
// Global Keys for DialogService
|
|
navigatorKey: DialogService().navigatorKey,
|
|
scaffoldMessengerKey: DialogService().scaffoldMessengerKey,
|
|
home: const SplashScreen(),
|
|
builder: (context, child) {
|
|
// Handle global errors
|
|
ErrorWidget.builder = (FlutterErrorDetails errorDetails) {
|
|
return Scaffold(
|
|
body: Center(
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
const Icon(Icons.error_outline, size: 64, color: Colors.red),
|
|
const SizedBox(height: 16),
|
|
const Text(
|
|
'Terjadi kesalahan',
|
|
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
|
),
|
|
const SizedBox(height: 8),
|
|
if (AppConfig.isDebugMode)
|
|
Padding(
|
|
padding: const EdgeInsets.all(16.0),
|
|
child: Text(
|
|
errorDetails.exception.toString(),
|
|
textAlign: TextAlign.center,
|
|
style: const TextStyle(
|
|
fontSize: 12,
|
|
color: Colors.grey,
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: 24),
|
|
ElevatedButton(
|
|
onPressed: () {
|
|
// Navigate back or restart
|
|
Navigator.of(context).pop();
|
|
},
|
|
child: const Text('Kembali'),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
};
|
|
|
|
return child!;
|
|
},
|
|
);
|
|
}
|
|
}
|