MIF_E31222653/aplikasi/lib/presentation/screens/user/user_dashboard.dart

621 lines
23 KiB
Dart

import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:provider/provider.dart';
import '../../providers/auth_provider.dart';
import '../features/cart_page.dart';
import '../features/activity_page.dart';
import '../features/chat_page.dart';
import '../features/profile_page.dart';
import '../features/food_detail_page.dart';
import '../features/kantin_page.dart';
import '../features/makanan_page.dart';
import '../features/minuman_page.dart';
import '../features/snack_page.dart';
class UserDashboard extends StatefulWidget {
const UserDashboard({super.key});
@override
State<UserDashboard> createState() => _UserDashboardState();
}
class _UserDashboardState extends State<UserDashboard> {
final TextEditingController _searchController = TextEditingController();
final List<Widget> _pages = [
_HomePage(),
const ActivityPage(),
const ChatPage(),
const ProfilePage(),
];
@override
void dispose() {
_searchController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final selectedIndex = context.watch<AuthProvider>().selectedIndex;
return Scaffold(
body: _pages[selectedIndex],
bottomNavigationBar: Container(
decoration: BoxDecoration(
color: Colors.white,
border: Border(
top: BorderSide(
color: Colors.grey.withOpacity(0.2),
width: 1,
),
),
),
child: BottomNavigationBar(
currentIndex: selectedIndex,
onTap: (index) {
context.read<AuthProvider>().setSelectedIndex(index);
},
type: BottomNavigationBarType.fixed,
backgroundColor: Colors.white,
selectedItemColor: Colors.blue,
unselectedItemColor: Colors.grey,
selectedFontSize: 12,
unselectedFontSize: 12,
selectedLabelStyle: const TextStyle(
fontWeight: FontWeight.normal,
height: 1.5,
),
unselectedLabelStyle: const TextStyle(
fontWeight: FontWeight.normal,
height: 1.5,
),
elevation: 0,
items: [
BottomNavigationBarItem(
icon: Padding(
padding: const EdgeInsets.only(bottom: 4),
child: Icon(
Icons.home_outlined,
size: 24,
color: selectedIndex == 0 ? Colors.blue : Colors.grey,
),
),
label: 'Beranda',
),
BottomNavigationBarItem(
icon: Padding(
padding: const EdgeInsets.only(bottom: 4),
child: Icon(
Icons.article_outlined,
size: 24,
color: selectedIndex == 1 ? Colors.blue : Colors.grey,
),
),
label: 'Aktifitas',
),
BottomNavigationBarItem(
icon: Padding(
padding: const EdgeInsets.only(bottom: 4),
child: Icon(
Icons.chat_bubble_outline,
size: 24,
color: selectedIndex == 2 ? Colors.blue : Colors.grey,
),
),
label: 'Chat',
),
BottomNavigationBarItem(
icon: Padding(
padding: const EdgeInsets.only(bottom: 4),
child: Icon(
Icons.person_outline,
size: 24,
color: selectedIndex == 3 ? Colors.blue : Colors.grey,
),
),
label: 'Profil',
),
],
),
),
);
}
}
class _HomePage extends StatelessWidget {
final TextEditingController _searchController = TextEditingController();
_HomePage({super.key});
Widget _buildCategoryItem(IconData icon, String label, {VoidCallback? onTap}) {
return Column(
children: [
GestureDetector(
onTap: onTap,
child: Container(
width: 60,
height: 60,
margin: const EdgeInsets.symmetric(horizontal: 8),
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(30),
),
child: Icon(icon, color: Colors.white),
),
),
const SizedBox(height: 4),
Text(
label,
style: const TextStyle(
fontSize: 12,
color: Colors.black87,
),
),
],
);
}
Widget _buildMenuItem(BuildContext context) {
return GestureDetector(
onTap: () {
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (context) => const FoodDetailPage(
name: 'Nasi Ayam Geprek',
description: 'Nasi + ayam geprek + timun + tahu + tempe + sambal bawang',
price: 'Rp 10.000',
imageUrl: 'assets/images/nasi_ayam.jpg',
location: 'Kantin 1',
),
);
},
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
spreadRadius: 1,
blurRadius: 4,
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ClipRRect(
borderRadius: const BorderRadius.vertical(top: Radius.circular(12)),
child: Image.asset(
'assets/images/nasi_ayam.jpg',
height: 120,
width: double.infinity,
fit: BoxFit.cover,
),
),
Padding(
padding: const EdgeInsets.all(8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Nasi Ayam Geprek',
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 14,
),
),
const SizedBox(height: 4),
const Text(
'Rp 10.000',
style: TextStyle(
color: Colors.blue,
fontWeight: FontWeight.bold,
fontSize: 14,
),
),
const SizedBox(height: 8),
GestureDetector(
onTap: () {
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (context) => const FoodDetailPage(
name: 'Nasi Ayam Geprek',
description: 'Nasi + ayam geprek + timun + tahu + tempe + sambal bawang',
price: 'Rp 10.000',
imageUrl: 'assets/images/nasi_ayam.jpg',
location: 'Kantin 1',
),
);
},
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(20),
),
child: const Text(
'Tambah',
style: TextStyle(
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.w500,
),
),
),
),
],
),
),
],
),
),
);
}
@override
Widget build(BuildContext context) {
return SafeArea(
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Header with Logo and Cart
Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Image.asset(
'assets/images/logo.jpg',
height: 30,
),
IconButton(
icon: const Icon(Icons.shopping_cart_outlined),
onPressed: () {
showModalBottomSheet(
context: context,
builder: (context) => const CartPage(),
isScrollControlled: true,
);
},
),
],
),
),
// Balance Card
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
spreadRadius: 1,
blurRadius: 4,
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Saldo Polije Pay',
style: TextStyle(
color: Colors.grey,
fontSize: 12,
),
),
const SizedBox(height: 4),
Row(
children: [
const Icon(
Icons.account_balance_wallet,
color: Colors.blue,
size: 20,
),
const SizedBox(width: 8),
const Text(
'Rp 25.000',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
const Spacer(),
Row(
children: [
Column(
children: [
Container(
padding: const EdgeInsets.all(6),
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(8),
),
child: const Icon(
Icons.add,
color: Colors.white,
size: 18,
),
),
const SizedBox(height: 2),
const Text(
'Bayar',
style: TextStyle(
fontSize: 10,
color: Colors.grey,
),
),
],
),
const SizedBox(width: 16),
Column(
children: [
Container(
padding: const EdgeInsets.all(6),
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(8),
),
child: const Icon(
Icons.arrow_upward,
color: Colors.white,
size: 18,
),
),
const SizedBox(height: 2),
const Text(
'Top Up',
style: TextStyle(
fontSize: 10,
color: Colors.grey,
),
),
],
),
const SizedBox(width: 16),
Column(
children: [
Container(
padding: const EdgeInsets.all(6),
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(8),
),
child: const Icon(
Icons.more_horiz,
color: Colors.white,
size: 18,
),
),
const SizedBox(height: 2),
const Text(
'Lainnya',
style: TextStyle(
fontSize: 10,
color: Colors.grey,
),
),
],
),
],
),
],
),
],
),
),
),
const SizedBox(height: 16),
// Search Bar
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: TextField(
controller: _searchController,
decoration: InputDecoration(
hintText: 'Mau makan apa hari ini?',
hintStyle: TextStyle(color: Colors.grey[400], fontSize: 14),
prefixIcon: const Icon(Icons.search, color: Colors.grey),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide.none,
),
filled: true,
fillColor: Colors.grey[100],
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
),
),
),
const SizedBox(height: 20),
// Category Icons
SizedBox(
height: 90,
child: ListView(
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.symmetric(horizontal: 12),
children: [
_buildCategoryItem(
Icons.store_outlined,
'Kantin',
onTap: () {
Navigator.push(
context,
PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) => const KantinPage(),
transitionsBuilder: (context, animation, secondaryAnimation, child) {
final scale = Tween<double>(begin: 0.8, end: 1.0).animate(
CurvedAnimation(parent: animation, curve: Curves.easeOutBack),
);
return ScaleTransition(
scale: scale,
child: child,
);
},
transitionDuration: const Duration(milliseconds: 350),
reverseTransitionDuration: const Duration(milliseconds: 250),
),
);
},
),
_buildCategoryItem(
Icons.restaurant_outlined,
'Makanan',
onTap: () {
Navigator.push(
context,
PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) => const MakananPage(),
transitionsBuilder: (context, animation, secondaryAnimation, child) {
final scale = Tween<double>(begin: 0.8, end: 1.0).animate(
CurvedAnimation(parent: animation, curve: Curves.easeOutBack),
);
return ScaleTransition(
scale: scale,
child: child,
);
},
transitionDuration: const Duration(milliseconds: 350),
reverseTransitionDuration: const Duration(milliseconds: 250),
),
);
},
),
_buildCategoryItem(
Icons.local_drink_outlined,
'Minuman',
onTap: () {
Navigator.push(
context,
PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) => const MinumanPage(),
transitionsBuilder: (context, animation, secondaryAnimation, child) {
final scale = Tween<double>(begin: 0.8, end: 1.0).animate(
CurvedAnimation(parent: animation, curve: Curves.easeOutBack),
);
return ScaleTransition(
scale: scale,
child: child,
);
},
transitionDuration: const Duration(milliseconds: 350),
reverseTransitionDuration: const Duration(milliseconds: 250),
),
);
},
),
_buildCategoryItem(
Icons.lunch_dining_outlined,
'Snack',
onTap: () {
Navigator.push(
context,
PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) => const SnackPage(),
transitionsBuilder: (context, animation, secondaryAnimation, child) {
final scale = Tween<double>(begin: 0.8, end: 1.0).animate(
CurvedAnimation(parent: animation, curve: Curves.easeOutBack),
);
return ScaleTransition(
scale: scale,
child: child,
);
},
transitionDuration: const Duration(milliseconds: 350),
reverseTransitionDuration: const Duration(milliseconds: 250),
),
);
},
),
_buildCategoryItem(Icons.grid_view, 'Semua'),
],
),
),
const SizedBox(height: 20),
// Banner
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: ClipRRect(
borderRadius: BorderRadius.circular(12),
child: Image.asset(
'assets/images/banner.jpg',
height: 120,
width: double.infinity,
fit: BoxFit.cover,
),
),
),
const SizedBox(height: 20),
// Recommended Menu
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'Rekomendasi Menu',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
TextButton(
onPressed: () {
context.push('/sort');
},
style: TextButton.styleFrom(
padding: EdgeInsets.zero,
minimumSize: Size.zero,
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
child: const Text(
'Lihat Semua',
style: TextStyle(
color: Colors.blue,
fontSize: 14,
),
),
),
],
),
),
const SizedBox(height: 12),
// Menu Grid
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 0.75,
crossAxisSpacing: 12,
mainAxisSpacing: 12,
),
itemCount: 4,
itemBuilder: (context, index) {
return _buildMenuItem(context);
},
),
),
const SizedBox(height: 16),
],
),
),
);
}
}