diff --git a/agrilink_vocpro/assets/images/valve.png b/agrilink_vocpro/assets/images/valve.png index 5a4bbfd..6472d7c 100644 Binary files a/agrilink_vocpro/assets/images/valve.png and b/agrilink_vocpro/assets/images/valve.png differ diff --git a/agrilink_vocpro/assets/images/water_pump.png b/agrilink_vocpro/assets/images/water_pump.png new file mode 100644 index 0000000..352235d Binary files /dev/null and b/agrilink_vocpro/assets/images/water_pump.png differ diff --git a/agrilink_vocpro/lib/features/control/provider/control_provider.dart b/agrilink_vocpro/lib/features/control/provider/control_provider.dart index 9361162..55b82e7 100644 --- a/agrilink_vocpro/lib/features/control/provider/control_provider.dart +++ b/agrilink_vocpro/lib/features/control/provider/control_provider.dart @@ -1,4 +1,5 @@ import 'package:agrilink_vocpro/core/state/result_state.dart'; +import 'package:agrilink_vocpro/data/model/relay_response.dart'; import 'package:agrilink_vocpro/domain/service/app_service.dart'; import 'package:flutter/foundation.dart'; @@ -11,6 +12,18 @@ class ControlProvider extends ChangeNotifier { bool _control_2 = false; bool get control_2 => _control_2; + bool _control_3 = false; + bool get control_3 => _control_3; + + Relay _relay1 = Relay(); + Relay get relay1 => _relay1; + + Relay _relay2 = Relay(); + Relay get relay2 => _relay2; + + Relay _relay3 = Relay(); + Relay get relay3 => _relay3; + ControlProvider() { getRelayStatus(); } @@ -28,10 +41,16 @@ class ControlProvider extends ChangeNotifier { if (result.success == true) { for (var element in result.data!) { if (element.number == 1) { - switchControl1(element.currentStatus ?? false); + _relay1 = element; + switchControl(element.currentStatus ?? false, relayNumber: 1); } if (element.number == 2) { - switchControl2(element.currentStatus ?? false); + _relay2 = element; + switchControl(element.currentStatus ?? false, relayNumber: 2); + } + if (element.number == 3) { + _relay3 = element; + switchControl(element.currentStatus ?? false, relayNumber: 3); } } relayState = ResultState.hasData; @@ -53,15 +72,13 @@ class ControlProvider extends ChangeNotifier { relayState = ResultState.loading; notifyListeners(); - final int stateConverted; - if (state == true) { - stateConverted = 1; - } else { - stateConverted = 0; - } + final int stateConverted = state ? 1 : 0; try { final result = await _appService.switchRelay( - relayNumber: relayNumber, state: stateConverted); + relayNumber: relayNumber, + state: stateConverted, + ); + if (result.success == true) { relayState = ResultState.hasData; notifyListeners(); @@ -82,13 +99,20 @@ class ControlProvider extends ChangeNotifier { } } - void switchControl1(bool value) { - _control_1 = value; - notifyListeners(); - } - - void switchControl2(bool value) { - _control_2 = value; + void switchControl(bool value, {required int relayNumber}) { + switch (relayNumber) { + case 1: + _control_1 = value; + break; + case 2: + _control_2 = value; + break; + case 3: + _control_3 = value; + break; + default: + return; + } notifyListeners(); } } diff --git a/agrilink_vocpro/lib/features/control/view/control_screen.dart b/agrilink_vocpro/lib/features/control/view/control_screen.dart index 0ed74d5..dcc7dea 100644 --- a/agrilink_vocpro/lib/features/control/view/control_screen.dart +++ b/agrilink_vocpro/lib/features/control/view/control_screen.dart @@ -1,11 +1,18 @@ +import 'dart:math'; + +import 'package:agrilink_vocpro/core/constant/app_color.dart'; import 'package:agrilink_vocpro/core/constant/app_theme.dart'; +import 'package:agrilink_vocpro/core/extension/extention.dart'; import 'package:agrilink_vocpro/core/state/result_state.dart'; import 'package:agrilink_vocpro/features/control/provider/control_provider.dart'; import 'package:agrilink_vocpro/features/control/widgets/control_button_widget.dart'; +import 'package:agrilink_vocpro/features/control/widgets/pump_status_widget.dart'; +import 'package:agrilink_vocpro/features/control/widgets/relay_status_history_widget.dart'; import 'package:bootstrap_icons/bootstrap_icons.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:gauge_indicator/gauge_indicator.dart'; import 'package:provider/provider.dart'; class ControlScreen extends StatelessWidget { @@ -34,7 +41,7 @@ class ControlScreen extends StatelessWidget { return const StatusBarWidget( text: 'Memuat...', icon: BootstrapIcons.cloud, - color: Colors.cyan, + color: Colors.grey, isLoading: true, ); case ResultState.hasData: @@ -59,6 +66,12 @@ class ControlScreen extends StatelessWidget { ); } }), + PumpStatusWidget( + title: 'Pompa Air', + subtitle: 'Relay 3', + isActive: provider.control_3, + onTap: () {}, + ), SizedBox(height: 16.h), GridView( padding: EdgeInsets.all(16.r), @@ -76,12 +89,11 @@ class ControlScreen extends StatelessWidget { subtitle: 'Relay 1', isActive: provider.control_1, onTap: () async { - final result = provider.control_1 != true - ? await provider.switchRelay(1, true) - : await provider.switchRelay(1, false); - result == true - ? provider.switchControl1(!provider.control_1) - : provider.switchControl1(provider.control_1); + await _changeRelayStateAction( + provider: provider, + relayOn: 1, + relayOff: 2, + ); }, ), ControlButtonWidget( @@ -89,22 +101,102 @@ class ControlScreen extends StatelessWidget { subtitle: 'Relay 2', isActive: provider.control_2, onTap: () async { - final result = provider.control_2 != true - ? await provider.switchRelay(2, true) - : await provider.switchRelay(2, false); - result == true - ? provider.switchControl2(!provider.control_2) - : provider.switchControl2(provider.control_2); + await _changeRelayStateAction( + provider: provider, + relayOn: 2, + relayOff: 1, + ); }, ), ], ), + SizedBox(height: 16.h), + Padding( + padding: EdgeInsets.fromLTRB(16.r, 0, 16.r, 16.h), + child: Row( + children: [ + Icon( + BootstrapIcons.clock, + size: 20.r, + ), + SizedBox(width: 8.w), + Text( + 'Terakhir Diperbarui', + style: AppTheme.labelMedium, + ), + ], + ), + ), + Consumer(builder: (context, provider, child) { + return Column( + children: [ + RelayStatusHistoryWidget( + name: 'Katup Nutrisi', + imageUrl: 'assets/images/valve.png', + enabledAt: provider.relay1.enabledAt, + disabledAt: provider.relay1.disabledAt, + ), + RelayStatusHistoryWidget( + name: 'Katup Air', + imageUrl: 'assets/images/valve.png', + enabledAt: provider.relay2.enabledAt, + disabledAt: provider.relay2.disabledAt, + ), + RelayStatusHistoryWidget( + name: 'Pompa Air', + imageUrl: 'assets/images/water_pump.png', + enabledAt: provider.relay3.enabledAt, + disabledAt: provider.relay3.disabledAt, + ), + ], + ); + }), ], ), ), ), ); } + + Future _changeRelayStateAction({ + required ControlProvider provider, + required int relayOn, + required int relayOff, + }) async { + if (relayOn == 1) { + // Aktifkan relay 1 dan relay 3, matikan relay 2 + if (provider.control_1 == false) { + await provider.switchRelay(relayOff, false); // Matikan relay 2 + await provider.switchRelay(3, true); // Aktifkan relay 3 + await provider.switchRelay(relayOn, true); // Aktifkan relay 1 + provider.switchControl(true, relayNumber: 1); + provider.switchControl(true, relayNumber: 3); + provider.switchControl(false, relayNumber: 2); + } else { + // Matikan semua relay jika relay 1 dimatikan + await provider.switchRelay(3, false); + await provider.switchRelay(relayOn, false); + provider.switchControl(false, relayNumber: 1); + provider.switchControl(false, relayNumber: 3); + } + } else if (relayOn == 2) { + // Aktifkan relay 2 dan relay 3, matikan relay 1 + if (provider.control_2 == false) { + await provider.switchRelay(relayOff, false); // Matikan relay 1 + await provider.switchRelay(3, true); // Aktifkan relay 3 + await provider.switchRelay(relayOn, true); // Aktifkan relay 2 + provider.switchControl(true, relayNumber: 2); + provider.switchControl(true, relayNumber: 3); + provider.switchControl(false, relayNumber: 1); + } else { + // Matikan semua relay jika relay 2 dimatikan + await provider.switchRelay(3, false); + await provider.switchRelay(relayOn, false); + provider.switchControl(false, relayNumber: 2); + provider.switchControl(false, relayNumber: 3); + } + } + } } class StatusBarWidget extends StatelessWidget { diff --git a/agrilink_vocpro/lib/features/control/widgets/pump_status_widget.dart b/agrilink_vocpro/lib/features/control/widgets/pump_status_widget.dart new file mode 100644 index 0000000..5bca5ba --- /dev/null +++ b/agrilink_vocpro/lib/features/control/widgets/pump_status_widget.dart @@ -0,0 +1,75 @@ +import 'package:agrilink_vocpro/core/constant/app_color.dart'; +import 'package:agrilink_vocpro/core/constant/app_theme.dart'; +import 'package:agrilink_vocpro/core/state/result_state.dart'; +import 'package:agrilink_vocpro/features/control/provider/control_provider.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:provider/provider.dart'; + +class PumpStatusWidget extends StatelessWidget { + const PumpStatusWidget({ + super.key, + required this.title, + required this.subtitle, + required this.isActive, + required this.onTap, + }); + + final String title; + final String subtitle; + final bool isActive; + final Function() onTap; + + @override + Widget build(BuildContext context) { + return Container( + height: 100.h, + padding: EdgeInsets.all(16.r), + margin: EdgeInsets.symmetric(horizontal: 16.r), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(16.r), + boxShadow: [ + BoxShadow( + color: isActive + ? AppColor.secondary.withOpacity(0.2) + : Colors.grey.withOpacity(0.2), + spreadRadius: 1.r, + blurRadius: 16.r, + offset: Offset(0, 12.r), + ), + ], + ), + child: Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(title, style: AppTheme.labelMedium), + Text(subtitle, style: AppTheme.labelSmall), + ], + ), + Spacer(), + Consumer( + builder: (context, provider, child) { + switch (provider.relayState) { + case ResultState.loading: + return Image.asset( + 'assets/images/water_pump.png', + width: 80.r, + color: AppColor.textDisable, + ); + default: + return Image.asset( + 'assets/images/water_pump.png', + width: 80.r, + color: isActive ? AppColor.secondary : AppColor.textDisable, + ); + } + }, + ) + ], + ), + ); + } +} diff --git a/agrilink_vocpro/lib/features/home/view/home_screen.dart b/agrilink_vocpro/lib/features/home/view/home_screen.dart index f3fea3d..47dee48 100644 --- a/agrilink_vocpro/lib/features/home/view/home_screen.dart +++ b/agrilink_vocpro/lib/features/home/view/home_screen.dart @@ -126,6 +126,7 @@ class _HomeScreenState extends State { ), ), body: DefaultTabController( + initialIndex: 1, length: 3, child: Stack( children: [ diff --git a/agrilink_vocpro/lib/features/splash/view/splash_screen.dart b/agrilink_vocpro/lib/features/splash/view/splash_screen.dart index e0e9dbd..ab584c2 100644 --- a/agrilink_vocpro/lib/features/splash/view/splash_screen.dart +++ b/agrilink_vocpro/lib/features/splash/view/splash_screen.dart @@ -58,7 +58,7 @@ class _SplashScreenState extends State { } void _navigateAfterSplash(bool isLoggedIn) { - Timer(const Duration(seconds: 2), () { + Timer(const Duration(seconds: 1), () { if (isLoggedIn == true) { context.go(AppRoute.dashboard); } else {