feat: integrate with Rest API, feature home latest data update

This commit is contained in:
Syaroful 2024-10-10 16:35:34 +07:00
parent 5ddc748bc9
commit 08c7012f1d
23 changed files with 1122 additions and 367 deletions

View File

@ -2,7 +2,7 @@ class AppConstant {
static const String appName = 'Kebun Pintar';
static const String appVersion = '1.0.0';
static const String baseUrl = 'http://192.168.11.41:3333/api/';
static const String baseUrl = 'https://jx027dj4-3333.asse.devtunnels.ms/api';
static const String mqttServer = 'armadillo.rmq.cloudamqp.com';
static const String mqttUsername = 'obyskxhx:obyskxhx';
@ -10,4 +10,19 @@ class AppConstant {
static const String soilTempInfo =
'Suhu tanah mengacu pada suhu tanah di permukaan atau pada kedalaman tertentu, yang berperan penting dalam pertumbuhan tanaman dan proses pertanian. Suhu ini memengaruhi perkecambahan benih, aktivitas akar, serta penyerapan air dan nutrisi, yang semuanya esensial bagi perkembangan tanaman. Selain itu, suhu tanah juga memengaruhi aktivitas mikroorganisme yang berkontribusi pada kesuburan tanah. Dalam pertanian pintar, sensor suhu tanah sering digunakan untuk memantau dan mengoptimalkan kondisi tanah, memastikan tanaman tumbuh dalam rentang suhu yang ideal.';
static const String npk1 = 'npk1';
static const String npk2 = 'npk2';
static const String dht = 'dht';
static const String soilTemp = 'soilTemperature';
static const String soilMoisture = 'soilMoisture';
static const String airTemp = 'viciTemperature';
static const String humidity = 'humidity';
static const String lightIntensity = 'lightIntensity';
static const String conductivity = 'conductivity';
static const String ph = 'ph';
static const String nitrogen = 'nitrogen';
static const String phosphorus = 'phosphorus';
static const String potassium = 'potassium';
}

View File

@ -10,6 +10,12 @@ String dateFormater(String date) {
return formatter.format(dateTime);
}
String dateFormatterShort(String date) {
final DateTime dateTime = DateTime.parse(date);
final DateFormat formatter = DateFormat('yyyy-MM-dd');
return formatter.format(dateTime);
}
String getGreeting(String time) {
DateTime parsedTime = DateTime.parse(time);
int hour = parsedTime.hour;

View File

@ -1,5 +1,8 @@
import 'package:agrilink_vocpro/core/constant/app_constant.dart';
import 'package:agrilink_vocpro/core/extension/extention.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:dio/dio.dart';
import 'package:flutter/foundation.dart';
@ -27,4 +30,47 @@ class AppService {
rethrow;
}
}
Future<Npk1SoilTempGrafik> getNpk1SoilTempGrafik({
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);
try {
final result = await _dioWithoutInterceptor.get(
'/sensor/getData?metric=$metric&range[start]=$formatedDateYesterday&range[end]=$formatedDateNow&range[time_range]=HOURLY&sensor=$sensor',
);
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;
}
}
Future<LatestDataResponse> getLatestData() async {
try {
final result = await _dioWithoutInterceptor.get(
'/sensor/getLatest',
);
if (result.statusCode == 200) {
final data = LatestDataResponse.fromJson(result.data);
return data;
} else {
throw Exception('Failed to load data');
}
} on DioException catch (e) {
print(e);
rethrow;
}
}
}

View File

@ -52,6 +52,7 @@ class ControlProvider extends ChangeNotifier {
relayState = ResultState.loading;
notifyListeners();
try {
print('try to get relay status...');
final result = await _appService.getRelayStatus();
if (result.success == true) {
for (var element in result.data!) {

View File

@ -61,33 +61,38 @@ class ControlScreen extends StatelessWidget {
}),
SizedBox(height: 16.h),
GridView(
padding: EdgeInsets.all(16.r),
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 16.r,
mainAxisSpacing: 16.r,
childAspectRatio: 1.4.h,
padding: EdgeInsets.all(16.r),
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 16.r,
mainAxisSpacing: 16.r,
childAspectRatio: 1.35.h,
),
children: [
ControlButtonWidget(
title: 'Katup Air',
subtitle: 'Relay 1',
isActive: provider.control_1,
onTap: () {
provider.control_1 != true
? provider.switchControl1(true)
: provider.switchControl1(false);
},
),
children: [
ControlButtonWidget(
title: 'Katup Air',
subtitle: 'Relay 1',
isActive: provider.control_1,
onTap: () {
provider.control_1 != true
? provider.switchControl1(true)
: provider.switchControl1(false);
},
),
ControlButtonWidget(
title: 'Lampu Utama',
subtitle: 'Relay 2',
isActive: provider.control_2,
onTap: () {},
),
]),
ControlButtonWidget(
title: 'Lampu Utama',
subtitle: 'Relay 2',
isActive: provider.control_2,
onTap: () {
provider.control_2 != true
? provider.switchControl2(true)
: provider.switchControl2(false);
},
),
],
),
],
),
),

View File

@ -0,0 +1,75 @@
class DhtGraphicResponse {
DataDht? data;
int? statusCode;
String? message;
DhtGraphicResponse({this.data, this.statusCode, this.message});
DhtGraphicResponse.fromJson(Map<String, dynamic> json) {
data = json['data'] != null ? DataDht.fromJson(json['data']) : null;
statusCode = json['statusCode'];
message = json['message'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
if (this.data != null) {
data['data'] = this.data!.toJson();
}
data['statusCode'] = statusCode;
data['message'] = message;
return data;
}
}
class DataDht {
List<Dht>? dht;
DataDht({this.dht});
DataDht.fromJson(Map<String, dynamic> json) {
if (json['dht'] != null) {
dht = <Dht>[];
json['dht'].forEach((v) {
dht!.add(Dht.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
if (dht != null) {
data['dht'] = dht!.map((v) => v.toJson()).toList();
}
return data;
}
}
class Dht {
int? hour;
double? vicitemperatureAvg;
double? vicihumidityAvg;
double? viciluminosityAvg;
Dht(
{this.hour,
this.vicitemperatureAvg,
this.vicihumidityAvg,
this.viciluminosityAvg});
Dht.fromJson(Map<String, dynamic> json) {
hour = json['hour'];
vicitemperatureAvg = json['vicitemperature_avg'];
vicihumidityAvg = json['vicihumidity_avg'];
viciluminosityAvg = json['viciluminosity_avg'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['hour'] = hour;
data['vicitemperature_avg'] = vicitemperatureAvg;
data['vicihumidity_avg'] = vicihumidityAvg;
data['viciluminosity_avg'] = viciluminosityAvg;
return data;
}
}

View File

@ -0,0 +1,185 @@
class LatestDataResponse {
Data? data;
int? statusCode;
String? message;
LatestDataResponse({this.data, this.statusCode, this.message});
LatestDataResponse.fromJson(Map<String, dynamic> json) {
data = json['data'] != null ? Data.fromJson(json['data']) : null;
statusCode = json['statusCode'];
message = json['message'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
if (this.data != null) {
data['data'] = this.data!.toJson();
}
data['statusCode'] = statusCode;
data['message'] = message;
return data;
}
}
class Data {
List<Dht>? dht;
List<Npk1>? npk1;
List<Npk2>? npk2;
Data({this.dht, this.npk1, this.npk2});
Data.fromJson(Map<String, dynamic> json) {
if (json['dht'] != null) {
dht = <Dht>[];
json['dht'].forEach((v) {
dht!.add(Dht.fromJson(v));
});
}
if (json['npk1'] != null) {
npk1 = <Npk1>[];
json['npk1'].forEach((v) {
npk1!.add(Npk1.fromJson(v));
});
}
if (json['npk2'] != null) {
npk2 = <Npk2>[];
json['npk2'].forEach((v) {
npk2!.add(Npk2.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
if (dht != null) {
data['dht'] = dht!.map((v) => v.toJson()).toList();
}
if (npk1 != null) {
data['npk1'] = npk1!.map((v) => v.toJson()).toList();
}
if (npk2 != null) {
data['npk2'] = npk2!.map((v) => v.toJson()).toList();
}
return data;
}
}
class Dht {
int? hour;
num? vicitemperatureAvg;
num? vicihumidityAvg;
num? viciluminosityAvg;
Dht(
{this.hour,
this.vicitemperatureAvg,
this.vicihumidityAvg,
this.viciluminosityAvg});
Dht.fromJson(Map<String, dynamic> json) {
hour = json['hour'];
vicitemperatureAvg = json['vicitemperature_avg'];
vicihumidityAvg = json['vicihumidity_avg'];
viciluminosityAvg = json['viciluminosity_avg'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['hour'] = hour;
data['vicitemperature_avg'] = vicitemperatureAvg;
data['vicihumidity_avg'] = vicihumidityAvg;
data['viciluminosity_avg'] = viciluminosityAvg;
return data;
}
}
class Npk1 {
int? hour;
num? soiltemperatureAvg;
num? soilhumidityAvg;
num? soilconductivityAvg;
num? soilphAvg;
num? soilnitrogenAvg;
num? soilphosphorusAvg;
num? soilpotassiumAvg;
Npk1(
{this.hour,
this.soiltemperatureAvg,
this.soilhumidityAvg,
this.soilconductivityAvg,
this.soilphAvg,
this.soilnitrogenAvg,
this.soilphosphorusAvg,
this.soilpotassiumAvg});
Npk1.fromJson(Map<String, dynamic> json) {
hour = json['hour'];
soiltemperatureAvg = json['soiltemperature_avg'];
soilhumidityAvg = json['soilhumidity_avg'];
soilconductivityAvg = json['soilconductivity_avg'];
soilphAvg = json['soilph_avg'];
soilnitrogenAvg = json['soilnitrogen_avg'];
soilphosphorusAvg = json['soilphosphorus_avg'];
soilpotassiumAvg = json['soilpotassium_avg'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['hour'] = hour;
data['soiltemperature_avg'] = soiltemperatureAvg;
data['soilhumidity_avg'] = soilhumidityAvg;
data['soilconductivity_avg'] = soilconductivityAvg;
data['soilph_avg'] = soilphAvg;
data['soilnitrogen_avg'] = soilnitrogenAvg;
data['soilphosphorus_avg'] = soilphosphorusAvg;
data['soilpotassium_avg'] = soilpotassiumAvg;
return data;
}
}
class Npk2 {
int? hour;
num? soiltemperatureAvg;
num? soilhumidityAvg;
num? soilconductivityAvg;
num? soilphAvg;
num? soilnitrogenAvg;
num? soilphosphorusAvg;
num? soilpotassiumAvg;
Npk2(
{this.hour,
this.soiltemperatureAvg,
this.soilhumidityAvg,
this.soilconductivityAvg,
this.soilphAvg,
this.soilnitrogenAvg,
this.soilphosphorusAvg,
this.soilpotassiumAvg});
Npk2.fromJson(Map<String, dynamic> json) {
hour = json['hour'];
soiltemperatureAvg = json['soiltemperature_avg'];
soilhumidityAvg = json['soilhumidity_avg'];
soilconductivityAvg = json['soilconductivity_avg'];
soilphAvg = json['soilph_avg'];
soilnitrogenAvg = json['soilnitrogen_avg'];
soilphosphorusAvg = json['soilphosphorus_avg'];
soilpotassiumAvg = json['soilpotassium_avg'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['hour'] = hour;
data['soiltemperature_avg'] = soiltemperatureAvg;
data['soilhumidity_avg'] = soilhumidityAvg;
data['soilconductivity_avg'] = soilconductivityAvg;
data['soilph_avg'] = soilphAvg;
data['soilnitrogen_avg'] = soilnitrogenAvg;
data['soilphosphorus_avg'] = soilphosphorusAvg;
data['soilpotassium_avg'] = soilpotassiumAvg;
return data;
}
}

View File

@ -0,0 +1,91 @@
class Npk1GraphicResponse {
DataNpk1? data;
int? statusCode;
String? message;
Npk1GraphicResponse({this.data, this.statusCode, this.message});
Npk1GraphicResponse.fromJson(Map<String, dynamic> json) {
data = json['data'] != null ? DataNpk1.fromJson(json['data']) : null;
statusCode = json['statusCode'];
message = json['message'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
if (this.data != null) {
data['data'] = this.data!.toJson();
}
data['statusCode'] = statusCode;
data['message'] = message;
return data;
}
}
class DataNpk1 {
List<Npk1>? npk1;
DataNpk1({this.npk1});
DataNpk1.fromJson(Map<String, dynamic> json) {
if (json['npk1'] != null) {
npk1 = <Npk1>[];
json['npk1'].forEach((v) {
npk1!.add(Npk1.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
if (npk1 != null) {
data['npk1'] = npk1!.map((v) => v.toJson()).toList();
}
return data;
}
}
class Npk1 {
int? hour;
double? soiltemperatureAvg;
double? soilhumidityAvg;
double? soilconductivityAvg;
double? soilphAvg;
double? soilnitrogenAvg;
double? soilphosphorusAvg;
double? soilpotassiumAvg;
Npk1(
{this.hour,
this.soiltemperatureAvg,
this.soilhumidityAvg,
this.soilconductivityAvg,
this.soilphAvg,
this.soilnitrogenAvg,
this.soilphosphorusAvg,
this.soilpotassiumAvg});
Npk1.fromJson(Map<String, dynamic> json) {
hour = json['hour'];
soiltemperatureAvg = json['soiltemperature_avg'];
soilhumidityAvg = json['soilhumidity_avg'];
soilconductivityAvg = json['soilconductivity_avg'];
soilphAvg = json['soilph_avg'];
soilnitrogenAvg = json['soilnitrogen_avg'];
soilphosphorusAvg = json['soilphosphorus_avg'];
soilpotassiumAvg = json['soilpotassium_avg'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['hour'] = hour;
data['soiltemperature_avg'] = soiltemperatureAvg;
data['soilhumidity_avg'] = soilhumidityAvg;
data['soilconductivity_avg'] = soilconductivityAvg;
data['soilph_avg'] = soilphAvg;
data['soilnitrogen_avg'] = soilnitrogenAvg;
data['soilphosphorus_avg'] = soilphosphorusAvg;
data['soilpotassium_avg'] = soilpotassiumAvg;
return data;
}
}

View File

@ -0,0 +1,65 @@
class Npk1SoilTempGrafik {
Data? data;
int? statusCode;
String? message;
Npk1SoilTempGrafik({this.data, this.statusCode, this.message});
Npk1SoilTempGrafik.fromJson(Map<String, dynamic> json) {
data = json['data'] != null ? Data.fromJson(json['data']) : null;
statusCode = json['statusCode'];
message = json['message'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
if (this.data != null) {
data['data'] = this.data!.toJson();
}
data['statusCode'] = statusCode;
data['message'] = message;
return data;
}
}
class Data {
List<Npk1>? npk1;
Data({this.npk1});
Data.fromJson(Map<String, dynamic> json) {
if (json['npk1'] != null) {
npk1 = <Npk1>[];
json['npk1'].forEach((v) {
npk1!.add(Npk1.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
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<String, dynamic> json) {
hour = json['hour'];
soiltemperatureAvg = json['soiltemperature_avg'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['hour'] = hour;
data['soiltemperature_avg'] = soiltemperatureAvg;
return data;
}
}

View File

@ -0,0 +1,91 @@
class Npk2GraphicResponse {
DataNpk2? data;
int? statusCode;
String? message;
Npk2GraphicResponse({this.data, this.statusCode, this.message});
Npk2GraphicResponse.fromJson(Map<String, dynamic> json) {
data = json['data'] != null ? DataNpk2.fromJson(json['data']) : null;
statusCode = json['statusCode'];
message = json['message'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
if (this.data != null) {
data['data'] = this.data!.toJson();
}
data['statusCode'] = statusCode;
data['message'] = message;
return data;
}
}
class DataNpk2 {
List<Npk2>? npk2;
DataNpk2({this.npk2});
DataNpk2.fromJson(Map<String, dynamic> json) {
if (json['npk2'] != null) {
npk2 = <Npk2>[];
json['npk2'].forEach((v) {
npk2!.add(Npk2.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
if (npk2 != null) {
data['npk2'] = npk2!.map((v) => v.toJson()).toList();
}
return data;
}
}
class Npk2 {
int? hour;
double? soiltemperatureAvg;
double? soilhumidityAvg;
double? soilconductivityAvg;
double? soilphAvg;
double? soilnitrogenAvg;
double? soilphosphorusAvg;
double? soilpotassiumAvg;
Npk2(
{this.hour,
this.soiltemperatureAvg,
this.soilhumidityAvg,
this.soilconductivityAvg,
this.soilphAvg,
this.soilnitrogenAvg,
this.soilphosphorusAvg,
this.soilpotassiumAvg});
Npk2.fromJson(Map<String, dynamic> json) {
hour = json['hour'];
soiltemperatureAvg = json['soiltemperature_avg'];
soilhumidityAvg = json['soilhumidity_avg'];
soilconductivityAvg = json['soilconductivity_avg'];
soilphAvg = json['soilph_avg'];
soilnitrogenAvg = json['soilnitrogen_avg'];
soilphosphorusAvg = json['soilphosphorus_avg'];
soilpotassiumAvg = json['soilpotassium_avg'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['hour'] = hour;
data['soiltemperature_avg'] = soiltemperatureAvg;
data['soilhumidity_avg'] = soilhumidityAvg;
data['soilconductivity_avg'] = soilconductivityAvg;
data['soilph_avg'] = soilphAvg;
data['soilnitrogen_avg'] = soilnitrogenAvg;
data['soilphosphorus_avg'] = soilphosphorusAvg;
data['soilpotassium_avg'] = soilpotassiumAvg;
return data;
}
}

View File

@ -54,7 +54,7 @@ class HumidityScreen extends StatelessWidget {
children: [
const Icon(BootstrapIcons.droplet_half,
size: 32, color: Colors.blue),
Text('60 %', style: AppTheme.headline1),
Text('$humidity %', style: AppTheme.headline1),
],
),
),
@ -63,7 +63,7 @@ class HumidityScreen extends StatelessWidget {
child: AnimatedRadialGauge(
duration: const Duration(seconds: 3),
curve: Curves.easeOut,
value: 60,
value: humidity,
axis: GaugeAxis(
degrees: 360,
min: 0,

View File

@ -50,7 +50,7 @@ class SoilMoistureScreen extends StatelessWidget {
children: [
const Icon(BootstrapIcons.water,
size: 32, color: Colors.blue),
Text('60 %', style: AppTheme.headline1),
Text('$moisture %', style: AppTheme.headline1),
],
),
),

View File

@ -0,0 +1,47 @@
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';
class SoilTempProvider extends ChangeNotifier {
SoilTempProvider() {
getSoilTempData();
}
List<Npk1> dataFetched = [];
void setSoilTempData(List<Npk1> data) {
dataFetched = data;
notifyListeners();
}
ResultState dataState = ResultState.initial;
Future<void> getSoilTempData() async {
dataState = ResultState.loading;
notifyListeners();
try {
final result = await HomeService().getNpk1SoilTempGrafik();
if (result.data == null || result.data!.npk1!.isEmpty) {
dataState = ResultState.noData;
notifyListeners();
return;
} else {
setSoilTempData(result.data?.npk1 ?? []);
dataState = ResultState.hasData;
notifyListeners();
}
} catch (e) {
print('Error: $e');
dataState = ResultState.error;
notifyListeners();
}
}
@override
void dispose() {
dataState = ResultState.initial;
dataFetched = [];
super.dispose();
}
}

View File

@ -1,11 +1,14 @@
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});
@ -15,218 +18,269 @@ class SoilTemperatureScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Soil Temperature', style: AppTheme.labelMedium),
centerTitle: true,
backgroundColor: Colors.white,
scrolledUnderElevation: 0,
actions: const [
Padding(
padding: EdgeInsets.only(right: 16),
child: Icon(
BootstrapIcons.water,
color: Colors.green,
),
)
],
),
body: SafeArea(
child: ListView(
padding: EdgeInsets.all(16.w),
children: [
SizedBox(
height: MediaQuery.of(context).size.height * 0.05,
),
SizedBox(
height: 240.h,
child: Stack(
fit: StackFit.expand,
children: [
Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
height: 80.h,
),
const Icon(BootstrapIcons.water,
size: 32, color: Colors.green),
Text(
'${value.toStringAsFixed(0)}°C', // Animated percentage text
style: const TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
color: Colors.black,
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,
),
],
),
),
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.blue,
Colors.orange,
]),
),
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),
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,
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,
),
),
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,
),
],
color: Colors.white,
borderRadius: BorderRadius.circular(16.w),
border: Border.all(color: Colors.grey.shade300, width: 1.w),
),
child: Consumer<SoilTempProvider>(
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'),
);
}
}),
),
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,
],
),
),
)
],
)
],
),
),
),
);

View File

@ -1,25 +1,101 @@
import 'package:agrilink_vocpro/core/state/result_state.dart';
import 'package:agrilink_vocpro/domain/service/app_service.dart';
import 'package:flutter/material.dart';
class HomeProvider extends ChangeNotifier {
final DateTime currentDate = DateTime.now();
num _dhtHumidity = 0;
num get dhtHumidity => _dhtHumidity;
num _dhtTemperature = 0;
num get dhtTemperature => _dhtTemperature;
num _dhtLuminosity = 0;
num get dhtLuminosity => _dhtLuminosity;
num _npk1SoilMoisture = 0;
num get npk1SoilMoisture => _npk1SoilMoisture;
num _npk1Temperature = 0;
num get npk1Temperature => _npk1Temperature;
num _npk1SoilPh = 0;
num get npk1SoilPh => _npk1SoilPh;
num _npk1SoilEc = 0;
num get npk1SoilEc => _npk1SoilEc;
num _npk1SoilNitrogen = 0;
num get npk1SoilNitrogen => _npk1SoilNitrogen;
num _npk1SoilPhosphorus = 0;
num get npk1SoilPhosphorus => _npk1SoilPhosphorus;
num _npk1SoilPotassium = 0;
num get npk1SoilPotassium => _npk1SoilPotassium;
num _npk2SoilMoisture = 0;
num get npk2SoilMoisture => _npk2SoilMoisture;
num _npk2Temperature = 0;
num get npk2Temperature => _npk2Temperature;
num _npk2SoilPh = 0;
num get npk2SoilPh => _npk2SoilPh;
num _npk2SoilEc = 0;
num get npk2SoilEc => _npk2SoilEc;
num _npk2SoilNitrogen = 0;
num get npk2SoilNitrogen => _npk2SoilNitrogen;
num _npk2SoilPhosphorus = 0;
num get npk2SoilPhosphorus => _npk2SoilPhosphorus;
num _npk2SoilPotassium = 0;
num get npk2SoilPotassium => _npk2SoilPotassium;
HomeProvider() {
getData();
getLatestData();
}
ResultState dataState = ResultState.initial;
Future<void> getData() async {
Future<void> getLatestData() async {
dataState = ResultState.loading;
notifyListeners();
try {
print('Fetching data...');
await Future.delayed(const Duration(seconds: 3));
print('Data fetched');
dataState = ResultState.hasData;
notifyListeners();
final result = await AppService().getLatestData();
if (result.data == null) {
dataState = ResultState.noData;
notifyListeners();
} else {
final data = result.data!;
_dhtHumidity = data.dht?[0].vicihumidityAvg ?? 0;
_dhtTemperature = data.dht?[0].vicitemperatureAvg ?? 0;
_dhtLuminosity = data.dht?[0].viciluminosityAvg ?? 0;
_npk1SoilMoisture = data.npk1?[0].soilhumidityAvg ?? 0;
_npk1Temperature = data.npk1?[0].soiltemperatureAvg ?? 0;
_npk1SoilPh = data.npk1?[0].soilphAvg ?? 0;
_npk1SoilEc = data.npk1?[0].soilconductivityAvg ?? 0;
_npk1SoilNitrogen = data.npk1?[0].soilnitrogenAvg ?? 0;
_npk1SoilPhosphorus = data.npk1?[0].soilphosphorusAvg ?? 0;
_npk1SoilPotassium = data.npk1?[0].soilpotassiumAvg ?? 0;
_npk2SoilMoisture = data.npk2?[0].soilhumidityAvg ?? 0;
_npk2Temperature = data.npk2?[0].soiltemperatureAvg ?? 0;
_npk2SoilPh = data.npk2?[0].soilphAvg ?? 0;
_npk2SoilEc = data.npk2?[0].soilconductivityAvg ?? 0;
_npk2SoilNitrogen = data.npk2?[0].soilnitrogenAvg ?? 0;
_npk2SoilPhosphorus = data.npk2?[0].soilphosphorusAvg ?? 0;
_npk2SoilPotassium = data.npk2?[0].soilpotassiumAvg ?? 0;
dataState = ResultState.hasData;
notifyListeners();
}
} catch (e) {
print('Error: $e');
dataState = ResultState.error;
notifyListeners();
}
@ -31,57 +107,3 @@ class HomeProvider extends ChangeNotifier {
super.dispose();
}
}
// List<CensorDataRule> 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,
// ),
// ];

View File

@ -0,0 +1,29 @@
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<Npk1SoilTempGrafik> 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;
}
}
}

View File

@ -60,7 +60,7 @@ class _HomeScreenState extends State<HomeScreen> {
const Spacer(),
IconButton(
onPressed: () {
context.read<HomeProvider>().getData();
context.read<HomeProvider>().getLatestData();
},
icon: const Icon(
Icons.refresh_rounded,

View File

@ -4,10 +4,12 @@ import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class GarphicWidget extends StatelessWidget {
const GarphicWidget({super.key, required this.gradientColors});
const GarphicWidget(
{super.key, required this.gradientColors, this.data, this.hour});
final List<Color> gradientColors;
final List<num>? hour;
final List<num>? data;
@override
Widget build(BuildContext context) {
return Padding(
@ -48,6 +50,16 @@ class GarphicWidget extends StatelessWidget {
);
}
int getMaxValue() {
int max = 0;
for (int i = 0; i < data!.length; i++) {
if (data![i] > max) {
max = data![i].toInt();
}
}
return max;
}
LineChartData mainData() {
return LineChartData(
gridData: const FlGridData(
@ -80,18 +92,16 @@ class GarphicWidget extends StatelessWidget {
minX: 0,
maxX: 24,
minY: 0,
maxY: 100,
maxY: data == null ? 0 : getMaxValue().toDouble(),
lineBarsData: [
LineChartBarData(
spots: const [
FlSpot(0, 38),
FlSpot(1, 42),
FlSpot(2, 50),
FlSpot(3, 53),
FlSpot(4, 58),
FlSpot(5, 64),
FlSpot(7, 49),
],
spots: data == null && hour == null
? [FlSpot(0, 0)]
: List.generate(
hour!.length,
(index) =>
FlSpot(hour![index].toDouble(), data![index].toDouble()),
),
isCurved: true,
gradient: LinearGradient(
colors: gradientColors,

View File

@ -50,7 +50,7 @@ class ListDataFromCensorDht extends StatelessWidget {
DataDisplayerWidget(
title: 'Humidity',
subtitle: 'kelembaban udara',
value: '60',
value: provider.dhtHumidity.toString(),
unit: '%',
icon: BootstrapIcons.droplet_half,
textColor: Colors.white,
@ -58,29 +58,32 @@ class ListDataFromCensorDht extends StatelessWidget {
iconColor: Colors.white,
censorIdentifier: 'NPK 1',
onTap: () async {
await context.push('${AppRoute.humidity}/60');
await context
.push('${AppRoute.humidity}/${provider.dhtHumidity}');
},
),
DataDisplayerWidget(
title: 'Temperature',
subtitle: 'suhu greenhouse',
value: '43',
value: provider.dhtTemperature.toString(),
unit: '°C',
icon: BootstrapIcons.thermometer_half,
color: Colors.white,
onTap: () async {
await context.push('${AppRoute.temperature}/43');
await context.push(
'${AppRoute.temperature}/${provider.dhtTemperature}');
},
),
DataDisplayerWidget(
title: 'Light',
subtitle: 'intensitas cahaya',
value: '320.5',
value: provider.dhtLuminosity.toString(),
unit: 'lux',
icon: BootstrapIcons.sun,
color: Colors.white,
onTap: () async {
await context.push('${AppRoute.light}/320.5');
await context
.push('${AppRoute.light}/${provider.dhtLuminosity}');
},
),
],

View File

@ -49,7 +49,7 @@ class ListDataFromCensorNpk1 extends StatelessWidget {
DataDisplayerWidget(
title: 'Temperature',
subtitle: 'Suhu tanah',
value: '28',
value: provider.npk1Temperature.toString(),
unit: '°C',
icon: BootstrapIcons.thermometer_half,
textColor: Colors.white,
@ -57,76 +57,82 @@ class ListDataFromCensorNpk1 extends StatelessWidget {
iconColor: Colors.white,
censorIdentifier: censorIdentifier,
onTap: () async {
await context.push('${AppRoute.soilTemperature}/28');
await context.push(
'${AppRoute.soilTemperature}/${provider.npk1Temperature}');
},
),
DataDisplayerWidget(
title: 'Soil Moisture',
subtitle: 'kelembaban tanah',
value: '40',
value: provider.npk1SoilMoisture.toString(),
unit: '%',
icon: Icons.water_outlined,
color: Colors.white,
censorIdentifier: censorIdentifier,
onTap: () async {
await context.push('${AppRoute.soilMoisture}/40');
await context.push(
'${AppRoute.soilMoisture}/${provider.npk1SoilMoisture}');
},
),
DataDisplayerWidget(
title: 'Acid Level (PH)',
subtitle: 'tingkat keasaman',
value: '6.5',
value: provider.npk1SoilPh.toString(),
unit: 'pH',
icon: BootstrapIcons.pie_chart,
color: Colors.white,
censorIdentifier: censorIdentifier,
onTap: () {
context.push('${AppRoute.ph}/6.5');
onTap: () async {
context.push('${AppRoute.ph}/${provider.npk1SoilPh}');
},
),
DataDisplayerWidget(
title: 'Conductivity',
subtitle: 'Daya Arus Listrik',
value: '234',
value: provider.npk1SoilEc.toString(),
unit: 'µS/cm',
icon: Icons.electric_bolt,
color: Colors.white,
censorIdentifier: censorIdentifier,
onTap: () async {
await context.push('${AppRoute.conductivity}/234');
await context
.push('${AppRoute.conductivity}/${provider.npk1SoilEc}');
},
),
DataDisplayerWidget(
title: 'Nitrogen',
subtitle: 'Kadar Nitrogen',
value: '30',
value: provider.npk1SoilNitrogen.toString(),
unit: 'ppm',
icon: CupertinoIcons.eyedropper,
color: Colors.white,
onTap: () async {
await context.push('${AppRoute.nitrogen}/30');
await context.push(
'${AppRoute.nitrogen}/${provider.npk1SoilNitrogen}');
},
),
DataDisplayerWidget(
title: 'Potassium',
subtitle: 'Kadar kalium',
value: '20',
value: provider.npk1SoilPotassium.toString(),
unit: 'ppm',
icon: CupertinoIcons.eyedropper,
color: Colors.white,
onTap: () async {
await context.push('${AppRoute.potassium}/20');
await context.push(
'${AppRoute.potassium}/${provider.npk1SoilPotassium}');
},
),
DataDisplayerWidget(
title: 'Phosphorus',
subtitle: 'Kadar Fosfor',
value: '54',
value: provider.npk1SoilPhosphorus.toString(),
unit: 'ppm',
icon: CupertinoIcons.eyedropper,
color: Colors.white,
onTap: () async {
await context.push('${AppRoute.phosphorus}/54');
await context.push(
'${AppRoute.phosphorus}/${provider.npk1SoilPhosphorus}');
},
),
],

View File

@ -1,8 +1,6 @@
import 'package:agrilink_vocpro/core/constant/app_color.dart';
import 'package:agrilink_vocpro/core/route/app_route.dart';
import 'package:agrilink_vocpro/core/state/result_state.dart';
import 'package:agrilink_vocpro/features/home/pages/soil_moisture/view/soil_moisture_screen.dart';
import 'package:agrilink_vocpro/features/home/pages/temperature/view/temperature_screen.dart';
import 'package:agrilink_vocpro/features/home/provider/home_provider.dart';
import 'package:agrilink_vocpro/features/home/widgets/censor_item_loading_widgets.dart';
import 'package:agrilink_vocpro/features/home/widgets/data_display_widget.dart';
@ -51,7 +49,7 @@ class ListDataFromCensorNpk2 extends StatelessWidget {
DataDisplayerWidget(
title: 'Temperature',
subtitle: 'Suhu tanah',
value: '28',
value: provider.npk2Temperature.toString(),
unit: '°C',
icon: BootstrapIcons.thermometer_half,
textColor: Colors.white,
@ -59,77 +57,83 @@ class ListDataFromCensorNpk2 extends StatelessWidget {
iconColor: Colors.white,
censorIdentifier: censorIdentifier,
onTap: () async {
await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const TemperatureScreen()));
await context.push(
'${AppRoute.soilTemperature}/${provider.npk2Temperature}');
},
),
DataDisplayerWidget(
title: 'Soil Moisture',
subtitle: 'kelembaban tanah',
value: '40',
value: provider.npk2SoilMoisture.toString(),
unit: '%',
icon: Icons.water_outlined,
color: Colors.white,
censorIdentifier: censorIdentifier,
onTap: () async {
await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const SoilMoistureScreen(),
),
);
await context.push(
'${AppRoute.soilMoisture}/${provider.npk2SoilMoisture}');
},
),
DataDisplayerWidget(
title: 'Acid Level (PH)',
subtitle: 'tingkat keasaman',
value: '6.5',
value: provider.npk2SoilPh.toString(),
unit: 'pH',
icon: BootstrapIcons.pie_chart,
color: Colors.white,
censorIdentifier: censorIdentifier,
onTap: () {},
onTap: () async {
context.push('${AppRoute.ph}/${provider.npk2SoilPh}');
},
),
DataDisplayerWidget(
title: 'Conductivity',
subtitle: 'Daya Arus Listrik',
value: '234',
value: provider.npk2SoilEc.toString(),
unit: 'µS/cm',
icon: Icons.electric_bolt,
color: Colors.white,
censorIdentifier: censorIdentifier,
onTap: () async {
context.push(AppRoute.humidity, extra: '60');
await context
.push('${AppRoute.conductivity}/${provider.npk2SoilEc}');
},
),
DataDisplayerWidget(
title: 'Nitrogen',
subtitle: 'Kadar Nitrogen',
value: '30',
value: provider.npk2SoilNitrogen.toString(),
unit: 'ppm',
icon: CupertinoIcons.eyedropper,
color: Colors.white,
onTap: () {},
onTap: () async {
await context.push(
'${AppRoute.nitrogen}/${provider.npk2SoilNitrogen}');
},
),
DataDisplayerWidget(
title: 'Potassium',
subtitle: 'Kadar kalium',
value: '20',
value: provider.npk2SoilPotassium.toString(),
unit: 'ppm',
icon: CupertinoIcons.eyedropper,
color: Colors.white,
onTap: () {},
onTap: () async {
await context.push(
'${AppRoute.potassium}/${provider.npk2SoilPotassium}');
},
),
DataDisplayerWidget(
title: 'Phosphorus',
subtitle: 'Kadar Fosfor',
value: '54',
value: provider.npk2SoilPhosphorus.toString(),
unit: 'ppm',
icon: CupertinoIcons.eyedropper,
color: Colors.white,
onTap: () {},
onTap: () async {
await context.push(
'${AppRoute.phosphorus}/${provider.npk2SoilPhosphorus}');
},
),
],
);

View File

@ -141,10 +141,10 @@ packages:
dependency: transitive
description:
name: file
sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c"
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
url: "https://pub.dev"
source: hosted
version: "7.0.0"
version: "7.0.1"
fl_chart:
dependency: "direct main"
description:
@ -162,10 +162,10 @@ packages:
dependency: "direct dev"
description:
name: flutter_lints
sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c"
sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1"
url: "https://pub.dev"
source: hosted
version: "4.0.0"
version: "5.0.0"
flutter_screenutil:
dependency: "direct main"
description:
@ -196,10 +196,10 @@ packages:
dependency: "direct main"
description:
name: go_router
sha256: "2ddb88e9ad56ae15ee144ed10e33886777eb5ca2509a914850a5faa7b52ff459"
sha256: "6f1b756f6e863259a99135ff3c95026c3cdca17d10ebef2bba2261a25ddc8bbc"
url: "https://pub.dev"
source: hosted
version: "14.2.7"
version: "14.3.0"
google_fonts:
dependency: "direct main"
description:
@ -268,10 +268,10 @@ packages:
dependency: transitive
description:
name: lints
sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235"
sha256: "3315600f3fb3b135be672bf4a178c55f274bebe368325ae18462c89ac1e3b413"
url: "https://pub.dev"
source: hosted
version: "4.0.0"
version: "5.0.0"
logging:
dependency: transitive
description:
@ -340,10 +340,10 @@ packages:
dependency: transitive
description:
name: path_provider_android
sha256: "6f01f8e37ec30b07bc424b4deabac37cacb1bc7e2e515ad74486039918a37eb7"
sha256: c464428172cb986b758c6d1724c603097febb8fb855aa265aeecc9280c294d4a
url: "https://pub.dev"
source: hosted
version: "2.2.10"
version: "2.2.12"
path_provider_foundation:
dependency: transitive
description:
@ -412,18 +412,18 @@ packages:
dependency: transitive
description:
name: shared_preferences_android
sha256: "480ba4345773f56acda9abf5f50bd966f581dac5d514e5fc4a18c62976bbba7e"
sha256: "3b9febd815c9ca29c9e3520d50ec32f49157711e143b7a4ca039eb87e8ade5ab"
url: "https://pub.dev"
source: hosted
version: "2.3.2"
version: "2.3.3"
shared_preferences_foundation:
dependency: transitive
description:
name: shared_preferences_foundation
sha256: c4b35f6cb8f63c147312c054ce7c2254c8066745125264f0c88739c417fc9d9f
sha256: "07e050c7cd39bad516f8d64c455f04508d09df104be326d8c02551590a0d513d"
url: "https://pub.dev"
source: hosted
version: "2.5.2"
version: "2.5.3"
shared_preferences_linux:
dependency: transitive
description:
@ -561,18 +561,18 @@ packages:
dependency: transitive
description:
name: web
sha256: d43c1d6b787bf0afad444700ae7f4db8827f701bc61c255ac8d328c6f4d52062
sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb
url: "https://pub.dev"
source: hosted
version: "1.0.0"
version: "1.1.0"
xdg_directories:
dependency: transitive
description:
name: xdg_directories
sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d
sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15"
url: "https://pub.dev"
source: hosted
version: "1.0.4"
version: "1.1.0"
sdks:
dart: ">=3.5.1 <4.0.0"
flutter: ">=3.22.0"
flutter: ">=3.24.0"

View File

@ -57,7 +57,7 @@ dev_dependencies:
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^4.0.0
flutter_lints: ^5.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec