Compare commits

...

10 Commits

34 changed files with 486 additions and 178 deletions

3
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"java.configuration.updateBuildConfiguration": "interactive"
}

View File

@ -35,11 +35,11 @@ This project uses several packages to support various functionalities. Below is
## Installation Guide
1. Clone the repository:
```bash
git clone https://github.com/username/smart-farming.git
git clone https://gitlab.com/profile-image/kedaireka/smartfarming/mobile-smartfarming.git
```
2. Navigate to the project directory:
2. Navigate to the flutter project directory:
```bash
cd smart-farming
cd agrilink_vocpro
```
3. Install dependencies:
```bash
@ -48,10 +48,22 @@ This project uses several packages to support various functionalities. Below is
if it does't work try:
```bash
flutter outdated
//follow with
// follow with
flutter pub upgrade --major-versions
```
or check your latest dart version make sure it compatible with this project
project tool version
```
• Flutter version 3.27.0-1.0.pre.685
• Upstream repository https://github.com/flutter/flutter.git
• Framework revision 227f6a04f7, 2024-12-03 13:21:59 +0100
• Engine revision f9d4f744e0
• Dart version 3.7.0 (build 3.7.0-207.0.dev)
• DevTools version 2.41.0
```
4. Run the project:
```bash
flutter run
@ -63,26 +75,48 @@ Berikan deskripsi singkat tentang struktur folder dan file penting di dalam proy
## Project Structure
- `lib/`: Contains the Flutter app source code.
- `core/`: Contains all constant vaalue and data needed for the project.
- `data/`: Conatains model and data response
- `core/`: Contains all constant value and data needed for the project.
- `data/`: Contains model and data response
- `domain/`: logic and service of the application
- `feature/`: Contains every feature in the app
- `provider/`
- `view`
- `widget`
- `provider/`: Contains a function to integrate with domain layer
- `view` : contains the main screen UI code
- `widget` : conatins the widget UI code
- `pubspec.yaml`: Project dependencies and configurations.
## Progress Report
| Date | Type | Description |
|------------|-------|-----------------------------------------------------------------------------------------------------|
| 2024-10-09 | feat | Completed the integration of MQTT for sensor data and IoT device control. |
| 2024-10-02 | fix | Implemented auto-reconnect for MQTT on app open/close. |
| 2024-09-30 | feat | Added UI for displaying soil pH level with a linear bar. |
| 2024-09-27 | feat | Finalized group display for 'Doses' in ExpansionTile for medicine dosage recommendations. |
| 2024-09-24 | feat | Completed sorting dialog functionality with single sorter selection capability. |
| 2024-09-20 | feat | Implemented patient data creation form with validation and state management using dialogs. |
| Date | Type | Description |
|------------|-------|------------------------------------------------------------------------------------------------------|
| 2024-09-09 | feat | Created basic app structure for Agrilink Vocpro. |
| 2024-09-10 | feat | Sliced splash screen and login screen. |
| 2024-09-11 | feat | Sliced home page for Agrilink Vocpro. |
| 2024-09-12 | fix | Revamped home page for Agrilink Vocpro. |
| 2024-09-13 | feat | Created and animated graph or flowchart with `fl_chart`. |
| 2024-09-16 | learn | Learned about implementing graphics UI in Flutter. |
| 2024-09-17 | feat | Created bottom nav bar and revamped data display widgets in Agrilink app. |
| 2024-09-18 | feat | Added detail screen for Potassium, Nitrogen, and Phosphorus. |
| 2024-09-19 | feat | Created custom screen for soil pH data. |
| 2024-09-20 | feat | Handled POST requests with dialog. |
| 2024-09-23 | feat | Added basic app routes for Agrilink app. |
| 2024-09-24 | fix | Revamped home page again in Agrilink app. |
| 2024-09-25 | feat | Created detail screen for temperature and light intensity features. |
| 2024-09-26 | feat | Sliced setting screen. |
| 2024-09-27 | feat | Implemented infinite scroll with `infinite_scroll_pagination` package. |
| 2024-10-01 | meet | Conducted progress report with Pak Vipkas. |
| 2024-10-02 | feat | Connected Flutter with MQTT broker using `mqtt_client` dependency. |
| 2024-10-03 | feat | Created request to publish message to MQTT broker. |
| 2024-10-04 | test | Tested subscribe and publish message to real device. |
| 2024-10-07 | feat | Added error and no data screens in Agrilink Vocpro. |
| 2024-10-08 | fix | Revamped graph UI in Agrilink app; integrated relay control with backend. |
| 2024-10-09 | doc | Created project documentation in `README.md` on GitLab. |
| 2024-10-10 | fix | Revamped detail screen for each DHT data sensor. |
| 2024-10-11 | fix | Revamped detail screen for each NPK data sensor. |
| 2024-10-14 | feat | Integrated REST API for fetching current data. |
| 2024-10-15 | feat | Integrated REST API for fetching historical data and displaying it in a graph. |
| 2024-10-16 | feat | Integrated REST API for controlling relay. |
| 2024-11-06 | feat | Tidy up the code |
## Contributing

View File

@ -11,7 +11,7 @@ plugins {
android {
namespace = "com.pis.agrilink_vocpro"
compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion
ndkVersion = "27.0.12077973"
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
@ -33,11 +33,19 @@ android {
versionName = flutter.versionName
}
signingConfigs {
release {
keyAlias 'agrilink-upload'
keyPassword '100403'
storeFile file('D:/Code/real_project/flutter/mobile-smartfarming/upload-keystore.jks')
storePassword '100403'
}
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig = signingConfigs.debug
signingConfig signingConfigs.release
minifyEnabled true // You can set to true for release builds
shrinkResources true // Set to true if minify is true
}
}
}

View File

@ -3,7 +3,7 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:label="agrilink_vocpro"
android:label="Agrilink Vocpro"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"
android:enableOnBackInvokedCallback="true">

View File

@ -18,11 +18,11 @@ pluginManagement {
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "7.3.0" apply false
id "com.android.application" version "8.7.0" apply false
// START: FlutterFire Configuration
id "com.google.gms.google-services" version "4.3.15" apply false
// END: FlutterFire Configuration
id "org.jetbrains.kotlin.android" version "1.7.10" apply false
id "org.jetbrains.kotlin.android" version "2.0.20" apply false
}
include ":app"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

@ -2,7 +2,7 @@ class AppConstant {
static const String appName = 'Kebun Pintar';
static const String appVersion = '1.0.0';
static const String baseUrl = 'http://54.196.58.97:3333';
static const String baseUrl = 'http://192.168.60.108';
static const String mqttServer = 'armadillo.rmq.cloudamqp.com';
static const String mqttUsername = 'obyskxhx:obyskxhx';

View File

@ -34,3 +34,32 @@ String getGreeting(String time) {
return 'Selamat Malam';
}
}
String timeAgo(String? dateString) {
if (dateString == null) {
return '';
}
DateTime date = DateTime.parse(dateString);
DateTime now = DateTime.now();
Duration difference = now.difference(date);
if (difference.inMinutes < 1) {
return "Baru saja";
} else if (difference.inMinutes < 60) {
return "${difference.inMinutes} menit yang lalu";
} else if (difference.inHours < 24) {
return "${difference.inHours} jam yang lalu";
} else if (difference.inDays < 7) {
return "${difference.inDays} hari yang lalu";
} else if (difference.inDays < 30) {
int weeks = (difference.inDays / 7).floor();
return "$weeks minggu yang lalu";
} else if (difference.inDays < 365) {
int months = (difference.inDays / 30).floor();
return "$months bulan yang lalu";
} else {
int years = (difference.inDays / 365).floor();
return "$years tahun yang lalu";
}
}

View File

@ -1,15 +1,15 @@
class RelayResponse {
bool? success;
List<Data>? data;
List<Relay>? data;
RelayResponse({this.success, this.data});
RelayResponse.fromJson(Map<String, dynamic> json) {
success = json['success'];
if (json['data'] != null) {
data = <Data>[];
data = <Relay>[];
json['data'].forEach((v) {
data!.add(Data.fromJson(v));
data!.add(Relay.fromJson(v));
});
}
}
@ -24,7 +24,7 @@ class RelayResponse {
}
}
class Data {
class Relay {
int? id;
int? number;
String? enabledAt;
@ -32,7 +32,7 @@ class Data {
bool? currentStatus;
String? createdAt;
Data(
Relay(
{this.id,
this.number,
this.enabledAt,
@ -40,7 +40,7 @@ class Data {
this.currentStatus,
this.createdAt});
Data.fromJson(Map<String, dynamic> json) {
Relay.fromJson(Map<String, dynamic> json) {
id = json['id'];
number = json['number'];
enabledAt = json['enabled_at'];

View File

@ -1,4 +1,5 @@
import 'package:agrilink_vocpro/core/state/result_state.dart';
import 'package:agrilink_vocpro/data/model/relay_response.dart';
import 'package:agrilink_vocpro/domain/service/app_service.dart';
import 'package:flutter/foundation.dart';
@ -11,6 +12,18 @@ class ControlProvider extends ChangeNotifier {
bool _control_2 = false;
bool get control_2 => _control_2;
bool _control_3 = false;
bool get control_3 => _control_3;
Relay _relay1 = Relay();
Relay get relay1 => _relay1;
Relay _relay2 = Relay();
Relay get relay2 => _relay2;
Relay _relay3 = Relay();
Relay get relay3 => _relay3;
ControlProvider() {
getRelayStatus();
}
@ -28,10 +41,16 @@ class ControlProvider extends ChangeNotifier {
if (result.success == true) {
for (var element in result.data!) {
if (element.number == 1) {
switchControl1(element.currentStatus ?? false);
_relay1 = element;
switchControl(element.currentStatus ?? false, relayNumber: 1);
}
if (element.number == 2) {
switchControl2(element.currentStatus ?? false);
_relay2 = element;
switchControl(element.currentStatus ?? false, relayNumber: 2);
}
if (element.number == 3) {
_relay3 = element;
switchControl(element.currentStatus ?? false, relayNumber: 3);
}
}
relayState = ResultState.hasData;
@ -53,15 +72,13 @@ class ControlProvider extends ChangeNotifier {
relayState = ResultState.loading;
notifyListeners();
final int stateConverted;
if (state == true) {
stateConverted = 1;
} else {
stateConverted = 0;
}
final int stateConverted = state ? 1 : 0;
try {
final result = await _appService.switchRelay(
relayNumber: relayNumber, state: stateConverted);
relayNumber: relayNumber,
state: stateConverted,
);
if (result.success == true) {
relayState = ResultState.hasData;
notifyListeners();
@ -82,13 +99,20 @@ class ControlProvider extends ChangeNotifier {
}
}
void switchControl1(bool value) {
_control_1 = value;
notifyListeners();
}
void switchControl2(bool value) {
_control_2 = value;
void switchControl(bool value, {required int relayNumber}) {
switch (relayNumber) {
case 1:
_control_1 = value;
break;
case 2:
_control_2 = value;
break;
case 3:
_control_3 = value;
break;
default:
return;
}
notifyListeners();
}
}

View File

@ -2,6 +2,8 @@ import 'package:agrilink_vocpro/core/constant/app_theme.dart';
import 'package:agrilink_vocpro/core/state/result_state.dart';
import 'package:agrilink_vocpro/features/control/provider/control_provider.dart';
import 'package:agrilink_vocpro/features/control/widgets/control_button_widget.dart';
import 'package:agrilink_vocpro/features/control/widgets/pump_status_widget.dart';
import 'package:agrilink_vocpro/features/control/widgets/relay_status_history_widget.dart';
import 'package:bootstrap_icons/bootstrap_icons.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
@ -34,7 +36,7 @@ class ControlScreen extends StatelessWidget {
return const StatusBarWidget(
text: 'Memuat...',
icon: BootstrapIcons.cloud,
color: Colors.cyan,
color: Colors.grey,
isLoading: true,
);
case ResultState.hasData:
@ -59,6 +61,12 @@ class ControlScreen extends StatelessWidget {
);
}
}),
PumpStatusWidget(
title: 'Pompa Air',
subtitle: 'Relay 3',
isActive: provider.control_3,
onTap: () {},
),
SizedBox(height: 16.h),
GridView(
padding: EdgeInsets.all(16.r),
@ -76,12 +84,11 @@ class ControlScreen extends StatelessWidget {
subtitle: 'Relay 1',
isActive: provider.control_1,
onTap: () async {
final result = provider.control_1 != true
? await provider.switchRelay(1, true)
: await provider.switchRelay(1, false);
result == true
? provider.switchControl1(!provider.control_1)
: provider.switchControl1(provider.control_1);
await _changeRelayStateAction(
provider: provider,
relayOn: 1,
relayOff: 2,
);
},
),
ControlButtonWidget(
@ -89,22 +96,102 @@ class ControlScreen extends StatelessWidget {
subtitle: 'Relay 2',
isActive: provider.control_2,
onTap: () async {
final result = provider.control_2 != true
? await provider.switchRelay(2, true)
: await provider.switchRelay(2, false);
result == true
? provider.switchControl2(!provider.control_2)
: provider.switchControl2(provider.control_2);
await _changeRelayStateAction(
provider: provider,
relayOn: 2,
relayOff: 1,
);
},
),
],
),
SizedBox(height: 16.h),
Padding(
padding: EdgeInsets.fromLTRB(16.r, 0, 16.r, 16.h),
child: Row(
children: [
Icon(
BootstrapIcons.clock,
size: 20.r,
),
SizedBox(width: 8.w),
Text(
'Terakhir Diperbarui',
style: AppTheme.labelMedium,
),
],
),
),
Consumer<ControlProvider>(builder: (context, provider, child) {
return Column(
children: [
RelayStatusHistoryWidget(
name: 'Katup Nutrisi',
imageUrl: 'assets/images/valve.png',
enabledAt: provider.relay1.enabledAt,
disabledAt: provider.relay1.disabledAt,
),
RelayStatusHistoryWidget(
name: 'Katup Air',
imageUrl: 'assets/images/valve.png',
enabledAt: provider.relay2.enabledAt,
disabledAt: provider.relay2.disabledAt,
),
RelayStatusHistoryWidget(
name: 'Pompa Air',
imageUrl: 'assets/images/water_pump.png',
enabledAt: provider.relay3.enabledAt,
disabledAt: provider.relay3.disabledAt,
),
],
);
}),
],
),
),
),
);
}
Future<void> _changeRelayStateAction({
required ControlProvider provider,
required int relayOn,
required int relayOff,
}) async {
if (relayOn == 1) {
// Aktifkan relay 1 dan relay 3, matikan relay 2
if (provider.control_1 == false) {
await provider.switchRelay(relayOff, false); // Matikan relay 2
await provider.switchRelay(3, true); // Aktifkan relay 3
await provider.switchRelay(relayOn, true); // Aktifkan relay 1
provider.switchControl(true, relayNumber: 1);
provider.switchControl(true, relayNumber: 3);
provider.switchControl(false, relayNumber: 2);
} else {
// Matikan semua relay jika relay 1 dimatikan
await provider.switchRelay(3, false);
await provider.switchRelay(relayOn, false);
provider.switchControl(false, relayNumber: 1);
provider.switchControl(false, relayNumber: 3);
}
} else if (relayOn == 2) {
// Aktifkan relay 2 dan relay 3, matikan relay 1
if (provider.control_2 == false) {
await provider.switchRelay(relayOff, false); // Matikan relay 1
await provider.switchRelay(3, true); // Aktifkan relay 3
await provider.switchRelay(relayOn, true); // Aktifkan relay 2
provider.switchControl(true, relayNumber: 2);
provider.switchControl(true, relayNumber: 3);
provider.switchControl(false, relayNumber: 1);
} else {
// Matikan semua relay jika relay 2 dimatikan
await provider.switchRelay(3, false);
await provider.switchRelay(relayOn, false);
provider.switchControl(false, relayNumber: 2);
provider.switchControl(false, relayNumber: 3);
}
}
}
}
class StatusBarWidget extends StatelessWidget {

View File

@ -32,8 +32,8 @@ class ControlButtonWidget extends StatelessWidget {
boxShadow: [
BoxShadow(
color: isActive
? AppColor.secondary.withOpacity(0.2)
: Colors.grey.withOpacity(0.2),
? AppColor.secondary.withValues(alpha: 0.2)
: Colors.grey.withValues(alpha: 0.2),
spreadRadius: 1.r,
blurRadius: 16.r,
offset: Offset(0, 12.r),

View File

@ -0,0 +1,75 @@
import 'package:agrilink_vocpro/core/constant/app_color.dart';
import 'package:agrilink_vocpro/core/constant/app_theme.dart';
import 'package:agrilink_vocpro/core/state/result_state.dart';
import 'package:agrilink_vocpro/features/control/provider/control_provider.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:provider/provider.dart';
class PumpStatusWidget extends StatelessWidget {
const PumpStatusWidget({
super.key,
required this.title,
required this.subtitle,
required this.isActive,
required this.onTap,
});
final String title;
final String subtitle;
final bool isActive;
final Function() onTap;
@override
Widget build(BuildContext context) {
return Container(
height: 100.h,
padding: EdgeInsets.all(16.r),
margin: EdgeInsets.symmetric(horizontal: 16.r),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16.r),
boxShadow: [
BoxShadow(
color: isActive
? AppColor.secondary.withValues(alpha: 0.2)
: Colors.grey.withValues(alpha: 0.2),
spreadRadius: 1.r,
blurRadius: 16.r,
offset: Offset(0, 12.r),
),
],
),
child: Row(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title, style: AppTheme.labelMedium),
Text(subtitle, style: AppTheme.labelSmall),
],
),
Spacer(),
Consumer<ControlProvider>(
builder: (context, provider, child) {
switch (provider.relayState) {
case ResultState.loading:
return Image.asset(
'assets/images/water_pump.png',
width: 80.r,
color: AppColor.textDisable,
);
default:
return Image.asset(
'assets/images/water_pump.png',
width: 80.r,
color: isActive ? AppColor.secondary : AppColor.textDisable,
);
}
},
)
],
),
);
}
}

View File

@ -0,0 +1,68 @@
import 'package:agrilink_vocpro/core/constant/app_theme.dart';
import 'package:agrilink_vocpro/core/extension/extention.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import '../../../core/constant/app_color.dart';
class RelayStatusHistoryWidget extends StatelessWidget {
const RelayStatusHistoryWidget({
super.key,
required this.disabledAt,
required this.enabledAt,
required this.name,
required this.imageUrl,
});
final String? disabledAt;
final String? enabledAt;
final String name;
final String imageUrl;
@override
Widget build(BuildContext context) {
return ListTile(
leading: CircleAvatar(
radius: 26.r,
backgroundColor: AppColor.ternary,
child: Image.asset(
imageUrl,
width: 24.r,
color: Colors.white,
),
),
title: Text(
name,
style: AppTheme.labelMedium,
),
subtitle: Column(
children: [
Row(
children: [
CircleAvatar(
backgroundColor: Colors.greenAccent,
radius: 4.r,
),
Text(
' Aktif ${timeAgo(enabledAt)}',
style: AppTheme.labelSmall,
),
],
),
Row(
children: [
CircleAvatar(
backgroundColor: Colors.grey.shade300,
radius: 4.r,
),
Text(
' Nonaktif ${timeAgo(disabledAt)}',
style: AppTheme.labelSmall,
),
],
)
],
),
);
}
}

View File

@ -144,14 +144,12 @@ class ConductivityScreen extends StatelessWidget {
? provider.dataFetchedNpk1[index].soilconductivity ?? 0
: provider.dataFetchedNpk2[index].soilconductivity ?? 0,
),
maxValue: 1,
);
case ResultState.error:
return const ErrorDataStateWidget();
case ResultState.noData:
return const NoDataStateWidget();
case ResultState.initial:
default:
return const SizedBox.shrink();
}
},

View File

@ -152,10 +152,6 @@ class HumidityScreen extends StatelessWidget {
return const NoDataStateWidget();
case ResultState.initial:
return const SizedBox.shrink();
default:
return const Center(
child: Text('Default Error'),
);
}
}),
),

View File

@ -137,6 +137,7 @@ class LightScreen extends StatelessWidget {
provider.dataFetched[index].vicihumidity
?.toDouble() ??
0),
maxValue: 500,
);
case ResultState.error:
return Center(
@ -174,10 +175,6 @@ class LightScreen extends StatelessWidget {
);
case ResultState.initial:
return const SizedBox.shrink();
default:
return const Center(
child: Text('Default Error'),
);
}
}),
),

View File

@ -84,7 +84,7 @@ class NitrogenScreen extends StatelessWidget {
size: 64.r,
color: Colors.blue,
),
Text('$value ppm', style: AppTheme.headline1),
Text('$value mg/L', style: AppTheme.headline1),
],
),
);
@ -144,14 +144,13 @@ class NitrogenScreen extends StatelessWidget {
? provider.dataFetchedNpk1[index].soilnitrogen ?? 0
: provider.dataFetchedNpk2[index].soilnitrogen ?? 0,
),
maxValue: 1,
maxValue: 10,
);
case ResultState.error:
return const ErrorDataStateWidget();
case ResultState.noData:
return const NoDataStateWidget();
case ResultState.initial:
default:
return const SizedBox.shrink();
}
},

View File

@ -142,7 +142,6 @@ class PhScreen extends StatelessWidget {
case ResultState.noData:
return const NoDataStateWidget();
case ResultState.initial:
default:
return const SizedBox.shrink();
}
},

View File

@ -84,7 +84,7 @@ class PhosphorusScreen extends StatelessWidget {
size: 64.r,
color: Colors.blue,
),
Text('$value ppm', style: AppTheme.headline1),
Text('$value mg/L', style: AppTheme.headline1),
],
),
);
@ -144,14 +144,13 @@ class PhosphorusScreen extends StatelessWidget {
? provider.dataFetchedNpk1[index].soilphosphorus ?? 0
: provider.dataFetchedNpk2[index].soilphosphorus ?? 0,
),
maxValue: 10,
maxValue: 100,
);
case ResultState.error:
return const ErrorDataStateWidget();
case ResultState.noData:
return const NoDataStateWidget();
case ResultState.initial:
default:
return const SizedBox.shrink();
}
},

View File

@ -84,7 +84,7 @@ class PotassiumScreen extends StatelessWidget {
size: 64.r,
color: Colors.red,
),
Text('$value ppm', style: AppTheme.headline1),
Text('$value mg/L', style: AppTheme.headline1),
],
),
);
@ -144,14 +144,13 @@ class PotassiumScreen extends StatelessWidget {
? provider.dataFetchedNpk1[index].soilpotassium ?? 0
: provider.dataFetchedNpk2[index].soilpotassium ?? 0,
),
maxValue: 1,
maxValue: 100,
);
case ResultState.error:
return const ErrorDataStateWidget();
case ResultState.noData:
return const NoDataStateWidget();
case ResultState.initial:
default:
return const SizedBox.shrink();
}
},

View File

@ -173,14 +173,12 @@ class SoilMoistureScreen extends StatelessWidget {
? provider.dataFetchedNpk1[index].soilhumidity ?? 0
: provider.dataFetchedNpk2[index].soilhumidity ?? 0,
),
maxValue: 1,
);
case ResultState.error:
return const ErrorDataStateWidget();
case ResultState.noData:
return const NoDataStateWidget();
case ResultState.initial:
default:
return const SizedBox.shrink();
}
},

View File

@ -149,7 +149,7 @@ class SoilTemperatureScreen extends StatelessWidget {
height: 100.h,
width: 100.w,
decoration: BoxDecoration(
color: color.withOpacity(0.1),
color: color.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: color, width: 2),
),
@ -230,7 +230,6 @@ class SoilTemperatureScreen extends StatelessWidget {
case ResultState.noData:
return const NoDataStateWidget();
case ResultState.initial:
default:
return const SizedBox.shrink();
}
},

View File

@ -118,7 +118,7 @@ class TemperatureScreen extends StatelessWidget {
width: 100.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
color: Colors.blue.withOpacity(0.1),
color: Colors.blue.withValues(alpha: 0.1),
border: Border.all(
color: Colors.blue,
width: 2,
@ -143,7 +143,7 @@ class TemperatureScreen extends StatelessWidget {
height: 100.h,
width: 100.w,
decoration: BoxDecoration(
color: Colors.green.withOpacity(0.1),
color: Colors.green.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: Colors.green,
@ -169,7 +169,7 @@ class TemperatureScreen extends StatelessWidget {
height: 100.h,
width: 100.w,
decoration: BoxDecoration(
color: Colors.orange.withOpacity(0.1),
color: Colors.orange.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: Colors.orange.shade800,
@ -228,6 +228,7 @@ class TemperatureScreen extends StatelessWidget {
provider.dataFetched[index].vicitemperature
?.toDouble() ??
0),
maxValue: 60,
);
case ResultState.error:
return ErrorDataStateWidget();
@ -235,10 +236,6 @@ class TemperatureScreen extends StatelessWidget {
return NoDataStateWidget();
case ResultState.initial:
return const SizedBox.shrink();
default:
return const Center(
child: Text('Default Error'),
);
}
},
),

View File

@ -126,6 +126,7 @@ class _HomeScreenState extends State<HomeScreen> {
),
),
body: DefaultTabController(
initialIndex: 1,
length: 3,
child: Stack(
children: [

View File

@ -41,7 +41,7 @@ class DataDisplayerWidget extends StatelessWidget {
borderRadius: BorderRadius.circular(16.r), // Bentuk sudut yang bundar
),
elevation: 20, // Efek bayangan
shadowColor: Colors.grey.withOpacity(0.2),
shadowColor: Colors.grey.withValues(alpha: 0.2),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -52,7 +52,7 @@ class DataDisplayerWidget extends StatelessWidget {
child: Text(
censorIdentifier ?? '',
style: AppTheme.labelSmall
.copyWith(color: textColor.withOpacity(0.5)),
.copyWith(color: textColor.withValues(alpha: 0.5)),
),
),
Icon(icon, color: iconColor, size: 32.r),
@ -67,7 +67,7 @@ class DataDisplayerWidget extends StatelessWidget {
Text(
subtitle,
style: AppTheme.labelSmall
.copyWith(color: textColor.withOpacity(0.5)),
.copyWith(color: textColor.withValues(alpha: 0.5)),
),
],
),

View File

@ -111,7 +111,7 @@ class GarphicWidget extends StatelessWidget {
show: true,
gradient: LinearGradient(
colors: gradientColors
.map((color) => color.withOpacity(0.3))
.map((color) => color.withValues(alpha: 0.3))
.toList()),
),
),

View File

@ -102,8 +102,6 @@ class ListDataFromCensorDht extends StatelessWidget {
));
case ResultState.initial:
return const SizedBox.shrink();
default:
return const SizedBox.shrink();
}
});
}

View File

@ -58,7 +58,7 @@ class ListDataFromCensorNpk1 extends StatelessWidget {
censorIdentifier: censorIdentifier,
onTap: () async {
await context.push(
'${AppRoute.nitrogen}/${provider.npk2SoilNitrogen}/${provider.npk1SoilNitrogen}');
'${AppRoute.nitrogen}/${provider.npk1SoilNitrogen}/${provider.npk2SoilNitrogen}');
},
),
DataDisplayerWidget(

View File

@ -58,7 +58,7 @@ class ListDataFromCensorNpk2 extends StatelessWidget {
iconColor: Colors.white,
onTap: () async {
await context.push(
'${AppRoute.nitrogen}/${provider.npk2SoilNitrogen}/${provider.npk1SoilNitrogen}');
'${AppRoute.nitrogen}/${provider.npk1SoilNitrogen}/${provider.npk2SoilNitrogen}');
},
),
DataDisplayerWidget(

View File

@ -58,7 +58,7 @@ class _SplashScreenState extends State<SplashScreen> {
}
void _navigateAfterSplash(bool isLoggedIn) {
Timer(const Duration(seconds: 2), () {
Timer(const Duration(seconds: 1), () {
if (isLoggedIn == true) {
context.go(AppRoute.dashboard);
} else {

View File

@ -5,10 +5,10 @@ packages:
dependency: transitive
description:
name: _flutterfire_internals
sha256: "5534e701a2c505fed1f0799e652dd6ae23bd4d2c4cf797220e5ced5764a7c1c2"
sha256: eae3133cbb06de9205899b822e3897fc6a8bc278ad4c944b4ce612689369694b
url: "https://pub.dev"
source: hosted
version: "1.3.44"
version: "1.3.47"
animated_segmented_tab_control:
dependency: "direct main"
description:
@ -29,18 +29,18 @@ packages:
dependency: transitive
description:
name: async
sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63
url: "https://pub.dev"
source: hosted
version: "2.11.0"
version: "2.12.0"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
version: "2.1.2"
bootstrap_icons:
dependency: "direct main"
description:
@ -61,42 +61,42 @@ packages:
dependency: transitive
description:
name: charcode
sha256: fb98c0f6d12c920a02ee2d998da788bca066ca5f148492b7085ee23372b12306
sha256: fb0f1107cac15a5ea6ef0a6ef71a807b9e4267c713bb93e00e92d737cc8dbd8a
url: "https://pub.dev"
source: hosted
version: "1.3.1"
version: "1.4.0"
clock:
dependency: transitive
description:
name: clock
sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
url: "https://pub.dev"
source: hosted
version: "1.1.1"
version: "1.1.2"
collection:
dependency: transitive
description:
name: collection
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf
url: "https://pub.dev"
source: hosted
version: "1.18.0"
version: "1.19.0"
crypto:
dependency: transitive
description:
name: crypto
sha256: ec30d999af904f33454ba22ed9a86162b35e52b44ac4807d1d93c288041d7d27
sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855"
url: "https://pub.dev"
source: hosted
version: "3.0.5"
version: "3.0.6"
csslib:
dependency: transitive
description:
name: csslib
sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb"
sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e"
url: "https://pub.dev"
source: hosted
version: "1.0.0"
version: "1.0.2"
cupertino_icons:
dependency: "direct main"
description:
@ -125,10 +125,10 @@ packages:
dependency: transitive
description:
name: equatable
sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2
sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7"
url: "https://pub.dev"
source: hosted
version: "2.0.5"
version: "2.0.7"
event_bus:
dependency: transitive
description:
@ -141,10 +141,10 @@ packages:
dependency: transitive
description:
name: fake_async
sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc"
url: "https://pub.dev"
source: hosted
version: "1.3.1"
version: "1.3.2"
ffi:
dependency: transitive
description:
@ -165,58 +165,58 @@ packages:
dependency: "direct main"
description:
name: firebase_core
sha256: "51dfe2fbf3a984787a2e7b8592f2f05c986bfedd6fdacea3f9e0a7beb334de96"
sha256: fef81a53ba1ca618def1f8bef4361df07968434e62cb204c1fb90bb880a03da2
url: "https://pub.dev"
source: hosted
version: "3.6.0"
version: "3.8.1"
firebase_core_platform_interface:
dependency: transitive
description:
name: firebase_core_platform_interface
sha256: e30da58198a6d4b49d5bce4e852f985c32cb10db329ebef9473db2b9f09ce810
sha256: b94b217e3ad745e784960603d33d99471621ecca151c99c670869b76e50ad2a6
url: "https://pub.dev"
source: hosted
version: "5.3.0"
version: "5.3.1"
firebase_core_web:
dependency: transitive
description:
name: firebase_core_web
sha256: f967a7138f5d2ffb1ce15950e2a382924239eaa521150a8f144af34e68b3b3e5
sha256: "9e69806bb3d905aeec3c1242e0e1475de6ea6d48f456af29d598fb229a2b4e5e"
url: "https://pub.dev"
source: hosted
version: "2.18.1"
version: "2.18.2"
firebase_messaging:
dependency: "direct main"
description:
name: firebase_messaging
sha256: eb6e28a3a35deda61fe8634967c84215efc19133ba58d8e0fc6c9a2af2cba05e
sha256: "151a3ee68736abf293aab66d1317ade53c88abe1db09c75a0460aebf7767bbdf"
url: "https://pub.dev"
source: hosted
version: "15.1.3"
version: "15.1.6"
firebase_messaging_platform_interface:
dependency: transitive
description:
name: firebase_messaging_platform_interface
sha256: b316c4ee10d93d32c033644207afc282d9b2b4372f3cf9c6022f3558b3873d2d
sha256: f331ee51e40c243f90cc7bc059222dfec4e5df53125b08d31fb28961b00d2a9d
url: "https://pub.dev"
source: hosted
version: "4.5.46"
version: "4.5.49"
firebase_messaging_web:
dependency: transitive
description:
name: firebase_messaging_web
sha256: d7f0147a1a9fe4313168e20154a01fd5cf332898de1527d3930ff77b8c7f5387
sha256: efaf3fdc54cd77e0eedb8e75f7f01c808828c64d052ddbf94d3009974e47d30f
url: "https://pub.dev"
source: hosted
version: "3.9.2"
version: "3.9.5"
fl_chart:
dependency: "direct main"
description:
name: fl_chart
sha256: "94307bef3a324a0d329d3ab77b2f0c6e5ed739185ffc029ed28c0f9b019ea7ef"
sha256: "74959b99b92b9eebeed1a4049426fd67c4abc3c5a0f4d12e2877097d6a11ae08"
url: "https://pub.dev"
source: hosted
version: "0.69.0"
version: "0.69.2"
flutter:
dependency: "direct main"
description: flutter
@ -260,10 +260,10 @@ packages:
dependency: "direct main"
description:
name: go_router
sha256: "6f1b756f6e863259a99135ff3c95026c3cdca17d10ebef2bba2261a25ddc8bbc"
sha256: "2fd11229f59e23e967b0775df8d5948a519cd7e1e8b6e849729e010587b46539"
url: "https://pub.dev"
source: hosted
version: "14.3.0"
version: "14.6.2"
google_fonts:
dependency: "direct main"
description:
@ -276,10 +276,10 @@ packages:
dependency: transitive
description:
name: google_identity_services_web
sha256: "5be191523702ba8d7a01ca97c17fca096822ccf246b0a9f11923a6ded06199b6"
sha256: "55580f436822d64c8ff9a77e37d61f5fb1e6c7ec9d632a43ee324e2a05c3c6c9"
url: "https://pub.dev"
source: hosted
version: "0.3.1+4"
version: "0.3.3"
googleapis_auth:
dependency: "direct main"
description:
@ -292,10 +292,10 @@ packages:
dependency: transitive
description:
name: html
sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a"
sha256: "1fc58edeaec4307368c60d59b7e15b9d658b57d7f3125098b6294153c75337ec"
url: "https://pub.dev"
source: hosted
version: "0.15.4"
version: "0.15.5"
http:
dependency: "direct main"
description:
@ -308,10 +308,10 @@ packages:
dependency: transitive
description:
name: http_parser
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
sha256: "76d306a1c3afb33fe82e2bbacad62a61f409b5634c915fceb0d799de1a913360"
url: "https://pub.dev"
source: hosted
version: "4.0.2"
version: "4.1.1"
intl:
dependency: "direct main"
description:
@ -332,18 +332,18 @@ packages:
dependency: transitive
description:
name: leak_tracker
sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05"
sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec
url: "https://pub.dev"
source: hosted
version: "10.0.5"
version: "10.0.8"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806"
sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573
url: "https://pub.dev"
source: hosted
version: "3.0.5"
version: "3.0.9"
leak_tracker_testing:
dependency: transitive
description:
@ -356,18 +356,18 @@ packages:
dependency: transitive
description:
name: lints
sha256: "3315600f3fb3b135be672bf4a178c55f274bebe368325ae18462c89ac1e3b413"
sha256: "4a16b3f03741e1252fda5de3ce712666d010ba2122f8e912c94f9f7b90e1a4c3"
url: "https://pub.dev"
source: hosted
version: "5.0.0"
version: "5.1.0"
logging:
dependency: transitive
description:
name: logging
sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340"
sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
url: "https://pub.dev"
source: hosted
version: "1.2.0"
version: "1.3.0"
matcher:
dependency: transitive
description:
@ -412,34 +412,34 @@ packages:
dependency: transitive
description:
name: path
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
url: "https://pub.dev"
source: hosted
version: "1.9.0"
version: "1.9.1"
path_provider:
dependency: transitive
description:
name: path_provider
sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
url: "https://pub.dev"
source: hosted
version: "2.1.4"
version: "2.1.5"
path_provider_android:
dependency: transitive
description:
name: path_provider_android
sha256: c464428172cb986b758c6d1724c603097febb8fb855aa265aeecc9280c294d4a
sha256: "4adf4fd5423ec60a29506c76581bc05854c55e3a0b72d35bb28d661c9686edf2"
url: "https://pub.dev"
source: hosted
version: "2.2.12"
version: "2.2.15"
path_provider_foundation:
dependency: transitive
description:
name: path_provider_foundation
sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16
sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942"
url: "https://pub.dev"
source: hosted
version: "2.4.0"
version: "2.4.1"
path_provider_linux:
dependency: transitive
description:
@ -468,10 +468,10 @@ packages:
dependency: transitive
description:
name: platform
sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65"
sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
url: "https://pub.dev"
source: hosted
version: "3.1.5"
version: "3.1.6"
plugin_platform_interface:
dependency: transitive
description:
@ -492,18 +492,18 @@ packages:
dependency: "direct main"
description:
name: shared_preferences
sha256: "746e5369a43170c25816cc472ee016d3a66bc13fcf430c0bc41ad7b4b2922051"
sha256: "95f9997ca1fb9799d494d0cb2a780fd7be075818d59f00c43832ed112b158a82"
url: "https://pub.dev"
source: hosted
version: "2.3.2"
version: "2.3.3"
shared_preferences_android:
dependency: transitive
description:
name: shared_preferences_android
sha256: "3b9febd815c9ca29c9e3520d50ec32f49157711e143b7a4ca039eb87e8ade5ab"
sha256: "7f172d1b06de5da47b6264c2692ee2ead20bbbc246690427cdb4fc301cd0c549"
url: "https://pub.dev"
source: hosted
version: "2.3.3"
version: "2.3.4"
shared_preferences_foundation:
dependency: transitive
description:
@ -556,7 +556,7 @@ packages:
dependency: transitive
description: flutter
source: sdk
version: "0.0.99"
version: "0.0.0"
source_span:
dependency: transitive
description:
@ -569,10 +569,10 @@ packages:
dependency: transitive
description:
name: stack_trace
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377"
url: "https://pub.dev"
source: hosted
version: "1.11.1"
version: "1.12.0"
stream_channel:
dependency: transitive
description:
@ -585,10 +585,10 @@ packages:
dependency: transitive
description:
name: string_scanner
sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
sha256: "0bd04f5bb74fcd6ff0606a888a30e917af9bd52820b178eaa464beb11dca84b6"
url: "https://pub.dev"
source: hosted
version: "1.2.0"
version: "1.4.0"
term_glyph:
dependency: transitive
description:
@ -601,18 +601,18 @@ packages:
dependency: transitive
description:
name: test_api
sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb"
sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c"
url: "https://pub.dev"
source: hosted
version: "0.7.2"
version: "0.7.3"
typed_data:
dependency: transitive
description:
name: typed_data
sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
url: "https://pub.dev"
source: hosted
version: "1.3.2"
version: "1.4.0"
universal_html:
dependency: transitive
description:
@ -641,10 +641,10 @@ packages:
dependency: transitive
description:
name: vm_service
sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d"
sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14"
url: "https://pub.dev"
source: hosted
version: "14.2.5"
version: "14.3.1"
web:
dependency: transitive
description:
@ -662,5 +662,5 @@ packages:
source: hosted
version: "1.1.0"
sdks:
dart: ">=3.5.1 <4.0.0"
dart: ">=3.6.0-0 <4.0.0"
flutter: ">=3.24.0"

BIN
upload-keystore.jks Normal file

Binary file not shown.