diff --git a/agrilink_vocpro/lib/core/route/app_route.dart b/agrilink_vocpro/lib/core/route/app_route.dart index 7588370..9b7b193 100644 --- a/agrilink_vocpro/lib/core/route/app_route.dart +++ b/agrilink_vocpro/lib/core/route/app_route.dart @@ -2,6 +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/humidity/view/humidity_screen.dart'; import 'package:agrilink_vocpro/features/home/pages/light/view/light_screen.dart'; +import 'package:agrilink_vocpro/features/home/pages/ph/view/ph_screen.dart'; import 'package:agrilink_vocpro/features/splash/view/splash_screen.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; @@ -17,6 +18,7 @@ class AppRoute { static const String temperature = '/temperature'; static const String soil = '/soil'; static const String light = '/dashboard/light'; + static const String ph = '/dashboard/ph'; static const String water = '/water'; static const String acidity = '/acidity'; @@ -53,7 +55,15 @@ class AppRoute { 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); + }, + ), ], ), ], diff --git a/agrilink_vocpro/lib/domain/service/mqtt_service.dart b/agrilink_vocpro/lib/domain/service/mqtt_service.dart index 37ea7e8..a7c4d31 100644 --- a/agrilink_vocpro/lib/domain/service/mqtt_service.dart +++ b/agrilink_vocpro/lib/domain/service/mqtt_service.dart @@ -27,19 +27,22 @@ class MQTTService { } Future publishMessage(String topic, String message) async { - final bool isConnected = await isMqttConnected(); - if (isConnected) { - final builder = MqttClientPayloadBuilder(); + final builder = MqttClientPayloadBuilder(); - try { - builder.addString(message); - client!.publishMessage(topic, MqttQos.atLeastOnce, builder.payload!); - return ResultState.hasData; - } catch (e) { - print(e); + try { + final bool isConnected = await isMqttConnected(); // Cek apakah terhubung + if (!isConnected) { + print('MQTT: Tidak terhubung ke broker. Tidak bisa publish message.'); return ResultState.error; } - } else { + + 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; } } @@ -68,4 +71,55 @@ class MQTTService { 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'); + + // 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: 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'); + + 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: No messages received'); + } + }, + ); + + return isActive; + } catch (e) { + print('MQTT: Error subscribing to $topic: $e'); + return isActive; + } + } else { + print('MQTT: Not connected, cannot subscribe.'); + return false; + } + } } diff --git a/agrilink_vocpro/lib/features/control/provider/control_provider.dart b/agrilink_vocpro/lib/features/control/provider/control_provider.dart index 59f82ab..6b56df4 100644 --- a/agrilink_vocpro/lib/features/control/provider/control_provider.dart +++ b/agrilink_vocpro/lib/features/control/provider/control_provider.dart @@ -3,59 +3,57 @@ import 'package:agrilink_vocpro/domain/service/mqtt_service.dart'; import 'package:flutter/material.dart'; class ControlProvider extends ChangeNotifier { - bool _control_1 = false; - bool _control_2 = false; - bool _control_3 = false; - bool _control_4 = false; - bool _control_5 = false; - bool _control_6 = false; + final MQTTService _mqttService = MQTTService(); + + bool _control_1 = false; - // Getters bool get control_1 => _control_1; - bool get control_2 => _control_2; - bool get control_3 => _control_3; - bool get control_4 => _control_4; - bool get control_5 => _control_5; - bool get control_6 => _control_6; ControlProvider() { connectMqtt(); } ResultState mqttState = ResultState.initial; + ResultState subscribeState = ResultState.initial; - // Koneksi MQTT Future connectMqtt() async { mqttState = ResultState.loading; + subscribeState = ResultState.loading; + notifyListeners(); + try { - final result = await MQTTService().setupMqtt(); + final result = await _mqttService.setupMqtt(); if (result == ResultState.hasData) { mqttState = result; - print('Connected to MQTT'); + final result2 = await _mqttService.subscribeToTopic('relay1'); + if (result2 == true) { + subscribeState = ResultState.hasData; + _control_1 = true; + } else { + subscribeState = ResultState.hasData; + _control_1 = false; + } } else { mqttState = ResultState.error; - print('Failed to connect to MQTT'); } } catch (e) { + mqttState = ResultState.error; print(e); } + notifyListeners(); } Future disconnectMqtt() async { try { - final result = await MQTTService().disconnectMqtt(); - if (result == ResultState.hasData) print('Disconnected from MQTT'); + await _mqttService.disconnectMqtt(); } catch (e) { + print(e); rethrow; } notifyListeners(); } - Future isMqttConnected() async { - return true; - } - @override void dispose() { disconnectMqtt(); @@ -67,28 +65,22 @@ class ControlProvider extends ChangeNotifier { notifyListeners(); } - void switchControl2() { - _control_2 = !_control_2; - notifyListeners(); + Future publishMessage(String topic, String message) async { + try { + final result = await _mqttService.publishMessage(topic, message); + return result; + } catch (e) { + print(e); + rethrow; + } } - void switchControl3() { - _control_3 = !_control_3; - notifyListeners(); - } - - void switchControl4() { - _control_4 = !_control_4; - notifyListeners(); - } - - void switchControl5() { - _control_5 = !_control_5; - notifyListeners(); - } - - void switchControl6() { - _control_6 = !_control_6; - notifyListeners(); + Future subscribeToTopic(String topic) async { + try { + await _mqttService.subscribeToTopic(topic); + } catch (e) { + print(e); + rethrow; + } } } diff --git a/agrilink_vocpro/lib/features/control/view/control_screen.dart b/agrilink_vocpro/lib/features/control/view/control_screen.dart index 22943de..9595b7c 100644 --- a/agrilink_vocpro/lib/features/control/view/control_screen.dart +++ b/agrilink_vocpro/lib/features/control/view/control_screen.dart @@ -1,6 +1,7 @@ import 'package:agrilink_vocpro/core/constant/app_theme.dart'; import 'package:agrilink_vocpro/core/state/result_state.dart'; import 'package:agrilink_vocpro/features/control/provider/control_provider.dart'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:provider/provider.dart'; @@ -21,15 +22,12 @@ class ControlScreen extends StatelessWidget { return SafeArea( child: ListView( children: [ - SizedBox( - height: 16.h, - child: Center( - child: provider.mqttState == ResultState.loading - ? const CircularProgressIndicator() - : provider.mqttState == ResultState.error - ? const Text('Failed to connect to MQTT') - : const Text('Connected to MQTT'), - ), + 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( @@ -38,7 +36,30 @@ class ControlScreen extends StatelessWidget { 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'), + // ) + // ], + // ), + // ); }, ), ), 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 e29157a..95ee1d3 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,11 +1,11 @@ import 'package:agrilink_vocpro/core/constant/app_theme.dart'; -import 'package:agrilink_vocpro/features/home/pages/humidity/widgets/circle_chart.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'; 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 HumidityScreen extends StatelessWidget { @@ -15,7 +15,7 @@ class HumidityScreen extends StatelessWidget { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text('Humidity', style: AppTheme.labelLarge), + title: Text('Humidity', style: AppTheme.labelMedium), centerTitle: true, scrolledUnderElevation: 0, leading: IconButton( @@ -40,12 +40,46 @@ class HumidityScreen extends StatelessWidget { SizedBox( height: MediaQuery.of(context).size.height * 0.05, ), - const SizedBox( - height: 320, - width: double.infinity, - child: CircleChart( - percentage: 60.5, - icon: BootstrapIcons.droplet_half, + 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('60 %', style: AppTheme.headline1), + ], + ), + ), + RotatedBox( + quarterTurns: 2, + child: AnimatedRadialGauge( + duration: const Duration(seconds: 3), + curve: Curves.easeOut, + value: 60, + 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), @@ -86,37 +120,37 @@ class HumidityScreen extends StatelessWidget { 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), - ], - ), - ); - }) + // 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), + // ], + // ), + // ); + // }) ], ); }), diff --git a/agrilink_vocpro/lib/features/home/pages/humidity/widgets/circle_chart.dart b/agrilink_vocpro/lib/features/home/pages/humidity/widgets/circle_chart.dart deleted file mode 100644 index f6d353b..0000000 --- a/agrilink_vocpro/lib/features/home/pages/humidity/widgets/circle_chart.dart +++ /dev/null @@ -1,120 +0,0 @@ -import 'package:fl_chart/fl_chart.dart'; -import 'package:flutter/material.dart'; - -class CircleChart extends StatefulWidget { - const CircleChart({ - super.key, - required this.percentage, - required this.icon, - this.colorStart, - this.colorEnd, - }); - - final double percentage; - final IconData icon; - final Color? colorStart; - final Color? colorEnd; - - @override - State createState() => _CircleChartState(); -} - -class _CircleChartState extends State { - double currentPercentage = 0; - @override - void initState() { - super.initState(); - Future.delayed(Duration.zero, () async { - for (double i = 0; i <= widget.percentage; i++) { - await Future.delayed(const Duration(milliseconds: 25), () { - setState(() { - currentPercentage = i; - }); - }); - } - }); - } - - @override - Widget build(BuildContext context) { - return Center( - child: Stack( - alignment: Alignment.center, - children: [ - ShaderMask( - shaderCallback: (Rect bounds) { - return RadialGradient( - center: Alignment.center, - radius: 0.6, - colors: [ - Colors.white, - _getAnimatedColor(currentPercentage) - ], - tileMode: TileMode.mirror, - ).createShader(bounds); - }, - child: PieChart( - PieChartData( - sections: _createSections(currentPercentage), - centerSpaceRadius: MediaQuery.of(context).size.width * 0.25, - sectionsSpace: 0, - startDegreeOffset: 270, - borderData: FlBorderData(show: false), - ), - ), - ), - TweenAnimationBuilder( - tween: Tween(begin: 0, end: currentPercentage), - duration: const Duration(milliseconds: 300), - builder: (context, value, child) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon(widget.icon, size: 32, color: _getAnimatedColor(value)), - Text( - '${value.toStringAsFixed(0)}%', // Animated percentage text - style: const TextStyle( - fontSize: 28, - fontWeight: FontWeight.bold, - color: Colors.black, - ), - ), - ], - ); - }, - ), - ], - ), - ); - } - - List _createSections(double percentage) { - return [ - PieChartSectionData( - color: Colors.white, - value: percentage, - title: '', - radius: 50, // Size of the pie slice - titleStyle: const TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - color: Colors.white, - ), - ), - PieChartSectionData( - color: Colors.white24, - value: 100 - percentage, - title: '', - radius: 50, - ), - ]; - } - - Color _getAnimatedColor(double percentage) { - return Color.lerp( - widget.colorStart ?? Colors.green, - widget.colorEnd ?? Colors.blue, - percentage / widget.percentage, - )!; - } -} 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 index 4e5facb..683a237 100644 --- a/agrilink_vocpro/lib/features/home/pages/light/view/light_screen.dart +++ b/agrilink_vocpro/lib/features/home/pages/light/view/light_screen.dart @@ -16,7 +16,7 @@ class LightScreen extends StatelessWidget { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text('Light', style: AppTheme.titleLarge), + title: Text('Light', style: AppTheme.labelMedium), centerTitle: true, backgroundColor: Colors.white, scrolledUnderElevation: 0, 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 new file mode 100644 index 0000000..a54000e --- /dev/null +++ b/agrilink_vocpro/lib/features/home/pages/ph/view/ph_screen.dart @@ -0,0 +1,178 @@ +import 'package:agrilink_vocpro/core/constant/app_theme.dart'; +import 'package:agrilink_vocpro/features/home/pages/ph/widget/ph_bar_pointer.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 PhScreen extends StatelessWidget { + const PhScreen({super.key, required this.phValue}); + + final double phValue; + + double get value => phValue; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('pH Tanah', 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, + ), + Center( + child: PhIndicator(phValue: value), // Set nilai pH di sini + ), + const SizedBox(height: 16), + const SizedBox(height: 16), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'pH', + 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), + // 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(), + ), + ) + ], + ), + ), + ); + } +} 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 new file mode 100644 index 0000000..4d9e804 --- /dev/null +++ b/agrilink_vocpro/lib/features/home/pages/ph/widget/ph_bar_pointer.dart @@ -0,0 +1,88 @@ +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 PhIndicator extends StatelessWidget { + final double phValue; // Nilai pH yang ditampilkan (misal 3.5 atau 6.7) + + const PhIndicator({super.key, required this.phValue}); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + Center( + child: Text( + 'pH : $phValue', + style: AppTheme.headline1, + ), + ), + SizedBox(height: 16.h), + SizedBox( + height: 80.h, + child: Stack( + alignment: Alignment.center, + children: [ + CustomPaint( + size: Size(300.w, 30.h), // Ukuran bar + painter: PhBarPainter(), + ), + Positioned( + top: 48.h, + left: calculatePointerPosition( + phValue, 300), // Hitung posisi pointer + child: const Icon( + Icons.arrow_drop_up, + size: 40, + color: Colors.black, // Warna pointer + ), + ), + const Positioned( + top: 0, + left: 0, + right: 0, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('0'), + Text('7'), + Text('14'), + ], + ), + ), + ], + ), + ), + ], + ); + } + + // Fungsi untuk menghitung posisi pointer berdasarkan nilai pH + double calculatePointerPosition(double phValue, double barWidth) { + // Nilai pH umumnya antara 0 dan 14, sesuaikan dengan lebar bar + double normalizedPh = phValue / 14; // Normalisasi nilai pH (-7 hingga 7) + return normalizedPh * barWidth; + } +} + +class PhBarPainter extends CustomPainter { + @override + void paint(Canvas canvas, Size size) { + // Buat gradasi dari biru ke merah + const gradient = LinearGradient( + colors: [Colors.blue, Colors.orange, Colors.red], + stops: [0.0, 0.5, 1.0], // Set stop untuk 0, 7, 14 (pH) + ); + + // Buat rect untuk menggambar gradasi + final rect = Rect.fromLTWH(0, 0, size.width, size.height); + final paint = Paint()..shader = gradient.createShader(rect); + + // Gambar rect dengan gradasi + canvas.drawRect(rect, paint); + } + + @override + bool shouldRepaint(CustomPainter oldDelegate) => false; +} 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 ebfc919..3847ae9 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 @@ -1,7 +1,9 @@ import 'package:agrilink_vocpro/core/constant/app_theme.dart'; -import 'package:agrilink_vocpro/features/home/pages/humidity/widgets/circle_chart.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'; class SoilMoistureScreen extends StatelessWidget { const SoilMoistureScreen({super.key}); @@ -33,14 +35,46 @@ class SoilMoistureScreen extends StatelessWidget { SizedBox( height: MediaQuery.of(context).size.height * 0.05, ), - const SizedBox( - height: 320, - width: double.infinity, - child: CircleChart( - percentage: 60.5, - icon: Icons.water_outlined, - colorStart: Colors.lime, - colorEnd: Colors.brown, + SizedBox( + height: 280.h, + child: Stack( + fit: StackFit.expand, + children: [ + Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(BootstrapIcons.water, + size: 32, color: Colors.blue), + Text('60 %', style: AppTheme.headline1), + ], + ), + ), + RotatedBox( + quarterTurns: 2, + child: AnimatedRadialGauge( + duration: const Duration(seconds: 3), + curve: Curves.easeOut, + value: 60, + 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), 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 aa02b8d..fe9c134 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 @@ -14,7 +14,7 @@ class TemperatureScreen extends StatelessWidget { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text('Temperature', style: AppTheme.titleLarge), + title: Text('Temperature', style: AppTheme.labelMedium), centerTitle: true, backgroundColor: Colors.white, scrolledUnderElevation: 0, diff --git a/agrilink_vocpro/lib/features/home/provider/home_provider.dart b/agrilink_vocpro/lib/features/home/provider/home_provider.dart index f42a083..12a161c 100644 --- a/agrilink_vocpro/lib/features/home/provider/home_provider.dart +++ b/agrilink_vocpro/lib/features/home/provider/home_provider.dart @@ -1,59 +1,87 @@ -import 'package:agrilink_vocpro/features/dashboard/model/censor_data_rule.dart'; +import 'package:agrilink_vocpro/core/state/result_state.dart'; import 'package:flutter/material.dart'; class HomeProvider extends ChangeNotifier { final DateTime currentDate = DateTime.now(); - List humidtyRules = [ - CensorDataRule( - minPercentage: 0, - maxPercentage: 30, - censorText: 'Very Low', - description: - 'Udara sangat kering. Tanaman bisa mengalami stress akibat kekurangan air.', - action: - 'Aktifkan sistem penyiraman atau humidifier untuk menaikkan kelembaban. Periksa juga apakah ada kebocoran pada sistem irigasi yang mengakibatkan kelembaban terlalu rendah.', - color: Colors.red, - ), - CensorDataRule( - minPercentage: 31, - maxPercentage: 50, - censorText: 'Low', - description: - 'Kelembaban masih cukup rendah. Beberapa jenis tanaman mungkin sudah mulai terpengaruh.', - action: - 'Pertimbangkan untuk menambah irigasi atau memperpanjang durasi penyiraman. Pantau tanaman secara berkala.', - color: Colors.orange, - ), - CensorDataRule( - minPercentage: 51, - maxPercentage: 70, - censorText: 'Normal', - description: - 'Ini adalah kelembaban yang ideal untuk sebagian besar tanaman dalam greenhouse.', - action: - 'Pertahankan kondisi ini. Tidak ada tindakan yang diperlukan kecuali jika ada perubahan mendadak.', - color: Colors.green, - ), - CensorDataRule( - minPercentage: 71, - maxPercentage: 85, - censorText: 'High', - description: - 'Udara mulai terlalu lembap. Kelembaban tinggi dapat meningkatkan risiko penyakit jamur atau bakteri.', - action: - 'Aktifkan ventilasi atau kipas untuk mengurangi kelembaban. Pastikan aliran udara di greenhouse cukup baik.', - color: Colors.lime, - ), - CensorDataRule( - minPercentage: 86, - maxPercentage: 100, - censorText: 'Very High', - description: - 'Udara sangat lembap, yang bisa berisiko menyebabkan jamur, lumut, dan penyakit tanaman.', - action: - 'Segera aktifkan sistem ventilasi maksimal, mungkin juga gunakan dehumidifier jika diperlukan. Kurangi frekuensi penyiraman atau periksa sistem irigasi agar tidak berlebihan.', - color: Colors.brown, - ), - ]; + HomeProvider() { + getData(); + } + + ResultState dataState = ResultState.initial; + + Future getData() async { + dataState = ResultState.loading; + notifyListeners(); + try { + print('Fetching data...'); + await Future.delayed(const Duration(seconds: 3)); + print('Data fetched'); + dataState = ResultState.hasData; + notifyListeners(); + } catch (e) { + dataState = ResultState.error; + notifyListeners(); + } + } + + @override + void dispose() { + dataState = ResultState.initial; + super.dispose(); + } } + + + // List humidtyRules = [ + // CensorDataRule( + // minPercentage: 0, + // maxPercentage: 30, + // censorText: 'Very Low', + // description: + // 'Udara sangat kering. Tanaman bisa mengalami stress akibat kekurangan air.', + // action: + // 'Aktifkan sistem penyiraman atau humidifier untuk menaikkan kelembaban. Periksa juga apakah ada kebocoran pada sistem irigasi yang mengakibatkan kelembaban terlalu rendah.', + // color: Colors.red, + // ), + // CensorDataRule( + // minPercentage: 31, + // maxPercentage: 50, + // censorText: 'Low', + // description: + // 'Kelembaban masih cukup rendah. Beberapa jenis tanaman mungkin sudah mulai terpengaruh.', + // action: + // 'Pertimbangkan untuk menambah irigasi atau memperpanjang durasi penyiraman. Pantau tanaman secara berkala.', + // color: Colors.orange, + // ), + // CensorDataRule( + // minPercentage: 51, + // maxPercentage: 70, + // censorText: 'Normal', + // description: + // 'Ini adalah kelembaban yang ideal untuk sebagian besar tanaman dalam greenhouse.', + // action: + // 'Pertahankan kondisi ini. Tidak ada tindakan yang diperlukan kecuali jika ada perubahan mendadak.', + // color: Colors.green, + // ), + // CensorDataRule( + // minPercentage: 71, + // maxPercentage: 85, + // censorText: 'High', + // description: + // 'Udara mulai terlalu lembap. Kelembaban tinggi dapat meningkatkan risiko penyakit jamur atau bakteri.', + // action: + // 'Aktifkan ventilasi atau kipas untuk mengurangi kelembaban. Pastikan aliran udara di greenhouse cukup baik.', + // color: Colors.lime, + // ), + // CensorDataRule( + // minPercentage: 86, + // maxPercentage: 100, + // censorText: 'Very High', + // description: + // 'Udara sangat lembap, yang bisa berisiko menyebabkan jamur, lumut, dan penyakit tanaman.', + // action: + // 'Segera aktifkan sistem ventilasi maksimal, mungkin juga gunakan dehumidifier jika diperlukan. Kurangi frekuensi penyiraman atau periksa sistem irigasi agar tidak berlebihan.', + // color: Colors.brown, + // ), + // ]; diff --git a/agrilink_vocpro/lib/features/home/view/home_screen.dart b/agrilink_vocpro/lib/features/home/view/home_screen.dart index ff42e8e..224cadc 100644 --- a/agrilink_vocpro/lib/features/home/view/home_screen.dart +++ b/agrilink_vocpro/lib/features/home/view/home_screen.dart @@ -1,12 +1,14 @@ import 'package:agrilink_vocpro/core/constant/app_color.dart'; import 'package:agrilink_vocpro/core/constant/app_theme.dart'; import 'package:agrilink_vocpro/core/extension/extention.dart'; +import 'package:agrilink_vocpro/features/home/provider/home_provider.dart'; import 'package:agrilink_vocpro/features/home/widgets/list_data_from_censor_npk1.dart'; import 'package:agrilink_vocpro/features/home/widgets/list_data_from_censor_npk2.dart'; -import 'package:agrilink_vocpro/features/home/widgets/list_data_from_main_censor.dart'; +import 'package:agrilink_vocpro/features/home/widgets/list_data_from_censor_dht.dart'; import 'package:animated_segmented_tab_control/animated_segmented_tab_control.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:provider/provider.dart'; class HomeScreen extends StatefulWidget { const HomeScreen({super.key}); @@ -23,7 +25,7 @@ class _HomeScreenState extends State { appBar: AppBar( toolbarHeight: 200.h, elevation: 0, - backgroundColor: AppColor.backgroundColor, + backgroundColor: Colors.white, scrolledUnderElevation: 0, flexibleSpace: Padding( padding: EdgeInsets.symmetric(horizontal: 12.w), @@ -55,6 +57,16 @@ class _HomeScreenState extends State { ), ], ), + const Spacer(), + IconButton( + onPressed: () { + context.read().getData(); + }, + icon: const Icon( + Icons.refresh_rounded, + color: AppColor.primary, + ), + ) ], ), SizedBox( @@ -130,7 +142,7 @@ class _HomeScreenState extends State { ), textStyle: AppTheme.labelSmall, tabs: const [ - SegmentTab(label: 'Main Censor'), + SegmentTab(label: 'DHT'), SegmentTab(label: 'NPK 1'), SegmentTab(label: 'NPK 2'), ]), @@ -138,7 +150,7 @@ class _HomeScreenState extends State { Padding( padding: EdgeInsets.only(top: 64.h), child: const TabBarView(children: [ - ListDataFromMainCensor(), + ListDataFromCensorDht(), ListDataFromCensorNpk1(), ListDataFromCensorNpk2(), ]), diff --git a/agrilink_vocpro/lib/features/home/widgets/censor_item_loading_widgets.dart b/agrilink_vocpro/lib/features/home/widgets/censor_item_loading_widgets.dart new file mode 100644 index 0000000..a92c618 --- /dev/null +++ b/agrilink_vocpro/lib/features/home/widgets/censor_item_loading_widgets.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:shimmer/shimmer.dart'; + +class CensorItemLoadingWidgets extends StatelessWidget { + const CensorItemLoadingWidgets({super.key}); + + @override + Widget build(BuildContext context) { + return Shimmer.fromColors( + baseColor: Colors.grey.shade300, + highlightColor: Colors.grey.shade100, + child: Container( + decoration: BoxDecoration( + color: Colors.grey[200], + borderRadius: BorderRadius.circular(16.r), + ), + ), + ); + } +} 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 new file mode 100644 index 0000000..43b7a29 --- /dev/null +++ b/agrilink_vocpro/lib/features/home/widgets/list_data_from_censor_dht.dart @@ -0,0 +1,103 @@ +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'; +import 'package:bootstrap_icons/bootstrap_icons.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 ListDataFromCensorDht extends StatelessWidget { + const ListDataFromCensorDht({ + super.key, + }); + + @override + Widget build(BuildContext context) { + return Consumer(builder: (context, provider, child) { + switch (provider.dataState) { + case ResultState.loading: + return GridView( + padding: EdgeInsets.all(16.r), + physics: const NeverScrollableScrollPhysics(), + 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), + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + crossAxisSpacing: 16.r, + mainAxisSpacing: 16.r, + childAspectRatio: 0.9, + ), + children: [ + DataDisplayerWidget( + title: 'Humidity', + subtitle: 'kelembaban udara', + value: '60', + unit: '%', + icon: BootstrapIcons.droplet_half, + textColor: Colors.white, + color: AppColor.secondary, + iconColor: Colors.white, + censorIdentifier: 'NPK 1', + onTap: () async { + context.push(AppRoute.humidity, extra: '60'); + }, + ), + DataDisplayerWidget( + title: 'Temperature', + subtitle: 'suhu greenhouse', + value: '28', + unit: '°C', + icon: BootstrapIcons.thermometer_half, + color: Colors.white, + onTap: () async { + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const TemperatureScreen())); + }, + ), + DataDisplayerWidget( + title: 'Light', + subtitle: 'intensitas cahaya', + value: '1000', + unit: 'lux', + icon: BootstrapIcons.sun, + color: Colors.white, + onTap: () async { + context.push('${AppRoute.light}/300'); + }, + ), + ], + ); + case ResultState.noData: + return const Center(child: Text('No Data')); + case ResultState.error: + return const Center(child: Text('Error')); + case ResultState.initial: + return const SizedBox.shrink(); + default: + return const SizedBox.shrink(); + } + }); + } +} 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 64d21f2..d3db9cd 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,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 ListDataFromCensorNpk1 extends StatelessWidget { const ListDataFromCensorNpk1({ @@ -17,100 +21,127 @@ class ListDataFromCensorNpk1 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: () { + context.push('${AppRoute.ph}/6.5'); + }, + ), + 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 const Center(child: Text('No Data')); + case ResultState.error: + return const Center(child: Text('Error')); + default: + return const SizedBox.shrink(); + } + }); } } diff --git a/agrilink_vocpro/lib/features/home/widgets/list_data_from_main_censor.dart b/agrilink_vocpro/lib/features/home/widgets/list_data_from_main_censor.dart deleted file mode 100644 index fb05633..0000000 --- a/agrilink_vocpro/lib/features/home/widgets/list_data_from_main_censor.dart +++ /dev/null @@ -1,70 +0,0 @@ -import 'package:agrilink_vocpro/core/constant/app_color.dart'; -import 'package:agrilink_vocpro/core/route/app_route.dart'; -import 'package:agrilink_vocpro/features/home/pages/temperature/view/temperature_screen.dart'; -import 'package:agrilink_vocpro/features/home/widgets/data_display_widget.dart'; -import 'package:bootstrap_icons/bootstrap_icons.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:go_router/go_router.dart'; - -class ListDataFromMainCensor extends StatelessWidget { - const ListDataFromMainCensor({ - super.key, - }); - - @override - Widget build(BuildContext context) { - return GridView( - padding: EdgeInsets.all(16.r), - physics: const NeverScrollableScrollPhysics(), - shrinkWrap: true, - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 2, - crossAxisSpacing: 16.r, - mainAxisSpacing: 16.r, - childAspectRatio: 0.9, - ), - children: [ - DataDisplayerWidget( - title: 'Humidity', - subtitle: 'kelembaban udara', - value: '60', - unit: '%', - icon: BootstrapIcons.droplet_half, - textColor: Colors.white, - color: AppColor.secondary, - iconColor: Colors.white, - censorIdentifier: 'NPK 1', - onTap: () async { - context.push(AppRoute.humidity, extra: '60'); - }, - ), - DataDisplayerWidget( - title: 'Temperature', - subtitle: 'suhu greenhouse', - value: '28', - unit: '°C', - icon: BootstrapIcons.thermometer_half, - color: Colors.white, - onTap: () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const TemperatureScreen())); - }, - ), - DataDisplayerWidget( - title: 'Light', - subtitle: 'intensitas cahaya', - value: '1000', - unit: 'lux', - icon: BootstrapIcons.sun, - color: Colors.white, - onTap: () async { - context.push('${AppRoute.light}/300'); - }, - ), - ], - ); - } -} diff --git a/agrilink_vocpro/lib/features/setting/view/setting_screen.dart b/agrilink_vocpro/lib/features/setting/view/setting_screen.dart index 94eea81..3b37841 100644 --- a/agrilink_vocpro/lib/features/setting/view/setting_screen.dart +++ b/agrilink_vocpro/lib/features/setting/view/setting_screen.dart @@ -2,10 +2,12 @@ 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'; class SettingScreen extends StatelessWidget { const SettingScreen({super.key}); @@ -96,51 +98,27 @@ class SettingScreen extends StatelessWidget { size: 16.r, ), onTap: () { - if (Platform.isAndroid) { - showDialog( - context: context, - builder: (context) => AlertDialog( - title: Text('Logout'), - content: Text('Apakah anda yakin ingin logout?'), - actions: [ - TextButton( - child: Text('Batal'), - onPressed: () { - Navigator.pop(context); - }, - ), - TextButton( - child: Text('Ya'), - onPressed: () { - Navigator.pop(context); - }, - ), - ], - ), - ); - } else { - showDialog( - context: context, - builder: (context) => CupertinoAlertDialog( - title: Text('Logout'), - content: Text('Apakah anda yakin ingin logout?'), - actions: [ - TextButton( - child: Text('Batal'), - onPressed: () { - Navigator.pop(context); - }, - ), - TextButton( - child: Text('Ya'), - onPressed: () { - Navigator.pop(context); - }, - ), - ], - ), - ); - } + showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text('Logout'), + content: Text('Apakah anda yakin ingin logout?'), + actions: [ + TextButton( + child: Text('Batal'), + onPressed: () { + Navigator.pop(context); + }, + ), + TextButton( + child: Text('Ya'), + onPressed: () { + context.go(AppRoute.root); + }, + ), + ], + ), + ); }), ], ), diff --git a/agrilink_vocpro/lib/main.dart b/agrilink_vocpro/lib/main.dart index 469a3b3..7d7f3c3 100644 --- a/agrilink_vocpro/lib/main.dart +++ b/agrilink_vocpro/lib/main.dart @@ -1,4 +1,3 @@ -import 'package:agrilink_vocpro/core/constant/app_color.dart'; import 'package:agrilink_vocpro/core/route/app_route.dart'; import 'package:agrilink_vocpro/features/auth/provider/auth_provider.dart'; import 'package:agrilink_vocpro/features/control/provider/control_provider.dart'; diff --git a/agrilink_vocpro/pubspec.lock b/agrilink_vocpro/pubspec.lock index e33a1a3..58cead9 100644 --- a/agrilink_vocpro/pubspec.lock +++ b/agrilink_vocpro/pubspec.lock @@ -456,6 +456,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.4.1" + shimmer: + dependency: "direct main" + description: + name: shimmer + sha256: "5f88c883a22e9f9f299e5ba0e4f7e6054857224976a5d9f839d4ebdc94a14ac9" + url: "https://pub.dev" + source: hosted + version: "3.0.0" sky_engine: dependency: transitive description: flutter diff --git a/agrilink_vocpro/pubspec.yaml b/agrilink_vocpro/pubspec.yaml index 108f867..8ab1293 100644 --- a/agrilink_vocpro/pubspec.yaml +++ b/agrilink_vocpro/pubspec.yaml @@ -46,6 +46,7 @@ dependencies: flutter_screenutil: ^5.9.3 gauge_indicator: ^0.4.3 mqtt_client: ^10.5.1 + shimmer: ^3.0.0 dev_dependencies: flutter_test: