diff --git a/android/app/build.gradle b/android/app/build.gradle
index a936555..87203d2 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -1,5 +1,8 @@
plugins {
id "com.android.application"
+ // START: FlutterFire Configuration
+ id 'com.google.gms.google-services'
+ // END: FlutterFire Configuration
id "kotlin-android"
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
id "dev.flutter.flutter-gradle-plugin"
diff --git a/android/app/google-services.json b/android/app/google-services.json
new file mode 100644
index 0000000..39008bf
--- /dev/null
+++ b/android/app/google-services.json
@@ -0,0 +1,29 @@
+{
+ "project_info": {
+ "project_number": "144700563392",
+ "project_id": "hamaguard-e911d",
+ "storage_bucket": "hamaguard-e911d.firebasestorage.app"
+ },
+ "client": [
+ {
+ "client_info": {
+ "mobilesdk_app_id": "1:144700563392:android:913dfe0c4205cca8e4b225",
+ "android_client_info": {
+ "package_name": "com.example.hamaguard"
+ }
+ },
+ "oauth_client": [],
+ "api_key": [
+ {
+ "current_key": "AIzaSyDgtq_bU0aNjKwErdns50EldaRRSdd5Sg0"
+ }
+ ],
+ "services": {
+ "appinvite_service": {
+ "other_platform_oauth_client": []
+ }
+ }
+ }
+ ],
+ "configuration_version": "1"
+}
\ No newline at end of file
diff --git a/android/settings.gradle b/android/settings.gradle
index b9e43bd..5914426 100644
--- a/android/settings.gradle
+++ b/android/settings.gradle
@@ -18,7 +18,10 @@ pluginManagement {
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
- id "com.android.application" version "8.1.0" apply false
+ id "com.android.application" version "8.2.1" apply false
+ // START: FlutterFire Configuration
+ id "com.google.gms.google-services" version "4.3.15" apply false
+ // END: FlutterFire Configuration
id "org.jetbrains.kotlin.android" version "1.8.22" apply false
}
diff --git a/firebase.json b/firebase.json
new file mode 100644
index 0000000..1ce47d3
--- /dev/null
+++ b/firebase.json
@@ -0,0 +1 @@
+{"flutter":{"platforms":{"android":{"default":{"projectId":"hamaguard-e911d","appId":"1:144700563392:android:913dfe0c4205cca8e4b225","fileOutput":"android/app/google-services.json"}},"dart":{"lib/firebase_options.dart":{"projectId":"hamaguard-e911d","configurations":{"android":"1:144700563392:android:913dfe0c4205cca8e4b225"}}}}}}
\ No newline at end of file
diff --git a/ios/Runner/GoogleService-Info.plist b/ios/Runner/GoogleService-Info.plist
new file mode 100644
index 0000000..377842e
--- /dev/null
+++ b/ios/Runner/GoogleService-Info.plist
@@ -0,0 +1,30 @@
+
+
+
+
+ API_KEY
+ AIzaSyBVDd8ZqMe_PK1So6pxng8zRHz8qzuToNE
+ GCM_SENDER_ID
+ 144700563392
+ PLIST_VERSION
+ 1
+ BUNDLE_ID
+ com.example.hamaguard
+ PROJECT_ID
+ hamaguard-e911d
+ STORAGE_BUCKET
+ hamaguard-e911d.firebasestorage.app
+ IS_ADS_ENABLED
+
+ IS_ANALYTICS_ENABLED
+
+ IS_APPINVITE_ENABLED
+
+ IS_GCM_ENABLED
+
+ IS_SIGNIN_ENABLED
+
+ GOOGLE_APP_ID
+ 1:144700563392:ios:76ebcf9db01d01cfe4b225
+
+
\ No newline at end of file
diff --git a/lib/firebase_options.dart b/lib/firebase_options.dart
new file mode 100644
index 0000000..4909f3c
--- /dev/null
+++ b/lib/firebase_options.dart
@@ -0,0 +1,62 @@
+// File generated by FlutterFire CLI.
+// ignore_for_file: type=lint
+import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
+import 'package:flutter/foundation.dart'
+ show defaultTargetPlatform, kIsWeb, TargetPlatform;
+
+/// Default [FirebaseOptions] for use with your Firebase apps.
+///
+/// Example:
+/// ```dart
+/// import 'firebase_options.dart';
+/// // ...
+/// await Firebase.initializeApp(
+/// options: DefaultFirebaseOptions.currentPlatform,
+/// );
+/// ```
+class DefaultFirebaseOptions {
+ static FirebaseOptions get currentPlatform {
+ if (kIsWeb) {
+ throw UnsupportedError(
+ 'DefaultFirebaseOptions have not been configured for web - '
+ 'you can reconfigure this by running the FlutterFire CLI again.',
+ );
+ }
+ switch (defaultTargetPlatform) {
+ case TargetPlatform.android:
+ return android;
+ case TargetPlatform.iOS:
+ throw UnsupportedError(
+ 'DefaultFirebaseOptions have not been configured for ios - '
+ 'you can reconfigure this by running the FlutterFire CLI again.',
+ );
+ case TargetPlatform.macOS:
+ throw UnsupportedError(
+ 'DefaultFirebaseOptions have not been configured for macos - '
+ 'you can reconfigure this by running the FlutterFire CLI again.',
+ );
+ case TargetPlatform.windows:
+ throw UnsupportedError(
+ 'DefaultFirebaseOptions have not been configured for windows - '
+ 'you can reconfigure this by running the FlutterFire CLI again.',
+ );
+ case TargetPlatform.linux:
+ throw UnsupportedError(
+ 'DefaultFirebaseOptions have not been configured for linux - '
+ 'you can reconfigure this by running the FlutterFire CLI again.',
+ );
+ default:
+ throw UnsupportedError(
+ 'DefaultFirebaseOptions are not supported for this platform.',
+ );
+ }
+ }
+
+ static const FirebaseOptions android = FirebaseOptions(
+ apiKey: 'AIzaSyDgtq_bU0aNjKwErdns50EldaRRSdd5Sg0',
+ appId: '1:144700563392:android:913dfe0c4205cca8e4b225',
+ messagingSenderId: '144700563392',
+ projectId: 'hamaguard-e911d',
+ storageBucket: 'hamaguard-e911d.firebasestorage.app',
+ );
+}
diff --git a/lib/main.dart b/lib/main.dart
index 3ee1741..a03060a 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -1,7 +1,19 @@
import 'package:flutter/material.dart';
+import 'package:firebase_core/firebase_core.dart';
+import 'firebase_options.dart';
import 'screens/splash/splash_screen.dart';
-void main() {
+void main() async {
+ WidgetsFlutterBinding.ensureInitialized();
+ try {
+ await Firebase.initializeApp(
+ options: DefaultFirebaseOptions.currentPlatform,
+ );
+ } catch (e) {
+ // Bisa log error atau tampilkan pesan khusus
+ debugPrint('Firebase init error: $e');
+ }
+
runApp(const MyApp());
}
@@ -17,7 +29,7 @@ class MyApp extends StatelessWidget {
colorScheme: ColorScheme.fromSeed(seedColor: Colors.green),
useMaterial3: true,
),
- home: const SplashScreen(), // << mulai dari splash
+ home: const SplashScreen(),
);
}
}
diff --git a/lib/screens/control/control_screen.dart b/lib/screens/control/control_screen.dart
index d206b81..b55c96e 100644
--- a/lib/screens/control/control_screen.dart
+++ b/lib/screens/control/control_screen.dart
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import '../../widgets/costum_header.dart';
+import '../../screens/notification/notification_screen.dart';
class ControlScreen extends StatefulWidget {
const ControlScreen({super.key});
@@ -50,9 +51,19 @@ class _ControlScreenState extends State {
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[100],
- appBar: const CustomHeader(
+ appBar: CustomHeader(
deviceName: 'HamaGuard',
+ notificationCount: 5,
+ onNotificationTap: () {
+ Navigator.push(
+ context,
+ MaterialPageRoute(
+ builder: (context) => NotificationScreen(),
),
+ );
+ },
+ ),
+
body: Padding(
padding: const EdgeInsets.all(20),
child: Column(
diff --git a/lib/screens/dashboard/dashboard_screen.dart b/lib/screens/dashboard/dashboard_screen.dart
index ec4c5d1..869959d 100644
--- a/lib/screens/dashboard/dashboard_screen.dart
+++ b/lib/screens/dashboard/dashboard_screen.dart
@@ -1,38 +1,103 @@
-import 'dart:math';
import 'package:flutter/material.dart';
-import '../../widgets/thermal_grid.dart';
+import 'package:firebase_database/firebase_database.dart';
+import '../../widgets/thermal_heatmap.dart';
import '../../widgets/costum_header.dart';
+import '../notification/notification_screen.dart';
-class DashboardScreen extends StatelessWidget {
+class DashboardScreen extends StatefulWidget {
const DashboardScreen({super.key});
+ @override
+ State createState() => _DashboardScreenState();
+}
+
+class _DashboardScreenState extends State {
+ late DatabaseReference _sensorRef;
+ late DatabaseReference _thermalRef;
+
+ Map sensorData = {
+ 'PIR': 'Memuat...',
+ 'Ultrasonik': 'Memuat...',
+ 'Status Pengusir': 'Memuat...',
+ };
+
+ List thermalData = List.filled(64, 0.0);
+
+ @override
+ void initState() {
+ super.initState();
+
+ _sensorRef = FirebaseDatabase.instance.ref('sensor');
+ _thermalRef = FirebaseDatabase.instance.ref('thermal/temperatures');
+
+ // Listener sensor data
+ _sensorRef.onValue.listen((event) {
+ final data = event.snapshot.value;
+ if (data != null && data is Map) {
+ setState(() {
+ sensorData = {
+ 'PIR': (data['pir'] == true) ? 'Terdeteksi' : 'Tidak',
+ 'Ultrasonik':
+ data['ultrasonik'] != null ? '${data['ultrasonik']} cm' : '-',
+ 'Status Pengusir':
+ (data['pengusir'] == true) ? 'Aktif' : 'Nonaktif',
+ };
+ });
+ }
+ });
+
+ // Listener thermal data
+ _thermalRef.onValue.listen((event) {
+ final data = event.snapshot.value;
+ if (data != null) {
+ // Firebase RTDB terkadang mengirim List atau Map
+ List temps = [];
+ if (data is List) {
+ temps = data.map((e) {
+ if (e is num) return e.toDouble();
+ return 0.0;
+ }).toList();
+ } else if (data is Map) {
+ // Jika data disimpan sebagai Map index:string -> value
+ temps = List.filled(64, 0);
+ data.forEach((key, value) {
+ int idx = int.tryParse(key) ?? -1;
+ if (idx >= 0 && idx < 64) {
+ if (value is num) {
+ temps[idx] = value.toDouble();
+ }
+ }
+ });
+ }
+
+ if (temps.length == 64) {
+ setState(() {
+ thermalData = temps;
+ });
+ }
+ }
+ });
+ }
+
@override
Widget build(BuildContext context) {
- // Dummy data sementara
- final List thermalData =
- List.generate(64, (index) => 25 + Random().nextDouble() * 10);
-
- final Map sensorData = const {
- 'PIR': 'Tidak',
- 'Ultrasonik': '50 cm',
- 'Status Pengusir': 'Nonaktif',
- };
-
return Scaffold(
backgroundColor: Colors.grey[100],
appBar: CustomHeader(
deviceName: 'HamaGuard',
notificationCount: 5,
onNotificationTap: () {
- // Aksi saat lonceng ditekan
+ Navigator.push(
+ context,
+ MaterialPageRoute(builder: (context) => NotificationScreen()),
+ );
},
),
-
body: SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
- // Thermal Sensor dalam Card
+ // Thermal Sensor
Card(
elevation: 4,
shape: RoundedRectangleBorder(
@@ -49,27 +114,25 @@ class DashboardScreen extends StatelessWidget {
Text(
'Thermal Sensor',
style: TextStyle(
- fontWeight: FontWeight.bold,
- fontSize: 16,
- ),
+ fontWeight: FontWeight.bold, fontSize: 16),
),
],
),
const SizedBox(height: 12),
- ThermalGrid(temperatures: thermalData),
+ ThermalHeatmap(temperatures: thermalData),
],
),
),
),
const SizedBox(height: 16),
- // PIR & Ultrasonik side by side
+ // PIR & Ultrasonik
Row(
children: [
Expanded(
child: SensorCard(
title: 'PIR',
- value: sensorData['PIR']!,
+ value: sensorData['PIR'] ?? '-',
icon: Icons.motion_photos_on_outlined,
color: Colors.orange,
),
@@ -78,7 +141,7 @@ class DashboardScreen extends StatelessWidget {
Expanded(
child: SensorCard(
title: 'Ultrasonik',
- value: sensorData['Ultrasonik']!,
+ value: sensorData['Ultrasonik'] ?? '-',
icon: Icons.straighten,
color: Colors.blue,
),
@@ -90,7 +153,7 @@ class DashboardScreen extends StatelessWidget {
// Status Pengusir
SensorCard(
title: 'Status Pengusir',
- value: sensorData['Status Pengusir']!,
+ value: sensorData['Status Pengusir'] ?? '-',
icon: Icons.speaker,
color: Colors.green,
),
@@ -122,7 +185,7 @@ class SensorCard extends StatelessWidget {
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: ListTile(
leading: CircleAvatar(
- backgroundColor: color.withOpacity(0.15),
+ backgroundColor: color.withAlpha(15),
child: Icon(icon, color: color),
),
title: Text(title),
diff --git a/lib/screens/notification/notification_screen.dart b/lib/screens/notification/notification_screen.dart
index e69de29..71000f5 100644
--- a/lib/screens/notification/notification_screen.dart
+++ b/lib/screens/notification/notification_screen.dart
@@ -0,0 +1,62 @@
+import 'package:flutter/material.dart';
+
+class NotificationScreen extends StatelessWidget {
+ final List