264 lines
8.0 KiB
Dart
264 lines
8.0 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:firebase_core/firebase_core.dart';
|
|
import 'package:firebase_auth/firebase_auth.dart';
|
|
import 'package:firebase_messaging/firebase_messaging.dart';
|
|
import 'package:firebase_database/firebase_database.dart';
|
|
import 'package:in_app_notification/in_app_notification.dart';
|
|
import 'firebase_options.dart';
|
|
import 'service/notification_service.dart';
|
|
|
|
// IMPORT PAGE
|
|
import 'screen/welcome.dart';
|
|
import 'screen/login.dart';
|
|
import 'screen/register.dart';
|
|
import 'screen/dashboard.dart';
|
|
import 'screen/pesan.dart';
|
|
import 'screen/history.dart';
|
|
import 'screen/akun.dart';
|
|
|
|
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
|
|
|
|
/// 🔔 HANDLER NOTIFIKASI BACKGROUND
|
|
@pragma('vm:entry-point')
|
|
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
|
|
// Inisialisasi Firebase di background
|
|
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
|
|
|
|
if (message.notification != null) {
|
|
String body = message.notification!.body ?? "";
|
|
body = body
|
|
.replaceAll(RegExp(r'Proses Optimal', caseSensitive: false), 'Belum Matang')
|
|
.replaceAll(RegExp(r'Proses Awal', caseSensitive: false), 'Belum Matang')
|
|
.replaceAll(RegExp(r'Menjelang Matang', caseSensitive: false), 'Belum Matang');
|
|
|
|
await NotificationService.showNotification(
|
|
message.notification!.title ?? "Notifikasi",
|
|
body,
|
|
);
|
|
}
|
|
}
|
|
|
|
Future<void> main() async {
|
|
// 1. Pastikan binding sudah siap
|
|
WidgetsFlutterBinding.ensureInitialized();
|
|
|
|
// 2. Inisialisasi Firebase (Gunakan Try-Catch agar aman)
|
|
try {
|
|
await Firebase.initializeApp(
|
|
options: DefaultFirebaseOptions.currentPlatform,
|
|
);
|
|
} catch (e) {
|
|
debugPrint("Firebase init error: $e");
|
|
}
|
|
|
|
// 3. Register Background Handler
|
|
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
|
|
|
|
// 4. Init Local Notification
|
|
await NotificationService.init();
|
|
|
|
// 5. Jalankan Aplikasi Dulu (SANGAT PENTING agar tidak layar hitam)
|
|
runApp(const GoTapeApp());
|
|
|
|
// 6. Jalankan konfigurasi tambahan setelah runApp agar tidak menghambat startup
|
|
_initNotification();
|
|
}
|
|
|
|
/// Fungsi tambahan untuk setup notifikasi tanpa menghambat UI
|
|
void _initNotification() async {
|
|
try {
|
|
FirebaseMessaging messaging = FirebaseMessaging.instance;
|
|
|
|
// Minta Izin
|
|
await messaging.requestPermission();
|
|
|
|
// Ambil & Simpan Token
|
|
String? token = await messaging.getToken();
|
|
if (token != null) {
|
|
// Simpan ke database tanpa await agar tidak nunggu lama
|
|
FirebaseDatabase.instance.ref("fcm_token").set(token);
|
|
debugPrint("🔥 FCM TOKEN: $token");
|
|
}
|
|
|
|
|
|
|
|
// Listener Foreground
|
|
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
|
|
if (message.notification != null) {
|
|
String title = message.notification!.title ?? "Notifikasi";
|
|
String body = message.notification!.body ?? "";
|
|
|
|
body = body
|
|
.replaceAll(RegExp(r'Proses Optimal', caseSensitive: false), 'Belum Matang')
|
|
.replaceAll(RegExp(r'Proses Awal', caseSensitive: false), 'Belum Matang')
|
|
.replaceAll(RegExp(r'Menjelang Matang', caseSensitive: false), 'Belum Matang');
|
|
|
|
NotificationService.showNotification(title, body);
|
|
|
|
// Tampilkan In-App Notification (Seperti WhatsApp)
|
|
final context = navigatorKey.currentContext;
|
|
if (context != null) {
|
|
InAppNotification.show(
|
|
child: _buildWhatsAppStyleNotification(title, body),
|
|
context: context,
|
|
duration: const Duration(seconds: 4),
|
|
curve: Curves.easeOutCubic,
|
|
);
|
|
}
|
|
}
|
|
});
|
|
|
|
// Listener saat notif diklik
|
|
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
|
|
debugPrint("🔔 User membuka notifikasi");
|
|
});
|
|
} catch (e) {
|
|
debugPrint("Notification Setup Error: $e");
|
|
}
|
|
}
|
|
|
|
Widget _buildWhatsAppStyleNotification(String title, String body) {
|
|
bool isAlert = title.toLowerCase().contains('berbahaya') || title.toLowerCase().contains('matang');
|
|
return SafeArea(
|
|
child: Container(
|
|
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
|
padding: const EdgeInsets.all(16),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.circular(15),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black.withOpacity(0.15),
|
|
blurRadius: 15,
|
|
offset: const Offset(0, 5),
|
|
),
|
|
],
|
|
),
|
|
child: Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Container(
|
|
padding: const EdgeInsets.all(10),
|
|
decoration: BoxDecoration(
|
|
color: isAlert ? Colors.redAccent : const Color(0xFF5B35D5),
|
|
shape: BoxShape.circle,
|
|
),
|
|
child: Icon(
|
|
isAlert ? Icons.warning_rounded : Icons.notifications_active,
|
|
color: Colors.white,
|
|
size: 24,
|
|
),
|
|
),
|
|
const SizedBox(width: 12),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Text(
|
|
title,
|
|
style: TextStyle(
|
|
fontWeight: FontWeight.bold,
|
|
fontSize: 16,
|
|
color: isAlert ? Colors.redAccent : Colors.black87,
|
|
),
|
|
),
|
|
const SizedBox(height: 4),
|
|
Text(
|
|
body,
|
|
style: const TextStyle(
|
|
color: Colors.black54,
|
|
fontSize: 14,
|
|
),
|
|
maxLines: 2,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
class GoTapeApp extends StatelessWidget {
|
|
const GoTapeApp({super.key});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return InAppNotification(
|
|
child: MaterialApp(
|
|
navigatorKey: navigatorKey,
|
|
debugShowCheckedModeBanner: false,
|
|
title: 'GoTape',
|
|
theme: ThemeData(
|
|
useMaterial3: true,
|
|
colorSchemeSeed: const Color(0xFF4DB6AC),
|
|
scaffoldBackgroundColor: const Color(0xFFF2F6F6),
|
|
appBarTheme: const AppBarTheme(
|
|
centerTitle: true,
|
|
elevation: 0,
|
|
backgroundColor: Colors.transparent,
|
|
),
|
|
),
|
|
|
|
// Halaman Utama menggunakan AuthGate
|
|
home: const AuthGate(),
|
|
|
|
// Daftar Route
|
|
routes: {
|
|
'/welcome': (_) => const WelcomeScreen(),
|
|
'/login': (_) => const LoginPage(),
|
|
'/register': (_) => const RegisterScreen(),
|
|
'/dashboard': (_) => const DashboardPage(),
|
|
'/pesan': (_) => const NotificationPage(),
|
|
'/riwayat': (_) => const HistoryPage(),
|
|
'/akun': (_) => const AkunScreen(),
|
|
},
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class AuthGate extends StatelessWidget {
|
|
const AuthGate({super.key});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return StreamBuilder<User?>(
|
|
stream: FirebaseAuth.instance.authStateChanges(),
|
|
builder: (context, snapshot) {
|
|
// Jika sedang mengecek status login
|
|
if (snapshot.connectionState == ConnectionState.waiting) {
|
|
return const Scaffold(
|
|
body: Center(
|
|
child: CircularProgressIndicator(color: Color(0xFF4DB6AC)),
|
|
),
|
|
);
|
|
}
|
|
|
|
// Jika terjadi error pada Firebase Auth
|
|
if (snapshot.hasError) {
|
|
return const Scaffold(
|
|
body: Center(
|
|
child: Text(
|
|
'Terjadi kesalahan autentikasi',
|
|
style: TextStyle(color: Colors.red),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
// Jika user sudah login, lempar ke Dashboard
|
|
if (snapshot.hasData && snapshot.data != null) {
|
|
return const DashboardPage();
|
|
}
|
|
|
|
// Jika belum login, tampilkan Welcome Screen
|
|
return const WelcomeScreen();
|
|
},
|
|
);
|
|
}
|
|
}
|