MIF_E31222656/lib/main.dart

249 lines
7.4 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import 'package:tugas_akhir_supabase/core/constants/app_constants.dart';
import 'package:tugas_akhir_supabase/di/service_locator.dart';
import 'package:tugas_akhir_supabase/core/routes/app_routes.dart';
import 'package:intl/date_symbol_data_local.dart';
import 'package:tugas_akhir_supabase/services/session_manager.dart';
import 'package:tugas_akhir_supabase/widgets/session_expired_dialog.dart';
// Tambahkan listener untuk hot reload
bool _hasDoneHotReloadSetup = false;
void main() async {
// Langsung memulai aplikasi utama
try {
// Initialize Flutter binding
WidgetsFlutterBinding.ensureInitialized();
// Tambahkan dukungan untuk hot reload
if (!_hasDoneHotReloadSetup) {
_hasDoneHotReloadSetup = true;
// Set debug flags
debugPrint('======= Setting up hot reload support =======');
// Pastikan semua yang memblokir hot reload dibersihkan
final binding = WidgetsFlutterBinding.ensureInitialized();
binding.addPostFrameCallback((_) {
// Execute after first frame is rendered
debugPrint(
'======= First frame rendered, hot reload should work =======',
);
});
}
// Debug log untuk pelacakan splash screen
debugPrint('======= App Start: Loading TaniSMART application =======');
// Set orientation to portrait
await SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
]);
// Set up error handlers
FlutterError.onError = (FlutterErrorDetails details) {
debugPrint('Flutter error: ${details.exception}');
};
// Initialize date formatting
await initializeDateFormatting('id_ID');
await initializeDateFormatting('en_US');
// Initialize Supabase
await Supabase.initialize(
url: AppConstants.supabaseUrl,
anonKey: AppConstants.supabaseAnonKey,
debug: false,
);
// Initialize service locator
await initServiceLocator();
// Initialize session management
await SessionManager.initializeSession();
// Debug log sebelum menjalankan aplikasi
debugPrint(
'======= App initialized: Running TaniSMART application =======',
);
// Run the app
runApp(const RealApp());
} catch (e, stack) {
debugPrint('Error starting full app: $e\n$stack');
// Show error screen
runApp(
MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('TaniSMART Error'),
backgroundColor: Colors.red,
),
body: Center(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.error_outline, color: Colors.red, size: 48),
const SizedBox(height: 16),
const Text(
'Aplikasi tidak dapat dimulai',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
),
const SizedBox(height: 8),
Text(
'Error: ${e.toString()}',
style: const TextStyle(color: Colors.red),
textAlign: TextAlign.center,
),
const SizedBox(height: 24),
ElevatedButton(
onPressed: () {
main();
},
child: const Text('Coba Lagi'),
),
],
),
),
),
),
),
);
}
}
class RealApp extends StatefulWidget {
const RealApp({super.key});
@override
State<RealApp> createState() => _RealAppState();
}
class _RealAppState extends State<RealApp> with WidgetsBindingObserver {
bool _showingSessionExpiredDialog = false;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
// Listen to session expired events
SessionManager.sessionExpiredStream.listen((isExpired) {
if (isExpired && !_showingSessionExpiredDialog) {
_showSessionExpiredDialog();
}
});
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
SessionManager.dispose();
super.dispose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
debugPrint('App lifecycle state changed to: $state');
switch (state) {
case AppLifecycleState.paused:
case AppLifecycleState.inactive:
case AppLifecycleState.detached:
// App went to background
SessionManager.onAppBackground();
break;
case AppLifecycleState.resumed:
// App came to foreground
SessionManager.onAppForeground().then((_) {
if (SessionManager.isExpired && !_showingSessionExpiredDialog) {
_showSessionExpiredDialog();
}
});
break;
default:
break;
}
}
void _showSessionExpiredDialog() {
if (_showingSessionExpiredDialog) return;
_showingSessionExpiredDialog = true;
// Use a post-frame callback to ensure the context is valid
WidgetsBinding.instance.addPostFrameCallback((_) {
if (!mounted) return;
try {
// Check if context is valid and has MaterialApp ancestor
if (context.findAncestorWidgetOfExactType<MaterialApp>() == null) {
debugPrint(
'Session: Cannot show dialog - no MaterialApp ancestor found',
);
_showingSessionExpiredDialog = false;
return;
}
showDialog(
context: context,
barrierDismissible: false,
builder: (dialogContext) => const SessionExpiredDialog(),
).then((_) {
_showingSessionExpiredDialog = false;
});
} catch (e) {
debugPrint('Session: Error showing session expired dialog: $e');
_showingSessionExpiredDialog = false;
// Navigate to login screen directly if dialog can't be shown
Navigator.of(
context,
rootNavigator: true,
).pushNamedAndRemoveUntil('/login', (route) => false).catchError((e) {
debugPrint('Session: Error navigating to login: $e');
});
}
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'TaniSMART',
theme: ThemeData(
primaryColor: const Color(0xFF056839),
colorScheme: ColorScheme.fromSeed(
seedColor: const Color(0xFF056839),
primary: const Color(0xFF056839),
secondary: const Color(0xFFF9B300),
tertiary: const Color(0xFF78B057),
),
scaffoldBackgroundColor: const Color(0xFFF5F5F5),
cardColor: Colors.white,
useMaterial3: true,
appBarTheme: const AppBarTheme(
backgroundColor: Color(0xFF056839),
foregroundColor: Colors.white,
elevation: 0,
),
inputDecorationTheme: InputDecorationTheme(
filled: true,
fillColor: Colors.white,
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
),
),
initialRoute: '/',
routes: AppRoutes.routes,
);
}
}