From fc4350f1ddf8e4164c9a450fb5ab2041d043a1cf Mon Sep 17 00:00:00 2001 From: Syaroful Date: Tue, 8 Oct 2024 09:23:48 +0700 Subject: [PATCH] feat: add detail screen (npk sensor) --- agrilink_vocpro/assets/images/error_image.png | Bin 0 -> 17103 bytes .../assets/images/no_data_image.png | Bin 0 -> 17286 bytes .../lib/core/constant/app_constant.dart | 13 + .../lib/core/constant/app_contant.dart | 6 - agrilink_vocpro/lib/core/route/app_route.dart | 173 ++++++++++-- .../lib/domain/provider/censor_provider.dart | 5 - .../lib/domain/service/app_service.dart | 20 ++ .../lib/domain/service/mqtt_service.dart | 55 +++- .../lib/domain/service/service.dart | 1 - .../control/provider/control_provider.dart | 16 +- .../features/control/view/control_screen.dart | 79 +++--- .../view/conductivity_screen.dart | 81 ++++++ .../pages/humidity/view/humidity_screen.dart | 5 +- .../pages/nitrogen/view/nitrogen_screen.dart | 82 ++++++ .../home/pages/ph/view/ph_screen.dart | 202 +++++++------- .../home/pages/ph/widget/ph_bar_pointer.dart | 1 - .../phosphorus/view/phosphorus_screen.dart | 82 ++++++ .../potassium/view/potassium_screen.dart | 82 ++++++ .../view/soil_moisture_screen.dart | 7 +- .../view/soil_temperature_screen.dart | 256 ++++++++++++++++++ .../temperature/view/temperature_screen.dart | 5 +- .../widgets/list_data_from_censor_dht.dart | 24 +- .../widgets/list_data_from_censor_npk1.dart | 40 +-- .../widgets/list_data_from_censor_npk2.dart | 225 ++++++++------- .../features/setting/view/setting_screen.dart | 3 - 25 files changed, 1130 insertions(+), 333 deletions(-) create mode 100644 agrilink_vocpro/assets/images/error_image.png create mode 100644 agrilink_vocpro/assets/images/no_data_image.png create mode 100644 agrilink_vocpro/lib/core/constant/app_constant.dart delete mode 100644 agrilink_vocpro/lib/core/constant/app_contant.dart delete mode 100644 agrilink_vocpro/lib/domain/provider/censor_provider.dart create mode 100644 agrilink_vocpro/lib/domain/service/app_service.dart delete mode 100644 agrilink_vocpro/lib/domain/service/service.dart create mode 100644 agrilink_vocpro/lib/features/home/pages/conductivity/view/conductivity_screen.dart create mode 100644 agrilink_vocpro/lib/features/home/pages/nitrogen/view/nitrogen_screen.dart create mode 100644 agrilink_vocpro/lib/features/home/pages/phosphorus/view/phosphorus_screen.dart create mode 100644 agrilink_vocpro/lib/features/home/pages/potassium/view/potassium_screen.dart create mode 100644 agrilink_vocpro/lib/features/home/pages/soil_temperature/view/soil_temperature_screen.dart diff --git a/agrilink_vocpro/assets/images/error_image.png b/agrilink_vocpro/assets/images/error_image.png new file mode 100644 index 0000000000000000000000000000000000000000..48a9090f673225d3c467957a7afde0ca75b67f6d GIT binary patch literal 17103 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 Lv3KQK^d4t}S(}zoez{~7%}>G0 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 literal 0 HcmV?d00001 diff --git a/agrilink_vocpro/assets/images/no_data_image.png b/agrilink_vocpro/assets/images/no_data_image.png new file mode 100644 index 0000000000000000000000000000000000000000..2ef663af37f32071fb1147204c19b9447cc23b38 GIT binary patch literal 17286 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�$ywD(J_&p4I=o(NbrE=yAA2KVnx^#t_ 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 literal 0 HcmV?d00001 diff --git a/agrilink_vocpro/lib/core/constant/app_constant.dart b/agrilink_vocpro/lib/core/constant/app_constant.dart new file mode 100644 index 0000000..a7dcbf6 --- /dev/null +++ b/agrilink_vocpro/lib/core/constant/app_constant.dart @@ -0,0 +1,13 @@ +class AppConstant { + static const String appName = 'Kebun Pintar'; + static const String appVersion = '1.0.0'; + + static const String baseUrl = 'https://agrilinkvocpropisdev.id/api/v1'; + + static const String mqttServer = 'armadillo.rmq.cloudamqp.com'; + static const String mqttUsername = 'obyskxhx:obyskxhx'; + static const String mqttPassword = 'Fe_3_tBuwmc8vMMqT2hYiboTsBlBmPz1'; + + static const String soilTempInfo = + 'Suhu tanah mengacu pada suhu tanah di permukaan atau pada kedalaman tertentu, yang berperan penting dalam pertumbuhan tanaman dan proses pertanian. Suhu ini memengaruhi perkecambahan benih, aktivitas akar, serta penyerapan air dan nutrisi, yang semuanya esensial bagi perkembangan tanaman. Selain itu, suhu tanah juga memengaruhi aktivitas mikroorganisme yang berkontribusi pada kesuburan tanah. Dalam pertanian pintar, sensor suhu tanah sering digunakan untuk memantau dan mengoptimalkan kondisi tanah, memastikan tanaman tumbuh dalam rentang suhu yang ideal.'; +} diff --git a/agrilink_vocpro/lib/core/constant/app_contant.dart b/agrilink_vocpro/lib/core/constant/app_contant.dart deleted file mode 100644 index 74b3f0c..0000000 --- a/agrilink_vocpro/lib/core/constant/app_contant.dart +++ /dev/null @@ -1,6 +0,0 @@ -class AppContant { - static const String appName = 'Kebun Pintar'; - static const String appVersion = '1.0.0'; - - static const String baseUrl = 'https://kebunpintar.id/api/v1'; -} diff --git a/agrilink_vocpro/lib/core/route/app_route.dart b/agrilink_vocpro/lib/core/route/app_route.dart index 9b7b193..eebfe73 100644 --- a/agrilink_vocpro/lib/core/route/app_route.dart +++ b/agrilink_vocpro/lib/core/route/app_route.dart @@ -1,8 +1,15 @@ import 'package:agrilink_vocpro/features/auth/view/login_screen.dart'; import 'package:agrilink_vocpro/features/dashboard/view/dashboard_screen.dart'; +import 'package:agrilink_vocpro/features/home/pages/conductivity/view/conductivity_screen.dart'; import 'package:agrilink_vocpro/features/home/pages/humidity/view/humidity_screen.dart'; import 'package:agrilink_vocpro/features/home/pages/light/view/light_screen.dart'; +import 'package:agrilink_vocpro/features/home/pages/nitrogen/view/nitrogen_screen.dart'; import 'package:agrilink_vocpro/features/home/pages/ph/view/ph_screen.dart'; +import 'package:agrilink_vocpro/features/home/pages/phosphorus/view/phosphorus_screen.dart'; +import 'package:agrilink_vocpro/features/home/pages/potassium/view/potassium_screen.dart'; +import 'package:agrilink_vocpro/features/home/pages/soil_moisture/view/soil_moisture_screen.dart'; +import 'package:agrilink_vocpro/features/home/pages/soil_temperature/view/soil_temperature_screen.dart'; +import 'package:agrilink_vocpro/features/home/pages/temperature/view/temperature_screen.dart'; import 'package:agrilink_vocpro/features/splash/view/splash_screen.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; @@ -15,12 +22,15 @@ class AppRoute { static const String profile = '/profile'; static const String setting = '/setting'; static const String humidity = '/dashboard/humidity'; - static const String temperature = '/temperature'; - static const String soil = '/soil'; + static const String temperature = '/dashboard/temperature'; + static const String soilTemperature = '/dashboard/soil_temperature'; + static const String soilMoisture = '/dashboard/soil_moisture'; static const String light = '/dashboard/light'; static const String ph = '/dashboard/ph'; - static const String water = '/water'; - static const String acidity = '/acidity'; + static const String conductivity = '/dashboard/conductivity'; + static const String nitrogen = '/dashboard/nitrogen'; + static const String phosphorus = '/dashboard/phosphorus'; + static const String potassium = '/dashboard/potassium'; static final GoRouter router = GoRouter( initialLocation: root, @@ -35,35 +45,30 @@ class AppRoute { path: login, builder: (context, state) => const LoginScreen(), ), - //dashboard - GoRoute( - path: dashboard, - builder: (context, state) => const DashboardScreen(), - ), GoRoute( path: dashboard, builder: (context, state) => const DashboardScreen(), routes: [ - GoRoute( - path: 'humidity', - builder: (context, state) => const HumidityScreen(), - ), - GoRoute( - path: 'light/:value', - builder: (context, state) { - final double value = - double.tryParse(state.pathParameters['value'] ?? '') ?? 0.0; - return LightScreen(lightIntensity: value); - }, - ), - GoRoute( - path: 'ph/:value', - builder: (context, state) { - final double value = - double.tryParse(state.pathParameters['value'] ?? '') ?? 0.0; - return PhScreen(phValue: value); - }, - ), + // humidity + buildHumidityRoute(), + // light intensity + buildLightIntensityRoute(), + // temperature + buildTemperatureRoute(), + // ph + buildAcidityRoute(), + // soil moisture + buildSoilTempRoute(), + // soil moisture + buildSoilMoistureRoute(), + // conductivity + buildConductivityRoute(), + // nitrogen + buildNitrogenRoute(), + // phosphorus + buildPhosphorusRoute(), + // potassium + buildPotassiumRoute(), ], ), ], @@ -73,4 +78,114 @@ class AppRoute { ), ), ); + + static GoRoute buildPotassiumRoute() { + return GoRoute( + path: 'potassium/:value', + builder: (context, state) { + final double value = + double.tryParse(state.pathParameters['value'] ?? '') ?? 0.0; + return PotassiumScreen(potassium: value); + }, + ); + } + + static GoRoute buildPhosphorusRoute() { + return GoRoute( + path: 'phosphorus/:value', + builder: (context, state) { + final double value = + double.tryParse(state.pathParameters['value'] ?? '') ?? 0.0; + return PhosphorusScreen(phosphorus: value); + }, + ); + } + + static GoRoute buildNitrogenRoute() { + return GoRoute( + path: 'nitrogen/:value', + builder: (context, state) { + final double value = + double.tryParse(state.pathParameters['value'] ?? '') ?? 0.0; + return NitrogenScreen(nitrogen: value); + }, + ); + } + + static GoRoute buildConductivityRoute() { + return GoRoute( + path: 'conductivity/:value', + builder: (context, state) { + final double value = + double.tryParse(state.pathParameters['value'] ?? '') ?? 0.0; + return ConductivityScreen(conductivity: value); + }, + ); + } + + static GoRoute buildSoilMoistureRoute() { + return GoRoute( + path: 'soil_moisture/:value', + builder: (context, state) { + final double value = + double.tryParse(state.pathParameters['value'] ?? '') ?? 0.0; + return SoilMoistureScreen(moisture: value); + }, + ); + } + + static GoRoute buildSoilTempRoute() { + return GoRoute( + path: 'soil_temperature/:value', + builder: (context, state) { + final double value = + double.tryParse(state.pathParameters['value'] ?? '') ?? 0.0; + return SoilTemperatureScreen(temperature: value); + }, + ); + } + + static GoRoute buildAcidityRoute() { + return GoRoute( + path: 'ph/:value', + builder: (context, state) { + final double value = + double.tryParse(state.pathParameters['value'] ?? '') ?? 0.0; + return PhScreen(phValue: value); + }, + ); + } + + static GoRoute buildTemperatureRoute() { + return GoRoute( + path: 'temperature/:value', + builder: (context, state) { + final double value = + double.tryParse(state.pathParameters['value'] ?? '') ?? 0.0; + return TemperatureScreen(temperature: value); + }, + ); + } + + static GoRoute buildLightIntensityRoute() { + return GoRoute( + path: 'light/:value', + builder: (context, state) { + final double value = + double.tryParse(state.pathParameters['value'] ?? '') ?? 0.0; + return LightScreen(lightIntensity: value); + }, + ); + } + + static GoRoute buildHumidityRoute() { + return GoRoute( + path: 'humidity/:value', + builder: (context, state) { + final double value = + double.tryParse(state.pathParameters['value'] ?? '') ?? 0.0; + return HumidityScreen(humidity: value); + }, + ); + } } diff --git a/agrilink_vocpro/lib/domain/provider/censor_provider.dart b/agrilink_vocpro/lib/domain/provider/censor_provider.dart deleted file mode 100644 index 194d624..0000000 --- a/agrilink_vocpro/lib/domain/provider/censor_provider.dart +++ /dev/null @@ -1,5 +0,0 @@ -import 'package:flutter/material.dart'; - -class CensorProvider extends ChangeNotifier { - Future setupMqtt() async {} -} diff --git a/agrilink_vocpro/lib/domain/service/app_service.dart b/agrilink_vocpro/lib/domain/service/app_service.dart new file mode 100644 index 0000000..bb89afc --- /dev/null +++ b/agrilink_vocpro/lib/domain/service/app_service.dart @@ -0,0 +1,20 @@ +import 'package:agrilink_vocpro/core/constant/app_contant.dart'; +import 'package:dio/dio.dart'; +import 'package:flutter/foundation.dart'; + +class AppService { + final Dio _dioWithoutInterceptor = Dio( + BaseOptions( + baseUrl: AppConstant.baseUrl, + ), + ); + + Future getGreenHouseData() async { + try {} on DioException catch (e) { + if (kDebugMode) { + print(e); + } + rethrow; + } + } +} diff --git a/agrilink_vocpro/lib/domain/service/mqtt_service.dart b/agrilink_vocpro/lib/domain/service/mqtt_service.dart index a7c4d31..21002a8 100644 --- a/agrilink_vocpro/lib/domain/service/mqtt_service.dart +++ b/agrilink_vocpro/lib/domain/service/mqtt_service.dart @@ -1,3 +1,6 @@ +import 'dart:convert'; + +import 'package:agrilink_vocpro/core/constant/app_constant.dart'; import 'package:agrilink_vocpro/core/state/result_state.dart'; import 'package:mqtt_client/mqtt_client.dart'; import 'package:mqtt_client/mqtt_server_client.dart'; @@ -6,11 +9,11 @@ class MQTTService { MqttServerClient? client; Future setupMqtt() async { - client = MqttServerClient('armadillo.rmq.cloudamqp.com', ''); + client = MqttServerClient(AppConstant.mqttServer, ''); client!.port = 1883; client!.connectionMessage = MqttConnectMessage() - .authenticateAs('obyskxhx:obyskxhx', 'Fe_3_tBuwmc8vMMqT2hYiboTsBlBmPz1') + .authenticateAs(AppConstant.mqttUsername, AppConstant.mqttPassword) .withClientIdentifier('mobile_client_controller') .startClean() // reset session .withWillQos(MqttQos.atLeastOnce); @@ -90,14 +93,14 @@ class MQTTService { client!.updates!.listen( (List>? messages) { - print('MQTT: Message received!'); + print('MQTT: Subscribe Message received!'); if (messages != null && messages.isNotEmpty) { final MqttPublishMessage recMessage = messages[0].payload as MqttPublishMessage; final String payload = MqttPublishPayload.bytesToStringAsString( recMessage.payload.message); print( - 'MQTT: Message received on topic ${messages[0].topic}: $payload'); + 'MQTT: Subscribe Message received on topic ${messages[0].topic}: $payload'); if (payload == 'ON') { isActive = true; @@ -105,9 +108,11 @@ class MQTTService { } else if (payload == 'OFF') { isActive = false; // Update UI atau provider untuk menandakan relay OFF + } else { + print('MQTT: Invalid Subscribe message received'); } } else { - print('MQTT: No messages received'); + print('MQTT: No Subscribe messages received'); } }, ); @@ -122,4 +127,44 @@ class MQTTService { return false; } } + + Future subscribeToRelayStatus() async { + if (client != null && + client!.connectionStatus!.state == MqttConnectionState.connected) { + try { + print('MQTT: Subscribing to /smartfarming/getRelayStatus'); + client!.subscribe('smartfarming/getRelayStatus', MqttQos.atMostOnce); + print('MQTT: Subscribed to /smartfarming/getRelayStatus'); + + client!.updates! + .listen((List>? messages) { + if (messages != null && messages.isNotEmpty) { + final MqttPublishMessage recMessage = + messages[0].payload as MqttPublishMessage; + final String payload = MqttPublishPayload.bytesToStringAsString( + recMessage.payload.message); + print( + 'MQTT: Message received on topic ${messages[0].topic}: $payload'); + + // Parse the received JSON payload + final Map relayStatus = jsonDecode(payload); + + print('MQTT: Relay status: $relayStatus'); + + // Assuming you are using provider, notify it with new relay status + // _updateRelayStatus(relayStatus); + } else { + print('MQTT: No messages received'); + } + }); + return ResultState.hasData; + } catch (e) { + print('MQTT: Error subscribing: $e'); + return ResultState.error; + } + } else { + print('MQTT: Not connected, cannot subscribe.'); + return ResultState.error; + } + } } diff --git a/agrilink_vocpro/lib/domain/service/service.dart b/agrilink_vocpro/lib/domain/service/service.dart deleted file mode 100644 index 97df96f..0000000 --- a/agrilink_vocpro/lib/domain/service/service.dart +++ /dev/null @@ -1 +0,0 @@ -class Service {} diff --git a/agrilink_vocpro/lib/features/control/provider/control_provider.dart b/agrilink_vocpro/lib/features/control/provider/control_provider.dart index 6b56df4..42b96a6 100644 --- a/agrilink_vocpro/lib/features/control/provider/control_provider.dart +++ b/agrilink_vocpro/lib/features/control/provider/control_provider.dart @@ -25,14 +25,14 @@ class ControlProvider extends ChangeNotifier { final result = await _mqttService.setupMqtt(); if (result == ResultState.hasData) { mqttState = result; - final result2 = await _mqttService.subscribeToTopic('relay1'); - if (result2 == true) { - subscribeState = ResultState.hasData; - _control_1 = true; - } else { - subscribeState = ResultState.hasData; - _control_1 = false; - } + final result2 = await _mqttService.subscribeToRelayStatus(); + // if (result2 == true) { + // subscribeState = ResultState.hasData; + // _control_1 = true; + // } else { + // subscribeState = ResultState.hasData; + // _control_1 = false; + // } } else { mqttState = ResultState.error; } diff --git a/agrilink_vocpro/lib/features/control/view/control_screen.dart b/agrilink_vocpro/lib/features/control/view/control_screen.dart index 9595b7c..c824b06 100644 --- a/agrilink_vocpro/lib/features/control/view/control_screen.dart +++ b/agrilink_vocpro/lib/features/control/view/control_screen.dart @@ -18,55 +18,40 @@ class ControlScreen extends StatelessWidget { backgroundColor: Colors.white, scrolledUnderElevation: 0, ), - body: Consumer(builder: (context, provider, child) { - return SafeArea( + body: Consumer( + builder: (context, provider, child) { + return SafeArea( child: ListView( - children: [ - Center( - child: provider.mqttState == ResultState.loading - ? const CupertinoActivityIndicator() - : provider.mqttState == ResultState.hasData - ? const Text('Terhubung ke Broker') - : const Text('Gagal terhubung ke Broker'), + children: [ + Center( + child: provider.mqttState == ResultState.loading + ? const CupertinoActivityIndicator() + : provider.mqttState == ResultState.hasData + ? const Text('Terhubung ke Broker') + : const Text('Gagal terhubung ke Broker'), + ), + SizedBox(height: 16.h), + ListTile( + title: Text('Control 1', style: AppTheme.labelMedium), + subtitle: const Text('Control 1 description'), + trailing: Switch( + value: provider.control_1, + onChanged: (value) { + provider.control_1 == false + ? provider.publishMessage( + 'smartfarming/relay/232', 'ON') + : provider.publishMessage( + 'smartfarming/relay/232', 'OFF'); + provider.switchControl1(); + }, + ), + ), + // Control lainnya... + ], ), - SizedBox(height: 16.h), - ListTile( - title: Text('Control 1', style: AppTheme.labelMedium), - subtitle: const Text('Control 1 description'), - trailing: Switch( - value: provider.control_1, - onChanged: (value) { - provider.control_1 == false - ? provider.publishMessage('relay1', 'ON') - : provider.publishMessage('relay1', 'OFF'); - provider.switchControl1(); - // showDialog( - // context: context, - // builder: (context) => AlertDialog( - // title: const Text('Konfirmasi'), - // content: const Text('Atur Relay 1?'), - // actions: [ - // TextButton( - // onPressed: () { - // provider.control_1 == false - // ? provider.publishMessage('relay1', 'ON') - // : provider.publishMessage('relay1', 'OFF'); - // provider.switchControl1(); - // Navigator.pop(context); - // }, - // child: - // Text(provider.control_1 == false ? 'ON' : 'OFF'), - // ) - // ], - // ), - // ); - }, - ), - ), - // Control lainnya... - ], - )); - }), + ); + }, + ), ); } } diff --git a/agrilink_vocpro/lib/features/home/pages/conductivity/view/conductivity_screen.dart b/agrilink_vocpro/lib/features/home/pages/conductivity/view/conductivity_screen.dart new file mode 100644 index 0000000..2129116 --- /dev/null +++ b/agrilink_vocpro/lib/features/home/pages/conductivity/view/conductivity_screen.dart @@ -0,0 +1,81 @@ +import 'package:agrilink_vocpro/core/constant/app_theme.dart'; +import 'package:agrilink_vocpro/features/home/widgets/graphic_widget.dart'; +import 'package:bootstrap_icons/bootstrap_icons.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; + +class ConductivityScreen extends StatelessWidget { + const ConductivityScreen({super.key, this.conductivity = 0.0}); + + final double conductivity; + double get value => conductivity; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Conductivity', style: AppTheme.labelMedium), + centerTitle: true, + backgroundColor: Colors.white, + scrolledUnderElevation: 0, + actions: const [ + Padding( + padding: EdgeInsets.only(right: 16), + child: Icon( + Icons.electric_bolt_rounded, + color: Colors.blue, + ), + ) + ], + ), + body: Center( + child: ListView( + padding: EdgeInsets.all(16.w), + children: [ + SizedBox(height: 32.h), + Column( + children: [ + Icon( + Icons.electric_bolt_rounded, + size: 64.r, + color: Colors.blue, + ), + Text('$value µS/cm', style: AppTheme.headline1), + ], + ), + SizedBox(height: 32.h), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'Daya Arus Listrik', + style: AppTheme.labelMedium, + textAlign: TextAlign.center, + ), + IconButton( + iconSize: 20.r, + color: Colors.blue, + onPressed: () {}, + icon: const Icon(BootstrapIcons.info_circle)) + ], + ), + SizedBox(height: 16.h), + const Text('Grafik'), + SizedBox(height: 16.h), + AspectRatio( + aspectRatio: 1.6.h, + child: Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(16.w), + border: Border.all(color: Colors.grey.shade300, width: 1.w), + ), + child: const GarphicWidget(), + ), + ) + ], + ), + ), + ); + } +} diff --git a/agrilink_vocpro/lib/features/home/pages/humidity/view/humidity_screen.dart b/agrilink_vocpro/lib/features/home/pages/humidity/view/humidity_screen.dart index 95ee1d3..40142d3 100644 --- a/agrilink_vocpro/lib/features/home/pages/humidity/view/humidity_screen.dart +++ b/agrilink_vocpro/lib/features/home/pages/humidity/view/humidity_screen.dart @@ -9,7 +9,10 @@ import 'package:gauge_indicator/gauge_indicator.dart'; import 'package:provider/provider.dart'; class HumidityScreen extends StatelessWidget { - const HumidityScreen({super.key}); + const HumidityScreen({super.key, this.humidity = 0}); + + final double humidity; + double get value => humidity; @override Widget build(BuildContext context) { diff --git a/agrilink_vocpro/lib/features/home/pages/nitrogen/view/nitrogen_screen.dart b/agrilink_vocpro/lib/features/home/pages/nitrogen/view/nitrogen_screen.dart new file mode 100644 index 0000000..5753173 --- /dev/null +++ b/agrilink_vocpro/lib/features/home/pages/nitrogen/view/nitrogen_screen.dart @@ -0,0 +1,82 @@ +import 'package:agrilink_vocpro/core/constant/app_theme.dart'; +import 'package:agrilink_vocpro/features/home/widgets/graphic_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'; + +class NitrogenScreen extends StatelessWidget { + const NitrogenScreen({super.key, this.nitrogen = 0.0}); + + final double nitrogen; + double get value => nitrogen; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Nitrogen', style: AppTheme.labelMedium), + centerTitle: true, + backgroundColor: Colors.white, + scrolledUnderElevation: 0, + actions: const [ + Padding( + padding: EdgeInsets.only(right: 16), + child: Icon( + CupertinoIcons.eyedropper, + color: Colors.blue, + ), + ) + ], + ), + body: Center( + child: ListView( + padding: EdgeInsets.all(16.w), + children: [ + SizedBox(height: 32.h), + Column( + children: [ + Icon( + CupertinoIcons.eyedropper, + size: 64.r, + color: Colors.blue, + ), + Text('$value µS/cm', style: AppTheme.headline1), + ], + ), + SizedBox(height: 32.h), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'Nitrogen', + style: AppTheme.labelMedium, + textAlign: TextAlign.center, + ), + IconButton( + iconSize: 20.r, + color: Colors.blue, + onPressed: () {}, + icon: const Icon(BootstrapIcons.info_circle)) + ], + ), + SizedBox(height: 16.h), + const Text('Grafik'), + SizedBox(height: 16.h), + AspectRatio( + aspectRatio: 1.6.h, + child: Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(16.w), + border: Border.all(color: Colors.grey.shade300, width: 1.w), + ), + child: const GarphicWidget(), + ), + ) + ], + ), + ), + ); + } +} diff --git a/agrilink_vocpro/lib/features/home/pages/ph/view/ph_screen.dart b/agrilink_vocpro/lib/features/home/pages/ph/view/ph_screen.dart index a54000e..85ea0d7 100644 --- a/agrilink_vocpro/lib/features/home/pages/ph/view/ph_screen.dart +++ b/agrilink_vocpro/lib/features/home/pages/ph/view/ph_screen.dart @@ -24,8 +24,8 @@ class PhScreen extends StatelessWidget { Padding( padding: EdgeInsets.only(right: 16), child: Icon( - BootstrapIcons.thermometer_half, - color: Colors.red, + BootstrapIcons.pie_chart, + color: Colors.orange, ), ) ], @@ -58,105 +58,105 @@ class PhScreen extends StatelessWidget { ], ), SizedBox(height: 16.h), - Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - Container( - height: 100.h, - width: 100.w, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(16), - color: Colors.blue.withOpacity(0.1), - border: Border.all( - color: Colors.blue, - width: 2, - ), - ), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text('Low', - style: AppTheme.labelMedium - .copyWith(color: Colors.blue)), - // SizedBox(height: 8.h), - // const Icon( - // BootstrapIcons.thermometer_low, - // color: Colors.blue, - // ), - SizedBox(height: 8.h), - Text( - '<20°C', - style: AppTheme.labelMedium, - textAlign: TextAlign.center, - ), - ], - ), - ), - Container( - height: 100.h, - width: 100.w, - decoration: BoxDecoration( - color: Colors.green.withOpacity(0.1), - borderRadius: BorderRadius.circular(16), - border: Border.all( - color: Colors.green, - width: 2, - ), - ), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text('Ideal', - style: AppTheme.labelMedium - .copyWith(color: Colors.green)), - // SizedBox(height: 8.h), - // const Icon( - // BootstrapIcons.thermometer_half, - // color: Colors.green, - // ), - SizedBox(height: 8.h), - Text( - '20-30°C', - style: AppTheme.labelMedium, - textAlign: TextAlign.center, - ), - ], - ), - ), - Container( - height: 100.h, - width: 100.w, - decoration: BoxDecoration( - color: Colors.orange.withOpacity(0.1), - borderRadius: BorderRadius.circular(16), - border: Border.all( - color: Colors.orange.shade800, - width: 2, - ), - ), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text('high', - style: AppTheme.labelMedium - .copyWith(color: Colors.orange)), - // SizedBox(height: 8.h), - // const Icon( - // BootstrapIcons.thermometer_high, - // color: Colors.orange, - // ), - SizedBox(height: 8.h), - Text( - '>30°C', - style: AppTheme.labelMedium, - textAlign: TextAlign.center, - ), - ], - ), - ), - ], - ), - SizedBox(height: 16.h), + // Row( + // mainAxisAlignment: MainAxisAlignment.spaceAround, + // children: [ + // Container( + // height: 100.h, + // width: 100.w, + // decoration: BoxDecoration( + // borderRadius: BorderRadius.circular(16), + // color: Colors.blue.withOpacity(0.1), + // border: Border.all( + // color: Colors.blue, + // width: 2, + // ), + // ), + // child: Column( + // mainAxisAlignment: MainAxisAlignment.center, + // children: [ + // Text('Low', + // style: AppTheme.labelMedium + // .copyWith(color: Colors.blue)), + // // SizedBox(height: 8.h), + // // const Icon( + // // BootstrapIcons.thermometer_low, + // // color: Colors.blue, + // // ), + // SizedBox(height: 8.h), + // Text( + // '<20°C', + // style: AppTheme.labelMedium, + // textAlign: TextAlign.center, + // ), + // ], + // ), + // ), + // Container( + // height: 100.h, + // width: 100.w, + // decoration: BoxDecoration( + // color: Colors.green.withOpacity(0.1), + // borderRadius: BorderRadius.circular(16), + // border: Border.all( + // color: Colors.green, + // width: 2, + // ), + // ), + // child: Column( + // mainAxisAlignment: MainAxisAlignment.center, + // children: [ + // Text('Ideal', + // style: AppTheme.labelMedium + // .copyWith(color: Colors.green)), + // // SizedBox(height: 8.h), + // // const Icon( + // // BootstrapIcons.thermometer_half, + // // color: Colors.green, + // // ), + // SizedBox(height: 8.h), + // Text( + // '20-30°C', + // style: AppTheme.labelMedium, + // textAlign: TextAlign.center, + // ), + // ], + // ), + // ), + // Container( + // height: 100.h, + // width: 100.w, + // decoration: BoxDecoration( + // color: Colors.orange.withOpacity(0.1), + // borderRadius: BorderRadius.circular(16), + // border: Border.all( + // color: Colors.orange.shade800, + // width: 2, + // ), + // ), + // child: Column( + // mainAxisAlignment: MainAxisAlignment.center, + // children: [ + // Text('high', + // style: AppTheme.labelMedium + // .copyWith(color: Colors.orange)), + // // SizedBox(height: 8.h), + // // const Icon( + // // BootstrapIcons.thermometer_high, + // // color: Colors.orange, + // // ), + // SizedBox(height: 8.h), + // Text( + // '>30°C', + // style: AppTheme.labelMedium, + // textAlign: TextAlign.center, + // ), + // ], + // ), + // ), + // ], + // ), + // SizedBox(height: 16.h), const Text('Grafik'), SizedBox(height: 16.h), AspectRatio( diff --git a/agrilink_vocpro/lib/features/home/pages/ph/widget/ph_bar_pointer.dart b/agrilink_vocpro/lib/features/home/pages/ph/widget/ph_bar_pointer.dart index 4d9e804..60f5f39 100644 --- a/agrilink_vocpro/lib/features/home/pages/ph/widget/ph_bar_pointer.dart +++ b/agrilink_vocpro/lib/features/home/pages/ph/widget/ph_bar_pointer.dart @@ -1,5 +1,4 @@ import 'package:agrilink_vocpro/core/constant/app_theme.dart'; -import 'package:bootstrap_icons/bootstrap_icons.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; diff --git a/agrilink_vocpro/lib/features/home/pages/phosphorus/view/phosphorus_screen.dart b/agrilink_vocpro/lib/features/home/pages/phosphorus/view/phosphorus_screen.dart new file mode 100644 index 0000000..d273827 --- /dev/null +++ b/agrilink_vocpro/lib/features/home/pages/phosphorus/view/phosphorus_screen.dart @@ -0,0 +1,82 @@ +import 'package:agrilink_vocpro/core/constant/app_theme.dart'; +import 'package:agrilink_vocpro/features/home/widgets/graphic_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'; + +class PhosphorusScreen extends StatelessWidget { + const PhosphorusScreen({super.key, this.phosphorus = 0.0}); + + final double phosphorus; + double get value => phosphorus; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Phosphorus', style: AppTheme.labelMedium), + centerTitle: true, + backgroundColor: Colors.white, + scrolledUnderElevation: 0, + actions: const [ + Padding( + padding: EdgeInsets.only(right: 16), + child: Icon( + CupertinoIcons.eyedropper, + color: Colors.blue, + ), + ) + ], + ), + body: Center( + child: ListView( + padding: EdgeInsets.all(16.w), + children: [ + SizedBox(height: 32.h), + Column( + children: [ + Icon( + CupertinoIcons.eyedropper, + size: 64.r, + color: Colors.blue, + ), + Text('$value ppm', style: AppTheme.headline1), + ], + ), + SizedBox(height: 32.h), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'Fosfor', + style: AppTheme.labelMedium, + textAlign: TextAlign.center, + ), + IconButton( + iconSize: 20.r, + color: Colors.blue, + onPressed: () {}, + icon: const Icon(BootstrapIcons.info_circle)) + ], + ), + SizedBox(height: 16.h), + const Text('Grafik'), + SizedBox(height: 16.h), + AspectRatio( + aspectRatio: 1.6.h, + child: Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(16.w), + border: Border.all(color: Colors.grey.shade300, width: 1.w), + ), + child: const GarphicWidget(), + ), + ) + ], + ), + ), + ); + } +} diff --git a/agrilink_vocpro/lib/features/home/pages/potassium/view/potassium_screen.dart b/agrilink_vocpro/lib/features/home/pages/potassium/view/potassium_screen.dart new file mode 100644 index 0000000..d38d726 --- /dev/null +++ b/agrilink_vocpro/lib/features/home/pages/potassium/view/potassium_screen.dart @@ -0,0 +1,82 @@ +import 'package:agrilink_vocpro/core/constant/app_theme.dart'; +import 'package:agrilink_vocpro/features/home/widgets/graphic_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'; + +class PotassiumScreen extends StatelessWidget { + const PotassiumScreen({super.key, this.potassium = 0.0}); + + final double potassium; + double get value => potassium; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Potassium', style: AppTheme.labelMedium), + centerTitle: true, + backgroundColor: Colors.white, + scrolledUnderElevation: 0, + actions: const [ + Padding( + padding: EdgeInsets.only(right: 16), + child: Icon( + CupertinoIcons.eyedropper, + color: Colors.green, + ), + ) + ], + ), + body: Center( + child: ListView( + padding: EdgeInsets.all(16.w), + children: [ + SizedBox(height: 32.h), + Column( + children: [ + Icon( + CupertinoIcons.eyedropper, + size: 64.r, + color: Colors.green, + ), + Text('$value ppm', style: AppTheme.headline1), + ], + ), + SizedBox(height: 32.h), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'Kalium', + style: AppTheme.labelMedium, + textAlign: TextAlign.center, + ), + IconButton( + iconSize: 20.r, + color: Colors.blue, + onPressed: () {}, + icon: const Icon(BootstrapIcons.info_circle)) + ], + ), + SizedBox(height: 16.h), + const Text('Grafik'), + SizedBox(height: 16.h), + AspectRatio( + aspectRatio: 1.6.h, + child: Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(16.w), + border: Border.all(color: Colors.grey.shade300, width: 1.w), + ), + child: const GarphicWidget(), + ), + ) + ], + ), + ), + ); + } +} diff --git a/agrilink_vocpro/lib/features/home/pages/soil_moisture/view/soil_moisture_screen.dart b/agrilink_vocpro/lib/features/home/pages/soil_moisture/view/soil_moisture_screen.dart index 3847ae9..05de5ca 100644 --- a/agrilink_vocpro/lib/features/home/pages/soil_moisture/view/soil_moisture_screen.dart +++ b/agrilink_vocpro/lib/features/home/pages/soil_moisture/view/soil_moisture_screen.dart @@ -6,7 +6,10 @@ import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:gauge_indicator/gauge_indicator.dart'; class SoilMoistureScreen extends StatelessWidget { - const SoilMoistureScreen({super.key}); + const SoilMoistureScreen({super.key, this.moisture = 0}); + + final double moisture; + double get value => moisture; @override Widget build(BuildContext context) { @@ -55,7 +58,7 @@ class SoilMoistureScreen extends StatelessWidget { child: AnimatedRadialGauge( duration: const Duration(seconds: 3), curve: Curves.easeOut, - value: 60, + value: value, axis: GaugeAxis( degrees: 360, min: 0, diff --git a/agrilink_vocpro/lib/features/home/pages/soil_temperature/view/soil_temperature_screen.dart b/agrilink_vocpro/lib/features/home/pages/soil_temperature/view/soil_temperature_screen.dart new file mode 100644 index 0000000..5dae429 --- /dev/null +++ b/agrilink_vocpro/lib/features/home/pages/soil_temperature/view/soil_temperature_screen.dart @@ -0,0 +1,256 @@ +import 'package:agrilink_vocpro/core/constant/app_constant.dart'; +import 'package:agrilink_vocpro/core/constant/app_theme.dart'; +import 'package:agrilink_vocpro/features/home/widgets/graphic_widget.dart'; +import 'package:bootstrap_icons/bootstrap_icons.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:gauge_indicator/gauge_indicator.dart'; + +class SoilTemperatureScreen extends StatelessWidget { + const SoilTemperatureScreen({super.key, this.temperature = 0}); + + final double temperature; + double get value => temperature; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Soil Temperature', style: AppTheme.labelMedium), + centerTitle: true, + backgroundColor: Colors.white, + scrolledUnderElevation: 0, + actions: const [ + Padding( + padding: EdgeInsets.only(right: 16), + child: Icon( + BootstrapIcons.water, + color: Colors.green, + ), + ) + ], + ), + body: SafeArea( + child: ListView( + padding: EdgeInsets.all(16.w), + children: [ + SizedBox( + height: MediaQuery.of(context).size.height * 0.05, + ), + SizedBox( + height: 240.h, + child: Stack( + fit: StackFit.expand, + children: [ + Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + height: 80.h, + ), + const Icon(BootstrapIcons.water, + size: 32, color: Colors.green), + Text( + '${value.toStringAsFixed(0)}°C', // Animated percentage text + style: const TextStyle( + fontSize: 28, + fontWeight: FontWeight.bold, + color: Colors.black, + ), + ), + ], + ), + ), + AnimatedRadialGauge( + duration: const Duration(seconds: 2), + curve: Curves.easeOut, + value: value, + axis: GaugeAxis( + degrees: 240, + min: 0, + max: 56.7, + style: GaugeAxisStyle( + background: Colors.grey.shade100, + thickness: 50, + ), + progressBar: const GaugeBasicProgressBar( + gradient: GaugeAxisGradient(colors: [ + Colors.blue, + Colors.orange, + ]), + ), + ), + ), + ], + ), + ), + const SizedBox(height: 16), + const SizedBox(height: 16), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'Soil Temperature', + style: AppTheme.labelMedium, + textAlign: TextAlign.center, + ), + IconButton( + iconSize: 20.r, + color: Colors.blue, + onPressed: () { + showInfo( + context, + 'Soil Temperature', + AppConstant.soilTempInfo, + ); + }, + icon: const Icon(BootstrapIcons.info_circle)) + ], + ), + SizedBox(height: 16.h), + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Container( + height: 100.h, + width: 100.w, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(16), + color: Colors.blue.withOpacity(0.1), + border: Border.all( + color: Colors.blue, + width: 2, + ), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('Low', + style: AppTheme.labelMedium + .copyWith(color: Colors.blue)), + // SizedBox(height: 8.h), + // const Icon( + // BootstrapIcons.thermometer_low, + // color: Colors.blue, + // ), + SizedBox(height: 8.h), + Text( + '<20°C', + style: AppTheme.labelMedium, + textAlign: TextAlign.center, + ), + ], + ), + ), + Container( + height: 100.h, + width: 100.w, + decoration: BoxDecoration( + color: Colors.green.withOpacity(0.1), + borderRadius: BorderRadius.circular(16), + border: Border.all( + color: Colors.green, + width: 2, + ), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('Ideal', + style: AppTheme.labelMedium + .copyWith(color: Colors.green)), + // SizedBox(height: 8.h), + // const Icon( + // BootstrapIcons.thermometer_half, + // color: Colors.green, + // ), + SizedBox(height: 8.h), + Text( + '20-30°C', + style: AppTheme.labelMedium, + textAlign: TextAlign.center, + ), + ], + ), + ), + Container( + height: 100.h, + width: 100.w, + decoration: BoxDecoration( + color: Colors.orange.withOpacity(0.1), + borderRadius: BorderRadius.circular(16), + border: Border.all( + color: Colors.orange.shade800, + width: 2, + ), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('high', + style: AppTheme.labelMedium + .copyWith(color: Colors.orange)), + // SizedBox(height: 8.h), + // const Icon( + // BootstrapIcons.thermometer_high, + // color: Colors.orange, + // ), + SizedBox(height: 8.h), + Text( + '>30°C', + style: AppTheme.labelMedium, + textAlign: TextAlign.center, + ), + ], + ), + ), + ], + ), + SizedBox(height: 16.h), + const Text('Grafik'), + SizedBox(height: 16.h), + AspectRatio( + aspectRatio: 1.6.h, + child: Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(16.w), + border: Border.all(color: Colors.grey.shade300, width: 1.w), + ), + child: const GarphicWidget(), + ), + ) + ], + ), + ), + ); + } + + Future showInfo(BuildContext context, String title, String content) { + return showDialog( + context: context, + builder: (context) => AlertDialog( + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text(title), + const SizedBox(height: 16), + Text( + style: Theme.of(context).textTheme.bodySmall, + content, + ), + ], + ), + actions: [ + TextButton( + onPressed: () { + Navigator.pop(context); + }, + child: const Text('OK'), + ) + ], + ), + ); + } +} diff --git a/agrilink_vocpro/lib/features/home/pages/temperature/view/temperature_screen.dart b/agrilink_vocpro/lib/features/home/pages/temperature/view/temperature_screen.dart index fe9c134..3340242 100644 --- a/agrilink_vocpro/lib/features/home/pages/temperature/view/temperature_screen.dart +++ b/agrilink_vocpro/lib/features/home/pages/temperature/view/temperature_screen.dart @@ -6,9 +6,10 @@ import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:gauge_indicator/gauge_indicator.dart'; class TemperatureScreen extends StatelessWidget { - const TemperatureScreen({super.key}); + const TemperatureScreen({super.key, this.temperature = 0}); - double get value => 29; + final double temperature; + double get value => temperature; @override Widget build(BuildContext context) { diff --git a/agrilink_vocpro/lib/features/home/widgets/list_data_from_censor_dht.dart b/agrilink_vocpro/lib/features/home/widgets/list_data_from_censor_dht.dart index 43b7a29..015f729 100644 --- a/agrilink_vocpro/lib/features/home/widgets/list_data_from_censor_dht.dart +++ b/agrilink_vocpro/lib/features/home/widgets/list_data_from_censor_dht.dart @@ -1,7 +1,6 @@ import 'package:agrilink_vocpro/core/constant/app_color.dart'; import 'package:agrilink_vocpro/core/route/app_route.dart'; import 'package:agrilink_vocpro/core/state/result_state.dart'; -import 'package:agrilink_vocpro/features/home/pages/temperature/view/temperature_screen.dart'; import 'package:agrilink_vocpro/features/home/provider/home_provider.dart'; import 'package:agrilink_vocpro/features/home/widgets/censor_item_loading_widgets.dart'; import 'package:agrilink_vocpro/features/home/widgets/data_display_widget.dart'; @@ -59,7 +58,7 @@ class ListDataFromCensorDht extends StatelessWidget { iconColor: Colors.white, censorIdentifier: 'NPK 1', onTap: () async { - context.push(AppRoute.humidity, extra: '60'); + await context.push('${AppRoute.humidity}/60'); }, ), DataDisplayerWidget( @@ -70,29 +69,34 @@ class ListDataFromCensorDht extends StatelessWidget { icon: BootstrapIcons.thermometer_half, color: Colors.white, onTap: () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const TemperatureScreen())); + await context.push('${AppRoute.temperature}/28'); }, ), DataDisplayerWidget( title: 'Light', subtitle: 'intensitas cahaya', - value: '1000', + value: '320.5', unit: 'lux', icon: BootstrapIcons.sun, color: Colors.white, onTap: () async { - context.push('${AppRoute.light}/300'); + await context.push('${AppRoute.light}/320.5'); }, ), ], ); case ResultState.noData: - return const Center(child: Text('No Data')); + return Center( + child: Image.asset( + 'assets/images/no_data_image.png', + width: 200.w, + )); case ResultState.error: - return const Center(child: Text('Error')); + return Center( + child: Image.asset( + 'assets/images/error_image.png', + width: 200.w, + )); case ResultState.initial: return const SizedBox.shrink(); default: diff --git a/agrilink_vocpro/lib/features/home/widgets/list_data_from_censor_npk1.dart b/agrilink_vocpro/lib/features/home/widgets/list_data_from_censor_npk1.dart index d3db9cd..0249cdd 100644 --- a/agrilink_vocpro/lib/features/home/widgets/list_data_from_censor_npk1.dart +++ b/agrilink_vocpro/lib/features/home/widgets/list_data_from_censor_npk1.dart @@ -1,8 +1,6 @@ import 'package:agrilink_vocpro/core/constant/app_color.dart'; import 'package:agrilink_vocpro/core/route/app_route.dart'; import 'package:agrilink_vocpro/core/state/result_state.dart'; -import 'package:agrilink_vocpro/features/home/pages/soil_moisture/view/soil_moisture_screen.dart'; -import 'package:agrilink_vocpro/features/home/pages/temperature/view/temperature_screen.dart'; import 'package:agrilink_vocpro/features/home/provider/home_provider.dart'; import 'package:agrilink_vocpro/features/home/widgets/censor_item_loading_widgets.dart'; import 'package:agrilink_vocpro/features/home/widgets/data_display_widget.dart'; @@ -59,10 +57,7 @@ class ListDataFromCensorNpk1 extends StatelessWidget { iconColor: Colors.white, censorIdentifier: censorIdentifier, onTap: () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const TemperatureScreen())); + await context.push('${AppRoute.soilTemperature}/28'); }, ), DataDisplayerWidget( @@ -74,12 +69,7 @@ class ListDataFromCensorNpk1 extends StatelessWidget { color: Colors.white, censorIdentifier: censorIdentifier, onTap: () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const SoilMoistureScreen(), - ), - ); + await context.push('${AppRoute.soilMoisture}/40'); }, ), DataDisplayerWidget( @@ -103,7 +93,7 @@ class ListDataFromCensorNpk1 extends StatelessWidget { color: Colors.white, censorIdentifier: censorIdentifier, onTap: () async { - context.push(AppRoute.humidity, extra: '60'); + await context.push('${AppRoute.conductivity}/234'); }, ), DataDisplayerWidget( @@ -113,7 +103,9 @@ class ListDataFromCensorNpk1 extends StatelessWidget { unit: 'ppm', icon: CupertinoIcons.eyedropper, color: Colors.white, - onTap: () {}, + onTap: () async { + await context.push('${AppRoute.nitrogen}/30'); + }, ), DataDisplayerWidget( title: 'Potassium', @@ -122,7 +114,9 @@ class ListDataFromCensorNpk1 extends StatelessWidget { unit: 'ppm', icon: CupertinoIcons.eyedropper, color: Colors.white, - onTap: () {}, + onTap: () async { + await context.push('${AppRoute.potassium}/20'); + }, ), DataDisplayerWidget( title: 'Phosphorus', @@ -131,14 +125,24 @@ class ListDataFromCensorNpk1 extends StatelessWidget { unit: 'ppm', icon: CupertinoIcons.eyedropper, color: Colors.white, - onTap: () {}, + onTap: () async { + await context.push('${AppRoute.phosphorus}/54'); + }, ), ], ); case ResultState.noData: - return const Center(child: Text('No Data')); + return Center( + child: Image.asset( + 'assets/images/no_data_image.png', + width: 200.w, + )); case ResultState.error: - return const Center(child: Text('Error')); + return Center( + child: Image.asset( + 'assets/images/error_image.png', + width: 200.w, + )); default: return const SizedBox.shrink(); } diff --git a/agrilink_vocpro/lib/features/home/widgets/list_data_from_censor_npk2.dart b/agrilink_vocpro/lib/features/home/widgets/list_data_from_censor_npk2.dart index 71f5013..c59a3c4 100644 --- a/agrilink_vocpro/lib/features/home/widgets/list_data_from_censor_npk2.dart +++ b/agrilink_vocpro/lib/features/home/widgets/list_data_from_censor_npk2.dart @@ -1,13 +1,17 @@ import 'package:agrilink_vocpro/core/constant/app_color.dart'; import 'package:agrilink_vocpro/core/route/app_route.dart'; +import 'package:agrilink_vocpro/core/state/result_state.dart'; import 'package:agrilink_vocpro/features/home/pages/soil_moisture/view/soil_moisture_screen.dart'; import 'package:agrilink_vocpro/features/home/pages/temperature/view/temperature_screen.dart'; +import 'package:agrilink_vocpro/features/home/provider/home_provider.dart'; +import 'package:agrilink_vocpro/features/home/widgets/censor_item_loading_widgets.dart'; import 'package:agrilink_vocpro/features/home/widgets/data_display_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:go_router/go_router.dart'; +import 'package:provider/provider.dart'; class ListDataFromCensorNpk2 extends StatelessWidget { const ListDataFromCensorNpk2({ @@ -17,100 +21,133 @@ class ListDataFromCensorNpk2 extends StatelessWidget { @override Widget build(BuildContext context) { const String censorIdentifier = 'NPK 1'; - return GridView( - padding: EdgeInsets.all(16.r), - shrinkWrap: true, - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 2, - crossAxisSpacing: 16.r, - mainAxisSpacing: 16.r, - childAspectRatio: 0.9, - ), - children: [ - DataDisplayerWidget( - title: 'Temperature', - subtitle: 'Suhu tanah', - value: '28', - unit: '°C', - icon: BootstrapIcons.thermometer_half, - textColor: Colors.white, - color: AppColor.secondary, - iconColor: Colors.white, - censorIdentifier: censorIdentifier, - onTap: () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const TemperatureScreen())); - }, - ), - DataDisplayerWidget( - title: 'Soil Moisture', - subtitle: 'kelembaban tanah', - value: '40', - unit: '%', - icon: Icons.water_outlined, - color: Colors.white, - censorIdentifier: censorIdentifier, - onTap: () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const SoilMoistureScreen(), + return Consumer(builder: (context, provider, child) { + switch (provider.dataState) { + case ResultState.loading: + return GridView( + padding: EdgeInsets.all(16.r), + shrinkWrap: true, + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + crossAxisSpacing: 16.r, + mainAxisSpacing: 16.r, + childAspectRatio: 0.9, + ), + children: [ + for (int i = 0; i < 4; i++) const CensorItemLoadingWidgets(), + ], + ); + case ResultState.hasData: + return GridView( + padding: EdgeInsets.all(16.r), + shrinkWrap: true, + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + crossAxisSpacing: 16.r, + mainAxisSpacing: 16.r, + childAspectRatio: 0.9, + ), + children: [ + DataDisplayerWidget( + title: 'Temperature', + subtitle: 'Suhu tanah', + value: '28', + unit: '°C', + icon: BootstrapIcons.thermometer_half, + textColor: Colors.white, + color: AppColor.secondary, + iconColor: Colors.white, + censorIdentifier: censorIdentifier, + onTap: () async { + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const TemperatureScreen())); + }, ), - ); - }, - ), - DataDisplayerWidget( - title: 'Acid Level (PH)', - subtitle: 'tingkat keasaman', - value: '6.5', - unit: 'pH', - icon: BootstrapIcons.pie_chart, - color: Colors.white, - censorIdentifier: censorIdentifier, - onTap: () {}, - ), - DataDisplayerWidget( - title: 'Conductivity', - subtitle: 'Daya Arus Listrik', - value: '234', - unit: 'µS/cm', - icon: Icons.electric_bolt, - color: Colors.white, - censorIdentifier: censorIdentifier, - onTap: () async { - context.push(AppRoute.humidity, extra: '60'); - }, - ), - DataDisplayerWidget( - title: 'Nitrogen', - subtitle: 'Kadar Nitrogen', - value: '30', - unit: 'ppm', - icon: CupertinoIcons.eyedropper, - color: Colors.white, - onTap: () {}, - ), - DataDisplayerWidget( - title: 'Potassium', - subtitle: 'Kadar kalium', - value: '20', - unit: 'ppm', - icon: CupertinoIcons.eyedropper, - color: Colors.white, - onTap: () {}, - ), - DataDisplayerWidget( - title: 'Phosphorus', - subtitle: 'Kadar Fosfor', - value: '54', - unit: 'ppm', - icon: CupertinoIcons.eyedropper, - color: Colors.white, - onTap: () {}, - ), - ], - ); + DataDisplayerWidget( + title: 'Soil Moisture', + subtitle: 'kelembaban tanah', + value: '40', + unit: '%', + icon: Icons.water_outlined, + color: Colors.white, + censorIdentifier: censorIdentifier, + onTap: () async { + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const SoilMoistureScreen(), + ), + ); + }, + ), + DataDisplayerWidget( + title: 'Acid Level (PH)', + subtitle: 'tingkat keasaman', + value: '6.5', + unit: 'pH', + icon: BootstrapIcons.pie_chart, + color: Colors.white, + censorIdentifier: censorIdentifier, + onTap: () {}, + ), + DataDisplayerWidget( + title: 'Conductivity', + subtitle: 'Daya Arus Listrik', + value: '234', + unit: 'µS/cm', + icon: Icons.electric_bolt, + color: Colors.white, + censorIdentifier: censorIdentifier, + onTap: () async { + context.push(AppRoute.humidity, extra: '60'); + }, + ), + DataDisplayerWidget( + title: 'Nitrogen', + subtitle: 'Kadar Nitrogen', + value: '30', + unit: 'ppm', + icon: CupertinoIcons.eyedropper, + color: Colors.white, + onTap: () {}, + ), + DataDisplayerWidget( + title: 'Potassium', + subtitle: 'Kadar kalium', + value: '20', + unit: 'ppm', + icon: CupertinoIcons.eyedropper, + color: Colors.white, + onTap: () {}, + ), + DataDisplayerWidget( + title: 'Phosphorus', + subtitle: 'Kadar Fosfor', + value: '54', + unit: 'ppm', + icon: CupertinoIcons.eyedropper, + color: Colors.white, + onTap: () {}, + ), + ], + ); + case ResultState.noData: + return Center( + child: Image.asset( + 'assets/images/no_data_image.png', + width: 200.w, + )); + case ResultState.error: + return Center( + child: Image.asset( + 'assets/images/error_image.png', + width: 200.w, + )); + default: + return const SizedBox.shrink(); + } + }); } } diff --git a/agrilink_vocpro/lib/features/setting/view/setting_screen.dart b/agrilink_vocpro/lib/features/setting/view/setting_screen.dart index 3b37841..ffcef03 100644 --- a/agrilink_vocpro/lib/features/setting/view/setting_screen.dart +++ b/agrilink_vocpro/lib/features/setting/view/setting_screen.dart @@ -1,10 +1,7 @@ -import 'dart:io'; - import 'package:agrilink_vocpro/core/constant/app_color.dart'; import 'package:agrilink_vocpro/core/constant/app_theme.dart'; import 'package:agrilink_vocpro/core/route/app_route.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:go_router/go_router.dart';