diff --git a/agrilink_vocpro/android/app/src/main/AndroidManifest.xml b/agrilink_vocpro/android/app/src/main/AndroidManifest.xml index 11f5c79..7f64aa4 100644 --- a/agrilink_vocpro/android/app/src/main/AndroidManifest.xml +++ b/agrilink_vocpro/android/app/src/main/AndroidManifest.xml @@ -2,7 +2,8 @@ + android:icon="@mipmap/ic_launcher" + android:enableOnBackInvokedCallback="true"> const HumidityScreen(), ), + GoRoute( + path: 'light/:value', + builder: (context, state) { + final double value = + double.tryParse(state.pathParameters['value'] ?? '') ?? 0.0; + return LightScreen(lightIntensity: value); + }, + ) ], ), ], diff --git a/agrilink_vocpro/lib/features/control/provider/control_provider.dart b/agrilink_vocpro/lib/features/control/provider/control_provider.dart new file mode 100644 index 0000000..07e0594 --- /dev/null +++ b/agrilink_vocpro/lib/features/control/provider/control_provider.dart @@ -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(); + } +} diff --git a/agrilink_vocpro/lib/features/control/view/control_screen.dart b/agrilink_vocpro/lib/features/control/view/control_screen.dart new file mode 100644 index 0000000..a049203 --- /dev/null +++ b/agrilink_vocpro/lib/features/control/view/control_screen.dart @@ -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(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(); + }, + ), + ), + ], + )); + }), + ); + } +} diff --git a/agrilink_vocpro/lib/features/dashboard/model/censor_data_rule.dart b/agrilink_vocpro/lib/features/dashboard/model/censor_data_rule.dart new file mode 100644 index 0000000..7406b71 --- /dev/null +++ b/agrilink_vocpro/lib/features/dashboard/model/censor_data_rule.dart @@ -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, + }); +} diff --git a/agrilink_vocpro/lib/features/dashboard/provider/dashboard_provider.dart b/agrilink_vocpro/lib/features/dashboard/provider/dashboard_provider.dart index 2d2c99e..8509b6b 100644 --- a/agrilink_vocpro/lib/features/dashboard/provider/dashboard_provider.dart +++ b/agrilink_vocpro/lib/features/dashboard/provider/dashboard_provider.dart @@ -1,4 +1,6 @@ +import 'package:agrilink_vocpro/features/control/view/control_screen.dart'; import 'package:agrilink_vocpro/features/home/view/home_screen.dart'; +import 'package:agrilink_vocpro/features/plants/view/plants_screen.dart'; import 'package:flutter/material.dart'; class DashboardProvider extends ChangeNotifier { @@ -14,8 +16,8 @@ class DashboardProvider extends ChangeNotifier { final List _screens = [ const HomeScreen(), - const Center(child: Text('Control')), - const Center(child: Text('Plants')), + const ControlScreen(), + const PlantsScreen(), const Center(child: Text('Settings')), ]; diff --git a/agrilink_vocpro/lib/features/home/pages/humidity/view/humidity_screen.dart b/agrilink_vocpro/lib/features/home/pages/humidity/view/humidity_screen.dart index be3c6fc..f6d0151 100644 --- a/agrilink_vocpro/lib/features/home/pages/humidity/view/humidity_screen.dart +++ b/agrilink_vocpro/lib/features/home/pages/humidity/view/humidity_screen.dart @@ -1,8 +1,11 @@ import 'package:agrilink_vocpro/core/constant/app_theme.dart'; import 'package:agrilink_vocpro/features/home/pages/humidity/widgets/circle_chart.dart'; +import 'package:agrilink_vocpro/features/home/provider/home_provider.dart'; import 'package:bootstrap_icons/bootstrap_icons.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:provider/provider.dart'; class HumidityScreen extends StatelessWidget { const HumidityScreen({super.key}); @@ -13,6 +16,7 @@ class HumidityScreen extends StatelessWidget { appBar: AppBar( title: Text('Humidity', style: AppTheme.labelLarge), centerTitle: true, + scrolledUnderElevation: 0, leading: IconButton( icon: const Icon(CupertinoIcons.back), onPressed: () => Navigator.pop(context), @@ -29,22 +33,76 @@ class HumidityScreen extends StatelessWidget { ], ), body: SafeArea( - child: ListView( - children: [ - SizedBox( - height: MediaQuery.of(context).size.height * 0.05, - ), - const SizedBox( - height: 320, - width: double.infinity, - child: CircleChart( - percentage: 60.5, - icon: BootstrapIcons.droplet_half, + child: Consumer(builder: (context, provider, child) { + return ListView( + children: [ + SizedBox( + height: MediaQuery.of(context).size.height * 0.05, ), - ), - const SizedBox(height: 16), - ], - ), + const SizedBox( + height: 320, + width: double.infinity, + child: CircleChart( + percentage: 60.5, + icon: BootstrapIcons.droplet_half, + ), + ), + const SizedBox(height: 16), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'Humidity', + style: AppTheme.labelMedium, + textAlign: TextAlign.center, + ), + IconButton( + iconSize: 20.r, + color: Colors.blue, + onPressed: () {}, + icon: const Icon(BootstrapIcons.info_circle)) + ], + ), + const SizedBox(height: 16), + Padding( + padding: EdgeInsets.only(left: 16.w), + child: const Text('Deskripsi'), + ), + const SizedBox(height: 16), + ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: provider.humidtyRules.length, + itemBuilder: (context, index) { + final item = provider.humidtyRules[index]; + return Theme( + data: Theme.of(context) + .copyWith(dividerColor: Colors.transparent), + child: ExpansionTile( + trailing: Text( + item.censorText, + style: TextStyle(color: item.color), + ), + expandedCrossAxisAlignment: CrossAxisAlignment.start, + childrenPadding: EdgeInsets.all(16.r), + title: Text( + 'Kelembaban ${item.minPercentage}% - ${item.maxPercentage}%'), + children: [ + Text( + item.description, + style: AppTheme.hintStyle, + ), + SizedBox(height: 8.h), + Text('Tindakan', style: AppTheme.labelMedium), + SizedBox(height: 8.h), + Text(item.action), + ], + ), + ); + }) + ], + ); + }), ), ); } diff --git a/agrilink_vocpro/lib/features/home/pages/humidity/widgets/circle_chart.dart b/agrilink_vocpro/lib/features/home/pages/humidity/widgets/circle_chart.dart index bb515bc..f6d353b 100644 --- a/agrilink_vocpro/lib/features/home/pages/humidity/widgets/circle_chart.dart +++ b/agrilink_vocpro/lib/features/home/pages/humidity/widgets/circle_chart.dart @@ -6,10 +6,14 @@ class CircleChart extends StatefulWidget { super.key, required this.percentage, required this.icon, + this.colorStart, + this.colorEnd, }); final double percentage; final IconData icon; + final Color? colorStart; + final Color? colorEnd; @override State createState() => _CircleChartState(); @@ -108,6 +112,9 @@ class _CircleChartState extends State { Color _getAnimatedColor(double percentage) { return Color.lerp( - Colors.green, Colors.blue, percentage / widget.percentage)!; + widget.colorStart ?? Colors.green, + widget.colorEnd ?? Colors.blue, + percentage / widget.percentage, + )!; } } diff --git a/agrilink_vocpro/lib/features/home/pages/light/view/light_screen.dart b/agrilink_vocpro/lib/features/home/pages/light/view/light_screen.dart new file mode 100644 index 0000000..5d811b0 --- /dev/null +++ b/agrilink_vocpro/lib/features/home/pages/light/view/light_screen.dart @@ -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, + // ), + // ], + // ), + // ), + // ], + // ) + ], + ), + ), + ); + } +} diff --git a/agrilink_vocpro/lib/features/home/pages/soil_moisture/view/soil_moisture_screen.dart b/agrilink_vocpro/lib/features/home/pages/soil_moisture/view/soil_moisture_screen.dart index b7ea90e..ebfc919 100644 --- a/agrilink_vocpro/lib/features/home/pages/soil_moisture/view/soil_moisture_screen.dart +++ b/agrilink_vocpro/lib/features/home/pages/soil_moisture/view/soil_moisture_screen.dart @@ -39,6 +39,8 @@ class SoilMoistureScreen extends StatelessWidget { child: CircleChart( percentage: 60.5, icon: Icons.water_outlined, + colorStart: Colors.lime, + colorEnd: Colors.brown, ), ), const SizedBox(height: 16), diff --git a/agrilink_vocpro/lib/features/home/pages/temperature/view/temperature_screen.dart b/agrilink_vocpro/lib/features/home/pages/temperature/view/temperature_screen.dart index 7cf42b1..149374c 100644 --- a/agrilink_vocpro/lib/features/home/pages/temperature/view/temperature_screen.dart +++ b/agrilink_vocpro/lib/features/home/pages/temperature/view/temperature_screen.dart @@ -1,10 +1,14 @@ import 'package:agrilink_vocpro/core/constant/app_theme.dart'; import 'package:bootstrap_icons/bootstrap_icons.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:gauge_indicator/gauge_indicator.dart'; class TemperatureScreen extends StatelessWidget { const TemperatureScreen({super.key}); + double get value => 29; + @override Widget build(BuildContext context) { return Scaffold( @@ -12,6 +16,7 @@ class TemperatureScreen extends StatelessWidget { title: Text('Temperature', style: AppTheme.titleLarge), centerTitle: true, backgroundColor: Colors.white, + scrolledUnderElevation: 0, actions: const [ Padding( padding: EdgeInsets.only(right: 16), @@ -24,22 +29,177 @@ class TemperatureScreen extends StatelessWidget { ), body: SafeArea( child: ListView( + padding: EdgeInsets.all(16.w), children: [ SizedBox( height: MediaQuery.of(context).size.height * 0.05, ), - Center( - child: SizedBox( - height: 320, - width: 320, - child: CircularProgressIndicator( - strokeWidth: 10, - backgroundColor: Colors.grey.shade200, - value: 0.5, - valueColor: const AlwaysStoppedAnimation(Colors.red), + SizedBox( + height: 240.h, + child: Stack( + fit: StackFit.expand, + children: [ + Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + height: 80.h, + ), + const Icon(BootstrapIcons.thermometer_half, + size: 32, color: Colors.orange), + Text( + '${value.toStringAsFixed(0)}°C', // Animated percentage text + style: const TextStyle( + fontSize: 28, + fontWeight: FontWeight.bold, + color: Colors.black, + ), + ), + ], + ), + ), + AnimatedRadialGauge( + duration: const Duration(seconds: 2), + curve: Curves.easeOut, + value: value, + axis: GaugeAxis( + degrees: 240, + min: 0, + max: 56.7, + style: GaugeAxisStyle( + background: Colors.grey.shade100, + thickness: 50, + ), + progressBar: const GaugeBasicProgressBar( + gradient: GaugeAxisGradient(colors: [ + Colors.blue, + Colors.orange, + ]), + ), + ), + ), + ], ), - )), + ), const SizedBox(height: 16), + const SizedBox(height: 16), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'Temperature', + style: AppTheme.labelMedium, + textAlign: TextAlign.center, + ), + IconButton( + iconSize: 20.r, + color: Colors.blue, + onPressed: () {}, + icon: const Icon(BootstrapIcons.info_circle)) + ], + ), + SizedBox(height: 16.h), + const Text('Deskripsi'), + SizedBox(height: 16.h), + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Container( + height: 100.h, + width: 100.w, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(16), + color: Colors.blue.withOpacity(0.1), + border: Border.all( + color: Colors.blue, + width: 2, + ), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('Low', + style: AppTheme.labelMedium + .copyWith(color: Colors.blue)), + // SizedBox(height: 8.h), + // const Icon( + // BootstrapIcons.thermometer_low, + // color: Colors.blue, + // ), + SizedBox(height: 8.h), + Text( + '<20°C', + style: AppTheme.labelMedium, + textAlign: TextAlign.center, + ), + ], + ), + ), + Container( + height: 100.h, + width: 100.w, + decoration: BoxDecoration( + color: Colors.green.withOpacity(0.1), + borderRadius: BorderRadius.circular(16), + border: Border.all( + color: Colors.green, + width: 2, + ), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('Ideal', + style: AppTheme.labelMedium + .copyWith(color: Colors.green)), + // SizedBox(height: 8.h), + // const Icon( + // BootstrapIcons.thermometer_half, + // color: Colors.green, + // ), + SizedBox(height: 8.h), + Text( + '20-30°C', + style: AppTheme.labelMedium, + textAlign: TextAlign.center, + ), + ], + ), + ), + Container( + height: 100.h, + width: 100.w, + decoration: BoxDecoration( + color: Colors.orange.withOpacity(0.1), + borderRadius: BorderRadius.circular(16), + border: Border.all( + color: Colors.orange.shade800, + width: 2, + ), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('high', + style: AppTheme.labelMedium + .copyWith(color: Colors.orange)), + // SizedBox(height: 8.h), + // const Icon( + // BootstrapIcons.thermometer_high, + // color: Colors.orange, + // ), + SizedBox(height: 8.h), + Text( + '>30°C', + style: AppTheme.labelMedium, + textAlign: TextAlign.center, + ), + ], + ), + ), + ], + ) ], ), ), diff --git a/agrilink_vocpro/lib/features/home/provider/home_provider.dart b/agrilink_vocpro/lib/features/home/provider/home_provider.dart index 438e0da..f42a083 100644 --- a/agrilink_vocpro/lib/features/home/provider/home_provider.dart +++ b/agrilink_vocpro/lib/features/home/provider/home_provider.dart @@ -1,5 +1,59 @@ +import 'package:agrilink_vocpro/features/dashboard/model/censor_data_rule.dart'; import 'package:flutter/material.dart'; class HomeProvider extends ChangeNotifier { final DateTime currentDate = DateTime.now(); + + List 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, + ), + ]; } diff --git a/agrilink_vocpro/lib/features/home/view/home_screen.dart b/agrilink_vocpro/lib/features/home/view/home_screen.dart index 578c2a1..1f746a1 100644 --- a/agrilink_vocpro/lib/features/home/view/home_screen.dart +++ b/agrilink_vocpro/lib/features/home/view/home_screen.dart @@ -1,61 +1,66 @@ import 'package:agrilink_vocpro/core/constant/app_color.dart'; import 'package:agrilink_vocpro/core/constant/app_theme.dart'; import 'package:agrilink_vocpro/core/extension/extention.dart'; -import 'package:agrilink_vocpro/core/route/app_route.dart'; -import 'package:agrilink_vocpro/features/home/pages/soil_moisture/view/soil_moisture_screen.dart'; -import 'package:agrilink_vocpro/features/home/pages/temperature/view/temperature_screen.dart'; -import 'package:agrilink_vocpro/features/home/provider/home_provider.dart'; -import 'package:agrilink_vocpro/features/home/widgets/data_display_widget.dart'; -import 'package:agrilink_vocpro/features/home/pages/humidity/view/humidity_screen.dart'; -import 'package:bootstrap_icons/bootstrap_icons.dart'; +import 'package:agrilink_vocpro/features/home/widgets/list_data_from_censor_npk1.dart'; +import 'package:agrilink_vocpro/features/home/widgets/list_data_from_censor_npk2.dart'; +import 'package:agrilink_vocpro/features/home/widgets/list_data_from_main_censor.dart'; +import 'package:animated_segmented_tab_control/animated_segmented_tab_control.dart'; import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; -import 'package:provider/provider.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; -class HomeScreen extends StatelessWidget { +class HomeScreen extends StatefulWidget { const HomeScreen({super.key}); + @override + State createState() => _HomeScreenState(); +} + +class _HomeScreenState extends State { @override Widget build(BuildContext context) { - return Scaffold( - body: SafeArea( - child: RefreshIndicator( - onRefresh: () async { - await Future.delayed(const Duration(seconds: 1)); - }, - child: ListView( - padding: const EdgeInsets.all(16), - children: [ - Row( - children: [ - const CircleAvatar( - radius: 24, - backgroundColor: AppColor.primary, - child: Icon( - Icons.person, - color: Colors.white, - size: 24, + return SafeArea( + child: Scaffold( + appBar: AppBar( + toolbarHeight: 200.h, + elevation: 0, + backgroundColor: Colors.white, + scrolledUnderElevation: 0, + flexibleSpace: Padding( + padding: EdgeInsets.symmetric(horizontal: 12.w), + child: Column( + children: [ + Row( + children: [ + CircleAvatar( + radius: 24.r, + backgroundColor: AppColor.primary, + child: Icon( + Icons.person, + color: Colors.white, + size: 24.r, + ), ), - ), - const SizedBox(width: 16), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - getGreeting(DateTime.now().toString()), - style: AppTheme.titleMedium, - ), - Text( - 'Fikril Mahesaputra', - style: AppTheme.titleLarge, - ), - ], - ), - ], - ), - const SizedBox(height: 24), - Consumer(builder: (context, provider, child) { - return Container( + SizedBox(width: 16.w), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + getGreeting(DateTime.now().toString()), + style: AppTheme.labelSmall, + ), + Text( + 'Fikril Mahesaputra', + style: AppTheme.labelMedium, + ), + ], + ), + ], + ), + SizedBox( + height: 8.h, + ), + Container( padding: const EdgeInsets.all(16), height: MediaQuery.of(context).size.height * 0.17, decoration: BoxDecoration( @@ -99,84 +104,47 @@ class HomeScreen extends StatelessWidget { ), ], ), - ); - }), - const SizedBox(height: 16), - Text('Linked Device', style: AppTheme.titleMedium), - const SizedBox(height: 16), - GridView( - physics: const NeverScrollableScrollPhysics(), - shrinkWrap: true, - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 2, - crossAxisSpacing: 16, - mainAxisSpacing: 16, ), - children: [ - DataDisplayerWidget( - title: 'Humidity', - subtitle: 'kelembaban udara', - value: '60', - unit: '%', - icon: BootstrapIcons.droplet_half, - textColor: Colors.white, - color: AppColor.secondary, - iconColor: Colors.white, - onTap: () async { - context.push(AppRoute.humidity, extra: '60'); - }, - ), - DataDisplayerWidget( - title: 'Temperature', - subtitle: 'suhu greenhouse', - value: '28', - unit: '°C', - icon: BootstrapIcons.thermometer_half, - color: Colors.white, - onTap: () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const TemperatureScreen())); - }, - ), - const DataDisplayerWidget( - title: 'Light', - subtitle: 'intensitas cahaya', - value: '1000', - unit: 'lux', - icon: BootstrapIcons.sun, - color: Colors.white, - ), - DataDisplayerWidget( - title: 'Soil Moisture', - subtitle: 'kelembaban tanah', - value: '40', - unit: '%', - icon: Icons.water_outlined, - color: Colors.white, - onTap: () async { - await Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const SoilMoistureScreen(), - ), - ); - }, - ), - const DataDisplayerWidget( - title: 'Acid Level', - subtitle: 'tingkat keasaman', - value: '6.5', - unit: '', - icon: BootstrapIcons.pie_chart, - color: Colors.white, - ) - ], - ) - ], + ], + ), ), ), + body: DefaultTabController( + length: 3, + child: Stack( + children: [ + Padding( + padding: + EdgeInsets.symmetric(horizontal: 64.w, vertical: 16.h), + child: SegmentedTabControl( + height: 32.h, + tabTextColor: Colors.black, + tabPadding: EdgeInsets.zero, + barDecoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(24.r), + ), + indicatorDecoration: BoxDecoration( + color: AppColor.primary, + borderRadius: BorderRadius.circular(24.r), + ), + textStyle: AppTheme.labelSmall, + tabs: const [ + SegmentTab(label: 'Main Censor'), + SegmentTab(label: 'NPK 1'), + SegmentTab(label: 'NPK 2'), + ]), + ), + Padding( + padding: EdgeInsets.only(top: 64.h), + child: const TabBarView(children: [ + ListDataFromMainCensor(), + ListDataFromCensorNpk1(), + ListDataFromCensorNpk2(), + ]), + ) + ], + )), ), ); } diff --git a/agrilink_vocpro/lib/features/home/widgets/data_display_widget.dart b/agrilink_vocpro/lib/features/home/widgets/data_display_widget.dart index aac1391..f31afe2 100644 --- a/agrilink_vocpro/lib/features/home/widgets/data_display_widget.dart +++ b/agrilink_vocpro/lib/features/home/widgets/data_display_widget.dart @@ -1,5 +1,6 @@ import 'package:agrilink_vocpro/core/constant/app_theme.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; class DataDisplayerWidget extends StatelessWidget { const DataDisplayerWidget({ @@ -12,6 +13,7 @@ class DataDisplayerWidget extends StatelessWidget { this.iconColor = Colors.teal, this.textColor = Colors.black, required this.unit, + this.censorIdentifier, this.onTap, }); @@ -23,6 +25,7 @@ class DataDisplayerWidget extends StatelessWidget { final Color iconColor; final Color textColor; final String unit; + final String? censorIdentifier; final VoidCallback? onTap; @override @@ -33,49 +36,54 @@ class DataDisplayerWidget extends StatelessWidget { }, style: ElevatedButton.styleFrom( backgroundColor: color, - padding: const EdgeInsets.all(12), // Padding di dalam button + padding: EdgeInsets.all(12.r), // Padding di dalam button shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), // Bentuk sudut yang bundar + borderRadius: BorderRadius.circular(16.r), // Bentuk sudut yang bundar ), elevation: 20, // Efek bayangan shadowColor: Colors.grey.withOpacity(0.2), ), - child: SizedBox( - height: - MediaQuery.of(context).size.height * 0.2, // Mengatur tinggi button - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Icon(icon, color: iconColor, size: 32), - const SizedBox(height: 8), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - title, - style: AppTheme.labelMedium.copyWith(color: textColor), - ), - Text( - subtitle, - style: AppTheme.labelSmall - .copyWith(color: textColor.withOpacity(0.5)), - ), - ], + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + // Tidak perlu menggunakan mainAxisSize: MainAxisSize.min di sini + children: [ + Align( + alignment: Alignment.centerRight, + child: Text( + censorIdentifier ?? '', + style: AppTheme.labelSmall + .copyWith(color: textColor.withOpacity(0.5)), ), - const Spacer(), - Row( - children: [ - Text( - value, - style: AppTheme.headline1.copyWith(color: textColor), - ), - const SizedBox(width: 4), - Text(unit, - style: AppTheme.titleMedium.copyWith(color: textColor)), - ], - ), - ], - ), + ), + Icon(icon, color: iconColor, size: 32.r), + SizedBox(height: 8.h), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: AppTheme.labelMedium.copyWith(color: textColor), + ), + Text( + subtitle, + style: AppTheme.labelSmall + .copyWith(color: textColor.withOpacity(0.5)), + ), + ], + ), + const Spacer(), + Row( + children: [ + Text( + value, + style: AppTheme.headline1.copyWith(color: textColor), + ), + SizedBox(width: 4.w), + Text(unit, + style: AppTheme.titleMedium.copyWith(color: textColor)), + ], + ), + ], ), ); } diff --git a/agrilink_vocpro/lib/features/home/widgets/list_data_from_censor_npk1.dart b/agrilink_vocpro/lib/features/home/widgets/list_data_from_censor_npk1.dart new file mode 100644 index 0000000..64d21f2 --- /dev/null +++ b/agrilink_vocpro/lib/features/home/widgets/list_data_from_censor_npk1.dart @@ -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: () {}, + ), + ], + ); + } +} diff --git a/agrilink_vocpro/lib/features/home/widgets/list_data_from_censor_npk2.dart b/agrilink_vocpro/lib/features/home/widgets/list_data_from_censor_npk2.dart new file mode 100644 index 0000000..71f5013 --- /dev/null +++ b/agrilink_vocpro/lib/features/home/widgets/list_data_from_censor_npk2.dart @@ -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: () {}, + ), + ], + ); + } +} diff --git a/agrilink_vocpro/lib/features/home/widgets/list_data_from_main_censor.dart b/agrilink_vocpro/lib/features/home/widgets/list_data_from_main_censor.dart new file mode 100644 index 0000000..fb05633 --- /dev/null +++ b/agrilink_vocpro/lib/features/home/widgets/list_data_from_main_censor.dart @@ -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'); + }, + ), + ], + ); + } +} diff --git a/agrilink_vocpro/lib/features/plants/view/plants_screen.dart b/agrilink_vocpro/lib/features/plants/view/plants_screen.dart new file mode 100644 index 0000000..ec4a78f --- /dev/null +++ b/agrilink_vocpro/lib/features/plants/view/plants_screen.dart @@ -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), + )), + ); + } +} diff --git a/agrilink_vocpro/lib/main.dart b/agrilink_vocpro/lib/main.dart index dee1ef1..7d7f3c3 100644 --- a/agrilink_vocpro/lib/main.dart +++ b/agrilink_vocpro/lib/main.dart @@ -1,12 +1,20 @@ import 'package:agrilink_vocpro/core/route/app_route.dart'; import 'package:agrilink_vocpro/features/auth/provider/auth_provider.dart'; +import 'package:agrilink_vocpro/features/control/provider/control_provider.dart'; import 'package:agrilink_vocpro/features/dashboard/provider/dashboard_provider.dart'; import 'package:agrilink_vocpro/features/home/provider/home_provider.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:provider/provider.dart'; void main() { - runApp(const MyApp()); + WidgetsFlutterBinding.ensureInitialized(); + SystemChrome.setPreferredOrientations([ + DeviceOrientation.portraitUp, // Locks the app in portrait mode + ]).then((_) { + runApp(const MyApp()); + }); } class MyApp extends StatelessWidget { @@ -20,17 +28,23 @@ class MyApp extends StatelessWidget { ChangeNotifierProvider(create: (context) => AuthProvider()), ChangeNotifierProvider(create: (context) => HomeProvider()), ChangeNotifierProvider(create: (context) => DashboardProvider()), + ChangeNotifierProvider(create: (context) => ControlProvider()), ], - child: MaterialApp.router( - debugShowCheckedModeBanner: false, - title: 'Flutter Demo', - theme: ThemeData( - scaffoldBackgroundColor: Colors.white, - colorScheme: ColorScheme.fromSeed(seedColor: Colors.teal), - useMaterial3: true, - ), - routerConfig: AppRoute.router, - ), + child: ScreenUtilInit( + designSize: const Size(360, 800), + minTextAdapt: true, + builder: (_, context) { + return MaterialApp.router( + debugShowCheckedModeBanner: false, + title: 'Flutter Demo', + theme: ThemeData( + scaffoldBackgroundColor: Colors.white, + colorScheme: ColorScheme.fromSeed(seedColor: Colors.teal), + useMaterial3: true, + ), + routerConfig: AppRoute.router, + ); + }), ); } } diff --git a/agrilink_vocpro/pubspec.lock b/agrilink_vocpro/pubspec.lock index f50baf4..c10bae7 100644 --- a/agrilink_vocpro/pubspec.lock +++ b/agrilink_vocpro/pubspec.lock @@ -1,6 +1,14 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + animated_segmented_tab_control: + dependency: "direct main" + description: + name: animated_segmented_tab_control + sha256: "4c04f5510037b1c30dbed00efae77559cc4e11f0fe2207c2358252d1870e4ab1" + url: "https://pub.dev" + source: hosted + version: "2.0.0" async: dependency: transitive description: @@ -134,6 +142,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.0" + flutter_screenutil: + dependency: "direct main" + description: + name: flutter_screenutil + sha256: "8239210dd68bee6b0577aa4a090890342d04a136ce1c81f98ee513fc0ce891de" + url: "https://pub.dev" + source: hosted + version: "5.9.3" flutter_test: dependency: "direct dev" description: flutter @@ -144,6 +160,14 @@ packages: description: flutter source: sdk version: "0.0.0" + gauge_indicator: + dependency: "direct main" + description: + name: gauge_indicator + sha256: "483abfae360ddffdf7b89fbd25192f83d1a6513d5da18f2388131566ab0793fd" + url: "https://pub.dev" + source: hosted + version: "0.4.3" go_router: dependency: "direct main" description: diff --git a/agrilink_vocpro/pubspec.yaml b/agrilink_vocpro/pubspec.yaml index 015adc8..91f3ef9 100644 --- a/agrilink_vocpro/pubspec.yaml +++ b/agrilink_vocpro/pubspec.yaml @@ -42,6 +42,9 @@ dependencies: bootstrap_icons: ^1.11.3 fl_chart: ^0.69.0 google_fonts: ^6.2.1 + animated_segmented_tab_control: ^2.0.0 + flutter_screenutil: ^5.9.3 + gauge_indicator: ^0.4.3 dev_dependencies: flutter_test: