add some features

This commit is contained in:
oxel 2026-04-08 09:05:50 +07:00
parent ededd3b57b
commit c4f7c1b3a2
39 changed files with 754 additions and 56 deletions

View File

@ -5,7 +5,7 @@
<application
android:label="monitoring_jamur"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
android:icon="@mipmap/launcher_icon">
<activity
android:name=".MainActivity"
android:exported="true"

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -431,7 +431,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
@ -488,7 +488,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 295 B

After

Width:  |  Height:  |  Size: 906 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 B

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 450 B

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 282 B

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 462 B

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 704 B

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 B

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 586 B

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 862 B

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 862 B

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 762 B

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 419 KiB

View File

@ -0,0 +1,27 @@
import 'package:shared_preferences/shared_preferences.dart';
class UserSession {
static const String _keyUsername = 'username';
static SharedPreferences? _prefs;
static String? username;
/// Initialize Preferences and load existing session
static Future<void> init() async {
_prefs = await SharedPreferences.getInstance();
username = _prefs?.getString(_keyUsername);
}
/// Save username to persistent storage
static Future<void> saveSession(String user) async {
username = user;
await _prefs?.setString(_keyUsername, user);
}
/// Clear session from persistent storage
static Future<void> logout() async {
username = null;
await _prefs?.remove(_keyUsername);
}
static bool get isLoggedIn => username != null;
}

View File

@ -1,16 +1,180 @@
import 'package:flutter/material.dart';
import 'package:monitoring_jamur/core/theme/app_theme.dart';
import 'package:monitoring_jamur/core/session/user_session.dart';
import 'package:monitoring_jamur/features/auth/presentation/pages/login_page.dart';
class AccountPage extends StatelessWidget {
const AccountPage({super.key});
@override
Widget build(BuildContext context) {
return const Scaffold(
body: Center(
child: Text(
'Account Content Coming Soon',
style: TextStyle(color: AppTheme.textLight),
return Scaffold(
backgroundColor: AppTheme.backgroundBeige,
body: SafeArea(
child: SingleChildScrollView(
padding: const EdgeInsets.all(24.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Manajemen Akun',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: AppTheme.textDark,
),
),
const SizedBox(height: 32),
// Main Consolidated Frame
Container(
width: double.infinity,
padding: const EdgeInsets.all(24.0),
decoration: BoxDecoration(
color: AppTheme.surfaceWhite,
borderRadius: BorderRadius.circular(32),
boxShadow: [
BoxShadow(
color: Colors.black.withAlpha(10),
blurRadius: 20,
offset: const Offset(0, 10),
),
],
),
child: Column(
children: [
// Cool User Icon
_buildCoolUserIcon(),
const SizedBox(height: 24),
// Username Info
Text(
UserSession.username ?? 'Guest',
style: const TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: AppTheme.textDark,
),
),
const Text(
'Account Owner',
style: TextStyle(
fontSize: 14,
color: AppTheme.textLight,
),
),
const SizedBox(height: 24),
const Divider(height: 32, thickness: 1.0, color: AppTheme.backgroundBeige),
const SizedBox(height: 8),
// Menu Items
_buildMenuTile(
icon: Icons.settings_rounded,
title: 'Kelola Akun',
onTap: () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Fitur Kelola Akun segera hadir')),
);
},
),
const SizedBox(height: 12),
_buildMenuTile(
icon: Icons.logout_rounded,
title: 'Log out',
isDestructive: true,
onTap: () async {
await UserSession.logout();
if (context.mounted) {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (context) => const LoginPage()),
(route) => false,
);
}
},
),
],
),
),
],
),
),
),
);
}
Widget _buildCoolUserIcon() {
return Container(
width: 110,
height: 110,
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: const LinearGradient(
colors: [AppTheme.primaryGreen, Color(0xFF81C784)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
boxShadow: [
BoxShadow(
color: AppTheme.primaryGreen.withAlpha(40),
blurRadius: 15,
spreadRadius: 2,
),
],
),
child: Container(
margin: const EdgeInsets.all(4),
decoration: const BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
),
child: const Center(
child: Icon(
Icons.manage_accounts_rounded,
size: 56,
color: AppTheme.primaryGreen,
),
),
),
);
}
Widget _buildMenuTile({
required IconData icon,
required String title,
required VoidCallback onTap,
bool isDestructive = false,
}) {
final color = isDestructive ? Colors.red.shade700 : AppTheme.primaryGreen;
return GestureDetector(
onTap: onTap,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
decoration: BoxDecoration(
color: isDestructive ? Colors.red.withAlpha(10) : AppTheme.backgroundBeige.withAlpha(50),
borderRadius: BorderRadius.circular(16),
),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: isDestructive ? Colors.red.withAlpha(20) : AppTheme.primaryGreen.withAlpha(20),
shape: BoxShape.circle,
),
child: Icon(icon, color: color, size: 22),
),
const SizedBox(width: 16),
Expanded(
child: Text(
title,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: isDestructive ? color : AppTheme.textDark,
),
),
),
Icon(Icons.chevron_right_rounded, color: AppTheme.textLight.withAlpha(100)),
],
),
),
);

View File

@ -3,6 +3,7 @@ import '../../data/user_repository.dart';
import 'register_page.dart';
import 'package:monitoring_jamur/core/theme/app_theme.dart';
import 'package:monitoring_jamur/features/home/presentation/pages/main_screen.dart';
import 'package:monitoring_jamur/core/session/user_session.dart';
class LoginPage extends StatefulWidget {
const LoginPage({super.key});
@ -33,10 +34,11 @@ class _LoginPageState extends State<LoginPage> {
setState(() => _isLoading = false);
if (user != null) {
await UserSession.saveSession(_usernameController.text);
if (mounted) {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (context) => MainScreen()),
MaterialPageRoute(builder: (context) => const MainScreen()),
(route) => false,
);
}
@ -56,10 +58,10 @@ class _LoginPageState extends State<LoginPage> {
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(
Icons.nature_outlined,
size: 80,
color: AppTheme.primaryGreen,
Image.asset(
'lib/assets/mushroom.png',
height: 120,
fit: BoxFit.contain,
),
const SizedBox(height: 16),
const Text(

View File

@ -6,11 +6,62 @@ class HistoryPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return const Scaffold(
body: Center(
child: Text(
'History Content Coming Soon',
style: TextStyle(color: AppTheme.textLight),
return Scaffold(
backgroundColor: AppTheme.backgroundBeige,
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Histori Monitoring',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: AppTheme.textDark,
),
),
const SizedBox(height: 24),
Expanded(
child: Container(
width: double.infinity,
decoration: BoxDecoration(
color: AppTheme.surfaceWhite,
borderRadius: BorderRadius.circular(32),
boxShadow: [
BoxShadow(
color: Colors.black.withAlpha(5),
blurRadius: 20,
offset: const Offset(0, 10),
),
],
),
child: const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.history_rounded,
size: 64,
color: AppTheme.backgroundBeige,
),
SizedBox(height: 16),
Text(
'tidak ada data',
style: TextStyle(
fontSize: 16,
color: AppTheme.textLight,
fontWeight: FontWeight.w500,
),
),
],
),
),
),
),
],
),
),
),
);

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:monitoring_jamur/core/theme/app_theme.dart';
import 'package:monitoring_jamur/features/home/presentation/pages/statistics_page.dart';
import 'dart:math' as math;
class DashboardPage extends StatefulWidget {
@ -12,6 +13,12 @@ class DashboardPage extends StatefulWidget {
class _DashboardPageState extends State<DashboardPage> {
// Demo humidity value
double _humidity = 85.0;
bool _isAutoMode = true;
bool _isPumpManual = false;
bool _isLightManual = false;
bool get _pumpStatus => _isAutoMode ? (_humidity < 80) : _isPumpManual;
bool get _lightStatus => _isAutoMode ? (_humidity > 90) : _isLightManual;
@override
Widget build(BuildContext context) {
@ -74,6 +81,16 @@ class _DashboardPageState extends State<DashboardPage> {
],
),
),
const SizedBox(height: 32),
// Control Mode Selector
_buildModeSelector(),
const SizedBox(height: 24),
// Device Controls
_buildDeviceControls(),
const SizedBox(height: 32),
// Statistics Button
_buildStatisticsButton(context),
const SizedBox(height: 32),
],
),
),
@ -81,6 +98,188 @@ class _DashboardPageState extends State<DashboardPage> {
);
}
Widget _buildModeSelector() {
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withAlpha(5),
blurRadius: 10,
offset: const Offset(0, 4),
),
],
),
child: Row(
children: [
Expanded(
child: GestureDetector(
onTap: () => setState(() => _isAutoMode = true),
child: AnimatedContainer(
duration: const Duration(milliseconds: 300),
padding: const EdgeInsets.symmetric(vertical: 12),
decoration: BoxDecoration(
color: _isAutoMode ? AppTheme.primaryGreen : Colors.transparent,
borderRadius: BorderRadius.circular(16),
),
alignment: Alignment.center,
child: Text(
'Otomatis',
style: TextStyle(
fontWeight: FontWeight.bold,
color: _isAutoMode ? Colors.white : AppTheme.textLight,
),
),
),
),
),
Expanded(
child: GestureDetector(
onTap: () => setState(() => _isAutoMode = false),
child: AnimatedContainer(
duration: const Duration(milliseconds: 300),
padding: const EdgeInsets.symmetric(vertical: 12),
decoration: BoxDecoration(
color: !_isAutoMode ? AppTheme.primaryGreen : Colors.transparent,
borderRadius: BorderRadius.circular(16),
),
alignment: Alignment.center,
child: Text(
'Manual',
style: TextStyle(
fontWeight: FontWeight.bold,
color: !_isAutoMode ? Colors.white : AppTheme.textLight,
),
),
),
),
),
],
),
);
}
Widget _buildDeviceControls() {
return Column(
children: [
_buildDeviceTile(
title: 'Pompa Air',
isOn: _pumpStatus,
onChanged: _isAutoMode ? null : (val) => setState(() => _isPumpManual = val),
),
const SizedBox(height: 16),
_buildDeviceTile(
title: 'Lampu Pemanas',
isOn: _lightStatus,
onChanged: _isAutoMode ? null : (val) => setState(() => _isLightManual = val),
),
],
);
}
Widget _buildDeviceTile({
required String title,
required bool isOn,
required ValueChanged<bool>? onChanged,
}) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
decoration: BoxDecoration(
color: AppTheme.surfaceWhite,
borderRadius: BorderRadius.circular(24),
boxShadow: [
BoxShadow(
color: Colors.black.withAlpha(5),
blurRadius: 10,
offset: const Offset(0, 4),
),
],
),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: isOn ? AppTheme.primaryGreen.withAlpha(30) : Colors.grey.withAlpha(20),
shape: BoxShape.circle,
),
child: Icon(
title.contains('Pompa') ? Icons.water_drop_rounded : Icons.lightbulb_rounded,
color: isOn ? AppTheme.primaryGreen : Colors.grey,
size: 28,
),
),
const SizedBox(width: 20),
Expanded(
child: Text(
title,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: AppTheme.textDark,
),
),
),
if (onChanged == null)
Text(
isOn ? 'MENYALA' : 'MATI',
style: TextStyle(
fontWeight: FontWeight.w900,
color: isOn ? AppTheme.primaryGreen : AppTheme.textLight,
),
)
else
Switch(
value: isOn,
onChanged: onChanged,
activeColor: AppTheme.primaryGreen,
),
],
),
);
}
Widget _buildStatisticsButton(BuildContext context) {
return GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const StatisticsPage()),
);
},
child: Container(
padding: const EdgeInsets.symmetric(vertical: 20),
decoration: BoxDecoration(
color: AppTheme.surfaceWhite,
borderRadius: BorderRadius.circular(24),
boxShadow: [
BoxShadow(
color: AppTheme.primaryGreen.withAlpha(20),
blurRadius: 15,
offset: const Offset(0, 8),
),
],
),
child: const Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.bar_chart_rounded, color: AppTheme.primaryGreen, size: 28),
SizedBox(width: 12),
Text(
'Lihat Statistik',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: AppTheme.primaryGreen,
),
),
],
),
),
);
}
Widget _buildStatusCard() {
return Container(
width: double.infinity,

View File

@ -52,26 +52,52 @@ class _MainScreenState extends State<MainScreen> {
Widget _buildAnimatedBottomBar() {
return Container(
margin: const EdgeInsets.fromLTRB(16, 0, 16, 24),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
margin: const EdgeInsets.fromLTRB(24, 0, 24, 24),
height: 70,
decoration: BoxDecoration(
color: AppTheme.surfaceWhite,
borderRadius: BorderRadius.circular(30),
borderRadius: BorderRadius.circular(35),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.08),
blurRadius: 20,
offset: const Offset(0, 10),
blurRadius: 25,
offset: const Offset(0, 12),
),
],
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildNavItem(0, Icons.dashboard_outlined, Icons.dashboard, 'Dashboard'),
_buildNavItem(1, Icons.history_outlined, Icons.history, 'History'),
_buildNavItem(2, Icons.person_outline, Icons.person, 'Account'),
],
child: LayoutBuilder(
builder: (context, constraints) {
double totalWidth = constraints.maxWidth;
double itemWidth = totalWidth / 3;
return Stack(
children: [
// Animated Background Bubble
AnimatedPositioned(
duration: const Duration(milliseconds: 600),
curve: Curves.elasticOut,
left: _currentIndex * itemWidth + (itemWidth * 0.15),
top: 12,
child: Container(
width: itemWidth * 0.7,
height: 46,
decoration: BoxDecoration(
color: AppTheme.primaryGreen.withOpacity(0.1),
borderRadius: BorderRadius.circular(23),
),
),
),
// Navigation Items
Row(
children: [
_buildNavItem(0, Icons.grid_view_outlined, Icons.grid_view_rounded, 'Home'),
_buildNavItem(1, Icons.history_rounded, Icons.history_rounded, 'History'),
_buildNavItem(2, Icons.person_outline_rounded, Icons.person_rounded, 'Account'),
],
),
],
);
},
),
);
}
@ -79,44 +105,38 @@ class _MainScreenState extends State<MainScreen> {
Widget _buildNavItem(int index, IconData outlineIcon, IconData filledIcon, String label) {
bool isSelected = _currentIndex == index;
return GestureDetector(
onTap: () => _onItemTapped(index),
behavior: HitTestBehavior.opaque,
child: AnimatedContainer(
duration: const Duration(milliseconds: 300),
curve: Curves.easeOutBack,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
decoration: BoxDecoration(
color: isSelected ? AppTheme.primaryGreen.withOpacity(0.12) : Colors.transparent,
borderRadius: BorderRadius.circular(20),
),
child: Row(
return Expanded(
child: GestureDetector(
onTap: () => _onItemTapped(index),
behavior: HitTestBehavior.opaque,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
AnimatedScale(
scale: isSelected ? 1.15 : 1.0,
scale: isSelected ? 1.0 : 0.9,
duration: const Duration(milliseconds: 300),
curve: Curves.easeOutBack,
child: Icon(
isSelected ? filledIcon : outlineIcon,
color: isSelected ? AppTheme.primaryGreen : AppTheme.textLight,
size: 26,
child: AnimatedContainer(
duration: const Duration(milliseconds: 300),
child: Icon(
isSelected ? filledIcon : outlineIcon,
color: isSelected ? AppTheme.primaryGreen : AppTheme.textLight.withOpacity(0.6),
size: 26,
),
),
),
if (isSelected) ...[
const SizedBox(width: 8),
AnimatedOpacity(
opacity: isSelected ? 1.0 : 0.0,
if (isSelected)
AnimatedContainer(
duration: const Duration(milliseconds: 300),
child: Text(
label,
style: const TextStyle(
color: AppTheme.primaryGreen,
fontSize: 10,
fontWeight: FontWeight.bold,
fontSize: 14,
color: AppTheme.primaryGreen,
),
),
),
],
],
),
),

View File

@ -0,0 +1,143 @@
import 'package:flutter/material.dart';
import 'package:monitoring_jamur/core/theme/app_theme.dart';
class StatisticsPage extends StatelessWidget {
const StatisticsPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppTheme.backgroundBeige,
appBar: AppBar(
title: const Text('Statistik Monitoring'),
backgroundColor: Colors.transparent,
elevation: 0,
foregroundColor: AppTheme.textDark,
centerTitle: true,
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(24.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildSummaryRow(),
const SizedBox(height: 32),
_buildChartSection('Tren Kelembapan', [85, 82, 88, 84, 86, 85, 87], AppTheme.primaryGreen),
const SizedBox(height: 32),
_buildChartSection('Tren Suhu', [24, 25, 24, 26, 25, 24, 25], Colors.orange),
const SizedBox(height: 40),
_buildAnalysisCard(),
],
),
),
);
}
Widget _buildSummaryRow() {
return Row(
children: [
_buildStatCard('Rata-rata', '85%', Icons.water_drop_rounded, AppTheme.primaryGreen),
const SizedBox(width: 16),
_buildStatCard('Tertinggi', '89%', Icons.trending_up_rounded, Colors.blue),
],
);
}
Widget _buildStatCard(String label, String value, IconData icon, Color color) {
return Expanded(
child: Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: AppTheme.surfaceWhite,
borderRadius: BorderRadius.circular(24),
boxShadow: [
BoxShadow(
color: Colors.black.withAlpha(5),
blurRadius: 10,
offset: const Offset(0, 4),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(icon, color: color, size: 28),
const SizedBox(height: 12),
Text(value, style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
Text(label, style: const TextStyle(fontSize: 12, color: AppTheme.textLight)),
],
),
),
);
}
Widget _buildChartSection(String title, List<double> values, Color color) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: AppTheme.textDark),
),
const SizedBox(height: 16),
Container(
height: 180,
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: AppTheme.surfaceWhite,
borderRadius: BorderRadius.circular(24),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.end,
children: values.map((v) {
double height = (v / 100) * 120;
if (title.contains('Suhu')) height = (v / 40) * 120; // Scale for temperature
return Container(
width: 24,
height: height,
decoration: BoxDecoration(
color: color.withAlpha(50),
borderRadius: BorderRadius.circular(8),
border: Border.all(color: color, width: 2),
),
);
}).toList(),
),
),
],
);
}
Widget _buildAnalysisCard() {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
color: AppTheme.primaryGreen.withAlpha(20),
borderRadius: BorderRadius.circular(24),
border: Border.all(color: AppTheme.primaryGreen.withAlpha(50), width: 2),
),
child: const Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(Icons.auto_awesome_rounded, color: AppTheme.primaryGreen),
SizedBox(width: 12),
Text(
'Analisis Sistem',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: AppTheme.primaryGreen),
),
],
),
SizedBox(height: 12),
Text(
'Berdasarkan data 24 jam terakhir, kelembapan stabil di rentang ideal (80-90%). Pertumbuhan jamur terpantau optimal.',
style: TextStyle(fontSize: 14, color: AppTheme.textDark, height: 1.5),
),
],
),
);
}
}

View File

@ -3,6 +3,8 @@ import 'package:supabase_flutter/supabase_flutter.dart';
import 'package:monitoring_jamur/core/constants/supabase_config.dart';
import 'package:monitoring_jamur/core/theme/app_theme.dart';
import 'package:monitoring_jamur/features/auth/presentation/pages/login_page.dart';
import 'package:monitoring_jamur/features/home/presentation/pages/main_screen.dart';
import 'package:monitoring_jamur/core/session/user_session.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
@ -12,6 +14,8 @@ void main() async {
anonKey: SupabaseConfig.anonKey,
);
await UserSession.init();
runApp(const MyApp());
}
@ -24,7 +28,7 @@ class MyApp extends StatelessWidget {
title: 'Mushroom Monitor',
debugShowCheckedModeBanner: false,
theme: AppTheme.lightTheme,
home: const LoginPage(),
home: UserSession.isLoggedIn ? const MainScreen() : const LoginPage(),
);
}
}

View File

@ -33,6 +33,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.4"
archive:
dependency: transitive
description:
name: archive
sha256: a96e8b390886ee8abb49b7bd3ac8df6f451c621619f52a26e815fdcf568959ff
url: "https://pub.dev"
source: hosted
version: "4.0.9"
args:
dependency: transitive
description:
name: args
sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
url: "https://pub.dev"
source: hosted
version: "2.7.0"
async:
dependency: transitive
description:
@ -57,6 +73,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.4.1"
checked_yaml:
dependency: transitive
description:
name: checked_yaml
sha256: "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f"
url: "https://pub.dev"
source: hosted
version: "2.0.4"
cli_util:
dependency: transitive
description:
name: cli_util
sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c
url: "https://pub.dev"
source: hosted
version: "0.4.2"
clock:
dependency: transitive
description:
@ -142,6 +174,14 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_launcher_icons:
dependency: "direct dev"
description:
name: flutter_launcher_icons
sha256: "526faf84284b86a4cb36d20a5e45147747b7563d921373d4ee0559c54fcdbcea"
url: "https://pub.dev"
source: hosted
version: "0.13.1"
flutter_lints:
dependency: "direct dev"
description:
@ -224,6 +264,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.1.2"
image:
dependency: transitive
description:
name: image
sha256: f9881ff4998044947ec38d098bc7c8316ae1186fa786eddffdb867b9bc94dfce
url: "https://pub.dev"
source: hosted
version: "4.8.0"
json_annotation:
dependency: transitive
description:
name: json_annotation
sha256: cb09e7dac6210041fad964ed7fbee004f14258b4eca4040f72d1234062ace4c8
url: "https://pub.dev"
source: hosted
version: "4.11.0"
jwt_decode:
dependency: transitive
description:
@ -376,6 +432,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.3.0"
petitparser:
dependency: transitive
description:
name: petitparser
sha256: "91bd59303e9f769f108f8df05e371341b15d59e995e6806aefab827b58336675"
url: "https://pub.dev"
source: hosted
version: "7.0.2"
platform:
dependency: transitive
description:
@ -400,6 +464,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.0.0"
posix:
dependency: transitive
description:
name: posix
sha256: "185ef7606574f789b40f289c233efa52e96dead518aed988e040a10737febb07"
url: "https://pub.dev"
source: hosted
version: "6.5.0"
postgrest:
dependency: transitive
description:
@ -441,7 +513,7 @@ packages:
source: hosted
version: "0.28.0"
shared_preferences:
dependency: transitive
dependency: "direct main"
description:
name: shared_preferences
sha256: c3025c5534b01739267eb7d76959bbc25a6d10f6988e1c2a3036940133dd10bf
@ -693,6 +765,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.1.0"
xml:
dependency: transitive
description:
name: xml
sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025"
url: "https://pub.dev"
source: hosted
version: "6.6.1"
yaml:
dependency: transitive
description:

View File

@ -36,6 +36,7 @@ dependencies:
cupertino_icons: ^1.0.8
supabase_flutter: ^2.8.1
google_fonts: ^6.2.1
shared_preferences: ^2.5.5
dev_dependencies:
flutter_test:
@ -47,6 +48,7 @@ dev_dependencies:
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^6.0.0
flutter_launcher_icons: ^0.13.1
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
@ -90,3 +92,9 @@ flutter:
#
# For details regarding fonts from package dependencies,
# see https://flutter.dev/to/font-from-package
flutter_launcher_icons:
android: "launcher_icon"
ios: true
image_path: "lib/assets/mushroom.png"
min_sdk_android: 21