This commit is contained in:
parent
60b2047eae
commit
1239b39e30
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
|
|
@ -1,50 +1 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:firebase_database/firebase_database.dart';
|
||||
|
||||
part 'monitoring_event.dart';
|
||||
part 'monitoring_state.dart';
|
||||
|
||||
class MonitoringBloc extends Bloc<MonitoringEvent, MonitoringState> {
|
||||
final DatabaseReference _realtimeRef =
|
||||
FirebaseDatabase.instance.ref('anemometer/realtime');
|
||||
StreamSubscription<DatabaseEvent>? _realtimeSub;
|
||||
|
||||
MonitoringBloc() : super(const MonitoringState.initial()) {
|
||||
on<LoadMonitoringData>(_onLoadMonitoringData);
|
||||
on<MonitoringDataUpdated>(_onMonitoringDataUpdated);
|
||||
}
|
||||
|
||||
void _onLoadMonitoringData(
|
||||
LoadMonitoringData event, Emitter<MonitoringState> emit) {
|
||||
emit(const MonitoringState.loading());
|
||||
|
||||
// Start listening to realtime data
|
||||
_realtimeSub = _realtimeRef.onValue.listen((event) {
|
||||
final data = event.snapshot.value;
|
||||
if (data is Map) {
|
||||
final speed = (data['kecepatan'] ?? 0).toDouble();
|
||||
final timestamp = data['timestamp'] ?? 0;
|
||||
final waktu = DateTime.fromMillisecondsSinceEpoch(timestamp * 1000);
|
||||
|
||||
add(MonitoringDataUpdated(speed: speed, timestamp: waktu));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _onMonitoringDataUpdated(
|
||||
MonitoringDataUpdated event, Emitter<MonitoringState> emit) {
|
||||
emit(MonitoringState.loaded(
|
||||
currentSpeed: event.speed,
|
||||
lastUpdateTime: event.timestamp,
|
||||
));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() {
|
||||
_realtimeSub?.cancel();
|
||||
return super.close();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,25 +1 @@
|
|||
part of 'monitoring_bloc.dart';
|
||||
|
||||
sealed class MonitoringEvent extends Equatable {
|
||||
const MonitoringEvent();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class LoadMonitoringData extends MonitoringEvent {
|
||||
const LoadMonitoringData();
|
||||
}
|
||||
|
||||
class MonitoringDataUpdated extends MonitoringEvent {
|
||||
final double speed;
|
||||
final DateTime timestamp;
|
||||
|
||||
const MonitoringDataUpdated({
|
||||
required this.speed,
|
||||
required this.timestamp,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object> get props => [speed, timestamp];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,34 +1 @@
|
|||
part of 'monitoring_bloc.dart';
|
||||
|
||||
class MonitoringState extends Equatable {
|
||||
final MonitoringStatus status;
|
||||
final double? currentSpeed;
|
||||
final DateTime? lastUpdateTime;
|
||||
|
||||
const MonitoringState._({
|
||||
this.status = MonitoringStatus.initial,
|
||||
this.currentSpeed,
|
||||
this.lastUpdateTime,
|
||||
});
|
||||
|
||||
const MonitoringState.initial() : this._();
|
||||
|
||||
const MonitoringState.loading() : this._(status: MonitoringStatus.loading);
|
||||
|
||||
const MonitoringState.loaded({
|
||||
required double currentSpeed,
|
||||
required DateTime lastUpdateTime,
|
||||
}) : this._(
|
||||
status: MonitoringStatus.loaded,
|
||||
currentSpeed: currentSpeed,
|
||||
lastUpdateTime: lastUpdateTime,
|
||||
);
|
||||
|
||||
const MonitoringState.error(String message)
|
||||
: this._(status: MonitoringStatus.error);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [status, currentSpeed, lastUpdateTime];
|
||||
}
|
||||
|
||||
enum MonitoringStatus { initial, loading, loaded, error }
|
||||
|
|
|
|||
|
|
@ -124,19 +124,22 @@ class _SignInScreenState extends State<SignInScreen> {
|
|||
)),
|
||||
)
|
||||
: const CircularProgressIndicator(),
|
||||
|
||||
/// ======= Tombol Login Google ======= ///
|
||||
const SizedBox(height: 20),
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width * 0.5,
|
||||
child: TextButton.icon(
|
||||
onPressed: () {
|
||||
// == Mengirim event login Google ke Bloc == //
|
||||
context.read<SignInBloc>().add(GoogleSignInRequired());
|
||||
},
|
||||
icon: const Text(
|
||||
'G',
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.blue,
|
||||
icon: SizedBox(
|
||||
height: 20,
|
||||
width: 20,
|
||||
child: Image.asset(
|
||||
'images/google-icon-logo.png',
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
label: const Text(
|
||||
|
|
|
|||
|
|
@ -234,16 +234,16 @@ class _SignUpScreenState extends State<SignUpScreen> {
|
|||
.read<SignUpBloc>()
|
||||
.add(GoogleSignInRequired());
|
||||
},
|
||||
icon: const Text(
|
||||
'G',
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.blue,
|
||||
icon: SizedBox(
|
||||
height: 20,
|
||||
width: 20,
|
||||
child: Image.asset(
|
||||
'images/google-icon-logo.png',
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
label: const Text(
|
||||
'Daftar dengan Google',
|
||||
'Sign In With Google',
|
||||
style: TextStyle(
|
||||
color: Colors.black,
|
||||
fontSize: 16,
|
||||
|
|
|
|||
|
|
@ -1,10 +1,66 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import '../../../blocs/authentication_bloc/authentication_bloc.dart';
|
||||
import 'main_drawer.dart';
|
||||
|
||||
class HomeScreen extends StatelessWidget {
|
||||
const HomeScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold();
|
||||
return Scaffold(
|
||||
drawer: const MainDrawer(),
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.white,
|
||||
elevation: 0.5, // Kasih sedikit bayangan tipis agar elegan
|
||||
centerTitle: false,
|
||||
leading: Builder(
|
||||
builder: (context) => IconButton(
|
||||
icon: const Icon(Icons.menu,
|
||||
color: Colors.black), // Ikon garis 3 horizontal
|
||||
onPressed: () {
|
||||
// ini kodenya untuk membuka:
|
||||
Scaffold.of(context).openDrawer();
|
||||
},
|
||||
)),
|
||||
title: Row(
|
||||
children: [
|
||||
// Ganti dengan Image.asset jika sudah ada logo
|
||||
const Icon(Icons.cloud_queue, color: Colors.blue, size: 28),
|
||||
const SizedBox(width: 10),
|
||||
Text(
|
||||
"Klimatologi",
|
||||
style: GoogleFonts.rubik(
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 20,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.notifications_none, color: Colors.black),
|
||||
onPressed: () {
|
||||
// Aksi notifikasi
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.logout, color: Colors.black),
|
||||
onPressed: () {
|
||||
// Trigger Logout di AuthenticationBloc kamu
|
||||
context
|
||||
.read<AuthenticationBloc>()
|
||||
.add(AuthenticationLogoutRequested());
|
||||
},
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
],
|
||||
),
|
||||
body: const Center(
|
||||
child: Text("body home page"),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,74 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import '../../../blocs/authentication_bloc/authentication_bloc.dart';
|
||||
|
||||
class MainDrawer extends StatelessWidget {
|
||||
const MainDrawer({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Drawer(
|
||||
child: Column(
|
||||
children: [
|
||||
// Bagian Header (Tempat Nama/Email)
|
||||
BlocBuilder<AuthenticationBloc, AuthenticationState>(
|
||||
builder: (context, state) {
|
||||
// Mengambil email user dari Bloc Global
|
||||
String userEmail = state.user?.email ?? "Guest";
|
||||
String userName =
|
||||
userEmail.split('@')[0]; // Ambil depan email saja
|
||||
|
||||
return UserAccountsDrawerHeader(
|
||||
decoration: const BoxDecoration(color: Colors.blue),
|
||||
currentAccountPicture: const CircleAvatar(
|
||||
backgroundColor: Colors.white,
|
||||
child: Icon(Icons.person, size: 40, color: Colors.blue),
|
||||
),
|
||||
accountName: Text(
|
||||
userName,
|
||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
accountEmail: Text(userEmail),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
// Daftar Menu
|
||||
ListTile(
|
||||
leading: const Icon(Icons.speed),
|
||||
title: const Text("Wind Speed"),
|
||||
onTap: () {
|
||||
// Navigasi ke Wind Speed
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.water_drop),
|
||||
title: const Text("Evaporasi"),
|
||||
onTap: () {},
|
||||
),
|
||||
ListTile(
|
||||
leading: Image.asset(
|
||||
'images/atmosfer.png',
|
||||
height: 20,
|
||||
width: 20,
|
||||
),
|
||||
title: const Text("Udara"),
|
||||
onTap: () {},
|
||||
),
|
||||
const Spacer(), // Dorong menu logout ke paling bawah
|
||||
const Divider(),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.logout, color: Colors.red),
|
||||
title: const Text("Logout", style: TextStyle(color: Colors.red)),
|
||||
onTap: () {
|
||||
context
|
||||
.read<AuthenticationBloc>()
|
||||
.add(AuthenticationLogoutRequested());
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import 'dart:developer';
|
||||
// import 'dart:developer';
|
||||
import 'dart:async';
|
||||
import 'package:rxdart/rxdart.dart';
|
||||
// import 'package:rxdart/rxdart.dart';
|
||||
import 'package:firebase_database/firebase_database.dart';
|
||||
import 'package:monitoring_repository/monitoring_repository.dart';
|
||||
|
||||
|
|
|
|||
|
|
@ -31,5 +31,5 @@ dev_dependencies:
|
|||
sdk: flutter
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
# assets:
|
||||
# - assets/images/
|
||||
assets:
|
||||
- images/
|
||||
|
|
|
|||
Loading…
Reference in New Issue