refactor: change all detail screen on npk sensor
This commit is contained in:
parent
426445a865
commit
a2dc5c4b3f
|
|
@ -2,7 +2,7 @@ class AppConstant {
|
|||
static const String appName = 'Kebun Pintar';
|
||||
static const String appVersion = '1.0.0';
|
||||
|
||||
static const String baseUrl = 'https://jx027dj4-3333.asse.devtunnels.ms/api';
|
||||
static const String baseUrl = 'https://jx027dj4-3333.asse.devtunnels.ms';
|
||||
|
||||
static const String mqttServer = 'armadillo.rmq.cloudamqp.com';
|
||||
static const String mqttUsername = 'obyskxhx:obyskxhx';
|
||||
|
|
|
|||
|
|
@ -81,55 +81,71 @@ class AppRoute {
|
|||
|
||||
static GoRoute buildPotassiumRoute() {
|
||||
return GoRoute(
|
||||
path: 'potassium/:value',
|
||||
path: 'potassium/:value1/:value2',
|
||||
builder: (context, state) {
|
||||
final double value =
|
||||
double.tryParse(state.pathParameters['value'] ?? '') ?? 0.0;
|
||||
return PotassiumScreen(potassium: value);
|
||||
final double value1 =
|
||||
double.tryParse(state.pathParameters['value1'] ?? '') ?? 0.0;
|
||||
final double value2 =
|
||||
double.tryParse(state.pathParameters['value2'] ?? '') ?? 0.0;
|
||||
return PotassiumScreen(potassiumNpk1: value1, potassiumNpk2: value2);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
static GoRoute buildPhosphorusRoute() {
|
||||
return GoRoute(
|
||||
path: 'phosphorus/:value',
|
||||
path: 'phosphorus/:value1/:value2',
|
||||
builder: (context, state) {
|
||||
final double value =
|
||||
double.tryParse(state.pathParameters['value'] ?? '') ?? 0.0;
|
||||
return PhosphorusScreen(phosphorus: value);
|
||||
final double value1 =
|
||||
double.tryParse(state.pathParameters['value1'] ?? '') ?? 0.0;
|
||||
final double value2 =
|
||||
double.tryParse(state.pathParameters['value2'] ?? '') ?? 0.0;
|
||||
return PhosphorusScreen(
|
||||
phosphorusNpk1: value1,
|
||||
phosphorusNpk2: value2,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
static GoRoute buildNitrogenRoute() {
|
||||
return GoRoute(
|
||||
path: 'nitrogen/:value',
|
||||
path: 'nitrogen/:value1/:value2',
|
||||
builder: (context, state) {
|
||||
final double value =
|
||||
double.tryParse(state.pathParameters['value'] ?? '') ?? 0.0;
|
||||
return NitrogenScreen(nitrogen: value);
|
||||
final double value1 =
|
||||
double.tryParse(state.pathParameters['value1'] ?? '') ?? 0.0;
|
||||
final double value2 =
|
||||
double.tryParse(state.pathParameters['value2'] ?? '') ?? 0.0;
|
||||
return NitrogenScreen(nitrogenNpk1: value1, nitrogenNpk2: value2);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
static GoRoute buildConductivityRoute() {
|
||||
return GoRoute(
|
||||
path: 'conductivity/:value',
|
||||
path: 'conductivity/:value1/:value2',
|
||||
builder: (context, state) {
|
||||
final double value =
|
||||
double.tryParse(state.pathParameters['value'] ?? '') ?? 0.0;
|
||||
return ConductivityScreen(conductivity: value);
|
||||
final double value1 =
|
||||
double.tryParse(state.pathParameters['value1'] ?? '') ?? 0.0;
|
||||
final double value2 =
|
||||
double.tryParse(state.pathParameters['value2'] ?? '') ?? 0.0;
|
||||
return ConductivityScreen(
|
||||
conductivityNpk1: value1,
|
||||
conductivityNpk2: value2,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
static GoRoute buildSoilMoistureRoute() {
|
||||
return GoRoute(
|
||||
path: 'soil_moisture/:value',
|
||||
path: 'soil_moisture/:value1/:value2',
|
||||
builder: (context, state) {
|
||||
final double value =
|
||||
double.tryParse(state.pathParameters['value'] ?? '') ?? 0.0;
|
||||
return SoilMoistureScreen(moisture: value);
|
||||
final double value1 =
|
||||
double.tryParse(state.pathParameters['value1'] ?? '') ?? 0.0;
|
||||
final double value2 =
|
||||
double.tryParse(state.pathParameters['value2'] ?? '') ?? 0.0;
|
||||
return SoilMoistureScreen(moistureNpk1: value1, moistureNpk2: value2);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
@ -150,11 +166,13 @@ class AppRoute {
|
|||
|
||||
static GoRoute buildAcidityRoute() {
|
||||
return GoRoute(
|
||||
path: 'ph/:value',
|
||||
path: 'ph/:value1/:value2',
|
||||
builder: (context, state) {
|
||||
final double value =
|
||||
double.tryParse(state.pathParameters['value'] ?? '') ?? 0.0;
|
||||
return PhScreen(phValue: value);
|
||||
final double value1 =
|
||||
double.tryParse(state.pathParameters['value1'] ?? '') ?? 0.0;
|
||||
final double value2 =
|
||||
double.tryParse(state.pathParameters['value2'] ?? '') ?? 0.0;
|
||||
return PhScreen(phValueNpk1: value1, phValueNpk2: value2);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,28 +8,33 @@ class AppTextfield extends StatelessWidget {
|
|||
required this.controller,
|
||||
this.hintText = 'Enter Here',
|
||||
this.suffixIcon,
|
||||
this.obscureText = false,
|
||||
});
|
||||
|
||||
final TextEditingController controller;
|
||||
final String hintText;
|
||||
final Widget? suffixIcon;
|
||||
final bool obscureText;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TextField(
|
||||
controller: controller,
|
||||
decoration: InputDecoration(
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: const BorderSide(color: AppColor.textDisable, width: 1),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: const BorderSide(color: AppColor.primary, width: 1),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
hintText: hintText,
|
||||
hintStyle: AppTheme.hintStyle,
|
||||
suffixIcon: (suffixIcon != null) ? suffixIcon : null),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: const BorderSide(color: AppColor.textDisable, width: 1),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: const BorderSide(color: AppColor.primary, width: 1),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
hintText: hintText,
|
||||
hintStyle: AppTheme.hintStyle,
|
||||
suffixIcon: (suffixIcon != null) ? suffixIcon : null,
|
||||
),
|
||||
onTapOutside: (event) => FocusScope.of(context).unfocus(),
|
||||
obscureText: obscureText,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import 'package:agrilink_vocpro/data/model/latest_data_response.dart';
|
||||
|
||||
class DhtGraphicResponse {
|
||||
DataDht? data;
|
||||
int? statusCode;
|
||||
|
|
@ -44,32 +46,3 @@ class DataDht {
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
114
agrilink_vocpro/lib/data/model/jwt_token_response.dart
Normal file
114
agrilink_vocpro/lib/data/model/jwt_token_response.dart
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
class JwtTokenResponse {
|
||||
User? user;
|
||||
int? iat;
|
||||
int? exp;
|
||||
|
||||
JwtTokenResponse({this.user, this.iat, this.exp});
|
||||
|
||||
JwtTokenResponse.fromJson(Map<String, dynamic> json) {
|
||||
user = json['user'] != null ? User.fromJson(json['user']) : null;
|
||||
iat = json['iat'];
|
||||
exp = json['exp'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
if (user != null) {
|
||||
data['user'] = user!.toJson();
|
||||
}
|
||||
data['iat'] = iat;
|
||||
data['exp'] = exp;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class User {
|
||||
String? id;
|
||||
String? uroleId;
|
||||
String? username;
|
||||
String? email;
|
||||
String? googleId;
|
||||
String? fullname;
|
||||
Null avatar;
|
||||
bool? isBan;
|
||||
String? createdAt;
|
||||
String? updatedAt;
|
||||
Null deletedAt;
|
||||
Role? role;
|
||||
|
||||
User(
|
||||
{this.id,
|
||||
this.uroleId,
|
||||
this.username,
|
||||
this.email,
|
||||
this.googleId,
|
||||
this.fullname,
|
||||
this.avatar,
|
||||
this.isBan,
|
||||
this.createdAt,
|
||||
this.updatedAt,
|
||||
this.deletedAt,
|
||||
this.role});
|
||||
|
||||
User.fromJson(Map<String, dynamic> json) {
|
||||
id = json['id'];
|
||||
uroleId = json['urole_id'];
|
||||
username = json['username'];
|
||||
email = json['email'];
|
||||
googleId = json['google_id'];
|
||||
fullname = json['fullname'];
|
||||
avatar = json['avatar'];
|
||||
isBan = json['is_ban'];
|
||||
createdAt = json['created_at'];
|
||||
updatedAt = json['updated_at'];
|
||||
deletedAt = json['deleted_at'];
|
||||
role = json['role'] != null ? Role.fromJson(json['role']) : null;
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
data['id'] = id;
|
||||
data['urole_id'] = uroleId;
|
||||
data['username'] = username;
|
||||
data['email'] = email;
|
||||
data['google_id'] = googleId;
|
||||
data['fullname'] = fullname;
|
||||
data['avatar'] = avatar;
|
||||
data['is_ban'] = isBan;
|
||||
data['created_at'] = createdAt;
|
||||
data['updated_at'] = updatedAt;
|
||||
data['deleted_at'] = deletedAt;
|
||||
if (role != null) {
|
||||
data['role'] = role!.toJson();
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class Role {
|
||||
String? id;
|
||||
String? code;
|
||||
String? name;
|
||||
String? createdAt;
|
||||
String? updatedAt;
|
||||
|
||||
Role({this.id, this.code, this.name, this.createdAt, this.updatedAt});
|
||||
|
||||
Role.fromJson(Map<String, dynamic> json) {
|
||||
id = json['id'];
|
||||
code = json['code'];
|
||||
name = json['name'];
|
||||
createdAt = json['created_at'];
|
||||
updatedAt = json['updated_at'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
data['id'] = id;
|
||||
data['code'] = code;
|
||||
data['name'] = name;
|
||||
data['created_at'] = createdAt;
|
||||
data['updated_at'] = updatedAt;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
42
agrilink_vocpro/lib/data/model/login_response.dart
Normal file
42
agrilink_vocpro/lib/data/model/login_response.dart
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
class LoginResponse {
|
||||
Data? data;
|
||||
int? statusCode;
|
||||
String? message;
|
||||
|
||||
LoginResponse({this.data, this.statusCode, this.message});
|
||||
|
||||
LoginResponse.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 {
|
||||
String? token;
|
||||
String? jwtToken;
|
||||
|
||||
Data({this.token, this.jwtToken});
|
||||
|
||||
Data.fromJson(Map<String, dynamic> json) {
|
||||
token = json['token'];
|
||||
jwtToken = json['jwtToken'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
data['token'] = token;
|
||||
data['jwtToken'] = jwtToken;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
import 'package:agrilink_vocpro/data/model/latest_data_response.dart';
|
||||
|
||||
class Npk1GraphicResponse {
|
||||
DataNpk1? data;
|
||||
int? statusCode;
|
||||
|
|
@ -44,48 +46,3 @@ class DataNpk1 {
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import 'package:agrilink_vocpro/data/model/latest_data_response.dart';
|
||||
|
||||
class Npk2GraphicResponse {
|
||||
DataNpk2? data;
|
||||
int? statusCode;
|
||||
|
|
@ -44,48 +46,3 @@ class DataNpk2 {
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,18 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:agrilink_vocpro/core/constant/app_constant.dart';
|
||||
import 'package:agrilink_vocpro/core/extension/extention.dart';
|
||||
import 'package:agrilink_vocpro/data/model/dht_graphic_response.dart';
|
||||
import 'package:agrilink_vocpro/data/model/jwt_token_response.dart';
|
||||
import 'package:agrilink_vocpro/data/model/login_response.dart';
|
||||
import 'package:agrilink_vocpro/data/model/npk1_graphic_response.dart';
|
||||
import 'package:agrilink_vocpro/data/model/npk2_graphic_response.dart';
|
||||
import 'package:agrilink_vocpro/data/model/relay_response.dart';
|
||||
import 'package:agrilink_vocpro/data/model/latest_data_response.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:jwt_decoder/jwt_decoder.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class AppService {
|
||||
final Dio _dioWithoutInterceptor = Dio(
|
||||
|
|
@ -15,10 +21,60 @@ class AppService {
|
|||
),
|
||||
);
|
||||
|
||||
Future<LoginResponse> login({
|
||||
required String username,
|
||||
required String password,
|
||||
String rememberMe = 'false',
|
||||
}) async {
|
||||
final SharedPreferences pref = await SharedPreferences.getInstance();
|
||||
final String basicAuth =
|
||||
'Basic ${base64Encode(utf8.encode('$username:$password'))}';
|
||||
|
||||
FormData formData = FormData.fromMap({
|
||||
'remember_me': rememberMe,
|
||||
});
|
||||
try {
|
||||
final response = await _dioWithoutInterceptor.post(
|
||||
'/auth/login',
|
||||
data: formData,
|
||||
options: Options(
|
||||
headers: {
|
||||
'Authorization': basicAuth,
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final data = LoginResponse.fromJson(response.data);
|
||||
final decodedToken =
|
||||
JwtTokenResponse.fromJson(JwtDecoder.decode(data.data!.jwtToken!));
|
||||
pref.setString('token', data.data!.token!);
|
||||
pref.setString('jwtToken', data.data!.jwtToken!);
|
||||
pref.setString('username', decodedToken.user?.username ?? 'unknown');
|
||||
pref.setString('email', decodedToken.user?.email ?? 'unknown');
|
||||
pref.setString('fullName', decodedToken.user?.fullname ?? 'unknown');
|
||||
pref.setBool('isLoggedIn', true);
|
||||
return data;
|
||||
} else {
|
||||
throw Exception('Failed to load data');
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
final String errorMessage = e.response?.data['message'];
|
||||
throw (errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
Future<RelayResponse> getRelayStatus() async {
|
||||
final SharedPreferences pref = await SharedPreferences.getInstance();
|
||||
final String auth = 'Bearer ${pref.getString('token')}';
|
||||
try {
|
||||
await Future.delayed(const Duration(seconds: 3));
|
||||
final result = await _dioWithoutInterceptor.get('get-relay');
|
||||
final result = await _dioWithoutInterceptor.get(
|
||||
'get-relay',
|
||||
options: Options(
|
||||
headers: {'Authorization': auth},
|
||||
),
|
||||
);
|
||||
if (result.statusCode == 200) {
|
||||
final data = RelayResponse.fromJson(result.data);
|
||||
return data;
|
||||
|
|
@ -38,11 +94,16 @@ class AppService {
|
|||
Future<DhtGraphicResponse> getGrafikDataDht({
|
||||
required String metric,
|
||||
}) async {
|
||||
final SharedPreferences pref = await SharedPreferences.getInstance();
|
||||
final String auth = 'Bearer ${pref.getString('token')}';
|
||||
String date = DateTime.now().toString();
|
||||
final formatedDate = dateFormatterShort(date);
|
||||
try {
|
||||
final result = await _dioWithoutInterceptor.get(
|
||||
'/sensor/getData?metric=$metric&range[start]=$formatedDate&range[end]=$formatedDate&range[time_range]=HOURLY&sensor=dht',
|
||||
'/api/sensor/getData?metric=$metric&range[start]=$formatedDate&range[end]=$formatedDate&range[time_range]=HOURLY&sensor=dht',
|
||||
options: Options(
|
||||
headers: {'Authorization': auth},
|
||||
),
|
||||
);
|
||||
if (result.statusCode == 200) {
|
||||
final data = DhtGraphicResponse.fromJson(result.data);
|
||||
|
|
@ -59,11 +120,16 @@ class AppService {
|
|||
// get grafik data npk1
|
||||
Future<Npk1GraphicResponse> getGraphicDataNpk1(
|
||||
{required String metric}) async {
|
||||
final SharedPreferences pref = await SharedPreferences.getInstance();
|
||||
final String auth = 'Bearer ${pref.getString('token')}';
|
||||
String date = DateTime.now().toString();
|
||||
final formatedDate = dateFormatterShort(date);
|
||||
try {
|
||||
final result = await _dioWithoutInterceptor.get(
|
||||
'/sensor/getData?metric=$metric&range[start]=$formatedDate&range[end]=$formatedDate&range[time_range]=HOURLY&sensor=npk1',
|
||||
'/api/sensor/getData?metric=$metric&range[start]=$formatedDate&range[end]=$formatedDate&range[time_range]=HOURLY&sensor=npk1',
|
||||
options: Options(
|
||||
headers: {'Authorization': auth},
|
||||
),
|
||||
);
|
||||
if (result.statusCode == 200) {
|
||||
final data = Npk1GraphicResponse.fromJson(result.data);
|
||||
|
|
@ -81,11 +147,16 @@ class AppService {
|
|||
|
||||
Future<Npk2GraphicResponse> getGraphicDataNpk2(
|
||||
{required String metric}) async {
|
||||
final SharedPreferences pref = await SharedPreferences.getInstance();
|
||||
final String auth = 'Bearer ${pref.getString('token')}';
|
||||
String date = DateTime.now().toString();
|
||||
final formatedDate = dateFormatterShort(date);
|
||||
try {
|
||||
final result = await _dioWithoutInterceptor.get(
|
||||
'/sensor/getData?metric=$metric&range[start]=$formatedDate&range[end]=$formatedDate&range[time_range]=HOURLY&sensor=npk2',
|
||||
'/api/sensor/getData?metric=$metric&range[start]=$formatedDate&range[end]=$formatedDate&range[time_range]=HOURLY&sensor=npk2',
|
||||
options: Options(
|
||||
headers: {'Authorization': auth},
|
||||
),
|
||||
);
|
||||
if (result.statusCode == 200) {
|
||||
final data = Npk2GraphicResponse.fromJson(result.data);
|
||||
|
|
@ -102,9 +173,14 @@ class AppService {
|
|||
// get latest data
|
||||
|
||||
Future<LatestDataResponse> getLatestData() async {
|
||||
final SharedPreferences pref = await SharedPreferences.getInstance();
|
||||
final String auth = 'Bearer ${pref.getString('token')}';
|
||||
try {
|
||||
final result = await _dioWithoutInterceptor.get(
|
||||
'/sensor/getLatest',
|
||||
'/api/sensor/getLatest',
|
||||
options: Options(
|
||||
headers: {'Authorization': auth},
|
||||
),
|
||||
);
|
||||
if (result.statusCode == 200) {
|
||||
final data = LatestDataResponse.fromJson(result.data);
|
||||
|
|
|
|||
|
|
@ -1,12 +1,77 @@
|
|||
import 'package:agrilink_vocpro/core/state/result_state.dart';
|
||||
import 'package:agrilink_vocpro/domain/service/app_service.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AuthProvider extends ChangeNotifier {
|
||||
TextEditingController emailController = TextEditingController();
|
||||
TextEditingController passwordController = TextEditingController();
|
||||
|
||||
bool _isRememberMe = false;
|
||||
bool get isRememberMe => _isRememberMe;
|
||||
|
||||
String errorMessage = '';
|
||||
|
||||
ResultState loginState = ResultState.initial;
|
||||
|
||||
void controllerClear() {
|
||||
emailController.clear();
|
||||
passwordController.clear();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void setRememberMe(bool value) {
|
||||
_isRememberMe = value;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> login(context,
|
||||
{required String email, required String password}) async {
|
||||
loginState = ResultState.loading;
|
||||
notifyListeners();
|
||||
try {
|
||||
final result = await AppService().login(
|
||||
username: email,
|
||||
password: password,
|
||||
rememberMe: isRememberMe.toString(),
|
||||
);
|
||||
if (result.data != null) {
|
||||
loginState = ResultState.hasData;
|
||||
notifyListeners();
|
||||
} else {
|
||||
errorMessage = 'Login gagal, data tidak ditemukan';
|
||||
loginState = ResultState.error;
|
||||
notifyListeners();
|
||||
}
|
||||
} catch (e) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: const Text('Error'),
|
||||
content: Text('$e'),
|
||||
actions: [
|
||||
TextButton(
|
||||
child: const Text('OK'),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
loginState = ResultState.error;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
bool validateInputs() {
|
||||
if (emailController.text.isEmpty || passwordController.text.isEmpty) {
|
||||
errorMessage = 'Email dan password tidak boleh kosong';
|
||||
loginState = ResultState.error;
|
||||
notifyListeners();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:agrilink_vocpro/core/constant/app_theme.dart';
|
||||
import 'package:agrilink_vocpro/core/route/app_route.dart';
|
||||
import 'package:agrilink_vocpro/core/state/result_state.dart';
|
||||
import 'package:agrilink_vocpro/core/widgets/app_button.dart';
|
||||
import 'package:agrilink_vocpro/core/widgets/app_textfield.dart';
|
||||
import 'package:agrilink_vocpro/features/auth/provider/auth_provider.dart';
|
||||
|
|
@ -13,18 +14,15 @@ class LoginScreen extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: GestureDetector(
|
||||
onTap: () {
|
||||
FocusScope.of(context).unfocus();
|
||||
},
|
||||
child: SafeArea(
|
||||
child: Consumer<AuthProvider>(builder: (context, authP, child) {
|
||||
body: SafeArea(
|
||||
child: Consumer<AuthProvider>(
|
||||
builder: (context, authP, child) {
|
||||
return ListView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
children: [
|
||||
const SizedBox(height: 40),
|
||||
Text(
|
||||
'Hello Wellcome back 👋',
|
||||
'Hello, Welcome back 👋',
|
||||
style: AppTheme.titleLarge,
|
||||
),
|
||||
Text(
|
||||
|
|
@ -36,7 +34,7 @@ class LoginScreen extends StatelessWidget {
|
|||
const SizedBox(height: 4),
|
||||
AppTextfield(
|
||||
controller: authP.emailController,
|
||||
hintText: 'Masukkan username',
|
||||
hintText: 'Masukkan email',
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Text('Password', style: AppTheme.labelLarge),
|
||||
|
|
@ -44,19 +42,63 @@ class LoginScreen extends StatelessWidget {
|
|||
AppTextfield(
|
||||
controller: authP.passwordController,
|
||||
hintText: 'Masukkan password',
|
||||
obscureText: true,
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Row(
|
||||
children: [
|
||||
Checkbox(
|
||||
value: authP.isRememberMe,
|
||||
onChanged: (value) {
|
||||
authP.setRememberMe(value!);
|
||||
},
|
||||
),
|
||||
Text('Remember me', style: AppTheme.labelLarge),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
AppButton(
|
||||
onPressed: () {
|
||||
GoRouter.of(context).go(AppRoute.dashboard);
|
||||
},
|
||||
text: 'Login'),
|
||||
authP.loginState == ResultState.loading
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: AppButton(
|
||||
onPressed: () async {
|
||||
await authP.login(
|
||||
context,
|
||||
email: authP.emailController.text,
|
||||
password: authP.passwordController.text,
|
||||
);
|
||||
if (context.mounted) {
|
||||
if (authP.loginState == ResultState.hasData) {
|
||||
context.go(AppRoute.dashboard);
|
||||
authP.controllerClear();
|
||||
authP.loginState = ResultState.initial;
|
||||
}
|
||||
}
|
||||
},
|
||||
text: 'Login',
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showErrorDialog(BuildContext context, String errorMessage) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('Error'),
|
||||
content: Text(errorMessage),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: const Text('OK'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
import 'package:agrilink_vocpro/core/state/result_state.dart';
|
||||
import 'package:agrilink_vocpro/data/model/latest_data_response.dart';
|
||||
import 'package:agrilink_vocpro/domain/service/app_service.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
class ConductivityProvider extends ChangeNotifier {
|
||||
ConductivityProvider() {
|
||||
getSoilConductivityNpk1Data();
|
||||
getSoilConductivityNpk2Data();
|
||||
}
|
||||
ResultState dataState = ResultState.initial;
|
||||
|
||||
List<Npk1> dataFetchedNpk1 = [];
|
||||
List<Npk2> dataFetchedNpk2 = [];
|
||||
|
||||
Future<void> getSoilConductivityNpk1Data() async {
|
||||
dataState = ResultState.loading;
|
||||
notifyListeners();
|
||||
try {
|
||||
final result =
|
||||
await AppService().getGraphicDataNpk1(metric: 'soilConductivity');
|
||||
if (result.data == null || result.data!.npk1!.isEmpty) {
|
||||
dataState = ResultState.noData;
|
||||
} else {
|
||||
dataFetchedNpk1 = result.data!.npk1 ?? [];
|
||||
dataState = ResultState.hasData;
|
||||
}
|
||||
} catch (e) {
|
||||
if (kDebugMode) {
|
||||
print('Get Grafik Soil Temp Error: $e');
|
||||
}
|
||||
dataState = ResultState.error;
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> getSoilConductivityNpk2Data() async {
|
||||
dataState = ResultState.loading;
|
||||
notifyListeners();
|
||||
try {
|
||||
final result =
|
||||
await AppService().getGraphicDataNpk2(metric: 'soilConductivity');
|
||||
if (result.data == null || result.data!.npk2!.isEmpty) {
|
||||
dataState = ResultState.noData;
|
||||
} else {
|
||||
dataFetchedNpk2 = result.data!.npk2 ?? [];
|
||||
dataState = ResultState.hasData;
|
||||
}
|
||||
} catch (e) {
|
||||
if (kDebugMode) {
|
||||
print('Get Grafik Soil Temp Error: $e');
|
||||
}
|
||||
dataState = ResultState.error;
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,84 +1,161 @@
|
|||
import 'package:agrilink_vocpro/core/constant/app_theme.dart';
|
||||
import 'package:agrilink_vocpro/core/state/result_state.dart';
|
||||
import 'package:agrilink_vocpro/features/home/pages/conductivity/provider/conductivity_provider.dart';
|
||||
import 'package:agrilink_vocpro/features/home/widgets/graphic_error_widget.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:provider/provider.dart';
|
||||
|
||||
class ConductivityScreen extends StatelessWidget {
|
||||
const ConductivityScreen({super.key, this.conductivity = 0.0});
|
||||
const ConductivityScreen(
|
||||
{super.key, this.conductivityNpk1 = 0.0, this.conductivityNpk2 = 0.0});
|
||||
|
||||
final double conductivity;
|
||||
double get value => conductivity;
|
||||
final double conductivityNpk1;
|
||||
final double conductivityNpk2;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Conductivity', style: AppTheme.labelMedium),
|
||||
centerTitle: true,
|
||||
backgroundColor: Colors.white,
|
||||
scrolledUnderElevation: 0,
|
||||
actions: const [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(right: 16),
|
||||
child: Icon(
|
||||
Icons.electric_bolt_rounded,
|
||||
color: Colors.teal,
|
||||
return ChangeNotifierProvider(
|
||||
create: (context) => ConductivityProvider(),
|
||||
child: DefaultTabController(
|
||||
length: 2,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Soil Conductivity', style: AppTheme.labelMedium),
|
||||
centerTitle: true,
|
||||
backgroundColor: Colors.white,
|
||||
scrolledUnderElevation: 0,
|
||||
actions: const [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(right: 16),
|
||||
child: Icon(
|
||||
Icons.electric_bolt_rounded,
|
||||
color: Colors.green,
|
||||
),
|
||||
)
|
||||
],
|
||||
bottom: const TabBar(
|
||||
tabs: [
|
||||
Tab(text: 'NPK 1'),
|
||||
Tab(text: 'NPK 2'),
|
||||
],
|
||||
),
|
||||
)
|
||||
),
|
||||
body: TabBarView(
|
||||
children: [
|
||||
buildTabContent(context, conductivityNpk1, 'NPK 1', true),
|
||||
buildTabContent(context, conductivityNpk2, 'NPK 2', false),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
SafeArea buildTabContent(
|
||||
BuildContext context, double value, String label, bool isNpk1) {
|
||||
return SafeArea(
|
||||
child: ListView(
|
||||
padding: EdgeInsets.all(16.w),
|
||||
children: [
|
||||
SizedBox(height: MediaQuery.of(context).size.height * 0.05),
|
||||
buildSoilInfo(context, value),
|
||||
SizedBox(height: 16.h),
|
||||
buildInfoRow(context),
|
||||
SizedBox(height: 16.h),
|
||||
const Text('Grafik'),
|
||||
SizedBox(height: 16.h),
|
||||
buildGraphicContent(context, isNpk1),
|
||||
],
|
||||
),
|
||||
body: Center(
|
||||
child: ListView(
|
||||
padding: EdgeInsets.all(16.w),
|
||||
children: [
|
||||
SizedBox(height: 32.h),
|
||||
Column(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.electric_bolt_rounded,
|
||||
size: 64.r,
|
||||
color: Colors.teal,
|
||||
),
|
||||
Text('$value µS/cm', style: AppTheme.headline1),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 32.h),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'Daya Arus Listrik',
|
||||
style: AppTheme.labelMedium,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
IconButton(
|
||||
iconSize: 20.r,
|
||||
color: Colors.blue,
|
||||
onPressed: () {},
|
||||
icon: const Icon(BootstrapIcons.info_circle))
|
||||
],
|
||||
),
|
||||
SizedBox(height: 16.h),
|
||||
const Text('Grafik'),
|
||||
SizedBox(height: 16.h),
|
||||
AspectRatio(
|
||||
aspectRatio: 1.6.h,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16.w),
|
||||
border: Border.all(color: Colors.grey.shade300, width: 1.w),
|
||||
),
|
||||
child: const GarphicWidget(
|
||||
gradientColors: [
|
||||
Colors.cyan,
|
||||
Colors.teal,
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildSoilInfo(BuildContext context, double value) {
|
||||
return Center(
|
||||
child: Column(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.electric_bolt_rounded,
|
||||
size: 64.r,
|
||||
color: Colors.teal,
|
||||
),
|
||||
Text('$value µS/cm', style: AppTheme.headline1),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildInfoRow(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'Soil Condutivity',
|
||||
style: AppTheme.labelMedium,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
IconButton(
|
||||
iconSize: 20.r,
|
||||
color: Colors.blue,
|
||||
onPressed: () {},
|
||||
icon: const Icon(BootstrapIcons.info_circle),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildGraphicContent(BuildContext context, bool isNpk1) {
|
||||
return AspectRatio(
|
||||
aspectRatio: 1.6.h,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16.w),
|
||||
border: Border.all(color: Colors.grey.shade300, width: 1.w),
|
||||
),
|
||||
child: Consumer<ConductivityProvider>(
|
||||
builder: (context, provider, child) {
|
||||
final dataState = provider.dataState;
|
||||
|
||||
switch (dataState) {
|
||||
case ResultState.loading:
|
||||
return const Center(child: CupertinoActivityIndicator());
|
||||
case ResultState.hasData:
|
||||
return GarphicWidget(
|
||||
gradientColors: const [Colors.teal, Colors.greenAccent],
|
||||
hour: List.generate(
|
||||
isNpk1
|
||||
? provider.dataFetchedNpk1.length
|
||||
: provider.dataFetchedNpk2.length,
|
||||
(index) => isNpk1
|
||||
? provider.dataFetchedNpk1[index].hour ?? 0
|
||||
: provider.dataFetchedNpk2[index].hour ?? 0,
|
||||
),
|
||||
data: List.generate(
|
||||
isNpk1
|
||||
? provider.dataFetchedNpk1.length
|
||||
: provider.dataFetchedNpk2.length,
|
||||
(index) => isNpk1
|
||||
? provider.dataFetchedNpk1[index].soilconductivityAvg ??
|
||||
0
|
||||
: provider.dataFetchedNpk2[index].soilconductivityAvg ??
|
||||
0,
|
||||
),
|
||||
maxValue: 1,
|
||||
);
|
||||
case ResultState.error:
|
||||
return const GraphicErrorWidget(message: 'Terjadi Kesalahan');
|
||||
case ResultState.noData:
|
||||
return const GraphicErrorWidget(message: 'Tidak Ada Data');
|
||||
case ResultState.initial:
|
||||
default:
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import 'package:agrilink_vocpro/core/state/result_state.dart';
|
||||
import 'package:agrilink_vocpro/data/model/dht_graphic_response.dart';
|
||||
import 'package:agrilink_vocpro/data/model/latest_data_response.dart';
|
||||
import 'package:agrilink_vocpro/domain/service/app_service.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import 'package:agrilink_vocpro/core/state/result_state.dart';
|
||||
import 'package:agrilink_vocpro/data/model/dht_graphic_response.dart';
|
||||
import 'package:agrilink_vocpro/data/model/latest_data_response.dart';
|
||||
import 'package:agrilink_vocpro/domain/service/app_service.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
|
|
@ -16,7 +16,7 @@ class LumProvider extends ChangeNotifier {
|
|||
notifyListeners();
|
||||
try {
|
||||
final result =
|
||||
await AppService().getGrafikDataDht(metric: 'viciLuminosity');
|
||||
await AppService().getGrafikDataDht(metric: 'viciluminosity');
|
||||
if (result.data == null || result.data!.dht!.isEmpty) {
|
||||
dataState = ResultState.noData;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
import 'package:agrilink_vocpro/core/state/result_state.dart';
|
||||
import 'package:agrilink_vocpro/data/model/latest_data_response.dart';
|
||||
import 'package:agrilink_vocpro/domain/service/app_service.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
class NitrogenProvider extends ChangeNotifier {
|
||||
NitrogenProvider() {
|
||||
getSoilNitrogenNpk1Data();
|
||||
getSoilNitrogenNpk2Data();
|
||||
}
|
||||
ResultState dataState = ResultState.initial;
|
||||
|
||||
List<Npk1> dataFetchedNpk1 = [];
|
||||
List<Npk2> dataFetchedNpk2 = [];
|
||||
|
||||
Future<void> getSoilNitrogenNpk1Data() async {
|
||||
dataState = ResultState.loading;
|
||||
notifyListeners();
|
||||
try {
|
||||
final result =
|
||||
await AppService().getGraphicDataNpk1(metric: 'soilNitrogen');
|
||||
if (result.data == null || result.data!.npk1!.isEmpty) {
|
||||
dataState = ResultState.noData;
|
||||
} else {
|
||||
dataFetchedNpk1 = result.data!.npk1 ?? [];
|
||||
dataState = ResultState.hasData;
|
||||
}
|
||||
} catch (e) {
|
||||
if (kDebugMode) {
|
||||
print('Get Grafik Soil Temp Error: $e');
|
||||
}
|
||||
dataState = ResultState.error;
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> getSoilNitrogenNpk2Data() async {
|
||||
dataState = ResultState.loading;
|
||||
notifyListeners();
|
||||
try {
|
||||
final result =
|
||||
await AppService().getGraphicDataNpk2(metric: 'soilNitrogen');
|
||||
if (result.data == null || result.data!.npk2!.isEmpty) {
|
||||
dataState = ResultState.noData;
|
||||
} else {
|
||||
dataFetchedNpk2 = result.data!.npk2 ?? [];
|
||||
dataState = ResultState.hasData;
|
||||
}
|
||||
} catch (e) {
|
||||
if (kDebugMode) {
|
||||
print('Get Grafik Soil Temp Error: $e');
|
||||
}
|
||||
dataState = ResultState.error;
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,85 +1,159 @@
|
|||
import 'package:agrilink_vocpro/core/constant/app_theme.dart';
|
||||
import 'package:agrilink_vocpro/core/state/result_state.dart';
|
||||
import 'package:agrilink_vocpro/features/home/pages/nitrogen/provider/nitrogen_provider.dart';
|
||||
import 'package:agrilink_vocpro/features/home/widgets/graphic_error_widget.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:provider/provider.dart';
|
||||
|
||||
class NitrogenScreen extends StatelessWidget {
|
||||
const NitrogenScreen({super.key, this.nitrogen = 0.0});
|
||||
const NitrogenScreen(
|
||||
{super.key, this.nitrogenNpk1 = 0.0, this.nitrogenNpk2 = 0.0});
|
||||
|
||||
final double nitrogen;
|
||||
double get value => nitrogen;
|
||||
final double nitrogenNpk1;
|
||||
final double nitrogenNpk2;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Nitrogen', style: AppTheme.labelMedium),
|
||||
centerTitle: true,
|
||||
backgroundColor: Colors.white,
|
||||
scrolledUnderElevation: 0,
|
||||
actions: const [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(right: 16),
|
||||
child: Icon(
|
||||
CupertinoIcons.eyedropper,
|
||||
color: Colors.blue,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
body: Center(
|
||||
child: ListView(
|
||||
padding: EdgeInsets.all(16.w),
|
||||
children: [
|
||||
SizedBox(height: 32.h),
|
||||
Column(
|
||||
children: [
|
||||
Icon(
|
||||
CupertinoIcons.eyedropper,
|
||||
size: 64.r,
|
||||
return ChangeNotifierProvider(
|
||||
create: (context) => NitrogenProvider(),
|
||||
child: DefaultTabController(
|
||||
length: 2,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Soil Nitrogen', style: AppTheme.labelMedium),
|
||||
centerTitle: true,
|
||||
backgroundColor: Colors.white,
|
||||
scrolledUnderElevation: 0,
|
||||
actions: const [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(right: 16),
|
||||
child: Icon(
|
||||
BootstrapIcons.eyedropper,
|
||||
color: Colors.blue,
|
||||
),
|
||||
Text('$value ppm', style: AppTheme.headline1),
|
||||
)
|
||||
],
|
||||
bottom: const TabBar(
|
||||
tabs: [
|
||||
Tab(text: 'NPK 1'),
|
||||
Tab(text: 'NPK 2'),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 32.h),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'Nitrogen',
|
||||
style: AppTheme.labelMedium,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
IconButton(
|
||||
iconSize: 20.r,
|
||||
color: Colors.blue,
|
||||
onPressed: () {},
|
||||
icon: const Icon(BootstrapIcons.info_circle))
|
||||
],
|
||||
),
|
||||
SizedBox(height: 16.h),
|
||||
const Text('Grafik'),
|
||||
SizedBox(height: 16.h),
|
||||
AspectRatio(
|
||||
aspectRatio: 1.6.h,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16.w),
|
||||
border: Border.all(color: Colors.grey.shade300, width: 1.w),
|
||||
),
|
||||
child: GarphicWidget(
|
||||
gradientColors: [
|
||||
Colors.blue.shade200,
|
||||
Colors.blue,
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
body: TabBarView(
|
||||
children: [
|
||||
buildTabContent(context, nitrogenNpk1, 'NPK 1', true),
|
||||
buildTabContent(context, nitrogenNpk2, 'NPK 2', false),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
SafeArea buildTabContent(
|
||||
BuildContext context, double value, String label, bool isNpk1) {
|
||||
return SafeArea(
|
||||
child: ListView(
|
||||
padding: EdgeInsets.all(16.w),
|
||||
children: [
|
||||
SizedBox(height: MediaQuery.of(context).size.height * 0.05),
|
||||
buildSoilInfo(context, value),
|
||||
SizedBox(height: 16.h),
|
||||
buildInfoRow(context),
|
||||
SizedBox(height: 16.h),
|
||||
const Text('Grafik'),
|
||||
SizedBox(height: 16.h),
|
||||
buildGraphicContent(context, isNpk1),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildSoilInfo(BuildContext context, double value) {
|
||||
return Center(
|
||||
child: Column(
|
||||
children: [
|
||||
Icon(
|
||||
CupertinoIcons.eyedropper,
|
||||
size: 64.r,
|
||||
color: Colors.blue,
|
||||
),
|
||||
Text('$value ppm', style: AppTheme.headline1),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildInfoRow(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'Soil Nitrogen',
|
||||
style: AppTheme.labelMedium,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
IconButton(
|
||||
iconSize: 20.r,
|
||||
color: Colors.blue,
|
||||
onPressed: () {},
|
||||
icon: const Icon(BootstrapIcons.info_circle),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildGraphicContent(BuildContext context, bool isNpk1) {
|
||||
return AspectRatio(
|
||||
aspectRatio: 1.6.h,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16.w),
|
||||
border: Border.all(color: Colors.grey.shade300, width: 1.w),
|
||||
),
|
||||
child: Consumer<NitrogenProvider>(
|
||||
builder: (context, provider, child) {
|
||||
final dataState = provider.dataState;
|
||||
|
||||
switch (dataState) {
|
||||
case ResultState.loading:
|
||||
return const Center(child: CupertinoActivityIndicator());
|
||||
case ResultState.hasData:
|
||||
return GarphicWidget(
|
||||
gradientColors: const [Colors.blue, Colors.blueAccent],
|
||||
hour: List.generate(
|
||||
isNpk1
|
||||
? provider.dataFetchedNpk1.length
|
||||
: provider.dataFetchedNpk2.length,
|
||||
(index) => isNpk1
|
||||
? provider.dataFetchedNpk1[index].hour ?? 0
|
||||
: provider.dataFetchedNpk2[index].hour ?? 0,
|
||||
),
|
||||
data: List.generate(
|
||||
isNpk1
|
||||
? provider.dataFetchedNpk1.length
|
||||
: provider.dataFetchedNpk2.length,
|
||||
(index) => isNpk1
|
||||
? provider.dataFetchedNpk1[index].soilnitrogenAvg ?? 0
|
||||
: provider.dataFetchedNpk2[index].soilnitrogenAvg ?? 0,
|
||||
),
|
||||
maxValue: 1,
|
||||
);
|
||||
case ResultState.error:
|
||||
return const GraphicErrorWidget(message: 'Terjadi Kesalahan');
|
||||
case ResultState.noData:
|
||||
return const GraphicErrorWidget(message: 'Tidak Ada Data');
|
||||
case ResultState.initial:
|
||||
default:
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
import 'package:agrilink_vocpro/core/state/result_state.dart';
|
||||
import 'package:agrilink_vocpro/data/model/latest_data_response.dart';
|
||||
import 'package:agrilink_vocpro/domain/service/app_service.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
class PhProvider extends ChangeNotifier {
|
||||
PhProvider() {
|
||||
getSoilPhNpk1Data();
|
||||
getSoilPhNpk2Data();
|
||||
}
|
||||
ResultState dataState = ResultState.initial;
|
||||
|
||||
List<Npk1> dataFetchedNpk1 = [];
|
||||
List<Npk2> dataFetchedNpk2 = [];
|
||||
|
||||
Future<void> getSoilPhNpk1Data() async {
|
||||
dataState = ResultState.loading;
|
||||
notifyListeners();
|
||||
try {
|
||||
final result = await AppService().getGraphicDataNpk1(metric: 'soilPh');
|
||||
if (result.data == null || result.data!.npk1!.isEmpty) {
|
||||
dataState = ResultState.noData;
|
||||
} else {
|
||||
dataFetchedNpk1 = result.data!.npk1 ?? [];
|
||||
dataState = ResultState.hasData;
|
||||
}
|
||||
} catch (e) {
|
||||
if (kDebugMode) {
|
||||
print('Get Grafik Soil Temp Error: $e');
|
||||
}
|
||||
dataState = ResultState.error;
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> getSoilPhNpk2Data() async {
|
||||
dataState = ResultState.loading;
|
||||
notifyListeners();
|
||||
try {
|
||||
final result = await AppService().getGraphicDataNpk2(metric: 'soilPh');
|
||||
if (result.data == null || result.data!.npk2!.isEmpty) {
|
||||
dataState = ResultState.noData;
|
||||
} else {
|
||||
dataFetchedNpk2 = result.data!.npk2 ?? [];
|
||||
dataState = ResultState.hasData;
|
||||
}
|
||||
} catch (e) {
|
||||
if (kDebugMode) {
|
||||
print('Get Grafik Soil Temp Error: $e');
|
||||
}
|
||||
dataState = ResultState.error;
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,181 +1,150 @@
|
|||
import 'package:agrilink_vocpro/core/constant/app_theme.dart';
|
||||
import 'package:agrilink_vocpro/core/state/result_state.dart';
|
||||
import 'package:agrilink_vocpro/features/home/pages/ph/provider/ph_provider.dart';
|
||||
import 'package:agrilink_vocpro/features/home/pages/ph/widget/ph_bar_pointer.dart';
|
||||
import 'package:agrilink_vocpro/features/home/widgets/graphic_error_widget.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:provider/provider.dart';
|
||||
|
||||
class PhScreen extends StatelessWidget {
|
||||
const PhScreen({super.key, required this.phValue});
|
||||
const PhScreen({super.key, this.phValueNpk1 = 0, this.phValueNpk2 = 0});
|
||||
|
||||
final double phValue;
|
||||
|
||||
double get value => phValue;
|
||||
final double phValueNpk1;
|
||||
final double phValueNpk2;
|
||||
|
||||
@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.pie_chart,
|
||||
color: Colors.orange,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
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,
|
||||
return ChangeNotifierProvider(
|
||||
create: (context) => PhProvider(),
|
||||
child: DefaultTabController(
|
||||
length: 2,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Soil Temperature', style: AppTheme.labelMedium),
|
||||
centerTitle: true,
|
||||
backgroundColor: Colors.white,
|
||||
scrolledUnderElevation: 0,
|
||||
actions: const [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(right: 16),
|
||||
child: Icon(
|
||||
BootstrapIcons.water,
|
||||
color: Colors.green,
|
||||
),
|
||||
IconButton(
|
||||
iconSize: 20.r,
|
||||
color: Colors.blue,
|
||||
onPressed: () {},
|
||||
icon: const Icon(BootstrapIcons.info_circle))
|
||||
)
|
||||
],
|
||||
bottom: const TabBar(
|
||||
tabs: [
|
||||
Tab(text: 'NPK 1'),
|
||||
Tab(text: 'NPK 2'),
|
||||
],
|
||||
),
|
||||
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: GarphicWidget(
|
||||
gradientColors: [
|
||||
Colors.amber.shade200,
|
||||
Colors.orange,
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
body: TabBarView(
|
||||
children: [
|
||||
buildTabContent(context, phValueNpk1, 'NPK 1', true),
|
||||
buildTabContent(context, phValueNpk2, 'NPK 2', false),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
SafeArea buildTabContent(
|
||||
BuildContext context, double value, String label, bool isNpk1) {
|
||||
return SafeArea(
|
||||
child: ListView(
|
||||
padding: EdgeInsets.all(16.w),
|
||||
children: [
|
||||
SizedBox(height: MediaQuery.of(context).size.height * 0.05),
|
||||
buildSoilInfo(context, value),
|
||||
SizedBox(height: 16.h),
|
||||
buildInfoRow(context),
|
||||
SizedBox(height: 16.h),
|
||||
const Text('Grafik'),
|
||||
SizedBox(height: 16.h),
|
||||
buildGraphicContent(context, isNpk1),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildSoilInfo(BuildContext context, double value) {
|
||||
return Center(
|
||||
child: PhIndicator(phValue: value),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildInfoRow(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'Soil Acidity',
|
||||
style: AppTheme.labelMedium,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
IconButton(
|
||||
iconSize: 20.r,
|
||||
color: Colors.blue,
|
||||
onPressed: () {},
|
||||
icon: const Icon(BootstrapIcons.info_circle),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildGraphicContent(BuildContext context, bool isNpk1) {
|
||||
return AspectRatio(
|
||||
aspectRatio: 1.6.h,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16.w),
|
||||
border: Border.all(color: Colors.grey.shade300, width: 1.w),
|
||||
),
|
||||
child: Consumer<PhProvider>(
|
||||
builder: (context, provider, child) {
|
||||
final dataState = provider.dataState;
|
||||
|
||||
switch (dataState) {
|
||||
case ResultState.loading:
|
||||
return const Center(child: CupertinoActivityIndicator());
|
||||
case ResultState.hasData:
|
||||
return GarphicWidget(
|
||||
gradientColors: const [Colors.cyan, Colors.amber],
|
||||
hour: List.generate(
|
||||
isNpk1
|
||||
? provider.dataFetchedNpk1.length
|
||||
: provider.dataFetchedNpk2.length,
|
||||
(index) => isNpk1
|
||||
? provider.dataFetchedNpk1[index].hour ?? 0
|
||||
: provider.dataFetchedNpk2[index].hour ?? 0,
|
||||
),
|
||||
data: List.generate(
|
||||
isNpk1
|
||||
? provider.dataFetchedNpk1.length
|
||||
: provider.dataFetchedNpk2.length,
|
||||
(index) => isNpk1
|
||||
? provider.dataFetchedNpk1[index].soilphAvg ?? 0
|
||||
: provider.dataFetchedNpk2[index].soilphAvg ?? 0,
|
||||
),
|
||||
maxValue: 14,
|
||||
);
|
||||
case ResultState.error:
|
||||
return const GraphicErrorWidget(message: 'Terjadi Kesalahan');
|
||||
case ResultState.noData:
|
||||
return const GraphicErrorWidget(message: 'Tidak Ada Data');
|
||||
case ResultState.initial:
|
||||
default:
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
import 'package:agrilink_vocpro/core/state/result_state.dart';
|
||||
import 'package:agrilink_vocpro/data/model/latest_data_response.dart';
|
||||
import 'package:agrilink_vocpro/domain/service/app_service.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
class PhosporusProvider extends ChangeNotifier {
|
||||
PhosporusProvider() {
|
||||
getSoilPhosporNpk1Data();
|
||||
getSoilPhosporNpk2Data();
|
||||
}
|
||||
ResultState dataState = ResultState.initial;
|
||||
|
||||
List<Npk1> dataFetchedNpk1 = [];
|
||||
List<Npk2> dataFetchedNpk2 = [];
|
||||
|
||||
Future<void> getSoilPhosporNpk1Data() async {
|
||||
dataState = ResultState.loading;
|
||||
notifyListeners();
|
||||
try {
|
||||
final result =
|
||||
await AppService().getGraphicDataNpk1(metric: 'soilPhosphorus');
|
||||
if (result.data == null || result.data!.npk1!.isEmpty) {
|
||||
dataState = ResultState.noData;
|
||||
} else {
|
||||
dataFetchedNpk1 = result.data!.npk1 ?? [];
|
||||
dataState = ResultState.hasData;
|
||||
}
|
||||
} catch (e) {
|
||||
if (kDebugMode) {
|
||||
print('Get Grafik Soil Temp Error: $e');
|
||||
}
|
||||
dataState = ResultState.error;
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> getSoilPhosporNpk2Data() async {
|
||||
dataState = ResultState.loading;
|
||||
notifyListeners();
|
||||
try {
|
||||
final result =
|
||||
await AppService().getGraphicDataNpk2(metric: 'soilPhosphorus');
|
||||
if (result.data == null || result.data!.npk2!.isEmpty) {
|
||||
dataState = ResultState.noData;
|
||||
} else {
|
||||
dataFetchedNpk2 = result.data!.npk2 ?? [];
|
||||
dataState = ResultState.hasData;
|
||||
}
|
||||
} catch (e) {
|
||||
if (kDebugMode) {
|
||||
print('Get Grafik Soil Temp Error: $e');
|
||||
}
|
||||
dataState = ResultState.error;
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,85 +1,160 @@
|
|||
import 'package:agrilink_vocpro/core/constant/app_theme.dart';
|
||||
import 'package:agrilink_vocpro/core/state/result_state.dart';
|
||||
import 'package:agrilink_vocpro/features/home/pages/phosphorus/provider/phosporus_provider.dart';
|
||||
import 'package:agrilink_vocpro/features/home/widgets/graphic_error_widget.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:provider/provider.dart';
|
||||
|
||||
class PhosphorusScreen extends StatelessWidget {
|
||||
const PhosphorusScreen({super.key, this.phosphorus = 0.0});
|
||||
const PhosphorusScreen(
|
||||
{super.key, this.phosphorusNpk1 = 0.0, this.phosphorusNpk2 = 0.0});
|
||||
|
||||
final double phosphorus;
|
||||
double get value => phosphorus;
|
||||
final double phosphorusNpk1;
|
||||
final double phosphorusNpk2;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Phosphorus', style: AppTheme.labelMedium),
|
||||
centerTitle: true,
|
||||
backgroundColor: Colors.white,
|
||||
scrolledUnderElevation: 0,
|
||||
actions: const [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(right: 16),
|
||||
child: Icon(
|
||||
CupertinoIcons.eyedropper,
|
||||
color: Colors.blue,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
body: Center(
|
||||
child: ListView(
|
||||
padding: EdgeInsets.all(16.w),
|
||||
children: [
|
||||
SizedBox(height: 32.h),
|
||||
Column(
|
||||
children: [
|
||||
Icon(
|
||||
CupertinoIcons.eyedropper,
|
||||
size: 64.r,
|
||||
return ChangeNotifierProvider(
|
||||
create: (context) => PhosporusProvider(),
|
||||
child: DefaultTabController(
|
||||
length: 2,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Soil Phosphorus', style: AppTheme.labelMedium),
|
||||
centerTitle: true,
|
||||
backgroundColor: Colors.white,
|
||||
scrolledUnderElevation: 0,
|
||||
actions: const [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(right: 16),
|
||||
child: Icon(
|
||||
BootstrapIcons.eyedropper,
|
||||
color: Colors.blue,
|
||||
),
|
||||
Text('$value ppm', style: AppTheme.headline1),
|
||||
)
|
||||
],
|
||||
bottom: const TabBar(
|
||||
tabs: [
|
||||
Tab(text: 'NPK 1'),
|
||||
Tab(text: 'NPK 2'),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 32.h),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'Fosfor',
|
||||
style: AppTheme.labelMedium,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
IconButton(
|
||||
iconSize: 20.r,
|
||||
color: Colors.blue,
|
||||
onPressed: () {},
|
||||
icon: const Icon(BootstrapIcons.info_circle))
|
||||
],
|
||||
),
|
||||
SizedBox(height: 16.h),
|
||||
const Text('Grafik'),
|
||||
SizedBox(height: 16.h),
|
||||
AspectRatio(
|
||||
aspectRatio: 1.6.h,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16.w),
|
||||
border: Border.all(color: Colors.grey.shade300, width: 1.w),
|
||||
),
|
||||
child: GarphicWidget(
|
||||
gradientColors: [
|
||||
Colors.blue.shade200,
|
||||
Colors.blue,
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
body: TabBarView(
|
||||
children: [
|
||||
buildTabContent(context, phosphorusNpk1, 'NPK 1', true),
|
||||
buildTabContent(context, phosphorusNpk2, 'NPK 2', false),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
SafeArea buildTabContent(
|
||||
BuildContext context, double value, String label, bool isNpk1) {
|
||||
return SafeArea(
|
||||
child: ListView(
|
||||
padding: EdgeInsets.all(16.w),
|
||||
children: [
|
||||
SizedBox(height: MediaQuery.of(context).size.height * 0.05),
|
||||
buildSoilInfo(context, value),
|
||||
SizedBox(height: 16.h),
|
||||
buildInfoRow(context),
|
||||
SizedBox(height: 16.h),
|
||||
const Text('Grafik'),
|
||||
SizedBox(height: 16.h),
|
||||
buildGraphicContent(context, isNpk1),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildSoilInfo(BuildContext context, double value) {
|
||||
return Center(
|
||||
child: Column(
|
||||
children: [
|
||||
Icon(
|
||||
CupertinoIcons.eyedropper,
|
||||
size: 64.r,
|
||||
color: Colors.blue,
|
||||
),
|
||||
Text('$value ppm', style: AppTheme.headline1),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildInfoRow(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'Soil Phosphorus',
|
||||
style: AppTheme.labelMedium,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
IconButton(
|
||||
iconSize: 20.r,
|
||||
color: Colors.blue,
|
||||
onPressed: () {},
|
||||
icon: const Icon(BootstrapIcons.info_circle),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildGraphicContent(BuildContext context, bool isNpk1) {
|
||||
return AspectRatio(
|
||||
aspectRatio: 1.6.h,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16.w),
|
||||
border: Border.all(color: Colors.grey.shade300, width: 1.w),
|
||||
),
|
||||
child: Consumer<PhosporusProvider>(
|
||||
builder: (context, provider, child) {
|
||||
final dataState = provider.dataState;
|
||||
|
||||
switch (dataState) {
|
||||
case ResultState.loading:
|
||||
return const Center(child: CupertinoActivityIndicator());
|
||||
case ResultState.hasData:
|
||||
return GarphicWidget(
|
||||
gradientColors: const [Colors.blue, Colors.blueAccent],
|
||||
hour: List.generate(
|
||||
isNpk1
|
||||
? provider.dataFetchedNpk1.length
|
||||
: provider.dataFetchedNpk2.length,
|
||||
(index) => isNpk1
|
||||
? provider.dataFetchedNpk1[index].hour ?? 0
|
||||
: provider.dataFetchedNpk2[index].hour ?? 0,
|
||||
),
|
||||
data: List.generate(
|
||||
isNpk1
|
||||
? provider.dataFetchedNpk1.length
|
||||
: provider.dataFetchedNpk2.length,
|
||||
(index) => isNpk1
|
||||
? provider.dataFetchedNpk1[index].soilphosphorusAvg ?? 0
|
||||
: provider.dataFetchedNpk2[index].soilphosphorusAvg ??
|
||||
0,
|
||||
),
|
||||
maxValue: 10,
|
||||
);
|
||||
case ResultState.error:
|
||||
return const GraphicErrorWidget(message: 'Terjadi Kesalahan');
|
||||
case ResultState.noData:
|
||||
return const GraphicErrorWidget(message: 'Tidak Ada Data');
|
||||
case ResultState.initial:
|
||||
default:
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
import 'package:agrilink_vocpro/core/state/result_state.dart';
|
||||
import 'package:agrilink_vocpro/data/model/latest_data_response.dart';
|
||||
import 'package:agrilink_vocpro/domain/service/app_service.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
class PotassiumProvider extends ChangeNotifier {
|
||||
PotassiumProvider() {
|
||||
getSoilPotassiumNpk1Data();
|
||||
getSoilPotassiumNpk2Data();
|
||||
}
|
||||
ResultState dataState = ResultState.initial;
|
||||
|
||||
List<Npk1> dataFetchedNpk1 = [];
|
||||
List<Npk2> dataFetchedNpk2 = [];
|
||||
|
||||
Future<void> getSoilPotassiumNpk1Data() async {
|
||||
dataState = ResultState.loading;
|
||||
notifyListeners();
|
||||
try {
|
||||
final result =
|
||||
await AppService().getGraphicDataNpk1(metric: 'soilPotassium');
|
||||
if (result.data == null || result.data!.npk1!.isEmpty) {
|
||||
dataState = ResultState.noData;
|
||||
} else {
|
||||
dataFetchedNpk1 = result.data!.npk1 ?? [];
|
||||
dataState = ResultState.hasData;
|
||||
}
|
||||
} catch (e) {
|
||||
if (kDebugMode) {
|
||||
print('Get Grafik Soil Temp Error: $e');
|
||||
}
|
||||
dataState = ResultState.error;
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> getSoilPotassiumNpk2Data() async {
|
||||
dataState = ResultState.loading;
|
||||
notifyListeners();
|
||||
try {
|
||||
final result =
|
||||
await AppService().getGraphicDataNpk2(metric: 'soilPotassium');
|
||||
if (result.data == null || result.data!.npk2!.isEmpty) {
|
||||
dataState = ResultState.noData;
|
||||
} else {
|
||||
dataFetchedNpk2 = result.data!.npk2 ?? [];
|
||||
dataState = ResultState.hasData;
|
||||
}
|
||||
} catch (e) {
|
||||
if (kDebugMode) {
|
||||
print('Get Grafik Soil Temp Error: $e');
|
||||
}
|
||||
dataState = ResultState.error;
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,85 +1,159 @@
|
|||
import 'package:agrilink_vocpro/core/constant/app_theme.dart';
|
||||
import 'package:agrilink_vocpro/core/state/result_state.dart';
|
||||
import 'package:agrilink_vocpro/features/home/pages/potassium/provider/potassium_provider.dart';
|
||||
import 'package:agrilink_vocpro/features/home/widgets/graphic_error_widget.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:provider/provider.dart';
|
||||
|
||||
class PotassiumScreen extends StatelessWidget {
|
||||
const PotassiumScreen({super.key, this.potassium = 0.0});
|
||||
const PotassiumScreen(
|
||||
{super.key, this.potassiumNpk1 = 0.0, this.potassiumNpk2 = 0.0});
|
||||
|
||||
final double potassium;
|
||||
double get value => potassium;
|
||||
final double potassiumNpk1;
|
||||
final double potassiumNpk2;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Potassium', style: AppTheme.labelMedium),
|
||||
centerTitle: true,
|
||||
backgroundColor: Colors.white,
|
||||
scrolledUnderElevation: 0,
|
||||
actions: const [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(right: 16),
|
||||
child: Icon(
|
||||
CupertinoIcons.eyedropper,
|
||||
color: Colors.green,
|
||||
return ChangeNotifierProvider(
|
||||
create: (context) => PotassiumProvider(),
|
||||
child: DefaultTabController(
|
||||
length: 2,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Soil Potassium', style: AppTheme.labelMedium),
|
||||
centerTitle: true,
|
||||
backgroundColor: Colors.white,
|
||||
scrolledUnderElevation: 0,
|
||||
actions: const [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(right: 16),
|
||||
child: Icon(
|
||||
BootstrapIcons.eyedropper,
|
||||
color: Colors.red,
|
||||
),
|
||||
)
|
||||
],
|
||||
bottom: const TabBar(
|
||||
tabs: [
|
||||
Tab(text: 'NPK 1'),
|
||||
Tab(text: 'NPK 2'),
|
||||
],
|
||||
),
|
||||
)
|
||||
),
|
||||
body: TabBarView(
|
||||
children: [
|
||||
buildTabContent(context, potassiumNpk1, 'NPK 1', true),
|
||||
buildTabContent(context, potassiumNpk2, 'NPK 2', false),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
SafeArea buildTabContent(
|
||||
BuildContext context, double value, String label, bool isNpk1) {
|
||||
return SafeArea(
|
||||
child: ListView(
|
||||
padding: EdgeInsets.all(16.w),
|
||||
children: [
|
||||
SizedBox(height: MediaQuery.of(context).size.height * 0.05),
|
||||
buildSoilInfo(context, value),
|
||||
SizedBox(height: 16.h),
|
||||
buildInfoRow(context),
|
||||
SizedBox(height: 16.h),
|
||||
const Text('Grafik'),
|
||||
SizedBox(height: 16.h),
|
||||
buildGraphicContent(context, isNpk1),
|
||||
],
|
||||
),
|
||||
body: Center(
|
||||
child: ListView(
|
||||
padding: EdgeInsets.all(16.w),
|
||||
children: [
|
||||
SizedBox(height: 32.h),
|
||||
Column(
|
||||
children: [
|
||||
Icon(
|
||||
CupertinoIcons.eyedropper,
|
||||
size: 64.r,
|
||||
color: Colors.green,
|
||||
),
|
||||
Text('$value ppm', style: AppTheme.headline1),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 32.h),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'Kalium',
|
||||
style: AppTheme.labelMedium,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
IconButton(
|
||||
iconSize: 20.r,
|
||||
color: Colors.blue,
|
||||
onPressed: () {},
|
||||
icon: const Icon(BootstrapIcons.info_circle))
|
||||
],
|
||||
),
|
||||
SizedBox(height: 16.h),
|
||||
const Text('Grafik'),
|
||||
SizedBox(height: 16.h),
|
||||
AspectRatio(
|
||||
aspectRatio: 1.6.h,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16.w),
|
||||
border: Border.all(color: Colors.grey.shade300, width: 1.w),
|
||||
),
|
||||
child: const GarphicWidget(
|
||||
gradientColors: [
|
||||
Colors.teal,
|
||||
Colors.green,
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildSoilInfo(BuildContext context, double value) {
|
||||
return Center(
|
||||
child: Column(
|
||||
children: [
|
||||
Icon(
|
||||
CupertinoIcons.eyedropper,
|
||||
size: 64.r,
|
||||
color: Colors.red,
|
||||
),
|
||||
Text('$value ppm', style: AppTheme.headline1),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildInfoRow(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'Soil Potassium',
|
||||
style: AppTheme.labelMedium,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
IconButton(
|
||||
iconSize: 20.r,
|
||||
color: Colors.blue,
|
||||
onPressed: () {},
|
||||
icon: const Icon(BootstrapIcons.info_circle),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildGraphicContent(BuildContext context, bool isNpk1) {
|
||||
return AspectRatio(
|
||||
aspectRatio: 1.6.h,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16.w),
|
||||
border: Border.all(color: Colors.grey.shade300, width: 1.w),
|
||||
),
|
||||
child: Consumer<PotassiumProvider>(
|
||||
builder: (context, provider, child) {
|
||||
final dataState = provider.dataState;
|
||||
|
||||
switch (dataState) {
|
||||
case ResultState.loading:
|
||||
return const Center(child: CupertinoActivityIndicator());
|
||||
case ResultState.hasData:
|
||||
return GarphicWidget(
|
||||
gradientColors: const [Colors.red, Colors.orange],
|
||||
hour: List.generate(
|
||||
isNpk1
|
||||
? provider.dataFetchedNpk1.length
|
||||
: provider.dataFetchedNpk2.length,
|
||||
(index) => isNpk1
|
||||
? provider.dataFetchedNpk1[index].hour ?? 0
|
||||
: provider.dataFetchedNpk2[index].hour ?? 0,
|
||||
),
|
||||
data: List.generate(
|
||||
isNpk1
|
||||
? provider.dataFetchedNpk1.length
|
||||
: provider.dataFetchedNpk2.length,
|
||||
(index) => isNpk1
|
||||
? provider.dataFetchedNpk1[index].soilpotassiumAvg ?? 0
|
||||
: provider.dataFetchedNpk2[index].soilpotassiumAvg ?? 0,
|
||||
),
|
||||
maxValue: 1,
|
||||
);
|
||||
case ResultState.error:
|
||||
return const GraphicErrorWidget(message: 'Terjadi Kesalahan');
|
||||
case ResultState.noData:
|
||||
return const GraphicErrorWidget(message: 'Tidak Ada Data');
|
||||
case ResultState.initial:
|
||||
default:
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
import 'package:agrilink_vocpro/core/state/result_state.dart';
|
||||
import 'package:agrilink_vocpro/data/model/latest_data_response.dart';
|
||||
import 'package:agrilink_vocpro/domain/service/app_service.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
class SoilMoistureProvider extends ChangeNotifier {
|
||||
SoilMoistureProvider() {
|
||||
getSoilMosNpk1Data();
|
||||
getSoilMosNpk2Data();
|
||||
}
|
||||
ResultState dataState = ResultState.initial;
|
||||
|
||||
List<Npk1> dataFetchedNpk1 = [];
|
||||
List<Npk2> dataFetchedNpk2 = [];
|
||||
|
||||
Future<void> getSoilMosNpk1Data() async {
|
||||
dataState = ResultState.loading;
|
||||
notifyListeners();
|
||||
try {
|
||||
final result =
|
||||
await AppService().getGraphicDataNpk1(metric: 'soilhumidity');
|
||||
if (result.data == null || result.data!.npk1!.isEmpty) {
|
||||
dataState = ResultState.noData;
|
||||
} else {
|
||||
dataFetchedNpk1 = result.data!.npk1 ?? [];
|
||||
dataState = ResultState.hasData;
|
||||
}
|
||||
} catch (e) {
|
||||
if (kDebugMode) {
|
||||
print('Get Grafik Soil Temp Error: $e');
|
||||
}
|
||||
dataState = ResultState.error;
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> getSoilMosNpk2Data() async {
|
||||
dataState = ResultState.loading;
|
||||
notifyListeners();
|
||||
try {
|
||||
final result =
|
||||
await AppService().getGraphicDataNpk2(metric: 'soilhumidity');
|
||||
if (result.data == null || result.data!.npk2!.isEmpty) {
|
||||
dataState = ResultState.noData;
|
||||
} else {
|
||||
dataFetchedNpk2 = result.data!.npk2 ?? [];
|
||||
dataState = ResultState.hasData;
|
||||
}
|
||||
} catch (e) {
|
||||
if (kDebugMode) {
|
||||
print('Get Grafik Soil Temp Error: $e');
|
||||
}
|
||||
dataState = ResultState.error;
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,128 +1,189 @@
|
|||
import 'package:agrilink_vocpro/core/constant/app_constant.dart';
|
||||
import 'package:agrilink_vocpro/core/constant/app_theme.dart';
|
||||
import 'package:agrilink_vocpro/core/widgets/show_info.dart';
|
||||
import 'package:agrilink_vocpro/core/state/result_state.dart';
|
||||
import 'package:agrilink_vocpro/features/home/pages/soil_moisture/provider/soil_moisture_provider.dart';
|
||||
import 'package:agrilink_vocpro/features/home/widgets/graphic_error_widget.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 SoilMoistureScreen extends StatelessWidget {
|
||||
const SoilMoistureScreen({super.key, this.moisture = 0});
|
||||
const SoilMoistureScreen(
|
||||
{super.key, this.moistureNpk1 = 0, this.moistureNpk2 = 0});
|
||||
|
||||
final double moisture;
|
||||
double get value => moisture;
|
||||
final double moistureNpk1;
|
||||
final double moistureNpk2;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Soil Moisture', style: AppTheme.labelMedium),
|
||||
centerTitle: true,
|
||||
backgroundColor: Colors.white,
|
||||
leading: IconButton(
|
||||
icon: const Icon(CupertinoIcons.back),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
),
|
||||
actions: const [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(right: 16),
|
||||
child: Icon(
|
||||
Icons.water_outlined,
|
||||
color: Colors.blue,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
body: SafeArea(
|
||||
child: ListView(
|
||||
padding: EdgeInsets.all(16.w),
|
||||
children: [
|
||||
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('$moisture %', style: AppTheme.headline1),
|
||||
],
|
||||
),
|
||||
),
|
||||
RotatedBox(
|
||||
quarterTurns: 2,
|
||||
child: AnimatedRadialGauge(
|
||||
duration: const Duration(seconds: 3),
|
||||
curve: Curves.easeOut,
|
||||
value: value,
|
||||
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,
|
||||
]),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16.h),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'Soil Temperature',
|
||||
style: AppTheme.labelMedium,
|
||||
textAlign: TextAlign.center,
|
||||
return ChangeNotifierProvider(
|
||||
create: (context) => SoilMoistureProvider(),
|
||||
child: DefaultTabController(
|
||||
length: 2,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Soil Moisture', 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,
|
||||
),
|
||||
IconButton(
|
||||
iconSize: 20.r,
|
||||
color: Colors.blue,
|
||||
onPressed: () {
|
||||
showInfo(
|
||||
context,
|
||||
'Soil Temperature',
|
||||
AppConstant.soilTempInfo,
|
||||
);
|
||||
},
|
||||
icon: const Icon(BootstrapIcons.info_circle))
|
||||
)
|
||||
],
|
||||
bottom: const TabBar(
|
||||
tabs: [
|
||||
Tab(text: 'NPK 1'),
|
||||
Tab(text: 'NPK 2'),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 16.h),
|
||||
const Text('Grafik'),
|
||||
SizedBox(height: 16.h),
|
||||
AspectRatio(
|
||||
aspectRatio: 1.8.h,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16.w),
|
||||
border: Border.all(color: Colors.grey.shade300, width: 1.w),
|
||||
),
|
||||
body: TabBarView(
|
||||
children: [
|
||||
buildTabContent(context, moistureNpk1, 'NPK 1', true),
|
||||
buildTabContent(context, moistureNpk2, 'NPK 2', false),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
SafeArea buildTabContent(
|
||||
BuildContext context, double value, String label, bool isNpk1) {
|
||||
return SafeArea(
|
||||
child: ListView(
|
||||
padding: EdgeInsets.all(16.w),
|
||||
children: [
|
||||
SizedBox(height: MediaQuery.of(context).size.height * 0.05),
|
||||
buildSoilMoistureInfo(context, value),
|
||||
SizedBox(height: 16.h),
|
||||
buildInfoRow(context),
|
||||
SizedBox(height: 16.h),
|
||||
const Text('Grafik'),
|
||||
SizedBox(height: 16.h),
|
||||
buildGraphicContent(context, isNpk1),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildSoilMoistureInfo(BuildContext context, double value) {
|
||||
return 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('$value %', style: AppTheme.headline1),
|
||||
],
|
||||
),
|
||||
),
|
||||
RotatedBox(
|
||||
quarterTurns: 2,
|
||||
child: AnimatedRadialGauge(
|
||||
duration: const Duration(seconds: 3),
|
||||
curve: Curves.easeOut,
|
||||
value: value,
|
||||
axis: GaugeAxis(
|
||||
degrees: 360,
|
||||
min: 0,
|
||||
max: 100,
|
||||
pointer: null,
|
||||
style: GaugeAxisStyle(
|
||||
background: Colors.grey.shade100,
|
||||
thickness: 50,
|
||||
),
|
||||
child: GarphicWidget(
|
||||
gradientColors: [
|
||||
progressBar: GaugeBasicProgressBar(
|
||||
gradient: GaugeAxisGradient(colors: [
|
||||
Colors.blue.shade200,
|
||||
Colors.blue,
|
||||
],
|
||||
]),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildInfoRow(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'Soil Moisture',
|
||||
style: AppTheme.labelMedium,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
IconButton(
|
||||
iconSize: 20.r,
|
||||
color: Colors.blue,
|
||||
onPressed: () {},
|
||||
icon: const Icon(BootstrapIcons.info_circle),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildGraphicContent(BuildContext context, bool isNpk1) {
|
||||
return AspectRatio(
|
||||
aspectRatio: 1.6.h,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16.w),
|
||||
border: Border.all(color: Colors.grey.shade300, width: 1.w),
|
||||
),
|
||||
child: Consumer<SoilMoistureProvider>(
|
||||
builder: (context, provider, child) {
|
||||
final dataState = provider.dataState;
|
||||
|
||||
switch (dataState) {
|
||||
case ResultState.loading:
|
||||
return const Center(child: CupertinoActivityIndicator());
|
||||
case ResultState.hasData:
|
||||
return GarphicWidget(
|
||||
gradientColors: const [Colors.cyan, Colors.blue],
|
||||
hour: List.generate(
|
||||
isNpk1
|
||||
? provider.dataFetchedNpk1.length
|
||||
: provider.dataFetchedNpk2.length,
|
||||
(index) => isNpk1
|
||||
? provider.dataFetchedNpk1[index].hour ?? 0
|
||||
: provider.dataFetchedNpk2[index].hour ?? 0,
|
||||
),
|
||||
data: List.generate(
|
||||
isNpk1
|
||||
? provider.dataFetchedNpk1.length
|
||||
: provider.dataFetchedNpk2.length,
|
||||
(index) => isNpk1
|
||||
? provider.dataFetchedNpk1[index].soilhumidityAvg ?? 0
|
||||
: provider.dataFetchedNpk2[index].soilhumidityAvg ?? 0,
|
||||
),
|
||||
maxValue: 1,
|
||||
);
|
||||
case ResultState.error:
|
||||
return const GraphicErrorWidget(message: 'Terjadi Kesalahan');
|
||||
case ResultState.noData:
|
||||
return const GraphicErrorWidget(message: 'Tidak Ada Data');
|
||||
case ResultState.initial:
|
||||
default:
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import 'package:agrilink_vocpro/core/state/result_state.dart';
|
||||
import 'package:agrilink_vocpro/data/model/npk1_graphic_response.dart';
|
||||
import 'package:agrilink_vocpro/data/model/npk2_graphic_response.dart';
|
||||
import 'package:agrilink_vocpro/data/model/latest_data_response.dart';
|
||||
import 'package:agrilink_vocpro/domain/service/app_service.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
|
|
|
|||
|
|
@ -225,6 +225,7 @@ class SoilTemperatureScreen extends StatelessWidget {
|
|||
: provider.dataFetchedNpk2[index].soiltemperatureAvg ??
|
||||
0,
|
||||
),
|
||||
maxValue: 70,
|
||||
);
|
||||
case ResultState.error:
|
||||
return const GraphicErrorWidget(message: 'Terjadi Kesalahan');
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import 'package:agrilink_vocpro/core/state/result_state.dart';
|
||||
import 'package:agrilink_vocpro/data/model/dht_graphic_response.dart';
|
||||
import 'package:agrilink_vocpro/data/model/latest_data_response.dart';
|
||||
import 'package:agrilink_vocpro/domain/service/app_service.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ class HomeProvider extends ChangeNotifier {
|
|||
if (kDebugMode) {
|
||||
print('Get Latest Error: $e');
|
||||
}
|
||||
dataState = ResultState.hasData;
|
||||
dataState = ResultState.error;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,11 +5,17 @@ import 'package:flutter_screenutil/flutter_screenutil.dart';
|
|||
|
||||
class GarphicWidget extends StatelessWidget {
|
||||
const GarphicWidget(
|
||||
{super.key, required this.gradientColors, this.data, this.hour});
|
||||
{super.key,
|
||||
required this.gradientColors,
|
||||
this.data,
|
||||
this.hour,
|
||||
this.maxValue = 100});
|
||||
|
||||
final List<Color> gradientColors;
|
||||
final List<num>? hour;
|
||||
final List<num>? data;
|
||||
final double maxValue;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
|
|
@ -50,16 +56,6 @@ 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(
|
||||
|
|
@ -92,7 +88,7 @@ class GarphicWidget extends StatelessWidget {
|
|||
minX: 0,
|
||||
maxX: 24,
|
||||
minY: 0,
|
||||
maxY: data == null ? 0 : getMaxValue().toDouble(),
|
||||
maxY: data == null ? 0 : maxValue,
|
||||
lineBarsData: [
|
||||
LineChartBarData(
|
||||
spots: data == null && hour == null
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ class ListDataFromCensorNpk1 extends StatelessWidget {
|
|||
censorIdentifier: censorIdentifier,
|
||||
onTap: () async {
|
||||
await context.push(
|
||||
'${AppRoute.soilMoisture}/${provider.npk1SoilMoisture}');
|
||||
'${AppRoute.soilMoisture}/${provider.npk1SoilMoisture}/${provider.npk2SoilMoisture}');
|
||||
},
|
||||
),
|
||||
DataDisplayerWidget(
|
||||
|
|
@ -83,7 +83,8 @@ class ListDataFromCensorNpk1 extends StatelessWidget {
|
|||
color: Colors.white,
|
||||
censorIdentifier: censorIdentifier,
|
||||
onTap: () async {
|
||||
context.push('${AppRoute.ph}/${provider.npk1SoilPh}');
|
||||
context.push(
|
||||
'${AppRoute.ph}/${provider.npk1SoilPh}/${provider.npk2SoilPh}');
|
||||
},
|
||||
),
|
||||
DataDisplayerWidget(
|
||||
|
|
@ -95,8 +96,8 @@ class ListDataFromCensorNpk1 extends StatelessWidget {
|
|||
color: Colors.white,
|
||||
censorIdentifier: censorIdentifier,
|
||||
onTap: () async {
|
||||
await context
|
||||
.push('${AppRoute.conductivity}/${provider.npk1SoilEc}');
|
||||
await context.push(
|
||||
'${AppRoute.conductivity}/${provider.npk1SoilEc}/${provider.npk2SoilEc}');
|
||||
},
|
||||
),
|
||||
DataDisplayerWidget(
|
||||
|
|
@ -108,7 +109,7 @@ class ListDataFromCensorNpk1 extends StatelessWidget {
|
|||
color: Colors.white,
|
||||
onTap: () async {
|
||||
await context.push(
|
||||
'${AppRoute.nitrogen}/${provider.npk1SoilNitrogen}');
|
||||
'${AppRoute.nitrogen}/${provider.npk2SoilNitrogen}/${provider.npk1SoilNitrogen}');
|
||||
},
|
||||
),
|
||||
DataDisplayerWidget(
|
||||
|
|
@ -120,7 +121,7 @@ class ListDataFromCensorNpk1 extends StatelessWidget {
|
|||
color: Colors.white,
|
||||
onTap: () async {
|
||||
await context.push(
|
||||
'${AppRoute.potassium}/${provider.npk1SoilPotassium}');
|
||||
'${AppRoute.potassium}/${provider.npk1SoilPotassium}/${provider.npk2SoilPotassium}');
|
||||
},
|
||||
),
|
||||
DataDisplayerWidget(
|
||||
|
|
@ -132,7 +133,7 @@ class ListDataFromCensorNpk1 extends StatelessWidget {
|
|||
color: Colors.white,
|
||||
onTap: () async {
|
||||
await context.push(
|
||||
'${AppRoute.phosphorus}/${provider.npk1SoilPhosphorus}');
|
||||
'${AppRoute.phosphorus}/${provider.npk1SoilPhosphorus}/${provider.npk2SoilPhosphorus}');
|
||||
},
|
||||
),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ class ListDataFromCensorNpk2 extends StatelessWidget {
|
|||
censorIdentifier: censorIdentifier,
|
||||
onTap: () async {
|
||||
await context.push(
|
||||
'${AppRoute.soilTemperature}/${provider.npk2Temperature}');
|
||||
'${AppRoute.soilTemperature}/${provider.npk1Temperature}/${provider.npk2Temperature}');
|
||||
},
|
||||
),
|
||||
DataDisplayerWidget(
|
||||
|
|
@ -71,7 +71,7 @@ class ListDataFromCensorNpk2 extends StatelessWidget {
|
|||
censorIdentifier: censorIdentifier,
|
||||
onTap: () async {
|
||||
await context.push(
|
||||
'${AppRoute.soilMoisture}/${provider.npk2SoilMoisture}');
|
||||
'${AppRoute.soilMoisture}/${provider.npk1SoilMoisture}/${provider.npk2SoilMoisture}');
|
||||
},
|
||||
),
|
||||
DataDisplayerWidget(
|
||||
|
|
@ -83,7 +83,8 @@ class ListDataFromCensorNpk2 extends StatelessWidget {
|
|||
color: Colors.white,
|
||||
censorIdentifier: censorIdentifier,
|
||||
onTap: () async {
|
||||
context.push('${AppRoute.ph}/${provider.npk2SoilPh}');
|
||||
context.push(
|
||||
'${AppRoute.ph}/${provider.npk1SoilPh}/${provider.npk2SoilPh}');
|
||||
},
|
||||
),
|
||||
DataDisplayerWidget(
|
||||
|
|
@ -95,8 +96,8 @@ class ListDataFromCensorNpk2 extends StatelessWidget {
|
|||
color: Colors.white,
|
||||
censorIdentifier: censorIdentifier,
|
||||
onTap: () async {
|
||||
await context
|
||||
.push('${AppRoute.conductivity}/${provider.npk2SoilEc}');
|
||||
await context.push(
|
||||
'${AppRoute.conductivity}/${provider.npk1SoilEc}/${provider.npk2SoilEc}');
|
||||
},
|
||||
),
|
||||
DataDisplayerWidget(
|
||||
|
|
@ -108,7 +109,7 @@ class ListDataFromCensorNpk2 extends StatelessWidget {
|
|||
color: Colors.white,
|
||||
onTap: () async {
|
||||
await context.push(
|
||||
'${AppRoute.nitrogen}/${provider.npk2SoilNitrogen}');
|
||||
'${AppRoute.nitrogen}/${provider.npk2SoilNitrogen}/${provider.npk1SoilNitrogen}');
|
||||
},
|
||||
),
|
||||
DataDisplayerWidget(
|
||||
|
|
@ -120,7 +121,7 @@ class ListDataFromCensorNpk2 extends StatelessWidget {
|
|||
color: Colors.white,
|
||||
onTap: () async {
|
||||
await context.push(
|
||||
'${AppRoute.potassium}/${provider.npk2SoilPotassium}');
|
||||
'${AppRoute.potassium}/${provider.npk1SoilPotassium}/${provider.npk2SoilPotassium}');
|
||||
},
|
||||
),
|
||||
DataDisplayerWidget(
|
||||
|
|
@ -132,7 +133,7 @@ class ListDataFromCensorNpk2 extends StatelessWidget {
|
|||
color: Colors.white,
|
||||
onTap: () async {
|
||||
await context.push(
|
||||
'${AppRoute.phosphorus}/${provider.npk2SoilPhosphorus}');
|
||||
'${AppRoute.phosphorus}/${provider.npk1SoilPhosphorus}/${provider.npk2SoilPhosphorus}');
|
||||
},
|
||||
),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class SettingProvider extends ChangeNotifier {
|
||||
SettingProvider() {
|
||||
_init();
|
||||
}
|
||||
|
||||
String userFullName = '';
|
||||
String userEmail = '';
|
||||
|
||||
void _init() async {
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
userFullName = prefs.getString('fullName') ?? 'unknown';
|
||||
userEmail = prefs.getString('email') ?? 'unknown';
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,12 @@
|
|||
import 'package:agrilink_vocpro/core/constant/app_color.dart';
|
||||
import 'package:agrilink_vocpro/core/constant/app_theme.dart';
|
||||
import 'package:agrilink_vocpro/core/route/app_route.dart';
|
||||
import 'package:agrilink_vocpro/features/setting/provider/setting_provider.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 SettingScreen extends StatelessWidget {
|
||||
const SettingScreen({super.key});
|
||||
|
|
@ -30,13 +32,15 @@ class SettingScreen extends StatelessWidget {
|
|||
child: Icon(BootstrapIcons.person_fill, color: Colors.white),
|
||||
),
|
||||
SizedBox(width: 8.w),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('User Name', style: AppTheme.labelMedium),
|
||||
Text('useremail@gmail.com', style: AppTheme.labelSmall)
|
||||
],
|
||||
)
|
||||
Consumer<SettingProvider>(builder: (context, provider, child) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(provider.userFullName, style: AppTheme.labelMedium),
|
||||
Text(provider.userEmail, style: AppTheme.labelSmall)
|
||||
],
|
||||
);
|
||||
})
|
||||
],
|
||||
),
|
||||
SizedBox(height: 16.h),
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:agrilink_vocpro/core/route/app_route.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:jwt_decoder/jwt_decoder.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class SplashScreen extends StatefulWidget {
|
||||
const SplashScreen({super.key});
|
||||
|
|
@ -21,33 +24,36 @@ class _SplashScreenState extends State<SplashScreen> {
|
|||
}
|
||||
|
||||
Future<void> _initialize() async {
|
||||
// final authProvider = Provider.of<AuthProvider>(context, listen: false);
|
||||
// bool isLoggedIn = await _checkLoginStatus(authProvider);
|
||||
bool isLoggedIn = await _checkLoginStatus();
|
||||
_navigateAfterSplash(isLoggedIn);
|
||||
}
|
||||
|
||||
// Future<bool> _checkLoginStatus(AuthProvider authProvider) async {
|
||||
// SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
Future<bool> _checkLoginStatus() async {
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
|
||||
// if (prefs.getKeys().isEmpty) return false;
|
||||
if (prefs.getKeys().isEmpty) return false;
|
||||
|
||||
// if (prefs.getBool('isLoggedIn') == true) {
|
||||
// String? token = prefs.getString('token');
|
||||
// String? refreshToken = prefs.getString('refreshToken');
|
||||
if (prefs.getBool('isLoggedIn') == true) {
|
||||
String? token = prefs.getString('jwtToken');
|
||||
|
||||
// if (token != null && !JwtDecoder.isExpired(token)) {
|
||||
// return true;
|
||||
// } else if (refreshToken != null && !JwtDecoder.isExpired(refreshToken)) {
|
||||
// final result = await authProvider.refreshToken();
|
||||
// return result == ResultState.hasData;
|
||||
// } else {
|
||||
// prefs.remove('token');
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
if (token != null && !JwtDecoder.isExpired(token)) {
|
||||
if (kDebugMode) {
|
||||
print('Token : ${prefs.getString('token')}');
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
prefs.remove('token');
|
||||
prefs.remove('jwtToken');
|
||||
prefs.remove('username');
|
||||
prefs.remove('email');
|
||||
prefs.remove('fullName');
|
||||
prefs.remove('isLoggedIn');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// return false;
|
||||
// }
|
||||
return false;
|
||||
}
|
||||
|
||||
void _navigateAfterSplash(bool isLoggedIn) {
|
||||
Timer(const Duration(seconds: 2), () {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import 'package:agrilink_vocpro/features/auth/provider/auth_provider.dart';
|
|||
import 'package:agrilink_vocpro/features/control/provider/control_provider.dart';
|
||||
import 'package:agrilink_vocpro/features/dashboard/provider/dashboard_provider.dart';
|
||||
import 'package:agrilink_vocpro/features/home/provider/home_provider.dart';
|
||||
import 'package:agrilink_vocpro/features/setting/provider/setting_provider.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
|
|
@ -29,6 +30,7 @@ class MyApp extends StatelessWidget {
|
|||
ChangeNotifierProvider(create: (context) => HomeProvider()),
|
||||
ChangeNotifierProvider(create: (context) => DashboardProvider()),
|
||||
ChangeNotifierProvider(create: (context) => ControlProvider()),
|
||||
ChangeNotifierProvider(create: (context) => SettingProvider()),
|
||||
],
|
||||
child: ScreenUtilInit(
|
||||
designSize: const Size(360, 800),
|
||||
|
|
|
|||
|
|
@ -240,6 +240,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.19.0"
|
||||
jwt_decoder:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: jwt_decoder
|
||||
sha256: "54774aebf83f2923b99e6416b4ea915d47af3bde56884eb622de85feabbc559f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ dependencies:
|
|||
gauge_indicator: ^0.4.3
|
||||
mqtt_client: ^10.5.1
|
||||
shimmer: ^3.0.0
|
||||
jwt_decoder: ^2.0.1
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user