feat: add basic route, splash, and login screen
|
Before Width: | Height: | Size: 544 B After Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 442 B After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 721 B After Width: | Height: | Size: 8.4 KiB |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 23 KiB |
BIN
agrilink_vocpro/assets/images/app_logo.png
Normal file
|
After Width: | Height: | Size: 362 KiB |
|
|
@ -11,4 +11,5 @@ class AppColor {
|
||||||
static const Color greenTextDark = Color(0xFF00913A);
|
static const Color greenTextDark = Color(0xFF00913A);
|
||||||
|
|
||||||
static const Color textDisable = Color(0xFFBDBDBD);
|
static const Color textDisable = Color(0xFFBDBDBD);
|
||||||
|
static const Color textDark = Color(0xFF4B4B51);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,15 +7,16 @@ class AppTheme {
|
||||||
letterSpacing: 1,
|
letterSpacing: 1,
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
);
|
);
|
||||||
|
|
||||||
static TextStyle titleLarge = const TextStyle(
|
static TextStyle titleLarge = const TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 17,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
);
|
);
|
||||||
static TextStyle titleMedium = const TextStyle(
|
static TextStyle titleMedium = TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w400,
|
||||||
color: Colors.black,
|
color: Colors.grey.shade600,
|
||||||
);
|
);
|
||||||
static TextStyle titleSmall = const TextStyle(
|
static TextStyle titleSmall = const TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
|
|
@ -23,13 +24,24 @@ class AppTheme {
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
static TextStyle labelLarge = const TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Colors.black,
|
||||||
|
);
|
||||||
static TextStyle labelMedium = const TextStyle(
|
static TextStyle labelMedium = const TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 14,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w500,
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
);
|
);
|
||||||
static TextStyle labelSmall = const TextStyle(
|
static TextStyle labelSmall = const TextStyle(
|
||||||
fontSize: 10,
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
color: Colors.grey,
|
||||||
|
);
|
||||||
|
|
||||||
|
static TextStyle hintStyle = const TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
fontWeight: FontWeight.w400,
|
fontWeight: FontWeight.w400,
|
||||||
color: Colors.grey,
|
color: Colors.grey,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
57
agrilink_vocpro/lib/core/route/app_route.dart
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
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/splash/view/splash_screen.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
|
class AppRoute {
|
||||||
|
static const String root = '/';
|
||||||
|
static const String login = '/login';
|
||||||
|
static const String dashboard = '/dashboard';
|
||||||
|
static const String home = '/home';
|
||||||
|
static const String profile = '/profile';
|
||||||
|
static const String setting = '/setting';
|
||||||
|
static const String humidity = '/dashboard/humidity';
|
||||||
|
static const String temperature = '/temperature';
|
||||||
|
static const String soil = '/soil';
|
||||||
|
static const String light = '/light';
|
||||||
|
static const String water = '/water';
|
||||||
|
static const String acidity = '/acidity';
|
||||||
|
|
||||||
|
static final GoRouter router = GoRouter(
|
||||||
|
initialLocation: root,
|
||||||
|
routes: [
|
||||||
|
//splash
|
||||||
|
GoRoute(
|
||||||
|
path: root,
|
||||||
|
builder: (context, state) => const SplashScreen(),
|
||||||
|
),
|
||||||
|
//login
|
||||||
|
GoRoute(
|
||||||
|
path: login,
|
||||||
|
builder: (context, state) => const LoginScreen(),
|
||||||
|
),
|
||||||
|
//dashboard
|
||||||
|
GoRoute(
|
||||||
|
path: dashboard,
|
||||||
|
builder: (context, state) => const DashboardScreen(),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: dashboard,
|
||||||
|
builder: (context, state) => const DashboardScreen(),
|
||||||
|
routes: [
|
||||||
|
GoRoute(
|
||||||
|
path: 'humidity',
|
||||||
|
builder: (context, state) => const HumidityScreen(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
errorBuilder: (context, state) => Scaffold(
|
||||||
|
body: Center(
|
||||||
|
child: Text('Error: ${state.error}'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
enum ResultState {
|
enum ResultState {
|
||||||
initial,
|
initial,
|
||||||
loading,
|
loading,
|
||||||
noData,
|
|
||||||
hasData,
|
hasData,
|
||||||
|
noData,
|
||||||
error,
|
error,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import 'package:agrilink_vocpro/core/constant/app_color.dart';
|
import 'package:agrilink_vocpro/core/constant/app_color.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
|
||||||
|
|
||||||
class AppButton extends StatelessWidget {
|
class AppButton extends StatelessWidget {
|
||||||
const AppButton({
|
const AppButton({
|
||||||
|
|
@ -24,14 +23,14 @@ class AppButton extends StatelessWidget {
|
||||||
onPressed: onPressed,
|
onPressed: onPressed,
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
shadowColor: Colors.transparent,
|
shadowColor: Colors.transparent,
|
||||||
textStyle: TextStyle(
|
textStyle: const TextStyle(
|
||||||
fontSize: 14.sp, fontWeight: FontWeight.w500, fontFamily: 'Onest'),
|
fontSize: 14, fontWeight: FontWeight.w500, fontFamily: 'Urbanist'),
|
||||||
backgroundColor: backgroundColor,
|
backgroundColor: backgroundColor,
|
||||||
foregroundColor: foregroundColor,
|
foregroundColor: foregroundColor,
|
||||||
minimumSize: Size(double.infinity, 48.h),
|
minimumSize: const Size(double.infinity, 48),
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
side: borderSide ?? BorderSide.none,
|
side: borderSide ?? BorderSide.none,
|
||||||
borderRadius: BorderRadius.circular(10.r),
|
borderRadius: BorderRadius.circular(10),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Text(text),
|
child: Text(text),
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
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:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
|
||||||
|
|
||||||
class CustomTextField extends StatelessWidget {
|
class AppTextfield extends StatelessWidget {
|
||||||
const CustomTextField({
|
const AppTextfield({
|
||||||
super.key,
|
super.key,
|
||||||
required this.controller,
|
required this.controller,
|
||||||
this.hintText = 'Enter Here',
|
this.hintText = 'Enter Here',
|
||||||
|
|
@ -20,16 +20,16 @@ class CustomTextField extends StatelessWidget {
|
||||||
controller: controller,
|
controller: controller,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
enabledBorder: OutlineInputBorder(
|
enabledBorder: OutlineInputBorder(
|
||||||
borderSide: BorderSide(color: AppColor.textDisable, width: 1.r),
|
borderSide: const BorderSide(color: AppColor.textDisable, width: 1),
|
||||||
borderRadius: BorderRadius.circular(8.r),
|
borderRadius: BorderRadius.circular(8),
|
||||||
gapPadding: 10.r),
|
),
|
||||||
focusedBorder: OutlineInputBorder(
|
focusedBorder: OutlineInputBorder(
|
||||||
borderSide: BorderSide(color: AppColor.primary, width: 1.r),
|
borderSide: const BorderSide(color: AppColor.primary, width: 1),
|
||||||
borderRadius: BorderRadius.circular(8.r),
|
borderRadius: BorderRadius.circular(8),
|
||||||
gapPadding: 10.r),
|
),
|
||||||
hintText: hintText,
|
hintText: hintText,
|
||||||
// hintStyle: AppTheme.hintStyle,
|
hintStyle: AppTheme.hintStyle,
|
||||||
suffixIcon: suffixIcon),
|
suffixIcon: (suffixIcon != null) ? suffixIcon : null),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class AuthProvider extends ChangeNotifier {
|
||||||
|
TextEditingController emailController = TextEditingController();
|
||||||
|
TextEditingController passwordController = TextEditingController();
|
||||||
|
|
||||||
|
void controllerClear() {
|
||||||
|
emailController.clear();
|
||||||
|
passwordController.clear();
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
import 'package:agrilink_vocpro/core/constant/app_color.dart';
|
||||||
|
import 'package:agrilink_vocpro/core/constant/app_theme.dart';
|
||||||
|
import 'package:agrilink_vocpro/core/route/app_route.dart';
|
||||||
|
import 'package:agrilink_vocpro/core/widgets/app_button.dart';
|
||||||
|
import 'package:agrilink_vocpro/core/widgets/app_textfield.dart';
|
||||||
|
import 'package:agrilink_vocpro/features/auth/provider/auth_provider.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
class LoginScreen extends StatelessWidget {
|
||||||
|
const LoginScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
body: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
FocusScope.of(context).unfocus();
|
||||||
|
},
|
||||||
|
child: SafeArea(
|
||||||
|
child: Consumer<AuthProvider>(builder: (context, authP, child) {
|
||||||
|
return ListView(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 40),
|
||||||
|
Text(
|
||||||
|
'Hello Wellcome back 👋',
|
||||||
|
style: AppTheme.titleLarge,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'Happy to have you back',
|
||||||
|
style: AppTheme.titleMedium,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 40),
|
||||||
|
Text('Email address', style: AppTheme.labelLarge),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
AppTextfield(
|
||||||
|
controller: authP.emailController,
|
||||||
|
hintText: 'Masukkan username',
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
Text('Password', style: AppTheme.labelLarge),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
AppTextfield(
|
||||||
|
controller: authP.passwordController,
|
||||||
|
hintText: 'Masukkan password',
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
print('Forgot password?');
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
'Forgot password?',
|
||||||
|
textAlign: TextAlign.end,
|
||||||
|
style: AppTheme.labelMedium
|
||||||
|
.copyWith(color: AppColor.secondary),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
AppButton(
|
||||||
|
onPressed: () {
|
||||||
|
GoRouter.of(context).go(AppRoute.dashboard);
|
||||||
|
},
|
||||||
|
text: 'Login'),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
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/features/humidity/widgets/circle_chart.dart';
|
import 'package:agrilink_vocpro/features/home/pages/humidity/widgets/circle_chart.dart';
|
||||||
import 'package:bootstrap_icons/bootstrap_icons.dart';
|
import 'package:bootstrap_icons/bootstrap_icons.dart';
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class HumidityScreen extends StatelessWidget {
|
class HumidityScreen extends StatelessWidget {
|
||||||
|
|
@ -11,8 +11,12 @@ class HumidityScreen extends StatelessWidget {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text('Humidity', style: AppTheme.titleLarge),
|
title: Text('Humidity', style: AppTheme.labelLarge),
|
||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
|
leading: IconButton(
|
||||||
|
icon: const Icon(CupertinoIcons.back),
|
||||||
|
onPressed: () => Navigator.pop(context),
|
||||||
|
),
|
||||||
backgroundColor: Colors.white,
|
backgroundColor: Colors.white,
|
||||||
actions: const [
|
actions: const [
|
||||||
Padding(
|
Padding(
|
||||||
|
|
@ -33,7 +37,10 @@ class HumidityScreen extends StatelessWidget {
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 320,
|
height: 320,
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
child: CircleChart(humidityPercentage: 60.5),
|
child: CircleChart(
|
||||||
|
percentage: 60.5,
|
||||||
|
icon: BootstrapIcons.droplet_half,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
],
|
],
|
||||||
|
|
@ -1,11 +1,15 @@
|
||||||
import 'package:bootstrap_icons/bootstrap_icons.dart';
|
|
||||||
import 'package:fl_chart/fl_chart.dart';
|
import 'package:fl_chart/fl_chart.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class CircleChart extends StatefulWidget {
|
class CircleChart extends StatefulWidget {
|
||||||
const CircleChart({super.key, required this.humidityPercentage});
|
const CircleChart({
|
||||||
|
super.key,
|
||||||
|
required this.percentage,
|
||||||
|
required this.icon,
|
||||||
|
});
|
||||||
|
|
||||||
final double humidityPercentage;
|
final double percentage;
|
||||||
|
final IconData icon;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<CircleChart> createState() => _CircleChartState();
|
State<CircleChart> createState() => _CircleChartState();
|
||||||
|
|
@ -17,7 +21,7 @@ class _CircleChartState extends State<CircleChart> {
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
Future.delayed(Duration.zero, () async {
|
Future.delayed(Duration.zero, () async {
|
||||||
for (double i = 0; i <= widget.humidityPercentage; i++) {
|
for (double i = 0; i <= widget.percentage; i++) {
|
||||||
await Future.delayed(const Duration(milliseconds: 25), () {
|
await Future.delayed(const Duration(milliseconds: 25), () {
|
||||||
setState(() {
|
setState(() {
|
||||||
currentPercentage = i;
|
currentPercentage = i;
|
||||||
|
|
@ -62,8 +66,7 @@ class _CircleChartState extends State<CircleChart> {
|
||||||
return Column(
|
return Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Icon(BootstrapIcons.droplet_fill,
|
Icon(widget.icon, size: 32, color: _getAnimatedColor(value)),
|
||||||
size: 32, color: _getAnimatedColor(value)),
|
|
||||||
Text(
|
Text(
|
||||||
'${value.toStringAsFixed(0)}%', // Animated percentage text
|
'${value.toStringAsFixed(0)}%', // Animated percentage text
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
|
|
@ -81,11 +84,11 @@ class _CircleChartState extends State<CircleChart> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<PieChartSectionData> _createSections(double humidityPercentage) {
|
List<PieChartSectionData> _createSections(double percentage) {
|
||||||
return [
|
return [
|
||||||
PieChartSectionData(
|
PieChartSectionData(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
value: humidityPercentage,
|
value: percentage,
|
||||||
title: '',
|
title: '',
|
||||||
radius: 50, // Size of the pie slice
|
radius: 50, // Size of the pie slice
|
||||||
titleStyle: const TextStyle(
|
titleStyle: const TextStyle(
|
||||||
|
|
@ -96,7 +99,7 @@ class _CircleChartState extends State<CircleChart> {
|
||||||
),
|
),
|
||||||
PieChartSectionData(
|
PieChartSectionData(
|
||||||
color: Colors.white24,
|
color: Colors.white24,
|
||||||
value: 100 - humidityPercentage,
|
value: 100 - percentage,
|
||||||
title: '',
|
title: '',
|
||||||
radius: 50,
|
radius: 50,
|
||||||
),
|
),
|
||||||
|
|
@ -105,6 +108,6 @@ 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.humidityPercentage)!;
|
Colors.green, Colors.blue, percentage / widget.percentage)!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
import 'package:agrilink_vocpro/core/constant/app_theme.dart';
|
||||||
|
import 'package:agrilink_vocpro/features/home/pages/humidity/widgets/circle_chart.dart';
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class SoilMoistureScreen extends StatelessWidget {
|
||||||
|
const SoilMoistureScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text('Soil Moisture', style: AppTheme.labelMedium),
|
||||||
|
centerTitle: true,
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
leading: IconButton(
|
||||||
|
icon: const Icon(CupertinoIcons.back),
|
||||||
|
onPressed: () => Navigator.pop(context),
|
||||||
|
),
|
||||||
|
actions: const [
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(right: 16),
|
||||||
|
child: Icon(
|
||||||
|
Icons.water_outlined,
|
||||||
|
color: Colors.blue,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: SafeArea(
|
||||||
|
child: ListView(
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: MediaQuery.of(context).size.height * 0.05,
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 320,
|
||||||
|
width: double.infinity,
|
||||||
|
child: CircleChart(
|
||||||
|
percentage: 60.5,
|
||||||
|
icon: Icons.water_outlined,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
import 'package:agrilink_vocpro/core/constant/app_theme.dart';
|
||||||
|
import 'package:bootstrap_icons/bootstrap_icons.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class TemperatureScreen extends StatelessWidget {
|
||||||
|
const TemperatureScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text('Temperature', style: AppTheme.titleLarge),
|
||||||
|
centerTitle: true,
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
actions: const [
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(right: 16),
|
||||||
|
child: Icon(
|
||||||
|
BootstrapIcons.thermometer_half,
|
||||||
|
color: Colors.red,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: SafeArea(
|
||||||
|
child: ListView(
|
||||||
|
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),
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,11 +1,15 @@
|
||||||
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/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/provider/home_provider.dart';
|
||||||
import 'package:agrilink_vocpro/features/home/widgets/data_display_widget.dart';
|
import 'package:agrilink_vocpro/features/home/widgets/data_display_widget.dart';
|
||||||
import 'package:agrilink_vocpro/features/humidity/view/humidity_screen.dart';
|
import 'package:agrilink_vocpro/features/home/pages/humidity/view/humidity_screen.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:go_router/go_router.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
class HomeScreen extends StatelessWidget {
|
class HomeScreen extends StatelessWidget {
|
||||||
|
|
@ -14,14 +18,6 @@ class HomeScreen extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
|
||||||
title: Text(
|
|
||||||
'Home',
|
|
||||||
style: AppTheme.titleMedium,
|
|
||||||
),
|
|
||||||
centerTitle: true,
|
|
||||||
backgroundColor: Colors.white,
|
|
||||||
),
|
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: RefreshIndicator(
|
child: RefreshIndicator(
|
||||||
onRefresh: () async {
|
onRefresh: () async {
|
||||||
|
|
@ -29,12 +25,35 @@ class HomeScreen extends StatelessWidget {
|
||||||
},
|
},
|
||||||
child: ListView(
|
child: ListView(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
const CircleAvatar(
|
||||||
|
radius: 24,
|
||||||
|
backgroundColor: AppColor.primary,
|
||||||
|
child: Icon(
|
||||||
|
Icons.person,
|
||||||
|
color: Colors.white,
|
||||||
|
size: 24,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 16),
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'${getGreeting(DateTime.now().toString())}, Fikril',
|
getGreeting(DateTime.now().toString()),
|
||||||
|
style: AppTheme.titleMedium,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'Fikril Mahesaputra',
|
||||||
style: AppTheme.titleLarge,
|
style: AppTheme.titleLarge,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
Consumer<HomeProvider>(builder: (context, provider, child) {
|
Consumer<HomeProvider>(builder: (context, provider, child) {
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
|
|
@ -83,7 +102,7 @@ class HomeScreen extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Text('Recent Activity', style: AppTheme.titleMedium),
|
Text('Linked Device', style: AppTheme.titleMedium),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
GridView(
|
GridView(
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
|
@ -104,19 +123,22 @@ class HomeScreen extends StatelessWidget {
|
||||||
color: AppColor.secondary,
|
color: AppColor.secondary,
|
||||||
iconColor: Colors.white,
|
iconColor: Colors.white,
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
Navigator.push(
|
context.push(AppRoute.humidity, extra: '60');
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => const HumidityScreen()));
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const DataDisplayerWidget(
|
DataDisplayerWidget(
|
||||||
title: 'Temperature',
|
title: 'Temperature',
|
||||||
subtitle: 'suhu greenhouse',
|
subtitle: 'suhu greenhouse',
|
||||||
value: '28',
|
value: '28',
|
||||||
unit: '°C',
|
unit: '°C',
|
||||||
icon: BootstrapIcons.thermometer_half,
|
icon: BootstrapIcons.thermometer_half,
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
|
onTap: () async {
|
||||||
|
await Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => const TemperatureScreen()));
|
||||||
|
},
|
||||||
),
|
),
|
||||||
const DataDisplayerWidget(
|
const DataDisplayerWidget(
|
||||||
title: 'Light',
|
title: 'Light',
|
||||||
|
|
@ -126,13 +148,29 @@ class HomeScreen extends StatelessWidget {
|
||||||
icon: BootstrapIcons.sun,
|
icon: BootstrapIcons.sun,
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
),
|
),
|
||||||
const DataDisplayerWidget(
|
DataDisplayerWidget(
|
||||||
title: 'Soil Moisture',
|
title: 'Soil Moisture',
|
||||||
subtitle: 'kelembaban tanah',
|
subtitle: 'kelembaban tanah',
|
||||||
value: '40',
|
value: '40',
|
||||||
unit: '%',
|
unit: '%',
|
||||||
icon: Icons.water_outlined,
|
icon: Icons.water_outlined,
|
||||||
color: Colors.white,
|
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,
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,8 @@
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:agrilink_vocpro/core/route/app_route.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
class SplashScreen extends StatefulWidget {
|
class SplashScreen extends StatefulWidget {
|
||||||
const SplashScreen({super.key});
|
const SplashScreen({super.key});
|
||||||
|
|
@ -8,8 +12,64 @@ class SplashScreen extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _SplashScreenState extends State<SplashScreen> {
|
class _SplashScreenState extends State<SplashScreen> {
|
||||||
|
bool isLoggedIn = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _initialize() async {
|
||||||
|
// final authProvider = Provider.of<AuthProvider>(context, listen: false);
|
||||||
|
// bool isLoggedIn = await _checkLoginStatus(authProvider);
|
||||||
|
_navigateAfterSplash(isLoggedIn);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Future<bool> _checkLoginStatus(AuthProvider authProvider) async {
|
||||||
|
// SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
|
|
||||||
|
// if (prefs.getKeys().isEmpty) return false;
|
||||||
|
|
||||||
|
// if (prefs.getBool('isLoggedIn') == true) {
|
||||||
|
// String? token = prefs.getString('token');
|
||||||
|
// String? refreshToken = prefs.getString('refreshToken');
|
||||||
|
|
||||||
|
// if (token != null && !JwtDecoder.isExpired(token)) {
|
||||||
|
// return true;
|
||||||
|
// } else if (refreshToken != null && !JwtDecoder.isExpired(refreshToken)) {
|
||||||
|
// final result = await authProvider.refreshToken();
|
||||||
|
// return result == ResultState.hasData;
|
||||||
|
// } else {
|
||||||
|
// prefs.remove('token');
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
|
||||||
|
void _navigateAfterSplash(bool isLoggedIn) {
|
||||||
|
Timer(const Duration(seconds: 2), () {
|
||||||
|
if (isLoggedIn == true) {
|
||||||
|
context.go(AppRoute.dashboard);
|
||||||
|
} else {
|
||||||
|
context.go(AppRoute.login);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return const Placeholder();
|
return Scaffold(
|
||||||
|
body: SafeArea(
|
||||||
|
child: Center(
|
||||||
|
child: Image.asset(
|
||||||
|
'assets/images/app_logo.png',
|
||||||
|
width: 80,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
|
import 'package:agrilink_vocpro/core/route/app_route.dart';
|
||||||
|
import 'package:agrilink_vocpro/features/auth/provider/auth_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/dashboard/view/dashboard_screen.dart';
|
|
||||||
import 'package:agrilink_vocpro/features/home/provider/home_provider.dart';
|
import 'package:agrilink_vocpro/features/home/provider/home_provider.dart';
|
||||||
import 'package:agrilink_vocpro/features/home/view/home_screen.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
|
@ -17,10 +17,11 @@ class MyApp extends StatelessWidget {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MultiProvider(
|
return MultiProvider(
|
||||||
providers: [
|
providers: [
|
||||||
|
ChangeNotifierProvider(create: (context) => AuthProvider()),
|
||||||
ChangeNotifierProvider(create: (context) => HomeProvider()),
|
ChangeNotifierProvider(create: (context) => HomeProvider()),
|
||||||
ChangeNotifierProvider(create: (context) => DashboardProvider()),
|
ChangeNotifierProvider(create: (context) => DashboardProvider()),
|
||||||
],
|
],
|
||||||
child: MaterialApp(
|
child: MaterialApp.router(
|
||||||
debugShowCheckedModeBanner: false,
|
debugShowCheckedModeBanner: false,
|
||||||
title: 'Flutter Demo',
|
title: 'Flutter Demo',
|
||||||
theme: ThemeData(
|
theme: ThemeData(
|
||||||
|
|
@ -28,7 +29,7 @@ class MyApp extends StatelessWidget {
|
||||||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.teal),
|
colorScheme: ColorScheme.fromSeed(seedColor: Colors.teal),
|
||||||
useMaterial3: true,
|
useMaterial3: true,
|
||||||
),
|
),
|
||||||
home: const DashboardScreen(),
|
routerConfig: AppRoute.router,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,10 @@
|
||||||
import FlutterMacOS
|
import FlutterMacOS
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
import path_provider_foundation
|
||||||
import shared_preferences_foundation
|
import shared_preferences_foundation
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
|
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.18.0"
|
version: "1.18.0"
|
||||||
|
crypto:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: crypto
|
||||||
|
sha256: ec30d999af904f33454ba22ed9a86162b35e52b44ac4807d1d93c288041d7d27
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.5"
|
||||||
cupertino_icons:
|
cupertino_icons:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
@ -126,14 +134,6 @@ 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
|
||||||
|
|
@ -152,6 +152,22 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "14.2.7"
|
version: "14.2.7"
|
||||||
|
google_fonts:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: google_fonts
|
||||||
|
sha256: b1ac0fe2832c9cc95e5e88b57d627c5e68c223b9657f4b96e1487aa9098c7b82
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.2.1"
|
||||||
|
http:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: http
|
||||||
|
sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.2"
|
||||||
http_parser:
|
http_parser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -248,6 +264,30 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.9.0"
|
version: "1.9.0"
|
||||||
|
path_provider:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider
|
||||||
|
sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.4"
|
||||||
|
path_provider_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_android
|
||||||
|
sha256: "6f01f8e37ec30b07bc424b4deabac37cacb1bc7e2e515ad74486039918a37eb7"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.10"
|
||||||
|
path_provider_foundation:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_foundation
|
||||||
|
sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.0"
|
||||||
path_provider_linux:
|
path_provider_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
||||||
|
|
@ -39,9 +39,9 @@ dependencies:
|
||||||
intl: ^0.19.0
|
intl: ^0.19.0
|
||||||
dio: ^5.7.0
|
dio: ^5.7.0
|
||||||
go_router: ^14.2.7
|
go_router: ^14.2.7
|
||||||
flutter_screenutil: ^5.9.3
|
|
||||||
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
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
|
||||||