249 lines
7.4 KiB
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,
|
|
);
|
|
}
|
|
}
|