diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..62c8935 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea/ \ No newline at end of file diff --git a/agrilink_vocpro/lib/core/route/app_route.dart b/agrilink_vocpro/lib/core/route/app_route.dart index eebfe73..d6ad20f 100644 --- a/agrilink_vocpro/lib/core/route/app_route.dart +++ b/agrilink_vocpro/lib/core/route/app_route.dart @@ -2,7 +2,7 @@ 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/luminosity/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'; @@ -136,11 +136,14 @@ class AppRoute { static GoRoute buildSoilTempRoute() { return GoRoute( - path: 'soil_temperature/:value', + path: 'soil_temperature/:value1/:value2', builder: (context, state) { - final double value = - double.tryParse(state.pathParameters['value'] ?? '') ?? 0.0; - return SoilTemperatureScreen(temperature: value); + final double value1 = + double.tryParse(state.pathParameters['value1'] ?? '') ?? 0.0; + final double value2 = + double.tryParse(state.pathParameters['value2'] ?? '') ?? 0.0; + return SoilTemperatureScreen( + npk1Temperature: value1, npk2Temperature: value2); }, ); } diff --git a/agrilink_vocpro/lib/features/home/model/dht_graphic_response.dart b/agrilink_vocpro/lib/data/model/dht_graphic_response.dart similarity index 100% rename from agrilink_vocpro/lib/features/home/model/dht_graphic_response.dart rename to agrilink_vocpro/lib/data/model/dht_graphic_response.dart diff --git a/agrilink_vocpro/lib/features/home/model/latest_data_response.dart b/agrilink_vocpro/lib/data/model/latest_data_response.dart similarity index 100% rename from agrilink_vocpro/lib/features/home/model/latest_data_response.dart rename to agrilink_vocpro/lib/data/model/latest_data_response.dart diff --git a/agrilink_vocpro/lib/features/home/model/npk1_graphic_response.dart b/agrilink_vocpro/lib/data/model/npk1_graphic_response.dart similarity index 100% rename from agrilink_vocpro/lib/features/home/model/npk1_graphic_response.dart rename to agrilink_vocpro/lib/data/model/npk1_graphic_response.dart diff --git a/agrilink_vocpro/lib/features/home/model/npk2_graphic_response.dart b/agrilink_vocpro/lib/data/model/npk2_graphic_response.dart similarity index 100% rename from agrilink_vocpro/lib/features/home/model/npk2_graphic_response.dart rename to agrilink_vocpro/lib/data/model/npk2_graphic_response.dart diff --git a/agrilink_vocpro/lib/domain/service/app_service.dart b/agrilink_vocpro/lib/domain/service/app_service.dart index a33557e..a889706 100644 --- a/agrilink_vocpro/lib/domain/service/app_service.dart +++ b/agrilink_vocpro/lib/domain/service/app_service.dart @@ -1,8 +1,10 @@ import 'package:agrilink_vocpro/core/constant/app_constant.dart'; import 'package:agrilink_vocpro/core/extension/extention.dart'; +import 'package:agrilink_vocpro/data/model/dht_graphic_response.dart'; +import 'package:agrilink_vocpro/data/model/npk1_graphic_response.dart'; +import 'package:agrilink_vocpro/data/model/npk2_graphic_response.dart'; import 'package:agrilink_vocpro/data/model/relay_response.dart'; -import 'package:agrilink_vocpro/features/home/model/latest_data_response.dart'; -import 'package:agrilink_vocpro/features/home/model/npk1_soil_temp_grafik.dart'; +import 'package:agrilink_vocpro/data/model/latest_data_response.dart'; import 'package:dio/dio.dart'; import 'package:flutter/foundation.dart'; @@ -31,32 +33,74 @@ class AppService { } } - Future getNpk1SoilTempGrafik({ + //get grafik data dht + + Future getGrafikDataDht({ required String metric, - required String sensor, }) async { - String dateNow = DateTime.now().toString(); - String dateYesterday = - DateTime.now().subtract(Duration(days: 1)).toString(); - final formatedDateNow = dateFormatterShort(dateNow); - final formatedDateYesterday = dateFormatterShort(dateYesterday); + String date = DateTime.now().toString(); + final formatedDate = dateFormatterShort(date); try { final result = await _dioWithoutInterceptor.get( - '/sensor/getData?metric=$metric&range[start]=$formatedDateYesterday&range[end]=$formatedDateNow&range[time_range]=HOURLY&sensor=$sensor', + '/sensor/getData?metric=$metric&range[start]=$formatedDate&range[end]=$formatedDate&range[time_range]=HOURLY&sensor=dht', ); if (result.statusCode == 200) { - print(result.data.toString()); - final data = Npk1SoilTempGrafik.fromJson(result.data); + final data = DhtGraphicResponse.fromJson(result.data); return data; } else { throw Exception('Failed to load data'); } } on DioException catch (e) { - print(e); - rethrow; + final errorMessage = e.response?.data['message']; + throw (errorMessage); } } +// get grafik data npk1 + Future getGraphicDataNpk1( + {required String metric}) async { + String date = DateTime.now().toString(); + final formatedDate = dateFormatterShort(date); + try { + final result = await _dioWithoutInterceptor.get( + '/sensor/getData?metric=$metric&range[start]=$formatedDate&range[end]=$formatedDate&range[time_range]=HOURLY&sensor=npk1', + ); + if (result.statusCode == 200) { + final data = Npk1GraphicResponse.fromJson(result.data); + return data; + } else { + throw Exception('Failed to load data'); + } + } on DioException catch (e) { + final errorMessage = e.response?.data['message']; + throw (errorMessage); + } + } + + // get grafik data npk2 + + Future getGraphicDataNpk2( + {required String metric}) async { + String date = DateTime.now().toString(); + final formatedDate = dateFormatterShort(date); + try { + final result = await _dioWithoutInterceptor.get( + '/sensor/getData?metric=$metric&range[start]=$formatedDate&range[end]=$formatedDate&range[time_range]=HOURLY&sensor=npk2', + ); + if (result.statusCode == 200) { + final data = Npk2GraphicResponse.fromJson(result.data); + return data; + } else { + throw Exception('Failed to load data'); + } + } on DioException catch (e) { + final errorMessage = e.response?.data['message']; + throw (errorMessage); + } + } + + // get latest data + Future getLatestData() async { try { final result = await _dioWithoutInterceptor.get( @@ -69,8 +113,8 @@ class AppService { throw Exception('Failed to load data'); } } on DioException catch (e) { - print(e); - rethrow; + final errorMessage = e.response?.data['message']; + throw (errorMessage); } } } diff --git a/agrilink_vocpro/lib/domain/service/mqtt_service.dart b/agrilink_vocpro/lib/domain/service/mqtt_service.dart index 21002a8..eb6361a 100644 --- a/agrilink_vocpro/lib/domain/service/mqtt_service.dart +++ b/agrilink_vocpro/lib/domain/service/mqtt_service.dart @@ -1,170 +1,170 @@ -import 'dart:convert'; +// 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'; +// 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'; -class MQTTService { - MqttServerClient? client; +// class MQTTService { +// MqttServerClient? client; - Future setupMqtt() async { - client = MqttServerClient(AppConstant.mqttServer, ''); - client!.port = 1883; +// Future setupMqtt() async { +// client = MqttServerClient(AppConstant.mqttServer, ''); +// client!.port = 1883; - client!.connectionMessage = MqttConnectMessage() - .authenticateAs(AppConstant.mqttUsername, AppConstant.mqttPassword) - .withClientIdentifier('mobile_client_controller') - .startClean() // reset session - .withWillQos(MqttQos.atLeastOnce); +// client!.connectionMessage = MqttConnectMessage() +// .authenticateAs(AppConstant.mqttUsername, AppConstant.mqttPassword) +// .withClientIdentifier('mobile_client_controller') +// .startClean() // reset session +// .withWillQos(MqttQos.atLeastOnce); - try { - print('MQTT: Connecting....'); - await client!.connect(); - print('MQTT: Connected'); - return ResultState.hasData; - } catch (e) { - print('MQTT: Error: $e'); - return ResultState.error; - } - } +// try { +// print('MQTT: Connecting....'); +// await client!.connect(); +// print('MQTT: Connected'); +// return ResultState.hasData; +// } catch (e) { +// print('MQTT: Error: $e'); +// return ResultState.error; +// } +// } - Future publishMessage(String topic, String message) async { - final builder = MqttClientPayloadBuilder(); +// Future publishMessage(String topic, String message) async { +// final builder = MqttClientPayloadBuilder(); - try { - final bool isConnected = await isMqttConnected(); // Cek apakah terhubung - if (!isConnected) { - print('MQTT: Tidak terhubung ke broker. Tidak bisa publish message.'); - return ResultState.error; - } +// try { +// final bool isConnected = await isMqttConnected(); // Cek apakah terhubung +// if (!isConnected) { +// print('MQTT: Tidak terhubung ke broker. Tidak bisa publish message.'); +// return ResultState.error; +// } - print('MQTT: Published message to $topic: $message'); - builder.addString(message); - client!.publishMessage(topic, MqttQos.atMostOnce, builder.payload!); - print('MQTT: Message published'); - return ResultState.hasData; - } catch (e) { - print('MQTT: Error: $e'); - return ResultState.error; - } - } +// print('MQTT: Published message to $topic: $message'); +// builder.addString(message); +// client!.publishMessage(topic, MqttQos.atMostOnce, builder.payload!); +// print('MQTT: Message published'); +// return ResultState.hasData; +// } catch (e) { +// print('MQTT: Error: $e'); +// return ResultState.error; +// } +// } - Future disconnectMqtt() async { - final bool isConnected = await isMqttConnected(); - if (isConnected) { - print('Memutus koneksi dari broker...'); +// Future disconnectMqtt() async { +// final bool isConnected = await isMqttConnected(); +// if (isConnected) { +// print('Memutus koneksi dari broker...'); - client!.disconnect(); +// client!.disconnect(); - await Future.delayed(const Duration(seconds: 1)); - print('Koneksi telah terputus.'); - return ResultState.hasData; - } else { - print('Tidak ada koneksi yang sedang aktif.'); - return ResultState.error; - } - } +// await Future.delayed(const Duration(seconds: 1)); +// print('Koneksi telah terputus.'); +// return ResultState.hasData; +// } else { +// print('Tidak ada koneksi yang sedang aktif.'); +// return ResultState.error; +// } +// } - Future isMqttConnected() async { - if (client != null && - client!.connectionStatus!.state == MqttConnectionState.connected) { - return true; //connected - } else { - return false; //not connected - } - } +// Future isMqttConnected() async { +// if (client != null && +// client!.connectionStatus!.state == MqttConnectionState.connected) { +// return true; //connected +// } else { +// return false; //not connected +// } +// } - Future subscribeToTopic(String topic) async { - bool isActive = false; - if (client != null && - client!.connectionStatus!.state == MqttConnectionState.connected) { - try { - print('MQTT: Subscribing to $topic'); - client!.subscribe(topic, MqttQos.atMostOnce); - print('MQTT: Subscribed to $topic'); +// Future subscribeToTopic(String topic) async { +// bool isActive = false; +// if (client != null && +// client!.connectionStatus!.state == MqttConnectionState.connected) { +// try { +// print('MQTT: Subscribing to $topic'); +// client!.subscribe(topic, MqttQos.atMostOnce); +// print('MQTT: Subscribed to $topic'); - // Tambahkan log ini untuk memastikan bahwa listener dijalankan - if (client!.updates != null) { - print('MQTT: Listening for updates...'); - } else { - print('MQTT: No updates stream available'); - } +// // Tambahkan log ini untuk memastikan bahwa listener dijalankan +// if (client!.updates != null) { +// print('MQTT: Listening for updates...'); +// } else { +// print('MQTT: No updates stream available'); +// } - client!.updates!.listen( - (List>? messages) { - 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: Subscribe Message received on topic ${messages[0].topic}: $payload'); +// client!.updates!.listen( +// (List>? messages) { +// 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: Subscribe Message received on topic ${messages[0].topic}: $payload'); - if (payload == 'ON') { - isActive = true; - // Update UI atau provider untuk menandakan relay ON - } 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 Subscribe messages received'); - } - }, - ); +// if (payload == 'ON') { +// isActive = true; +// // Update UI atau provider untuk menandakan relay ON +// } 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 Subscribe messages received'); +// } +// }, +// ); - return isActive; - } catch (e) { - print('MQTT: Error subscribing to $topic: $e'); - return isActive; - } - } else { - print('MQTT: Not connected, cannot subscribe.'); - return false; - } - } +// return isActive; +// } catch (e) { +// print('MQTT: Error subscribing to $topic: $e'); +// return isActive; +// } +// } else { +// print('MQTT: Not connected, cannot subscribe.'); +// 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'); +// 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'); +// 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); +// // Parse the received JSON payload +// final Map relayStatus = jsonDecode(payload); - print('MQTT: Relay status: $relayStatus'); +// 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; - } - } -} +// // 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/features/auth/view/login_screen.dart b/agrilink_vocpro/lib/features/auth/view/login_screen.dart index fd99e32..73f5797 100644 --- a/agrilink_vocpro/lib/features/auth/view/login_screen.dart +++ b/agrilink_vocpro/lib/features/auth/view/login_screen.dart @@ -1,4 +1,3 @@ -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:agrilink_vocpro/core/widgets/app_button.dart'; @@ -47,17 +46,6 @@ class LoginScreen extends StatelessWidget { hintText: 'Masukkan password', ), const SizedBox(height: 24), - GestureDetector( - onTap: () { - print('Forgot password?'); - }, - child: Text( - 'Forgot password?', - textAlign: TextAlign.end, - style: AppTheme.labelMedium - .copyWith(color: AppColor.secondary), - ), - ), const SizedBox(height: 24), AppButton( onPressed: () { diff --git a/agrilink_vocpro/lib/features/control/provider/control_provider.dart b/agrilink_vocpro/lib/features/control/provider/control_provider.dart index 6e0dbad..4109176 100644 --- a/agrilink_vocpro/lib/features/control/provider/control_provider.dart +++ b/agrilink_vocpro/lib/features/control/provider/control_provider.dart @@ -1,6 +1,6 @@ import 'package:agrilink_vocpro/core/state/result_state.dart'; import 'package:agrilink_vocpro/domain/service/app_service.dart'; -import 'package:flutter/material.dart'; +import 'package:flutter/foundation.dart'; class ControlProvider extends ChangeNotifier { final AppService _appService = AppService(); @@ -52,7 +52,9 @@ class ControlProvider extends ChangeNotifier { relayState = ResultState.loading; notifyListeners(); try { - print('try to get relay status...'); + if (kDebugMode) { + print('try to get relay status...'); + } final result = await _appService.getRelayStatus(); if (result.success == true) { for (var element in result.data!) { @@ -72,27 +74,13 @@ class ControlProvider extends ChangeNotifier { } catch (e) { relayState = ResultState.error; notifyListeners(); - print(e); + if (kDebugMode) { + print(e); + } rethrow; } } - // Future disconnectMqtt() async { - // try { - // await _mqttService.disconnectMqtt(); - // } catch (e) { - // print(e); - // rethrow; - // } - // notifyListeners(); - // } - - // @override - // void dispose() { - // disconnectMqtt(); - // super.dispose(); - // } - void switchControl1(bool value) { _control_1 = value; notifyListeners(); diff --git a/agrilink_vocpro/lib/features/home/model/npk1_soil_temp_grafik.dart b/agrilink_vocpro/lib/features/home/model/npk1_soil_temp_grafik.dart deleted file mode 100644 index 6877233..0000000 --- a/agrilink_vocpro/lib/features/home/model/npk1_soil_temp_grafik.dart +++ /dev/null @@ -1,65 +0,0 @@ -class Npk1SoilTempGrafik { - Data? data; - int? statusCode; - String? message; - - Npk1SoilTempGrafik({this.data, this.statusCode, this.message}); - - Npk1SoilTempGrafik.fromJson(Map json) { - data = json['data'] != null ? Data.fromJson(json['data']) : null; - statusCode = json['statusCode']; - message = json['message']; - } - - Map toJson() { - final Map data = {}; - if (this.data != null) { - data['data'] = this.data!.toJson(); - } - data['statusCode'] = statusCode; - data['message'] = message; - return data; - } -} - -class Data { - List? npk1; - - Data({this.npk1}); - - Data.fromJson(Map json) { - if (json['npk1'] != null) { - npk1 = []; - json['npk1'].forEach((v) { - npk1!.add(Npk1.fromJson(v)); - }); - } - } - - Map toJson() { - final Map data = {}; - if (npk1 != null) { - data['npk1'] = npk1!.map((v) => v.toJson()).toList(); - } - return data; - } -} - -class Npk1 { - num? hour; - num? soiltemperatureAvg; - - Npk1({this.hour, this.soiltemperatureAvg}); - - Npk1.fromJson(Map json) { - hour = json['hour']; - soiltemperatureAvg = json['soiltemperature_avg']; - } - - Map toJson() { - final Map data = {}; - data['hour'] = hour; - data['soiltemperature_avg'] = soiltemperatureAvg; - return data; - } -} diff --git a/agrilink_vocpro/lib/features/home/pages/humidity/provider/humidity_provider.dart b/agrilink_vocpro/lib/features/home/pages/humidity/provider/humidity_provider.dart new file mode 100644 index 0000000..c1981eb --- /dev/null +++ b/agrilink_vocpro/lib/features/home/pages/humidity/provider/humidity_provider.dart @@ -0,0 +1,34 @@ +import 'package:agrilink_vocpro/core/state/result_state.dart'; +import 'package:agrilink_vocpro/data/model/dht_graphic_response.dart'; +import 'package:agrilink_vocpro/domain/service/app_service.dart'; +import 'package:flutter/foundation.dart'; + +class HumidityProvider extends ChangeNotifier { + HumidityProvider() { + getHumidityData(); + } + ResultState dataState = ResultState.initial; + + List dataFetched = []; + + Future getHumidityData() async { + dataState = ResultState.loading; + notifyListeners(); + try { + final result = + await AppService().getGrafikDataDht(metric: 'viciHumidity'); + if (result.data == null || result.data!.dht!.isEmpty) { + dataState = ResultState.noData; + } else { + dataFetched = result.data!.dht ?? []; + dataState = ResultState.hasData; + } + } catch (e) { + if (kDebugMode) { + print('Get Grafik Humidity Error: $e'); + } + dataState = ResultState.error; + } + notifyListeners(); + } +} 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 7a8ca86..6e812b8 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 @@ -1,4 +1,6 @@ import 'package:agrilink_vocpro/core/constant/app_theme.dart'; +import 'package:agrilink_vocpro/core/state/result_state.dart'; +import 'package:agrilink_vocpro/features/home/pages/humidity/provider/humidity_provider.dart'; import 'package:agrilink_vocpro/features/home/provider/home_provider.dart'; import 'package:agrilink_vocpro/features/home/widgets/graphic_widget.dart'; import 'package:bootstrap_icons/bootstrap_icons.dart'; @@ -16,153 +18,185 @@ class HumidityScreen extends StatelessWidget { @override Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text('Humidity', style: AppTheme.labelMedium), - centerTitle: true, - scrolledUnderElevation: 0, - leading: IconButton( - icon: const Icon(CupertinoIcons.back), - onPressed: () => Navigator.pop(context), - ), - backgroundColor: Colors.white, - actions: const [ - Padding( - padding: EdgeInsets.only(right: 16), - child: Icon( - BootstrapIcons.droplet_half, - color: Colors.blue, - ), - ) - ], - ), - body: SafeArea( - child: Consumer(builder: (context, provider, child) { - return ListView( - children: [ - SizedBox( - height: MediaQuery.of(context).size.height * 0.05, + return ChangeNotifierProvider( + create: (context) => HumidityProvider(), + child: Scaffold( + appBar: AppBar( + title: Text('Humidity', style: AppTheme.labelMedium), + centerTitle: true, + scrolledUnderElevation: 0, + leading: IconButton( + icon: const Icon(CupertinoIcons.back), + onPressed: () => Navigator.pop(context), + ), + backgroundColor: Colors.white, + actions: const [ + Padding( + padding: EdgeInsets.only(right: 16), + child: Icon( + BootstrapIcons.droplet_half, + color: Colors.blue, ), - SizedBox( - height: 280.h, - child: Stack( - fit: StackFit.expand, - children: [ - Center( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - const Icon(BootstrapIcons.droplet_half, - size: 32, color: Colors.blue), - Text('$humidity %', style: AppTheme.headline1), - ], + ) + ], + ), + body: SafeArea( + child: Consumer(builder: (context, provider, child) { + return ListView( + children: [ + SizedBox( + height: MediaQuery.of(context).size.height * 0.05, + ), + SizedBox( + height: 280.h, + child: Stack( + fit: StackFit.expand, + children: [ + Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(BootstrapIcons.droplet_half, + size: 32, color: Colors.blue), + Text('$humidity %', style: AppTheme.headline1), + ], + ), ), - ), - RotatedBox( - quarterTurns: 2, - child: AnimatedRadialGauge( - duration: const Duration(seconds: 3), - curve: Curves.easeOut, - value: humidity, - axis: GaugeAxis( - degrees: 360, - min: 0, - max: 100, - pointer: null, - style: GaugeAxisStyle( - background: Colors.grey.shade100, - thickness: 50, - ), - progressBar: GaugeBasicProgressBar( - gradient: GaugeAxisGradient(colors: [ - Colors.blue.shade200, - Colors.blue, - ]), + RotatedBox( + quarterTurns: 2, + child: AnimatedRadialGauge( + duration: const Duration(seconds: 3), + curve: Curves.easeOut, + value: humidity, + axis: GaugeAxis( + degrees: 360, + min: 0, + max: 100, + pointer: null, + style: GaugeAxisStyle( + background: Colors.grey.shade100, + thickness: 50, + ), + progressBar: GaugeBasicProgressBar( + gradient: GaugeAxisGradient(colors: [ + Colors.blue.shade200, + Colors.blue, + ]), + ), ), ), ), - ), - ], - ), - ), - const SizedBox(height: 16), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - 'Humidity', - style: AppTheme.labelMedium, - textAlign: TextAlign.center, - ), - IconButton( - iconSize: 20.r, - color: Colors.blue, - onPressed: () {}, - icon: const Icon(BootstrapIcons.info_circle)) - ], - ), - const SizedBox(height: 16), - Padding( - padding: EdgeInsets.only(left: 16.w), - child: const Text('Grafik dalam 7 hari terakhir'), - ), - const SizedBox(height: 16), - AspectRatio( - aspectRatio: 2.h, - child: Container( - margin: EdgeInsets.symmetric(horizontal: 16.w), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(16.w), - border: - Border.all(color: Colors.grey.shade300, width: 1.w)), - child: GarphicWidget( - gradientColors: [ - Colors.blue.shade200, - Colors.blue, ], ), ), - ), - SizedBox(height: 16.h), - Padding( - padding: EdgeInsets.only(left: 16.w), - child: const Text('Deskripsi'), - ), - // ListView.builder( - // shrinkWrap: true, - // physics: const NeverScrollableScrollPhysics(), - // itemCount: provider.humidtyRules.length, - // itemBuilder: (context, index) { - // final item = provider.humidtyRules[index]; - // return Theme( - // data: Theme.of(context) - // .copyWith(dividerColor: Colors.transparent), - // child: ExpansionTile( - // trailing: Text( - // item.censorText, - // style: TextStyle(color: item.color), - // ), - // expandedCrossAxisAlignment: CrossAxisAlignment.start, - // childrenPadding: EdgeInsets.all(16.r), - // title: Text( - // 'Kelembaban ${item.minPercentage}% - ${item.maxPercentage}%'), - // children: [ - // Text( - // item.description, - // style: AppTheme.labelMedium, - // ), - // SizedBox(height: 8.h), - // Text('Tindakan', style: AppTheme.labelSmall), - // SizedBox(height: 8.h), - // Text(item.action), - // ], - // ), - // ); - // }) - ], - ); - }), + const SizedBox(height: 16), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'Humidity', + style: AppTheme.labelMedium, + textAlign: TextAlign.center, + ), + IconButton( + iconSize: 20.r, + color: Colors.blue, + onPressed: () {}, + icon: const Icon(BootstrapIcons.info_circle)) + ], + ), + const SizedBox(height: 16), + Padding( + padding: EdgeInsets.only(left: 16.w), + child: const Text('Grafik dalam 7 hari terakhir'), + ), + const SizedBox(height: 16), + AspectRatio( + aspectRatio: 2.h, + child: Container( + margin: EdgeInsets.symmetric(horizontal: 16.w), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(16.w), + border: Border.all( + color: Colors.grey.shade300, width: 1.w)), + child: Consumer( + builder: (context, provider, child) { + switch (provider.dataState) { + case ResultState.loading: + return const Center( + child: CupertinoActivityIndicator(), + ); + case ResultState.hasData: + return GarphicWidget( + gradientColors: const [ + Colors.cyan, + Colors.amber, + ], + hour: List.generate( + provider.dataFetched.length, + (index) => + provider.dataFetched[index].hour ?? 0), + data: List.generate( + provider.dataFetched.length, + (index) => + provider.dataFetched[index].vicihumidityAvg + ?.toDouble() ?? + 0), + ); + case ResultState.error: + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + BootstrapIcons.exclamation_circle, + color: Colors.grey.shade400, + ), + SizedBox(height: 8.h), + Text( + 'Terjadi Kesalahan', + style: AppTheme.labelSmall, + ), + ], + ), + ); + case ResultState.noData: + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + BootstrapIcons.database_fill_x, + color: Colors.grey.shade400, + ), + SizedBox(height: 8.h), + Text( + 'Tidak Ada Data', + style: AppTheme.labelSmall, + ), + ], + ), + ); + case ResultState.initial: + return const SizedBox.shrink(); + default: + return const Center( + child: Text('Default Error'), + ); + } + }), + ), + ), + SizedBox(height: 16.h), + Padding( + padding: EdgeInsets.only(left: 16.w), + child: const Text('Deskripsi'), + ), + ], + ); + }), + ), ), ); } diff --git a/agrilink_vocpro/lib/features/home/pages/light/view/light_screen.dart b/agrilink_vocpro/lib/features/home/pages/light/view/light_screen.dart deleted file mode 100644 index 2a80b81..0000000 --- a/agrilink_vocpro/lib/features/home/pages/light/view/light_screen.dart +++ /dev/null @@ -1,224 +0,0 @@ -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 LightScreen extends StatelessWidget { - const LightScreen({super.key, this.lightIntensity = 0}); - - final double lightIntensity; - - double get value => lightIntensity; - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text('Light', style: AppTheme.labelMedium), - centerTitle: true, - backgroundColor: Colors.white, - scrolledUnderElevation: 0, - actions: [ - Padding( - padding: const EdgeInsets.only(right: 16), - child: Icon( - BootstrapIcons.sun, - color: Colors.yellow.shade600, - ), - ) - ], - ), - 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: [ - const Center( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Icon(BootstrapIcons.sun, - size: 32, color: Colors.orange), - ], - ), - ), - AnimatedRadialGauge( - duration: const Duration(seconds: 3), - curve: Curves.easeOut, - value: value, - axis: GaugeAxis( - degrees: 360, - min: 0, - max: 1000, - style: GaugeAxisStyle( - background: Colors.grey.shade100, - thickness: 100, - ), - progressBar: GaugeBasicProgressBar( - gradient: GaugeAxisGradient(colors: [ - Colors.yellow.shade100, - Colors.orange.shade200, - ]), - ), - ), - ), - ], - ), - ), - const SizedBox(height: 16), - Text( - '${value.toStringAsFixed(0)} lux', - textAlign: TextAlign.center, - style: const TextStyle( - fontSize: 28, - fontWeight: FontWeight.bold, - color: Colors.black, - ), - ), - const SizedBox(height: 16), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - 'Intensitas Cahaya', - 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.8.h, - child: Container( - decoration: BoxDecoration( - border: Border.all(color: Colors.grey.shade300, width: 1.w), - borderRadius: BorderRadius.circular(16.w), - ), - child: GarphicWidget( - gradientColors: [ - Colors.yellow.shade100, - Colors.orange.shade200, - ], - ), - ), - ) - // 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, - // ), - // ], - // ), - // ), - // ], - // ) - ], - ), - ), - ); - } -} diff --git a/agrilink_vocpro/lib/features/home/pages/luminosity/provider/lum_provider.dart b/agrilink_vocpro/lib/features/home/pages/luminosity/provider/lum_provider.dart new file mode 100644 index 0000000..dc0ea2d --- /dev/null +++ b/agrilink_vocpro/lib/features/home/pages/luminosity/provider/lum_provider.dart @@ -0,0 +1,34 @@ +import 'package:agrilink_vocpro/core/state/result_state.dart'; +import 'package:agrilink_vocpro/data/model/dht_graphic_response.dart'; +import 'package:agrilink_vocpro/domain/service/app_service.dart'; +import 'package:flutter/foundation.dart'; + +class LumProvider extends ChangeNotifier { + LumProvider() { + getLumData(); + } + ResultState dataState = ResultState.initial; + + List dataFetched = []; + + Future getLumData() async { + dataState = ResultState.loading; + notifyListeners(); + try { + final result = + await AppService().getGrafikDataDht(metric: 'viciLuminosity'); + if (result.data == null || result.data!.dht!.isEmpty) { + dataState = ResultState.noData; + } else { + dataFetched = result.data!.dht ?? []; + dataState = ResultState.hasData; + } + } catch (e) { + if (kDebugMode) { + print('Get Grafik Luminosity Error: $e'); + } + dataState = ResultState.error; + } + notifyListeners(); + } +} diff --git a/agrilink_vocpro/lib/features/home/pages/luminosity/view/light_screen.dart b/agrilink_vocpro/lib/features/home/pages/luminosity/view/light_screen.dart new file mode 100644 index 0000000..7c93dcd --- /dev/null +++ b/agrilink_vocpro/lib/features/home/pages/luminosity/view/light_screen.dart @@ -0,0 +1,191 @@ +import 'package:agrilink_vocpro/core/constant/app_theme.dart'; +import 'package:agrilink_vocpro/core/state/result_state.dart'; +import 'package:agrilink_vocpro/features/home/pages/luminosity/provider/lum_provider.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'; +import 'package:gauge_indicator/gauge_indicator.dart'; +import 'package:provider/provider.dart'; + +class LightScreen extends StatelessWidget { + const LightScreen({super.key, this.lightIntensity = 0}); + + final double lightIntensity; + + double get value => lightIntensity; + + @override + Widget build(BuildContext context) { + return ChangeNotifierProvider( + create: (context) => LumProvider(), + child: Scaffold( + appBar: AppBar( + title: Text('Light', style: AppTheme.labelMedium), + centerTitle: true, + backgroundColor: Colors.white, + scrolledUnderElevation: 0, + actions: [ + Padding( + padding: const EdgeInsets.only(right: 16), + child: Icon( + BootstrapIcons.sun, + color: Colors.yellow.shade600, + ), + ) + ], + ), + 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: [ + const Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(BootstrapIcons.sun, + size: 32, color: Colors.orange), + ], + ), + ), + AnimatedRadialGauge( + duration: const Duration(seconds: 3), + curve: Curves.easeOut, + value: value, + axis: GaugeAxis( + degrees: 360, + min: 0, + max: 1000, + style: GaugeAxisStyle( + background: Colors.grey.shade100, + thickness: 100, + ), + progressBar: GaugeBasicProgressBar( + gradient: GaugeAxisGradient(colors: [ + Colors.yellow.shade100, + Colors.orange.shade200, + ]), + ), + ), + ), + ], + ), + ), + const SizedBox(height: 16), + Text( + '${value.toStringAsFixed(0)} lux', + textAlign: TextAlign.center, + style: const TextStyle( + fontSize: 28, + fontWeight: FontWeight.bold, + color: Colors.black, + ), + ), + const SizedBox(height: 16), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'Intensitas Cahaya', + 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.8.h, + child: Container( + decoration: BoxDecoration( + border: Border.all(color: Colors.grey.shade300, width: 1.w), + borderRadius: BorderRadius.circular(16.w), + ), + child: Consumer( + builder: (context, provider, child) { + switch (provider.dataState) { + case ResultState.loading: + return const Center( + child: CupertinoActivityIndicator(), + ); + case ResultState.hasData: + return GarphicWidget( + gradientColors: [ + Colors.yellow.shade100, + Colors.orange.shade200, + ], + hour: List.generate(provider.dataFetched.length, + (index) => provider.dataFetched[index].hour ?? 0), + data: List.generate( + provider.dataFetched.length, + (index) => + provider.dataFetched[index].vicihumidityAvg + ?.toDouble() ?? + 0), + ); + case ResultState.error: + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + BootstrapIcons.exclamation_circle, + color: Colors.grey.shade400, + ), + SizedBox(height: 8.h), + Text( + 'Terjadi Kesalahan', + style: AppTheme.labelSmall, + ), + ], + ), + ); + case ResultState.noData: + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + BootstrapIcons.database_fill_x, + color: Colors.grey.shade400, + ), + SizedBox(height: 8.h), + Text( + 'Tidak Ada Data', + style: AppTheme.labelSmall, + ), + ], + ), + ); + case ResultState.initial: + return const SizedBox.shrink(); + default: + return const Center( + child: Text('Default Error'), + ); + } + }), + ), + ) + ], + ), + ), + ), + ); + } +} diff --git a/agrilink_vocpro/lib/features/home/pages/soil_temperature/provider/soil_temp_provider.dart b/agrilink_vocpro/lib/features/home/pages/soil_temperature/provider/soil_temp_provider.dart index 85098c3..eaefd42 100644 --- a/agrilink_vocpro/lib/features/home/pages/soil_temperature/provider/soil_temp_provider.dart +++ b/agrilink_vocpro/lib/features/home/pages/soil_temperature/provider/soil_temp_provider.dart @@ -1,47 +1,58 @@ import 'package:agrilink_vocpro/core/state/result_state.dart'; -import 'package:agrilink_vocpro/features/home/model/npk1_soil_temp_grafik.dart'; -import 'package:agrilink_vocpro/features/home/service/home_service.dart'; -import 'package:flutter/material.dart'; +import 'package:agrilink_vocpro/data/model/npk1_graphic_response.dart'; +import 'package:agrilink_vocpro/data/model/npk2_graphic_response.dart'; +import 'package:agrilink_vocpro/domain/service/app_service.dart'; +import 'package:flutter/foundation.dart'; class SoilTempProvider extends ChangeNotifier { SoilTempProvider() { - getSoilTempData(); + getSoilTempNpk1Data(); + getSoilTempNpk2Data(); } - - List dataFetched = []; - - void setSoilTempData(List data) { - dataFetched = data; - notifyListeners(); - } - ResultState dataState = ResultState.initial; - Future getSoilTempData() async { + List dataFetchedNpk1 = []; + List dataFetchedNpk2 = []; + + Future getSoilTempNpk1Data() async { dataState = ResultState.loading; notifyListeners(); try { - final result = await HomeService().getNpk1SoilTempGrafik(); + final result = + await AppService().getGraphicDataNpk1(metric: 'soilTemperature'); if (result.data == null || result.data!.npk1!.isEmpty) { dataState = ResultState.noData; - notifyListeners(); - return; } else { - setSoilTempData(result.data?.npk1 ?? []); + dataFetchedNpk1 = result.data!.npk1 ?? []; dataState = ResultState.hasData; - notifyListeners(); } } catch (e) { - print('Error: $e'); + if (kDebugMode) { + print('Get Grafik Soil Temp Error: $e'); + } dataState = ResultState.error; - notifyListeners(); } + notifyListeners(); } - @override - void dispose() { - dataState = ResultState.initial; - dataFetched = []; - super.dispose(); + Future getSoilTempNpk2Data() async { + dataState = ResultState.loading; + notifyListeners(); + try { + final result = + await AppService().getGraphicDataNpk2(metric: 'soilTemperature'); + if (result.data == null || result.data!.npk2!.isEmpty) { + dataState = ResultState.noData; + } else { + dataFetchedNpk2 = result.data!.npk2 ?? []; + dataState = ResultState.hasData; + } + } catch (e) { + if (kDebugMode) { + print('Get Grafik Soil Temp Error: $e'); + } + dataState = ResultState.error; + } + notifyListeners(); } } 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 index ccc10a2..9635963 100644 --- 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 @@ -1,288 +1,242 @@ +import 'package:agrilink_vocpro/features/home/widgets/graphic_error_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:bootstrap_icons/bootstrap_icons.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:provider/provider.dart'; import 'package:agrilink_vocpro/core/constant/app_constant.dart'; import 'package:agrilink_vocpro/core/constant/app_theme.dart'; import 'package:agrilink_vocpro/core/state/result_state.dart'; import 'package:agrilink_vocpro/core/widgets/show_info.dart'; import 'package:agrilink_vocpro/features/home/pages/soil_temperature/provider/soil_temp_provider.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'; -import 'package:provider/provider.dart'; class SoilTemperatureScreen extends StatelessWidget { - const SoilTemperatureScreen({super.key, this.temperature = 0}); + const SoilTemperatureScreen({ + super.key, + this.npk1Temperature = 0, + this.npk2Temperature = 0, + }); - final double temperature; - double get value => temperature; + final double npk1Temperature; + final double npk2Temperature; @override Widget build(BuildContext context) { return ChangeNotifierProvider( create: (context) => SoilTempProvider(), - child: 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°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, - ]), - ), - ), - ), - ], - ), - ), - SizedBox(height: 16.h), - 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), - 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), - 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: Consumer( - builder: (context, provider, child) { - switch (provider.dataState) { - case ResultState.loading: - return const Center( - child: CircularProgressIndicator(), - ); - case ResultState.hasData: - return GarphicWidget( - gradientColors: const [ - Colors.cyan, - Colors.amber, - ], - hour: List.generate(provider.dataFetched.length, - (index) => provider.dataFetched[index].hour ?? 0), - data: List.generate( - provider.dataFetched.length, - (index) => - provider.dataFetched[index].soiltemperatureAvg - ?.toDouble() ?? - 0), - ); - case ResultState.error: - return Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - BootstrapIcons.exclamation_circle, - color: Colors.grey.shade400, - ), - SizedBox(height: 8.h), - Text( - 'Terjadi Kesalahan', - style: AppTheme.labelSmall, - ), - ], - ), - ); - case ResultState.noData: - return Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - BootstrapIcons.database_fill_x, - color: Colors.grey.shade400, - ), - SizedBox(height: 8.h), - Text( - 'Tidak Ada Data', - style: AppTheme.labelSmall, - ), - ], - ), - ); - case ResultState.initial: - return const SizedBox.shrink(); - default: - return const Center( - child: Text('Default Error'), - ); - } - }), + child: DefaultTabController( + length: 2, + child: 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, ), ) ], + bottom: const TabBar( + tabs: [ + Tab(text: 'NPK 1'), + Tab(text: 'NPK 2'), + ], + ), + ), + body: TabBarView( + children: [ + buildTabContent(context, npk1Temperature, 'NPK 1', true), + buildTabContent(context, npk2Temperature, 'NPK 2', false), + ], ), ), ), ); } + + // Generalized method for tab content to avoid duplication + SafeArea buildTabContent( + BuildContext context, double value, String label, bool isNpk1) { + return SafeArea( + child: ListView( + padding: EdgeInsets.all(16.w), + children: [ + SizedBox(height: MediaQuery.of(context).size.height * 0.05), + buildTemperatureInfo(context, value), + SizedBox(height: 16.h), + buildInfoRow(context), + SizedBox(height: 16.h), + buildTemperatureRange(), + SizedBox(height: 16.h), + const Text('Grafik'), + SizedBox(height: 16.h), + buildGraphicContent(context, isNpk1), + ], + ), + ); + } + + Widget buildTemperatureInfo(BuildContext context, double value) { + return 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°C', // Display temperature + 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, + ]), + ), + ), + ), + ], + ), + ); + } + + // Build the row with 'Low', 'Ideal', 'High' temperature containers + Widget buildTemperatureRange() { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + _buildInfoContainer('Low', '<20°C', Colors.blue), + _buildInfoContainer('Ideal', '20-30°C', Colors.green), + _buildInfoContainer('High', '>30°C', Colors.orange), + ], + ); + } + + // Reusable container builder + Widget _buildInfoContainer(String label, String tempRange, Color color) { + return Container( + height: 100.h, + width: 100.w, + decoration: BoxDecoration( + color: color.withOpacity(0.1), + borderRadius: BorderRadius.circular(16), + border: Border.all(color: color, width: 2), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(label, style: AppTheme.labelMedium.copyWith(color: color)), + SizedBox(height: 8.h), + Text(tempRange, + style: AppTheme.labelMedium, textAlign: TextAlign.center), + ], + ), + ); + } + + // Information row with IconButton + Widget buildInfoRow(BuildContext context) { + return 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), + ), + ], + ); + } + + // Generalized method to build the graphic content based on NPK type + Widget buildGraphicContent(BuildContext context, bool isNpk1) { + return 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: Consumer( + builder: (context, provider, child) { + final dataState = provider.dataState; + + switch (dataState) { + case ResultState.loading: + return const Center(child: CupertinoActivityIndicator()); + case ResultState.hasData: + return GarphicWidget( + gradientColors: const [Colors.cyan, Colors.amber], + hour: List.generate( + isNpk1 + ? provider.dataFetchedNpk1.length + : provider.dataFetchedNpk2.length, + (index) => isNpk1 + ? provider.dataFetchedNpk1[index].hour ?? 0 + : provider.dataFetchedNpk2[index].hour ?? 0, + ), + data: List.generate( + isNpk1 + ? provider.dataFetchedNpk1.length + : provider.dataFetchedNpk2.length, + (index) => isNpk1 + ? provider.dataFetchedNpk1[index].soiltemperatureAvg ?? + 0 + : provider.dataFetchedNpk2[index].soiltemperatureAvg ?? + 0, + ), + ); + case ResultState.error: + return const GraphicErrorWidget(message: 'Terjadi Kesalahan'); + case ResultState.noData: + return const GraphicErrorWidget(message: 'Tidak Ada Data'); + case ResultState.initial: + default: + return const SizedBox.shrink(); + } + }, + ), + ), + ); + } } diff --git a/agrilink_vocpro/lib/features/home/pages/temperature/provider/temp_provider.dart b/agrilink_vocpro/lib/features/home/pages/temperature/provider/temp_provider.dart new file mode 100644 index 0000000..6a99340 --- /dev/null +++ b/agrilink_vocpro/lib/features/home/pages/temperature/provider/temp_provider.dart @@ -0,0 +1,34 @@ +import 'package:agrilink_vocpro/core/state/result_state.dart'; +import 'package:agrilink_vocpro/data/model/dht_graphic_response.dart'; +import 'package:agrilink_vocpro/domain/service/app_service.dart'; +import 'package:flutter/foundation.dart'; + +class TempProvider extends ChangeNotifier { + TempProvider() { + getTempData(); + } + ResultState dataState = ResultState.initial; + + List dataFetched = []; + + Future getTempData() async { + dataState = ResultState.loading; + notifyListeners(); + try { + final result = + await AppService().getGrafikDataDht(metric: 'viciTemperature'); + if (result.data == null || result.data!.dht!.isEmpty) { + dataState = ResultState.noData; + } else { + dataFetched = result.data!.dht ?? []; + dataState = ResultState.hasData; + } + } catch (e) { + if (kDebugMode) { + print('Get Grafik Temperature Error: $e'); + } + dataState = ResultState.error; + } + notifyListeners(); + } +} 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 dad687f..7146dbe 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 @@ -1,9 +1,13 @@ import 'package:agrilink_vocpro/core/constant/app_theme.dart'; +import 'package:agrilink_vocpro/core/state/result_state.dart'; +import 'package:agrilink_vocpro/features/home/pages/temperature/provider/temp_provider.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'; import 'package:gauge_indicator/gauge_indicator.dart'; +import 'package:provider/provider.dart'; class TemperatureScreen extends StatelessWidget { const TemperatureScreen({super.key, this.temperature = 0}); @@ -13,213 +17,264 @@ class TemperatureScreen extends StatelessWidget { @override Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text('Temperature', style: AppTheme.labelMedium), - centerTitle: true, - backgroundColor: Colors.white, - scrolledUnderElevation: 0, - actions: const [ - Padding( - padding: EdgeInsets.only(right: 16), - child: Icon( - BootstrapIcons.thermometer_half, - color: Colors.red, - ), - ) - ], - ), - 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.thermometer_half, - size: 32, color: Colors.orange), - Text( - '${value.toStringAsFixed(0)}°C', // Animated percentage text - style: const TextStyle( - fontSize: 28, - fontWeight: FontWeight.bold, - color: Colors.black, + return ChangeNotifierProvider( + create: (context) => TempProvider(), + child: Scaffold( + appBar: AppBar( + title: Text('Temperature', style: AppTheme.labelMedium), + centerTitle: true, + backgroundColor: Colors.white, + scrolledUnderElevation: 0, + actions: const [ + Padding( + padding: EdgeInsets.only(right: 16), + child: Icon( + BootstrapIcons.thermometer_half, + color: Colors.red, + ), + ) + ], + ), + 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.thermometer_half, + size: 32, color: Colors.orange), + 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.white12, + Colors.orange, + ]), + ), + ), + ), + ], + ), + ), + const SizedBox(height: 16), + const SizedBox(height: 16), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'Temperature', + style: AppTheme.labelMedium, + textAlign: TextAlign.center, + ), + IconButton( + iconSize: 20.r, + color: Colors.blue, + onPressed: () {}, + 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), + Text( + '<20°C', + style: AppTheme.labelMedium, + textAlign: TextAlign.center, ), ], ), ), - 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, + 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, ), - progressBar: const GaugeBasicProgressBar( - gradient: GaugeAxisGradient(colors: [ - Colors.white12, - Colors.orange, - ]), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('Ideal', + style: AppTheme.labelMedium + .copyWith(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), + Text( + '>30°C', + style: AppTheme.labelMedium, + textAlign: TextAlign.center, + ), + ], + ), ), ], ), - ), - const SizedBox(height: 16), - const SizedBox(height: 16), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - 'Temperature', - style: AppTheme.labelMedium, - textAlign: TextAlign.center, - ), - IconButton( - iconSize: 20.r, - color: Colors.blue, - onPressed: () {}, - icon: const Icon(BootstrapIcons.info_circle)) - ], - ), - SizedBox(height: 16.h), - Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - Container( - height: 100.h, - width: 100.w, + SizedBox(height: 16.h), + const Text('Grafik'), + SizedBox(height: 16.h), + AspectRatio( + aspectRatio: 1.6.h, + child: Container( decoration: BoxDecoration( - borderRadius: BorderRadius.circular(16), - color: Colors.blue.withOpacity(0.1), - border: Border.all( - color: Colors.blue, - width: 2, - ), + color: Colors.white, + borderRadius: BorderRadius.circular(16.w), + border: Border.all(color: Colors.grey.shade300, width: 1.w), ), - 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, - ), - ], + child: Consumer( + builder: (context, provider, child) { + switch (provider.dataState) { + case ResultState.loading: + return const Center( + child: CupertinoActivityIndicator(), + ); + + case ResultState.hasData: + return GarphicWidget( + gradientColors: const [ + Colors.cyan, + Colors.amber, + ], + hour: List.generate( + provider.dataFetched.length, + (index) => + provider.dataFetched[index].hour ?? 0), + data: List.generate( + provider.dataFetched.length, + (index) => + provider + .dataFetched[index].vicitemperatureAvg + ?.toDouble() ?? + 0), + ); + case ResultState.error: + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + BootstrapIcons.exclamation_circle, + color: Colors.grey.shade400, + ), + SizedBox(height: 8.h), + Text( + 'Terjadi Kesalahan', + style: AppTheme.labelSmall, + ), + ], + ), + ); + case ResultState.noData: + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + BootstrapIcons.database_fill_x, + color: Colors.grey.shade400, + ), + SizedBox(height: 8.h), + Text( + 'Tidak Ada Data', + style: AppTheme.labelSmall, + ), + ], + ), + ); + case ResultState.initial: + return const SizedBox.shrink(); + default: + return const Center( + child: Text('Default Error'), + ); + } + }, ), ), - 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( - gradientColors: [ - Colors.cyan, - Colors.amber, - ], - ), - ), - ) - ], + ) + ], + ), ), ), ); diff --git a/agrilink_vocpro/lib/features/home/provider/home_provider.dart b/agrilink_vocpro/lib/features/home/provider/home_provider.dart index 1c749c4..3235ea8 100644 --- a/agrilink_vocpro/lib/features/home/provider/home_provider.dart +++ b/agrilink_vocpro/lib/features/home/provider/home_provider.dart @@ -1,6 +1,6 @@ import 'package:agrilink_vocpro/core/state/result_state.dart'; import 'package:agrilink_vocpro/domain/service/app_service.dart'; -import 'package:flutter/material.dart'; +import 'package:flutter/foundation.dart'; class HomeProvider extends ChangeNotifier { final DateTime currentDate = DateTime.now(); @@ -95,8 +95,10 @@ class HomeProvider extends ChangeNotifier { notifyListeners(); } } catch (e) { - print('Error: $e'); - dataState = ResultState.error; + if (kDebugMode) { + print('Get Latest Error: $e'); + } + dataState = ResultState.hasData; notifyListeners(); } } diff --git a/agrilink_vocpro/lib/features/home/service/home_service.dart b/agrilink_vocpro/lib/features/home/service/home_service.dart index fa5a71e..8b13789 100644 --- a/agrilink_vocpro/lib/features/home/service/home_service.dart +++ b/agrilink_vocpro/lib/features/home/service/home_service.dart @@ -1,29 +1 @@ -import 'package:agrilink_vocpro/core/constant/app_constant.dart'; -import 'package:agrilink_vocpro/features/home/model/npk1_soil_temp_grafik.dart'; -import 'package:dio/dio.dart'; -class HomeService { - final Dio _dioWithoutInterceptor = Dio( - BaseOptions( - baseUrl: AppConstant.baseUrl, - ), - ); - - Future getNpk1SoilTempGrafik() async { - try { - final result = await _dioWithoutInterceptor.get( - '/sensor/getData?metric=soilTemperature&range[start]=2024-10-03&range[end]=2024-10-03&range[time_range]=HOURLY&sensor=npk1', - ); - if (result.statusCode == 200) { - print(result.data.toString()); - final data = Npk1SoilTempGrafik.fromJson(result.data); - return data; - } else { - throw Exception('Failed to load data'); - } - } on DioException catch (e) { - print(e); - rethrow; - } - } -} diff --git a/agrilink_vocpro/lib/features/home/widgets/graphic_error_widget.dart b/agrilink_vocpro/lib/features/home/widgets/graphic_error_widget.dart new file mode 100644 index 0000000..9654ce3 --- /dev/null +++ b/agrilink_vocpro/lib/features/home/widgets/graphic_error_widget.dart @@ -0,0 +1,24 @@ +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'; + +class GraphicErrorWidget extends StatelessWidget { + const GraphicErrorWidget({super.key, required this.message}); + + final String message; + + @override + Widget build(BuildContext context) { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(BootstrapIcons.exclamation_circle, color: Colors.grey.shade400), + SizedBox(height: 8.h), + Text(message, style: AppTheme.labelSmall), + ], + ), + ); + } +} 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 b60b491..593b0cb 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 @@ -58,7 +58,7 @@ class ListDataFromCensorNpk1 extends StatelessWidget { censorIdentifier: censorIdentifier, onTap: () async { await context.push( - '${AppRoute.soilTemperature}/${provider.npk1Temperature}'); + '${AppRoute.soilTemperature}/${provider.npk1Temperature}/${provider.npk2Temperature}'); }, ), DataDisplayerWidget( diff --git a/agrilink_vocpro/lib/main.dart b/agrilink_vocpro/lib/main.dart index 7d7f3c3..a4413dd 100644 --- a/agrilink_vocpro/lib/main.dart +++ b/agrilink_vocpro/lib/main.dart @@ -31,20 +31,21 @@ class MyApp extends StatelessWidget { ChangeNotifierProvider(create: (context) => ControlProvider()), ], child: ScreenUtilInit( - designSize: const Size(360, 800), - minTextAdapt: true, - builder: (_, context) { - return MaterialApp.router( - debugShowCheckedModeBanner: false, - title: 'Flutter Demo', - theme: ThemeData( - scaffoldBackgroundColor: Colors.white, - colorScheme: ColorScheme.fromSeed(seedColor: Colors.teal), - useMaterial3: true, - ), - routerConfig: AppRoute.router, - ); - }), + designSize: const Size(360, 800), + minTextAdapt: true, + builder: (_, context) { + return MaterialApp.router( + debugShowCheckedModeBanner: false, + title: 'Flutter Demo', + theme: ThemeData( + scaffoldBackgroundColor: Colors.white, + colorScheme: ColorScheme.fromSeed(seedColor: Colors.teal), + useMaterial3: true, + ), + routerConfig: AppRoute.router, + ); + }, + ), ); } }