feat: add every censor detail screen
|
|
@ -2,7 +2,8 @@
|
||||||
<application
|
<application
|
||||||
android:label="agrilink_vocpro"
|
android:label="agrilink_vocpro"
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
android:icon="@mipmap/ic_launcher">
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:enableOnBackInvokedCallback="true">
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:exported="true"
|
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
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
|
||||||
|
networkTimeout=10000
|
||||||
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
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/material.dart';
|
||||||
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
|
|
||||||
class AppTheme {
|
class AppTheme {
|
||||||
static TextStyle headline1 = const TextStyle(
|
static TextStyle headline1 = TextStyle(
|
||||||
fontSize: 32,
|
fontSize: 32.sp,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
letterSpacing: 1,
|
letterSpacing: 1,
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
);
|
);
|
||||||
|
|
||||||
static TextStyle titleLarge = const TextStyle(
|
static TextStyle titleLarge = TextStyle(
|
||||||
fontSize: 17,
|
fontSize: 17.sp,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
);
|
);
|
||||||
static TextStyle titleMedium = TextStyle(
|
static TextStyle titleMedium = TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16.sp,
|
||||||
fontWeight: FontWeight.w400,
|
fontWeight: FontWeight.w400,
|
||||||
color: Colors.grey.shade600,
|
color: Colors.grey.shade600,
|
||||||
);
|
);
|
||||||
static TextStyle titleSmall = const TextStyle(
|
static TextStyle titleSmall = TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12.sp,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
);
|
);
|
||||||
|
|
||||||
static TextStyle labelLarge = const TextStyle(
|
static TextStyle labelLarge = TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16.sp,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
);
|
);
|
||||||
static TextStyle labelMedium = const TextStyle(
|
static TextStyle labelMedium = TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14.sp,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
);
|
);
|
||||||
static TextStyle labelSmall = const TextStyle(
|
static TextStyle labelSmall = TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12.sp,
|
||||||
fontWeight: FontWeight.w400,
|
fontWeight: FontWeight.w400,
|
||||||
color: Colors.grey,
|
color: Colors.grey,
|
||||||
);
|
);
|
||||||
|
|
||||||
static TextStyle hintStyle = const TextStyle(
|
static TextStyle hintStyle = TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14.sp,
|
||||||
fontWeight: FontWeight.w400,
|
fontWeight: FontWeight.w400,
|
||||||
color: Colors.grey,
|
color: Colors.grey,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import 'package:agrilink_vocpro/features/auth/view/login_screen.dart';
|
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/dashboard/view/dashboard_screen.dart';
|
||||||
import 'package:agrilink_vocpro/features/home/pages/humidity/view/humidity_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:agrilink_vocpro/features/splash/view/splash_screen.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
@ -15,7 +16,7 @@ class AppRoute {
|
||||||
static const String humidity = '/dashboard/humidity';
|
static const String humidity = '/dashboard/humidity';
|
||||||
static const String temperature = '/temperature';
|
static const String temperature = '/temperature';
|
||||||
static const String soil = '/soil';
|
static const String soil = '/soil';
|
||||||
static const String light = '/light';
|
static const String light = '/dashboard/light';
|
||||||
static const String water = '/water';
|
static const String water = '/water';
|
||||||
static const String acidity = '/acidity';
|
static const String acidity = '/acidity';
|
||||||
|
|
||||||
|
|
@ -45,6 +46,14 @@ class AppRoute {
|
||||||
path: 'humidity',
|
path: 'humidity',
|
||||||
builder: (context, state) => const HumidityScreen(),
|
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/home/view/home_screen.dart';
|
||||||
|
import 'package:agrilink_vocpro/features/plants/view/plants_screen.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class DashboardProvider extends ChangeNotifier {
|
class DashboardProvider extends ChangeNotifier {
|
||||||
|
|
@ -14,8 +16,8 @@ class DashboardProvider extends ChangeNotifier {
|
||||||
|
|
||||||
final List<Widget> _screens = [
|
final List<Widget> _screens = [
|
||||||
const HomeScreen(),
|
const HomeScreen(),
|
||||||
const Center(child: Text('Control')),
|
const ControlScreen(),
|
||||||
const Center(child: Text('Plants')),
|
const PlantsScreen(),
|
||||||
const Center(child: Text('Settings')),
|
const Center(child: Text('Settings')),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
import 'package:agrilink_vocpro/core/constant/app_theme.dart';
|
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/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:bootstrap_icons/bootstrap_icons.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
class HumidityScreen extends StatelessWidget {
|
class HumidityScreen extends StatelessWidget {
|
||||||
const HumidityScreen({super.key});
|
const HumidityScreen({super.key});
|
||||||
|
|
@ -13,6 +16,7 @@ class HumidityScreen extends StatelessWidget {
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text('Humidity', style: AppTheme.labelLarge),
|
title: Text('Humidity', style: AppTheme.labelLarge),
|
||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
|
scrolledUnderElevation: 0,
|
||||||
leading: IconButton(
|
leading: IconButton(
|
||||||
icon: const Icon(CupertinoIcons.back),
|
icon: const Icon(CupertinoIcons.back),
|
||||||
onPressed: () => Navigator.pop(context),
|
onPressed: () => Navigator.pop(context),
|
||||||
|
|
@ -29,22 +33,76 @@ class HumidityScreen extends StatelessWidget {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: ListView(
|
child: Consumer<HomeProvider>(builder: (context, provider, child) {
|
||||||
children: [
|
return ListView(
|
||||||
SizedBox(
|
children: [
|
||||||
height: MediaQuery.of(context).size.height * 0.05,
|
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,
|
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(
|
||||||
const SizedBox(height: 16),
|
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,
|
super.key,
|
||||||
required this.percentage,
|
required this.percentage,
|
||||||
required this.icon,
|
required this.icon,
|
||||||
|
this.colorStart,
|
||||||
|
this.colorEnd,
|
||||||
});
|
});
|
||||||
|
|
||||||
final double percentage;
|
final double percentage;
|
||||||
final IconData icon;
|
final IconData icon;
|
||||||
|
final Color? colorStart;
|
||||||
|
final Color? colorEnd;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<CircleChart> createState() => _CircleChartState();
|
State<CircleChart> createState() => _CircleChartState();
|
||||||
|
|
@ -108,6 +112,9 @@ class _CircleChartState extends State<CircleChart> {
|
||||||
|
|
||||||
Color _getAnimatedColor(double percentage) {
|
Color _getAnimatedColor(double percentage) {
|
||||||
return Color.lerp(
|
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(
|
child: CircleChart(
|
||||||
percentage: 60.5,
|
percentage: 60.5,
|
||||||
icon: Icons.water_outlined,
|
icon: Icons.water_outlined,
|
||||||
|
colorStart: Colors.lime,
|
||||||
|
colorEnd: Colors.brown,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,14 @@
|
||||||
import 'package:agrilink_vocpro/core/constant/app_theme.dart';
|
import 'package:agrilink_vocpro/core/constant/app_theme.dart';
|
||||||
import 'package:bootstrap_icons/bootstrap_icons.dart';
|
import 'package:bootstrap_icons/bootstrap_icons.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
|
import 'package:gauge_indicator/gauge_indicator.dart';
|
||||||
|
|
||||||
class TemperatureScreen extends StatelessWidget {
|
class TemperatureScreen extends StatelessWidget {
|
||||||
const TemperatureScreen({super.key});
|
const TemperatureScreen({super.key});
|
||||||
|
|
||||||
|
double get value => 29;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
|
@ -12,6 +16,7 @@ class TemperatureScreen extends StatelessWidget {
|
||||||
title: Text('Temperature', style: AppTheme.titleLarge),
|
title: Text('Temperature', style: AppTheme.titleLarge),
|
||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
backgroundColor: Colors.white,
|
backgroundColor: Colors.white,
|
||||||
|
scrolledUnderElevation: 0,
|
||||||
actions: const [
|
actions: const [
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.only(right: 16),
|
padding: EdgeInsets.only(right: 16),
|
||||||
|
|
@ -24,22 +29,177 @@ class TemperatureScreen extends StatelessWidget {
|
||||||
),
|
),
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: ListView(
|
child: ListView(
|
||||||
|
padding: EdgeInsets.all(16.w),
|
||||||
children: [
|
children: [
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: MediaQuery.of(context).size.height * 0.05,
|
height: MediaQuery.of(context).size.height * 0.05,
|
||||||
),
|
),
|
||||||
Center(
|
SizedBox(
|
||||||
child: SizedBox(
|
height: 240.h,
|
||||||
height: 320,
|
child: Stack(
|
||||||
width: 320,
|
fit: StackFit.expand,
|
||||||
child: CircularProgressIndicator(
|
children: [
|
||||||
strokeWidth: 10,
|
Center(
|
||||||
backgroundColor: Colors.grey.shade200,
|
child: Column(
|
||||||
value: 0.5,
|
mainAxisSize: MainAxisSize.min,
|
||||||
valueColor: const AlwaysStoppedAnimation<Color>(Colors.red),
|
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),
|
||||||
|
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';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class HomeProvider extends ChangeNotifier {
|
class HomeProvider extends ChangeNotifier {
|
||||||
final DateTime currentDate = DateTime.now();
|
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_color.dart';
|
||||||
import 'package:agrilink_vocpro/core/constant/app_theme.dart';
|
import 'package:agrilink_vocpro/core/constant/app_theme.dart';
|
||||||
import 'package:agrilink_vocpro/core/extension/extention.dart';
|
import 'package:agrilink_vocpro/core/extension/extention.dart';
|
||||||
import 'package:agrilink_vocpro/core/route/app_route.dart';
|
import 'package:agrilink_vocpro/features/home/widgets/list_data_from_censor_npk1.dart';
|
||||||
import 'package:agrilink_vocpro/features/home/pages/soil_moisture/view/soil_moisture_screen.dart';
|
import 'package:agrilink_vocpro/features/home/widgets/list_data_from_censor_npk2.dart';
|
||||||
import 'package:agrilink_vocpro/features/home/pages/temperature/view/temperature_screen.dart';
|
import 'package:agrilink_vocpro/features/home/widgets/list_data_from_main_censor.dart';
|
||||||
import 'package:agrilink_vocpro/features/home/provider/home_provider.dart';
|
import 'package:animated_segmented_tab_control/animated_segmented_tab_control.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:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
|
|
||||||
class HomeScreen extends StatelessWidget {
|
class HomeScreen extends StatefulWidget {
|
||||||
const HomeScreen({super.key});
|
const HomeScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<HomeScreen> createState() => _HomeScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _HomeScreenState extends State<HomeScreen> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return SafeArea(
|
||||||
body: SafeArea(
|
child: Scaffold(
|
||||||
child: RefreshIndicator(
|
appBar: AppBar(
|
||||||
onRefresh: () async {
|
toolbarHeight: 200.h,
|
||||||
await Future.delayed(const Duration(seconds: 1));
|
elevation: 0,
|
||||||
},
|
backgroundColor: Colors.white,
|
||||||
child: ListView(
|
scrolledUnderElevation: 0,
|
||||||
padding: const EdgeInsets.all(16),
|
flexibleSpace: Padding(
|
||||||
children: [
|
padding: EdgeInsets.symmetric(horizontal: 12.w),
|
||||||
Row(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
const CircleAvatar(
|
Row(
|
||||||
radius: 24,
|
children: [
|
||||||
backgroundColor: AppColor.primary,
|
CircleAvatar(
|
||||||
child: Icon(
|
radius: 24.r,
|
||||||
Icons.person,
|
backgroundColor: AppColor.primary,
|
||||||
color: Colors.white,
|
child: Icon(
|
||||||
size: 24,
|
Icons.person,
|
||||||
|
color: Colors.white,
|
||||||
|
size: 24.r,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
SizedBox(width: 16.w),
|
||||||
const SizedBox(width: 16),
|
Column(
|
||||||
Column(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
getGreeting(DateTime.now().toString()),
|
getGreeting(DateTime.now().toString()),
|
||||||
style: AppTheme.titleMedium,
|
style: AppTheme.labelSmall,
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'Fikril Mahesaputra',
|
'Fikril Mahesaputra',
|
||||||
style: AppTheme.titleLarge,
|
style: AppTheme.labelMedium,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
SizedBox(
|
||||||
Consumer<HomeProvider>(builder: (context, provider, child) {
|
height: 8.h,
|
||||||
return Container(
|
),
|
||||||
|
Container(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
height: MediaQuery.of(context).size.height * 0.17,
|
height: MediaQuery.of(context).size.height * 0.17,
|
||||||
decoration: BoxDecoration(
|
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:agrilink_vocpro/core/constant/app_theme.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
|
|
||||||
class DataDisplayerWidget extends StatelessWidget {
|
class DataDisplayerWidget extends StatelessWidget {
|
||||||
const DataDisplayerWidget({
|
const DataDisplayerWidget({
|
||||||
|
|
@ -12,6 +13,7 @@ class DataDisplayerWidget extends StatelessWidget {
|
||||||
this.iconColor = Colors.teal,
|
this.iconColor = Colors.teal,
|
||||||
this.textColor = Colors.black,
|
this.textColor = Colors.black,
|
||||||
required this.unit,
|
required this.unit,
|
||||||
|
this.censorIdentifier,
|
||||||
this.onTap,
|
this.onTap,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -23,6 +25,7 @@ class DataDisplayerWidget extends StatelessWidget {
|
||||||
final Color iconColor;
|
final Color iconColor;
|
||||||
final Color textColor;
|
final Color textColor;
|
||||||
final String unit;
|
final String unit;
|
||||||
|
final String? censorIdentifier;
|
||||||
final VoidCallback? onTap;
|
final VoidCallback? onTap;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -33,49 +36,54 @@ class DataDisplayerWidget extends StatelessWidget {
|
||||||
},
|
},
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: color,
|
backgroundColor: color,
|
||||||
padding: const EdgeInsets.all(12), // Padding di dalam button
|
padding: EdgeInsets.all(12.r), // Padding di dalam button
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(16), // Bentuk sudut yang bundar
|
borderRadius: BorderRadius.circular(16.r), // Bentuk sudut yang bundar
|
||||||
),
|
),
|
||||||
elevation: 20, // Efek bayangan
|
elevation: 20, // Efek bayangan
|
||||||
shadowColor: Colors.grey.withOpacity(0.2),
|
shadowColor: Colors.grey.withOpacity(0.2),
|
||||||
),
|
),
|
||||||
child: SizedBox(
|
child: Column(
|
||||||
height:
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
MediaQuery.of(context).size.height * 0.2, // Mengatur tinggi button
|
// Tidak perlu menggunakan mainAxisSize: MainAxisSize.min di sini
|
||||||
child: Column(
|
children: [
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
Align(
|
||||||
children: [
|
alignment: Alignment.centerRight,
|
||||||
Icon(icon, color: iconColor, size: 32),
|
child: Text(
|
||||||
const SizedBox(height: 8),
|
censorIdentifier ?? '',
|
||||||
Column(
|
style: AppTheme.labelSmall
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
.copyWith(color: textColor.withOpacity(0.5)),
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
title,
|
|
||||||
style: AppTheme.labelMedium.copyWith(color: textColor),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
subtitle,
|
|
||||||
style: AppTheme.labelSmall
|
|
||||||
.copyWith(color: textColor.withOpacity(0.5)),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
const Spacer(),
|
),
|
||||||
Row(
|
Icon(icon, color: iconColor, size: 32.r),
|
||||||
children: [
|
SizedBox(height: 8.h),
|
||||||
Text(
|
Column(
|
||||||
value,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
style: AppTheme.headline1.copyWith(color: textColor),
|
children: [
|
||||||
),
|
Text(
|
||||||
const SizedBox(width: 4),
|
title,
|
||||||
Text(unit,
|
style: AppTheme.labelMedium.copyWith(color: textColor),
|
||||||
style: AppTheme.titleMedium.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/core/route/app_route.dart';
|
||||||
import 'package:agrilink_vocpro/features/auth/provider/auth_provider.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/dashboard/provider/dashboard_provider.dart';
|
||||||
import 'package:agrilink_vocpro/features/home/provider/home_provider.dart';
|
import 'package:agrilink_vocpro/features/home/provider/home_provider.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
void main() {
|
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 {
|
class MyApp extends StatelessWidget {
|
||||||
|
|
@ -20,17 +28,23 @@ class MyApp extends StatelessWidget {
|
||||||
ChangeNotifierProvider(create: (context) => AuthProvider()),
|
ChangeNotifierProvider(create: (context) => AuthProvider()),
|
||||||
ChangeNotifierProvider(create: (context) => HomeProvider()),
|
ChangeNotifierProvider(create: (context) => HomeProvider()),
|
||||||
ChangeNotifierProvider(create: (context) => DashboardProvider()),
|
ChangeNotifierProvider(create: (context) => DashboardProvider()),
|
||||||
|
ChangeNotifierProvider(create: (context) => ControlProvider()),
|
||||||
],
|
],
|
||||||
child: MaterialApp.router(
|
child: ScreenUtilInit(
|
||||||
debugShowCheckedModeBanner: false,
|
designSize: const Size(360, 800),
|
||||||
title: 'Flutter Demo',
|
minTextAdapt: true,
|
||||||
theme: ThemeData(
|
builder: (_, context) {
|
||||||
scaffoldBackgroundColor: Colors.white,
|
return MaterialApp.router(
|
||||||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.teal),
|
debugShowCheckedModeBanner: false,
|
||||||
useMaterial3: true,
|
title: 'Flutter Demo',
|
||||||
),
|
theme: ThemeData(
|
||||||
routerConfig: AppRoute.router,
|
scaffoldBackgroundColor: Colors.white,
|
||||||
),
|
colorScheme: ColorScheme.fromSeed(seedColor: Colors.teal),
|
||||||
|
useMaterial3: true,
|
||||||
|
),
|
||||||
|
routerConfig: AppRoute.router,
|
||||||
|
);
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,14 @@
|
||||||
# Generated by pub
|
# Generated by pub
|
||||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||||
packages:
|
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:
|
async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -134,6 +142,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.0"
|
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:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
|
|
@ -144,6 +160,14 @@ packages:
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
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:
|
go_router:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,9 @@ dependencies:
|
||||||
bootstrap_icons: ^1.11.3
|
bootstrap_icons: ^1.11.3
|
||||||
fl_chart: ^0.69.0
|
fl_chart: ^0.69.0
|
||||||
google_fonts: ^6.2.1
|
google_fonts: ^6.2.1
|
||||||
|
animated_segmented_tab_control: ^2.0.0
|
||||||
|
flutter_screenutil: ^5.9.3
|
||||||
|
gauge_indicator: ^0.4.3
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
|
||||||