feat: add every censor detail screen
|
|
@ -2,7 +2,8 @@
|
|||
<application
|
||||
android:label="agrilink_vocpro"
|
||||
android:name="${applicationName}"
|
||||
android:icon="@mipmap/ic_launcher">
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:enableOnBackInvokedCallback="true">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 9.3 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 13 KiB |
|
|
@ -1,5 +1,7 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 362 KiB After Width: | Height: | Size: 38 KiB |
|
|
@ -1,47 +1,48 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
|
||||
class AppTheme {
|
||||
static TextStyle headline1 = const TextStyle(
|
||||
fontSize: 32,
|
||||
static TextStyle headline1 = TextStyle(
|
||||
fontSize: 32.sp,
|
||||
fontWeight: FontWeight.w500,
|
||||
letterSpacing: 1,
|
||||
color: Colors.black,
|
||||
);
|
||||
|
||||
static TextStyle titleLarge = const TextStyle(
|
||||
fontSize: 17,
|
||||
static TextStyle titleLarge = TextStyle(
|
||||
fontSize: 17.sp,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.black,
|
||||
);
|
||||
static TextStyle titleMedium = TextStyle(
|
||||
fontSize: 16,
|
||||
fontSize: 16.sp,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Colors.grey.shade600,
|
||||
);
|
||||
static TextStyle titleSmall = const TextStyle(
|
||||
fontSize: 12,
|
||||
static TextStyle titleSmall = TextStyle(
|
||||
fontSize: 12.sp,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.black,
|
||||
);
|
||||
|
||||
static TextStyle labelLarge = const TextStyle(
|
||||
fontSize: 16,
|
||||
static TextStyle labelLarge = TextStyle(
|
||||
fontSize: 16.sp,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.black,
|
||||
);
|
||||
static TextStyle labelMedium = const TextStyle(
|
||||
fontSize: 14,
|
||||
static TextStyle labelMedium = TextStyle(
|
||||
fontSize: 14.sp,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.black,
|
||||
);
|
||||
static TextStyle labelSmall = const TextStyle(
|
||||
fontSize: 12,
|
||||
static TextStyle labelSmall = TextStyle(
|
||||
fontSize: 12.sp,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Colors.grey,
|
||||
);
|
||||
|
||||
static TextStyle hintStyle = const TextStyle(
|
||||
fontSize: 14,
|
||||
static TextStyle hintStyle = TextStyle(
|
||||
fontSize: 14.sp,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Colors.grey,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:agrilink_vocpro/features/auth/view/login_screen.dart';
|
||||
import 'package:agrilink_vocpro/features/dashboard/view/dashboard_screen.dart';
|
||||
import 'package:agrilink_vocpro/features/home/pages/humidity/view/humidity_screen.dart';
|
||||
import 'package:agrilink_vocpro/features/home/pages/light/view/light_screen.dart';
|
||||
import 'package:agrilink_vocpro/features/splash/view/splash_screen.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
|
@ -15,7 +16,7 @@ class AppRoute {
|
|||
static const String humidity = '/dashboard/humidity';
|
||||
static const String temperature = '/temperature';
|
||||
static const String soil = '/soil';
|
||||
static const String light = '/light';
|
||||
static const String light = '/dashboard/light';
|
||||
static const String water = '/water';
|
||||
static const String acidity = '/acidity';
|
||||
|
||||
|
|
@ -45,6 +46,14 @@ class AppRoute {
|
|||
path: 'humidity',
|
||||
builder: (context, state) => const HumidityScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: 'light/:value',
|
||||
builder: (context, state) {
|
||||
final double value =
|
||||
double.tryParse(state.pathParameters['value'] ?? '') ?? 0.0;
|
||||
return LightScreen(lightIntensity: value);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
class ControlProvider extends ChangeNotifier {
|
||||
// control 1
|
||||
bool _control_1 = false;
|
||||
bool get control_1 => _control_1;
|
||||
|
||||
switchControl1() {
|
||||
_control_1 = !_control_1;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// control 2
|
||||
bool _control_2 = false;
|
||||
bool get control_2 => _control_2;
|
||||
|
||||
switchControl2() {
|
||||
_control_2 = !_control_2;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// control 3
|
||||
bool _control_3 = false;
|
||||
bool get control_3 => _control_3;
|
||||
|
||||
switchControl3() {
|
||||
_control_3 = !_control_3;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// control 4
|
||||
bool _control_4 = false;
|
||||
bool get control_4 => _control_4;
|
||||
|
||||
switchControl4() {
|
||||
_control_4 = !_control_4;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// control 5
|
||||
bool _control_5 = false;
|
||||
bool get control_5 => _control_5;
|
||||
|
||||
switchControl5() {
|
||||
_control_5 = !_control_5;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// control 6
|
||||
bool _control_6 = false;
|
||||
bool get control_6 => _control_6;
|
||||
|
||||
switchControl6() {
|
||||
_control_6 = !_control_6;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
import 'package:agrilink_vocpro/core/constant/app_theme.dart';
|
||||
import 'package:agrilink_vocpro/features/control/provider/control_provider.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class ControlScreen extends StatelessWidget {
|
||||
const ControlScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Control', style: AppTheme.labelMedium),
|
||||
centerTitle: true,
|
||||
backgroundColor: Colors.white,
|
||||
scrolledUnderElevation: 0,
|
||||
),
|
||||
body: Consumer<ControlProvider>(builder: (context, provider, child) {
|
||||
return SafeArea(
|
||||
child: ListView(
|
||||
children: [
|
||||
ListTile(
|
||||
title: const Text('Control 1'),
|
||||
subtitle: const Text('Control 1 description'),
|
||||
trailing: Switch(
|
||||
value: provider.control_1,
|
||||
onChanged: (value) {
|
||||
provider.switchControl1();
|
||||
},
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
title: const Text('Control 2'),
|
||||
subtitle: const Text('Control 2 description'),
|
||||
trailing: Switch(
|
||||
value: provider.control_2,
|
||||
onChanged: (value) {
|
||||
provider.switchControl2();
|
||||
},
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
title: const Text('Control 3'),
|
||||
subtitle: const Text('Control 3 description'),
|
||||
trailing: Switch(
|
||||
value: provider.control_3,
|
||||
onChanged: (value) {
|
||||
provider.switchControl3();
|
||||
},
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
title: const Text('Control 4'),
|
||||
subtitle: const Text('Control 4 description'),
|
||||
trailing: Switch(
|
||||
value: provider.control_4,
|
||||
onChanged: (value) {
|
||||
provider.switchControl4();
|
||||
},
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
title: const Text('Control 5'),
|
||||
subtitle: const Text('Control 5 description'),
|
||||
trailing: Switch(
|
||||
value: provider.control_5,
|
||||
onChanged: (value) {
|
||||
provider.switchControl5();
|
||||
},
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
title: const Text('Control 6'),
|
||||
subtitle: const Text('Control 6 description'),
|
||||
trailing: Switch(
|
||||
value: provider.control_6,
|
||||
onChanged: (value) {
|
||||
provider.switchControl6();
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
));
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
import 'dart:ui';
|
||||
|
||||
class CensorDataRule {
|
||||
int minPercentage;
|
||||
int maxPercentage;
|
||||
String censorText;
|
||||
String description;
|
||||
String action;
|
||||
Color? color;
|
||||
|
||||
CensorDataRule({
|
||||
required this.minPercentage,
|
||||
required this.maxPercentage,
|
||||
required this.censorText,
|
||||
required this.description,
|
||||
required this.action,
|
||||
this.color,
|
||||
});
|
||||
}
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
import 'package:agrilink_vocpro/features/control/view/control_screen.dart';
|
||||
import 'package:agrilink_vocpro/features/home/view/home_screen.dart';
|
||||
import 'package:agrilink_vocpro/features/plants/view/plants_screen.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class DashboardProvider extends ChangeNotifier {
|
||||
|
|
@ -14,8 +16,8 @@ class DashboardProvider extends ChangeNotifier {
|
|||
|
||||
final List<Widget> _screens = [
|
||||
const HomeScreen(),
|
||||
const Center(child: Text('Control')),
|
||||
const Center(child: Text('Plants')),
|
||||
const ControlScreen(),
|
||||
const PlantsScreen(),
|
||||
const Center(child: Text('Settings')),
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
import 'package:agrilink_vocpro/core/constant/app_theme.dart';
|
||||
import 'package:agrilink_vocpro/features/home/pages/humidity/widgets/circle_chart.dart';
|
||||
import 'package:agrilink_vocpro/features/home/provider/home_provider.dart';
|
||||
import 'package: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 HumidityScreen extends StatelessWidget {
|
||||
const HumidityScreen({super.key});
|
||||
|
|
@ -13,6 +16,7 @@ class HumidityScreen extends StatelessWidget {
|
|||
appBar: AppBar(
|
||||
title: Text('Humidity', style: AppTheme.labelLarge),
|
||||
centerTitle: true,
|
||||
scrolledUnderElevation: 0,
|
||||
leading: IconButton(
|
||||
icon: const Icon(CupertinoIcons.back),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
|
|
@ -29,22 +33,76 @@ class HumidityScreen extends StatelessWidget {
|
|||
],
|
||||
),
|
||||
body: SafeArea(
|
||||
child: ListView(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: MediaQuery.of(context).size.height * 0.05,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 320,
|
||||
width: double.infinity,
|
||||
child: CircleChart(
|
||||
percentage: 60.5,
|
||||
icon: BootstrapIcons.droplet_half,
|
||||
child: Consumer<HomeProvider>(builder: (context, provider, child) {
|
||||
return ListView(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: MediaQuery.of(context).size.height * 0.05,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 320,
|
||||
width: double.infinity,
|
||||
child: CircleChart(
|
||||
percentage: 60.5,
|
||||
icon: BootstrapIcons.droplet_half,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'Humidity',
|
||||
style: AppTheme.labelMedium,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
IconButton(
|
||||
iconSize: 20.r,
|
||||
color: Colors.blue,
|
||||
onPressed: () {},
|
||||
icon: const Icon(BootstrapIcons.info_circle))
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 16.w),
|
||||
child: const Text('Deskripsi'),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
ListView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: provider.humidtyRules.length,
|
||||
itemBuilder: (context, index) {
|
||||
final item = provider.humidtyRules[index];
|
||||
return Theme(
|
||||
data: Theme.of(context)
|
||||
.copyWith(dividerColor: Colors.transparent),
|
||||
child: ExpansionTile(
|
||||
trailing: Text(
|
||||
item.censorText,
|
||||
style: TextStyle(color: item.color),
|
||||
),
|
||||
expandedCrossAxisAlignment: CrossAxisAlignment.start,
|
||||
childrenPadding: EdgeInsets.all(16.r),
|
||||
title: Text(
|
||||
'Kelembaban ${item.minPercentage}% - ${item.maxPercentage}%'),
|
||||
children: [
|
||||
Text(
|
||||
item.description,
|
||||
style: AppTheme.hintStyle,
|
||||
),
|
||||
SizedBox(height: 8.h),
|
||||
Text('Tindakan', style: AppTheme.labelMedium),
|
||||
SizedBox(height: 8.h),
|
||||
Text(item.action),
|
||||
],
|
||||
),
|
||||
);
|
||||
})
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,10 +6,14 @@ class CircleChart extends StatefulWidget {
|
|||
super.key,
|
||||
required this.percentage,
|
||||
required this.icon,
|
||||
this.colorStart,
|
||||
this.colorEnd,
|
||||
});
|
||||
|
||||
final double percentage;
|
||||
final IconData icon;
|
||||
final Color? colorStart;
|
||||
final Color? colorEnd;
|
||||
|
||||
@override
|
||||
State<CircleChart> createState() => _CircleChartState();
|
||||
|
|
@ -108,6 +112,9 @@ class _CircleChartState extends State<CircleChart> {
|
|||
|
||||
Color _getAnimatedColor(double percentage) {
|
||||
return Color.lerp(
|
||||
Colors.green, Colors.blue, percentage / widget.percentage)!;
|
||||
widget.colorStart ?? Colors.green,
|
||||
widget.colorEnd ?? Colors.blue,
|
||||
percentage / widget.percentage,
|
||||
)!;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,207 @@
|
|||
import 'package:agrilink_vocpro/core/constant/app_theme.dart';
|
||||
import 'package:bootstrap_icons/bootstrap_icons.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:gauge_indicator/gauge_indicator.dart';
|
||||
|
||||
class LightScreen extends StatelessWidget {
|
||||
const LightScreen({super.key, this.lightIntensity = 0});
|
||||
|
||||
final double lightIntensity;
|
||||
|
||||
double get value => lightIntensity;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Light', style: AppTheme.titleLarge),
|
||||
centerTitle: true,
|
||||
backgroundColor: Colors.white,
|
||||
scrolledUnderElevation: 0,
|
||||
actions: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 16),
|
||||
child: Icon(
|
||||
BootstrapIcons.sun,
|
||||
color: Colors.yellow.shade600,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
body: SafeArea(
|
||||
child: ListView(
|
||||
padding: EdgeInsets.all(16.w),
|
||||
children: [
|
||||
SizedBox(
|
||||
height: MediaQuery.of(context).size.height * 0.05,
|
||||
),
|
||||
SizedBox(
|
||||
height: 240.h,
|
||||
child: Stack(
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Icon(BootstrapIcons.sun,
|
||||
size: 32, color: Colors.orange),
|
||||
Text(
|
||||
'${value.toStringAsFixed(0)} lux', // Animated percentage text
|
||||
style: const TextStyle(
|
||||
fontSize: 28,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
AnimatedRadialGauge(
|
||||
duration: const Duration(seconds: 3),
|
||||
curve: Curves.easeOut,
|
||||
value: value,
|
||||
axis: GaugeAxis(
|
||||
degrees: 360,
|
||||
min: 0,
|
||||
max: 5000,
|
||||
style: GaugeAxisStyle(
|
||||
background: Colors.grey.shade100,
|
||||
thickness: 50,
|
||||
),
|
||||
progressBar: GaugeBasicProgressBar(
|
||||
gradient: GaugeAxisGradient(colors: [
|
||||
Colors.yellow.shade100,
|
||||
Colors.orange.shade200,
|
||||
]),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
const SizedBox(height: 16),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'Intensitas Cahaya',
|
||||
style: AppTheme.labelMedium,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
IconButton(
|
||||
iconSize: 20.r,
|
||||
color: Colors.blue,
|
||||
onPressed: () {},
|
||||
icon: const Icon(BootstrapIcons.info_circle))
|
||||
],
|
||||
),
|
||||
SizedBox(height: 16.h),
|
||||
const Text('Deskripsi'),
|
||||
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,
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// )
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -39,6 +39,8 @@ class SoilMoistureScreen extends StatelessWidget {
|
|||
child: CircleChart(
|
||||
percentage: 60.5,
|
||||
icon: Icons.water_outlined,
|
||||
colorStart: Colors.lime,
|
||||
colorEnd: Colors.brown,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
|
|
|||
|
|
@ -1,10 +1,14 @@
|
|||
import 'package:agrilink_vocpro/core/constant/app_theme.dart';
|
||||
import 'package:bootstrap_icons/bootstrap_icons.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:gauge_indicator/gauge_indicator.dart';
|
||||
|
||||
class TemperatureScreen extends StatelessWidget {
|
||||
const TemperatureScreen({super.key});
|
||||
|
||||
double get value => 29;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
|
|
@ -12,6 +16,7 @@ class TemperatureScreen extends StatelessWidget {
|
|||
title: Text('Temperature', style: AppTheme.titleLarge),
|
||||
centerTitle: true,
|
||||
backgroundColor: Colors.white,
|
||||
scrolledUnderElevation: 0,
|
||||
actions: const [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(right: 16),
|
||||
|
|
@ -24,22 +29,177 @@ class TemperatureScreen extends StatelessWidget {
|
|||
),
|
||||
body: SafeArea(
|
||||
child: ListView(
|
||||
padding: EdgeInsets.all(16.w),
|
||||
children: [
|
||||
SizedBox(
|
||||
height: MediaQuery.of(context).size.height * 0.05,
|
||||
),
|
||||
Center(
|
||||
child: SizedBox(
|
||||
height: 320,
|
||||
width: 320,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 10,
|
||||
backgroundColor: Colors.grey.shade200,
|
||||
value: 0.5,
|
||||
valueColor: const AlwaysStoppedAnimation<Color>(Colors.red),
|
||||
SizedBox(
|
||||
height: 240.h,
|
||||
child: Stack(
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 80.h,
|
||||
),
|
||||
const Icon(BootstrapIcons.thermometer_half,
|
||||
size: 32, color: Colors.orange),
|
||||
Text(
|
||||
'${value.toStringAsFixed(0)}°C', // Animated percentage text
|
||||
style: const TextStyle(
|
||||
fontSize: 28,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
AnimatedRadialGauge(
|
||||
duration: const Duration(seconds: 2),
|
||||
curve: Curves.easeOut,
|
||||
value: value,
|
||||
axis: GaugeAxis(
|
||||
degrees: 240,
|
||||
min: 0,
|
||||
max: 56.7,
|
||||
style: GaugeAxisStyle(
|
||||
background: Colors.grey.shade100,
|
||||
thickness: 50,
|
||||
),
|
||||
progressBar: const GaugeBasicProgressBar(
|
||||
gradient: GaugeAxisGradient(colors: [
|
||||
Colors.blue,
|
||||
Colors.orange,
|
||||
]),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
const SizedBox(height: 16),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'Temperature',
|
||||
style: AppTheme.labelMedium,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
IconButton(
|
||||
iconSize: 20.r,
|
||||
color: Colors.blue,
|
||||
onPressed: () {},
|
||||
icon: const Icon(BootstrapIcons.info_circle))
|
||||
],
|
||||
),
|
||||
SizedBox(height: 16.h),
|
||||
const Text('Deskripsi'),
|
||||
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,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,5 +1,59 @@
|
|||
import 'package:agrilink_vocpro/features/dashboard/model/censor_data_rule.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class HomeProvider extends ChangeNotifier {
|
||||
final DateTime currentDate = DateTime.now();
|
||||
|
||||
List<CensorDataRule> humidtyRules = [
|
||||
CensorDataRule(
|
||||
minPercentage: 0,
|
||||
maxPercentage: 30,
|
||||
censorText: 'Very Low',
|
||||
description:
|
||||
'Udara sangat kering. Tanaman bisa mengalami stress akibat kekurangan air.',
|
||||
action:
|
||||
'Aktifkan sistem penyiraman atau humidifier untuk menaikkan kelembaban. Periksa juga apakah ada kebocoran pada sistem irigasi yang mengakibatkan kelembaban terlalu rendah.',
|
||||
color: Colors.red,
|
||||
),
|
||||
CensorDataRule(
|
||||
minPercentage: 31,
|
||||
maxPercentage: 50,
|
||||
censorText: 'Low',
|
||||
description:
|
||||
'Kelembaban masih cukup rendah. Beberapa jenis tanaman mungkin sudah mulai terpengaruh.',
|
||||
action:
|
||||
'Pertimbangkan untuk menambah irigasi atau memperpanjang durasi penyiraman. Pantau tanaman secara berkala.',
|
||||
color: Colors.orange,
|
||||
),
|
||||
CensorDataRule(
|
||||
minPercentage: 51,
|
||||
maxPercentage: 70,
|
||||
censorText: 'Normal',
|
||||
description:
|
||||
'Ini adalah kelembaban yang ideal untuk sebagian besar tanaman dalam greenhouse.',
|
||||
action:
|
||||
'Pertahankan kondisi ini. Tidak ada tindakan yang diperlukan kecuali jika ada perubahan mendadak.',
|
||||
color: Colors.green,
|
||||
),
|
||||
CensorDataRule(
|
||||
minPercentage: 71,
|
||||
maxPercentage: 85,
|
||||
censorText: 'High',
|
||||
description:
|
||||
'Udara mulai terlalu lembap. Kelembaban tinggi dapat meningkatkan risiko penyakit jamur atau bakteri.',
|
||||
action:
|
||||
'Aktifkan ventilasi atau kipas untuk mengurangi kelembaban. Pastikan aliran udara di greenhouse cukup baik.',
|
||||
color: Colors.lime,
|
||||
),
|
||||
CensorDataRule(
|
||||
minPercentage: 86,
|
||||
maxPercentage: 100,
|
||||
censorText: 'Very High',
|
||||
description:
|
||||
'Udara sangat lembap, yang bisa berisiko menyebabkan jamur, lumut, dan penyakit tanaman.',
|
||||
action:
|
||||
'Segera aktifkan sistem ventilasi maksimal, mungkin juga gunakan dehumidifier jika diperlukan. Kurangi frekuensi penyiraman atau periksa sistem irigasi agar tidak berlebihan.',
|
||||
color: Colors.brown,
|
||||
),
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,61 +1,66 @@
|
|||
import 'package:agrilink_vocpro/core/constant/app_color.dart';
|
||||
import 'package:agrilink_vocpro/core/constant/app_theme.dart';
|
||||
import 'package:agrilink_vocpro/core/extension/extention.dart';
|
||||
import 'package:agrilink_vocpro/core/route/app_route.dart';
|
||||
import 'package:agrilink_vocpro/features/home/pages/soil_moisture/view/soil_moisture_screen.dart';
|
||||
import 'package:agrilink_vocpro/features/home/pages/temperature/view/temperature_screen.dart';
|
||||
import 'package:agrilink_vocpro/features/home/provider/home_provider.dart';
|
||||
import 'package:agrilink_vocpro/features/home/widgets/data_display_widget.dart';
|
||||
import 'package:agrilink_vocpro/features/home/pages/humidity/view/humidity_screen.dart';
|
||||
import 'package:bootstrap_icons/bootstrap_icons.dart';
|
||||
import 'package:agrilink_vocpro/features/home/widgets/list_data_from_censor_npk1.dart';
|
||||
import 'package:agrilink_vocpro/features/home/widgets/list_data_from_censor_npk2.dart';
|
||||
import 'package:agrilink_vocpro/features/home/widgets/list_data_from_main_censor.dart';
|
||||
import 'package:animated_segmented_tab_control/animated_segmented_tab_control.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
|
||||
class HomeScreen extends StatelessWidget {
|
||||
class HomeScreen extends StatefulWidget {
|
||||
const HomeScreen({super.key});
|
||||
|
||||
@override
|
||||
State<HomeScreen> createState() => _HomeScreenState();
|
||||
}
|
||||
|
||||
class _HomeScreenState extends State<HomeScreen> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: SafeArea(
|
||||
child: RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
},
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
const CircleAvatar(
|
||||
radius: 24,
|
||||
backgroundColor: AppColor.primary,
|
||||
child: Icon(
|
||||
Icons.person,
|
||||
color: Colors.white,
|
||||
size: 24,
|
||||
return SafeArea(
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
toolbarHeight: 200.h,
|
||||
elevation: 0,
|
||||
backgroundColor: Colors.white,
|
||||
scrolledUnderElevation: 0,
|
||||
flexibleSpace: Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 12.w),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
CircleAvatar(
|
||||
radius: 24.r,
|
||||
backgroundColor: AppColor.primary,
|
||||
child: Icon(
|
||||
Icons.person,
|
||||
color: Colors.white,
|
||||
size: 24.r,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
getGreeting(DateTime.now().toString()),
|
||||
style: AppTheme.titleMedium,
|
||||
),
|
||||
Text(
|
||||
'Fikril Mahesaputra',
|
||||
style: AppTheme.titleLarge,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Consumer<HomeProvider>(builder: (context, provider, child) {
|
||||
return Container(
|
||||
SizedBox(width: 16.w),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
getGreeting(DateTime.now().toString()),
|
||||
style: AppTheme.labelSmall,
|
||||
),
|
||||
Text(
|
||||
'Fikril Mahesaputra',
|
||||
style: AppTheme.labelMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(
|
||||
height: 8.h,
|
||||
),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
height: MediaQuery.of(context).size.height * 0.17,
|
||||
decoration: BoxDecoration(
|
||||
|
|
@ -99,84 +104,47 @@ class HomeScreen extends StatelessWidget {
|
|||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
const SizedBox(height: 16),
|
||||
Text('Linked Device', style: AppTheme.titleMedium),
|
||||
const SizedBox(height: 16),
|
||||
GridView(
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 2,
|
||||
crossAxisSpacing: 16,
|
||||
mainAxisSpacing: 16,
|
||||
),
|
||||
children: [
|
||||
DataDisplayerWidget(
|
||||
title: 'Humidity',
|
||||
subtitle: 'kelembaban udara',
|
||||
value: '60',
|
||||
unit: '%',
|
||||
icon: BootstrapIcons.droplet_half,
|
||||
textColor: Colors.white,
|
||||
color: AppColor.secondary,
|
||||
iconColor: Colors.white,
|
||||
onTap: () async {
|
||||
context.push(AppRoute.humidity, extra: '60');
|
||||
},
|
||||
),
|
||||
DataDisplayerWidget(
|
||||
title: 'Temperature',
|
||||
subtitle: 'suhu greenhouse',
|
||||
value: '28',
|
||||
unit: '°C',
|
||||
icon: BootstrapIcons.thermometer_half,
|
||||
color: Colors.white,
|
||||
onTap: () async {
|
||||
await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const TemperatureScreen()));
|
||||
},
|
||||
),
|
||||
const DataDisplayerWidget(
|
||||
title: 'Light',
|
||||
subtitle: 'intensitas cahaya',
|
||||
value: '1000',
|
||||
unit: 'lux',
|
||||
icon: BootstrapIcons.sun,
|
||||
color: Colors.white,
|
||||
),
|
||||
DataDisplayerWidget(
|
||||
title: 'Soil Moisture',
|
||||
subtitle: 'kelembaban tanah',
|
||||
value: '40',
|
||||
unit: '%',
|
||||
icon: Icons.water_outlined,
|
||||
color: Colors.white,
|
||||
onTap: () async {
|
||||
await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const SoilMoistureScreen(),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
const DataDisplayerWidget(
|
||||
title: 'Acid Level',
|
||||
subtitle: 'tingkat keasaman',
|
||||
value: '6.5',
|
||||
unit: '',
|
||||
icon: BootstrapIcons.pie_chart,
|
||||
color: Colors.white,
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
body: DefaultTabController(
|
||||
length: 3,
|
||||
child: Stack(
|
||||
children: [
|
||||
Padding(
|
||||
padding:
|
||||
EdgeInsets.symmetric(horizontal: 64.w, vertical: 16.h),
|
||||
child: SegmentedTabControl(
|
||||
height: 32.h,
|
||||
tabTextColor: Colors.black,
|
||||
tabPadding: EdgeInsets.zero,
|
||||
barDecoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(24.r),
|
||||
),
|
||||
indicatorDecoration: BoxDecoration(
|
||||
color: AppColor.primary,
|
||||
borderRadius: BorderRadius.circular(24.r),
|
||||
),
|
||||
textStyle: AppTheme.labelSmall,
|
||||
tabs: const [
|
||||
SegmentTab(label: 'Main Censor'),
|
||||
SegmentTab(label: 'NPK 1'),
|
||||
SegmentTab(label: 'NPK 2'),
|
||||
]),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 64.h),
|
||||
child: const TabBarView(children: [
|
||||
ListDataFromMainCensor(),
|
||||
ListDataFromCensorNpk1(),
|
||||
ListDataFromCensorNpk2(),
|
||||
]),
|
||||
)
|
||||
],
|
||||
)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:agrilink_vocpro/core/constant/app_theme.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
|
||||
class DataDisplayerWidget extends StatelessWidget {
|
||||
const DataDisplayerWidget({
|
||||
|
|
@ -12,6 +13,7 @@ class DataDisplayerWidget extends StatelessWidget {
|
|||
this.iconColor = Colors.teal,
|
||||
this.textColor = Colors.black,
|
||||
required this.unit,
|
||||
this.censorIdentifier,
|
||||
this.onTap,
|
||||
});
|
||||
|
||||
|
|
@ -23,6 +25,7 @@ class DataDisplayerWidget extends StatelessWidget {
|
|||
final Color iconColor;
|
||||
final Color textColor;
|
||||
final String unit;
|
||||
final String? censorIdentifier;
|
||||
final VoidCallback? onTap;
|
||||
|
||||
@override
|
||||
|
|
@ -33,49 +36,54 @@ class DataDisplayerWidget extends StatelessWidget {
|
|||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: color,
|
||||
padding: const EdgeInsets.all(12), // Padding di dalam button
|
||||
padding: EdgeInsets.all(12.r), // Padding di dalam button
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16), // Bentuk sudut yang bundar
|
||||
borderRadius: BorderRadius.circular(16.r), // Bentuk sudut yang bundar
|
||||
),
|
||||
elevation: 20, // Efek bayangan
|
||||
shadowColor: Colors.grey.withOpacity(0.2),
|
||||
),
|
||||
child: SizedBox(
|
||||
height:
|
||||
MediaQuery.of(context).size.height * 0.2, // Mengatur tinggi button
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Icon(icon, color: iconColor, size: 32),
|
||||
const SizedBox(height: 8),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: AppTheme.labelMedium.copyWith(color: textColor),
|
||||
),
|
||||
Text(
|
||||
subtitle,
|
||||
style: AppTheme.labelSmall
|
||||
.copyWith(color: textColor.withOpacity(0.5)),
|
||||
),
|
||||
],
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// Tidak perlu menggunakan mainAxisSize: MainAxisSize.min di sini
|
||||
children: [
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: Text(
|
||||
censorIdentifier ?? '',
|
||||
style: AppTheme.labelSmall
|
||||
.copyWith(color: textColor.withOpacity(0.5)),
|
||||
),
|
||||
const Spacer(),
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
value,
|
||||
style: AppTheme.headline1.copyWith(color: textColor),
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(unit,
|
||||
style: AppTheme.titleMedium.copyWith(color: textColor)),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Icon(icon, color: iconColor, size: 32.r),
|
||||
SizedBox(height: 8.h),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: AppTheme.labelMedium.copyWith(color: textColor),
|
||||
),
|
||||
Text(
|
||||
subtitle,
|
||||
style: AppTheme.labelSmall
|
||||
.copyWith(color: textColor.withOpacity(0.5)),
|
||||
),
|
||||
],
|
||||
),
|
||||
const Spacer(),
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
value,
|
||||
style: AppTheme.headline1.copyWith(color: textColor),
|
||||
),
|
||||
SizedBox(width: 4.w),
|
||||
Text(unit,
|
||||
style: AppTheme.titleMedium.copyWith(color: textColor)),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,116 @@
|
|||
import 'package:agrilink_vocpro/core/constant/app_color.dart';
|
||||
import 'package:agrilink_vocpro/core/route/app_route.dart';
|
||||
import 'package:agrilink_vocpro/features/home/pages/soil_moisture/view/soil_moisture_screen.dart';
|
||||
import 'package:agrilink_vocpro/features/home/pages/temperature/view/temperature_screen.dart';
|
||||
import 'package:agrilink_vocpro/features/home/widgets/data_display_widget.dart';
|
||||
import 'package:bootstrap_icons/bootstrap_icons.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
class ListDataFromCensorNpk1 extends StatelessWidget {
|
||||
const ListDataFromCensorNpk1({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
const String censorIdentifier = 'NPK 1';
|
||||
return GridView(
|
||||
padding: EdgeInsets.all(16.r),
|
||||
shrinkWrap: true,
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 2,
|
||||
crossAxisSpacing: 16.r,
|
||||
mainAxisSpacing: 16.r,
|
||||
childAspectRatio: 0.9,
|
||||
),
|
||||
children: [
|
||||
DataDisplayerWidget(
|
||||
title: 'Temperature',
|
||||
subtitle: 'Suhu tanah',
|
||||
value: '28',
|
||||
unit: '°C',
|
||||
icon: BootstrapIcons.thermometer_half,
|
||||
textColor: Colors.white,
|
||||
color: AppColor.secondary,
|
||||
iconColor: Colors.white,
|
||||
censorIdentifier: censorIdentifier,
|
||||
onTap: () async {
|
||||
await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const TemperatureScreen()));
|
||||
},
|
||||
),
|
||||
DataDisplayerWidget(
|
||||
title: 'Soil Moisture',
|
||||
subtitle: 'kelembaban tanah',
|
||||
value: '40',
|
||||
unit: '%',
|
||||
icon: Icons.water_outlined,
|
||||
color: Colors.white,
|
||||
censorIdentifier: censorIdentifier,
|
||||
onTap: () async {
|
||||
await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const SoilMoistureScreen(),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
DataDisplayerWidget(
|
||||
title: 'Acid Level (PH)',
|
||||
subtitle: 'tingkat keasaman',
|
||||
value: '6.5',
|
||||
unit: 'pH',
|
||||
icon: BootstrapIcons.pie_chart,
|
||||
color: Colors.white,
|
||||
censorIdentifier: censorIdentifier,
|
||||
onTap: () {},
|
||||
),
|
||||
DataDisplayerWidget(
|
||||
title: 'Conductivity',
|
||||
subtitle: 'Daya Arus Listrik',
|
||||
value: '234',
|
||||
unit: 'µS/cm',
|
||||
icon: Icons.electric_bolt,
|
||||
color: Colors.white,
|
||||
censorIdentifier: censorIdentifier,
|
||||
onTap: () async {
|
||||
context.push(AppRoute.humidity, extra: '60');
|
||||
},
|
||||
),
|
||||
DataDisplayerWidget(
|
||||
title: 'Nitrogen',
|
||||
subtitle: 'Kadar Nitrogen',
|
||||
value: '30',
|
||||
unit: 'ppm',
|
||||
icon: CupertinoIcons.eyedropper,
|
||||
color: Colors.white,
|
||||
onTap: () {},
|
||||
),
|
||||
DataDisplayerWidget(
|
||||
title: 'Potassium',
|
||||
subtitle: 'Kadar kalium',
|
||||
value: '20',
|
||||
unit: 'ppm',
|
||||
icon: CupertinoIcons.eyedropper,
|
||||
color: Colors.white,
|
||||
onTap: () {},
|
||||
),
|
||||
DataDisplayerWidget(
|
||||
title: 'Phosphorus',
|
||||
subtitle: 'Kadar Fosfor',
|
||||
value: '54',
|
||||
unit: 'ppm',
|
||||
icon: CupertinoIcons.eyedropper,
|
||||
color: Colors.white,
|
||||
onTap: () {},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
import 'package:agrilink_vocpro/core/constant/app_color.dart';
|
||||
import 'package:agrilink_vocpro/core/route/app_route.dart';
|
||||
import 'package:agrilink_vocpro/features/home/pages/soil_moisture/view/soil_moisture_screen.dart';
|
||||
import 'package:agrilink_vocpro/features/home/pages/temperature/view/temperature_screen.dart';
|
||||
import 'package:agrilink_vocpro/features/home/widgets/data_display_widget.dart';
|
||||
import 'package:bootstrap_icons/bootstrap_icons.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
class ListDataFromCensorNpk2 extends StatelessWidget {
|
||||
const ListDataFromCensorNpk2({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
const String censorIdentifier = 'NPK 1';
|
||||
return GridView(
|
||||
padding: EdgeInsets.all(16.r),
|
||||
shrinkWrap: true,
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 2,
|
||||
crossAxisSpacing: 16.r,
|
||||
mainAxisSpacing: 16.r,
|
||||
childAspectRatio: 0.9,
|
||||
),
|
||||
children: [
|
||||
DataDisplayerWidget(
|
||||
title: 'Temperature',
|
||||
subtitle: 'Suhu tanah',
|
||||
value: '28',
|
||||
unit: '°C',
|
||||
icon: BootstrapIcons.thermometer_half,
|
||||
textColor: Colors.white,
|
||||
color: AppColor.secondary,
|
||||
iconColor: Colors.white,
|
||||
censorIdentifier: censorIdentifier,
|
||||
onTap: () async {
|
||||
await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const TemperatureScreen()));
|
||||
},
|
||||
),
|
||||
DataDisplayerWidget(
|
||||
title: 'Soil Moisture',
|
||||
subtitle: 'kelembaban tanah',
|
||||
value: '40',
|
||||
unit: '%',
|
||||
icon: Icons.water_outlined,
|
||||
color: Colors.white,
|
||||
censorIdentifier: censorIdentifier,
|
||||
onTap: () async {
|
||||
await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const SoilMoistureScreen(),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
DataDisplayerWidget(
|
||||
title: 'Acid Level (PH)',
|
||||
subtitle: 'tingkat keasaman',
|
||||
value: '6.5',
|
||||
unit: 'pH',
|
||||
icon: BootstrapIcons.pie_chart,
|
||||
color: Colors.white,
|
||||
censorIdentifier: censorIdentifier,
|
||||
onTap: () {},
|
||||
),
|
||||
DataDisplayerWidget(
|
||||
title: 'Conductivity',
|
||||
subtitle: 'Daya Arus Listrik',
|
||||
value: '234',
|
||||
unit: 'µS/cm',
|
||||
icon: Icons.electric_bolt,
|
||||
color: Colors.white,
|
||||
censorIdentifier: censorIdentifier,
|
||||
onTap: () async {
|
||||
context.push(AppRoute.humidity, extra: '60');
|
||||
},
|
||||
),
|
||||
DataDisplayerWidget(
|
||||
title: 'Nitrogen',
|
||||
subtitle: 'Kadar Nitrogen',
|
||||
value: '30',
|
||||
unit: 'ppm',
|
||||
icon: CupertinoIcons.eyedropper,
|
||||
color: Colors.white,
|
||||
onTap: () {},
|
||||
),
|
||||
DataDisplayerWidget(
|
||||
title: 'Potassium',
|
||||
subtitle: 'Kadar kalium',
|
||||
value: '20',
|
||||
unit: 'ppm',
|
||||
icon: CupertinoIcons.eyedropper,
|
||||
color: Colors.white,
|
||||
onTap: () {},
|
||||
),
|
||||
DataDisplayerWidget(
|
||||
title: 'Phosphorus',
|
||||
subtitle: 'Kadar Fosfor',
|
||||
value: '54',
|
||||
unit: 'ppm',
|
||||
icon: CupertinoIcons.eyedropper,
|
||||
color: Colors.white,
|
||||
onTap: () {},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
import 'package:agrilink_vocpro/core/constant/app_color.dart';
|
||||
import 'package:agrilink_vocpro/core/route/app_route.dart';
|
||||
import 'package:agrilink_vocpro/features/home/pages/temperature/view/temperature_screen.dart';
|
||||
import 'package:agrilink_vocpro/features/home/widgets/data_display_widget.dart';
|
||||
import 'package:bootstrap_icons/bootstrap_icons.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
class ListDataFromMainCensor extends StatelessWidget {
|
||||
const ListDataFromMainCensor({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GridView(
|
||||
padding: EdgeInsets.all(16.r),
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 2,
|
||||
crossAxisSpacing: 16.r,
|
||||
mainAxisSpacing: 16.r,
|
||||
childAspectRatio: 0.9,
|
||||
),
|
||||
children: [
|
||||
DataDisplayerWidget(
|
||||
title: 'Humidity',
|
||||
subtitle: 'kelembaban udara',
|
||||
value: '60',
|
||||
unit: '%',
|
||||
icon: BootstrapIcons.droplet_half,
|
||||
textColor: Colors.white,
|
||||
color: AppColor.secondary,
|
||||
iconColor: Colors.white,
|
||||
censorIdentifier: 'NPK 1',
|
||||
onTap: () async {
|
||||
context.push(AppRoute.humidity, extra: '60');
|
||||
},
|
||||
),
|
||||
DataDisplayerWidget(
|
||||
title: 'Temperature',
|
||||
subtitle: 'suhu greenhouse',
|
||||
value: '28',
|
||||
unit: '°C',
|
||||
icon: BootstrapIcons.thermometer_half,
|
||||
color: Colors.white,
|
||||
onTap: () async {
|
||||
await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const TemperatureScreen()));
|
||||
},
|
||||
),
|
||||
DataDisplayerWidget(
|
||||
title: 'Light',
|
||||
subtitle: 'intensitas cahaya',
|
||||
value: '1000',
|
||||
unit: 'lux',
|
||||
icon: BootstrapIcons.sun,
|
||||
color: Colors.white,
|
||||
onTap: () async {
|
||||
context.push('${AppRoute.light}/300');
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
16
agrilink_vocpro/lib/features/plants/view/plants_screen.dart
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import 'package:agrilink_vocpro/core/constant/app_theme.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class PlantsScreen extends StatelessWidget {
|
||||
const PlantsScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: SafeArea(
|
||||
child: Center(
|
||||
child: Text('Coming Soon', style: AppTheme.labelMedium),
|
||||
)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +1,20 @@
|
|||
import 'package:agrilink_vocpro/core/route/app_route.dart';
|
||||
import 'package:agrilink_vocpro/features/auth/provider/auth_provider.dart';
|
||||
import 'package:agrilink_vocpro/features/control/provider/control_provider.dart';
|
||||
import 'package:agrilink_vocpro/features/dashboard/provider/dashboard_provider.dart';
|
||||
import 'package:agrilink_vocpro/features/home/provider/home_provider.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
void main() {
|
||||
runApp(const MyApp());
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
SystemChrome.setPreferredOrientations([
|
||||
DeviceOrientation.portraitUp, // Locks the app in portrait mode
|
||||
]).then((_) {
|
||||
runApp(const MyApp());
|
||||
});
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
|
|
@ -20,17 +28,23 @@ class MyApp extends StatelessWidget {
|
|||
ChangeNotifierProvider(create: (context) => AuthProvider()),
|
||||
ChangeNotifierProvider(create: (context) => HomeProvider()),
|
||||
ChangeNotifierProvider(create: (context) => DashboardProvider()),
|
||||
ChangeNotifierProvider(create: (context) => ControlProvider()),
|
||||
],
|
||||
child: MaterialApp.router(
|
||||
debugShowCheckedModeBanner: false,
|
||||
title: 'Flutter Demo',
|
||||
theme: ThemeData(
|
||||
scaffoldBackgroundColor: Colors.white,
|
||||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.teal),
|
||||
useMaterial3: true,
|
||||
),
|
||||
routerConfig: AppRoute.router,
|
||||
),
|
||||
child: ScreenUtilInit(
|
||||
designSize: const Size(360, 800),
|
||||
minTextAdapt: true,
|
||||
builder: (_, context) {
|
||||
return MaterialApp.router(
|
||||
debugShowCheckedModeBanner: false,
|
||||
title: 'Flutter Demo',
|
||||
theme: ThemeData(
|
||||
scaffoldBackgroundColor: Colors.white,
|
||||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.teal),
|
||||
useMaterial3: true,
|
||||
),
|
||||
routerConfig: AppRoute.router,
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,14 @@
|
|||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
animated_segmented_tab_control:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: animated_segmented_tab_control
|
||||
sha256: "4c04f5510037b1c30dbed00efae77559cc4e11f0fe2207c2358252d1870e4ab1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -134,6 +142,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.0"
|
||||
flutter_screenutil:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_screenutil
|
||||
sha256: "8239210dd68bee6b0577aa4a090890342d04a136ce1c81f98ee513fc0ce891de"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.9.3"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
|
|
@ -144,6 +160,14 @@ packages:
|
|||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
gauge_indicator:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: gauge_indicator
|
||||
sha256: "483abfae360ddffdf7b89fbd25192f83d1a6513d5da18f2388131566ab0793fd"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.4.3"
|
||||
go_router:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
|
|||
|
|
@ -42,6 +42,9 @@ dependencies:
|
|||
bootstrap_icons: ^1.11.3
|
||||
fl_chart: ^0.69.0
|
||||
google_fonts: ^6.2.1
|
||||
animated_segmented_tab_control: ^2.0.0
|
||||
flutter_screenutil: ^5.9.3
|
||||
gauge_indicator: ^0.4.3
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
|
|
|||