import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'Hello', theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), useMaterial3: true, ), home: const MyHomePage(), routes: { '/shop': (context) => const ShopLandingPage(), '/shop/grid-right': (context) => const ShopGridRightSidebarPage(), }, ); } } // REUSABLE HEADER WIDGET class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { final String title; const CustomAppBar({ super.key, required this.title, }); @override Size get preferredSize => const Size.fromHeight(60); @override Widget build(BuildContext context) { return AppBar( backgroundColor: Theme.of(context).colorScheme.inversePrimary, leading: Builder( builder: (context) { return IconButton( icon: const Icon(Icons.menu), onPressed: () { Scaffold.of(context).openDrawer(); }, tooltip: 'Menu', ); }, ), title: Row( children: [ Container( width: 120, height: 40, padding: const EdgeInsets.all(4), child: SvgPicture.network( 'https://nest-frontend-v6.vercel.app/assets/imgs/theme/logo.svg', placeholderBuilder: (context) => const Icon( Icons.shopping_bag, color: Colors.green, size: 42, ), fit: BoxFit.contain, ), ), ], ), actions: [ Stack( children: [ IconButton( icon: const Icon(Icons.favorite_border), onPressed: () {}, ), Positioned( top: 4, right: 4, child: Container( padding: const EdgeInsets.all(1), decoration: const BoxDecoration( color: Colors.red, shape: BoxShape.circle, ), constraints: const BoxConstraints(minWidth: 16, minHeight: 16), child: const Center( child: Text( '4', style: TextStyle(color: Colors.white, fontSize: 10), ), ), ), ), ], ), Stack( children: [ IconButton( icon: const Icon(Icons.shopping_cart_outlined), onPressed: () {}, ), Positioned( top: 4, right: 4, child: Container( padding: const EdgeInsets.all(1), decoration: const BoxDecoration( color: Colors.red, shape: BoxShape.circle, ), constraints: const BoxConstraints(minWidth: 16, minHeight: 16), child: const Center( child: Text( '2', style: TextStyle(color: Colors.white, fontSize: 10), ), ), ), ), ], ), ], ); } } // REUSABLE DRAWER WIDGET class AppDrawer extends StatefulWidget { const AppDrawer({super.key}); @override State createState() => _AppDrawerState(); } class _AppDrawerState extends State { Set _expandedTopItems = {'Shop'}; Widget _buildSubItem(String title, {VoidCallback? onTap}) { return MouseRegion( cursor: SystemMouseCursors.click, child: InkWell( onTap: onTap, borderRadius: BorderRadius.circular(8), hoverColor: Colors.green[50], splashColor: Colors.transparent, highlightColor: Colors.transparent, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 8), child: Text(title), ), ), ); } Widget _buildTopItem(String title) { final bool isActive = _expandedTopItems.contains(title); return MouseRegion( cursor: SystemMouseCursors.click, child: Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), decoration: BoxDecoration( borderRadius: BorderRadius.circular(4), color: isActive ? Colors.green[50] : null, ), child: Text( title, style: TextStyle( color: isActive ? Colors.green[700] : null, fontWeight: isActive ? FontWeight.bold : null, ), ), ), ); } static const String logoUrl = 'https://nest-frontend-v6.vercel.app/assets/imgs/theme/logo.svg'; @override Widget build(BuildContext context) { return Drawer( child: ListView( padding: EdgeInsets.zero, children: [ DrawerHeader( decoration: BoxDecoration( color: Colors.green[50], ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Container( height: 40, padding: const EdgeInsets.all(4), child: SvgPicture.network( logoUrl, placeholderBuilder: (context) => const Row( children: [ Icon(Icons.shopping_bag, color: Colors.green, size: 32), SizedBox(width: 8), Text( 'Nest', style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, color: Colors.green, ), ), ], ), fit: BoxFit.contain, ), ), IconButton( icon: const Icon(Icons.close), onPressed: () { Navigator.pop(context); }, ), ], ), ), Padding( padding: const EdgeInsets.all(16.0), child: TextField( decoration: InputDecoration( hintText: 'Search for items...', prefixIcon: const Icon(Icons.search), border: OutlineInputBorder(), ), ), ), // Home ExpansionTile( title: _buildTopItem('Home'), onExpansionChanged: (expanded) { setState(() { if (expanded) { _expandedTopItems.add('Home'); } else { _expandedTopItems.remove('Home'); } }); }, children: [ _buildSubItem('Home 1'), _buildSubItem('Home 2'), _buildSubItem('Home 3'), _buildSubItem('Home 4'), _buildSubItem('Home 5'), _buildSubItem('Home 6'), ], ), // Shop (pre-expanded) ExpansionTile( initiallyExpanded: true, title: _buildTopItem('Shop'), onExpansionChanged: (expanded) { setState(() { if (expanded) { _expandedTopItems.add('Shop'); } else { _expandedTopItems.remove('Shop'); } }); }, children: [ _buildSubItem( 'Shop Grid - Right Sidebar', onTap: () { Navigator.pop(context); Navigator.pushNamed(context, '/shop/grid-right'); }, ), _buildSubItem('Shop Grid - Left Sidebar'), _buildSubItem('Shop List - Right Sidebar'), _buildSubItem('Shop - Wide'), ExpansionTile( title: const Text('Single Product'), children: [ _buildSubItem('Product - Right Sidebar'), _buildSubItem('Product - Left Sidebar'), _buildSubItem('Product - No Sidebar'), _buildSubItem('Product - Vendor Infor'), ], ), ExpansionTile( title: const Text('Shop Invoice'), children: [ _buildSubItem('Shop Invoice 1'), _buildSubItem('Shop Invoice 2'), _buildSubItem('Shop Invoice 3'), _buildSubItem('Shop Invoice 4'), _buildSubItem('Shop Invoice 5'), _buildSubItem('Shop Invoice 6'), ], ), ], ), // Vendors ExpansionTile( title: _buildTopItem('Vendors'), onExpansionChanged: (expanded) { setState(() { if (expanded) { _expandedTopItems.add('Vendors'); } else { _expandedTopItems.remove('Vendors'); } }); }, children: [ _buildSubItem('Vendors Grid'), _buildSubItem('Vendore List'), _buildSubItem('Vendore Details 01'), _buildSubItem('Vendors Details 02'), _buildSubItem('Vendors Dashboard'), _buildSubItem('Vendors Guide') ], ), // Mega Menu ExpansionTile( title: _buildTopItem('Mega Menu'), onExpansionChanged: (expanded) { setState(() { if (expanded) { _expandedTopItems.add('Mega Menu'); } else { _expandedTopItems.remove('Mega Menu'); } }); }, children: [ _buildSubItem("Women's Fashion"), _buildSubItem("Men's Fashion"), _buildSubItem('Technology'), ], ), // Blog ExpansionTile( title: _buildTopItem('Blog'), onExpansionChanged: (expanded) { setState(() { if (expanded) { _expandedTopItems.add('Blog'); } else { _expandedTopItems.remove('Blog'); } }); }, children: [ _buildSubItem('Blog Category Grid'), _buildSubItem('Blog Category List'), _buildSubItem('Blog Category Big'), _buildSubItem('Blog Category Wide'), ExpansionTile( title: const Text('Single Product Layout'), children: [ _buildSubItem('Layout A'), _buildSubItem('Layout B'), ], ), ], ), // Pages ExpansionTile( title: _buildTopItem('Pages'), onExpansionChanged: (expanded) { setState(() { if (expanded) { _expandedTopItems.add('Pages'); } else { _expandedTopItems.remove('Pages'); } }); }, children: [ _buildSubItem('About Us'), _buildSubItem('Contact'), _buildSubItem('My Account'), _buildSubItem('Register'), _buildSubItem('Forget Password'), _buildSubItem('Reset Password'), _buildSubItem('Purchase Guide'), _buildSubItem('Privacy Policy'), _buildSubItem('Terms of Service'), _buildSubItem('404 Page'), ], ), // Language ExpansionTile( title: _buildTopItem('Language'), onExpansionChanged: (expanded) { setState(() { if (expanded) { _expandedTopItems.add('Language'); } else { _expandedTopItems.remove('Language'); } }); }, children: [ _buildSubItem('English'), _buildSubItem('French'), _buildSubItem('German'), _buildSubItem('Spanish'), ], ), const Divider(), // Footer inside drawer Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ MouseRegion( cursor: SystemMouseCursors.click, child: InkWell( onTap: () => Navigator.pop(context), borderRadius: BorderRadius.circular(8), hoverColor: Colors.green[50], splashColor: Colors.transparent, highlightColor: Colors.transparent, child: Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: Row( children: [ const Icon(Icons.location_on, color: Colors.green), const SizedBox(width: 8), const Text('Our location'), ], ), ), ), ), MouseRegion( cursor: SystemMouseCursors.click, child: InkWell( onTap: () {}, borderRadius: BorderRadius.circular(8), hoverColor: Colors.green[50], splashColor: Colors.transparent, highlightColor: Colors.transparent, child: Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: Row( children: [ const Icon(Icons.person_outline, color: Colors.green), const SizedBox(width: 8), const Text('Log In / Sign Up'), ], ), ), ), ), MouseRegion( cursor: SystemMouseCursors.click, child: InkWell( onTap: () {}, borderRadius: BorderRadius.circular(8), hoverColor: Colors.green[50], splashColor: Colors.transparent, highlightColor: Colors.transparent, child: Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: Row( children: [ const Icon(Icons.headset_mic, color: Colors.green), const SizedBox(width: 8), const Text('(+01) - 2345 - 6789'), ], ), ), ), ), const SizedBox(height: 16), const Text( 'Follow Us', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 12), Row( children: [ CircleAvatar( backgroundColor: Colors.green, radius: 20, child: IconButton( icon: const Icon(Icons.facebook, color: Colors.white, size: 20), onPressed: () {}, ), ), const SizedBox(width: 8), CircleAvatar( backgroundColor: Colors.green, radius: 20, child: IconButton( icon: const Icon(Icons.access_time, color: Colors.white, size: 20), onPressed: () {}, ), ), const SizedBox(width: 8), CircleAvatar( backgroundColor: Colors.green, radius: 20, child: IconButton( icon: const Icon(Icons.camera_alt, color: Colors.white, size: 20), onPressed: () {}, ), ), const SizedBox(width: 8), CircleAvatar( backgroundColor: Colors.green, radius: 20, child: IconButton( icon: const Icon(Icons.play_arrow, color: Colors.white, size: 20), onPressed: () {}, ), ), const SizedBox(width: 8), CircleAvatar( backgroundColor: Colors.green, radius: 20, child: IconButton( icon: const Icon(Icons.play_circle_outline, color: Colors.white, size: 20), onPressed: () {}, ), ), ], ), const SizedBox(height: 20), Text( 'Copyright 2024 © Nest. All rights reserved. Powered by AliThemes.', style: TextStyle( fontSize: 12, color: Colors.grey[600], ), ), ], ), ), ], ), ); } } // DECORATIVE ICONS FOR BREADCRUMB BAR class DecorativeIcons extends StatelessWidget { const DecorativeIcons({super.key}); @override Widget build(BuildContext context) { return Positioned.fill( child: IgnorePointer( child: Opacity( opacity: 0.1, child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ Transform.rotate( angle: -0.3, child: Icon(Icons.straighten, size: 40, color: Colors.green), ), const SizedBox(width: 16), Transform.scale( scale: 0.8, child: Icon(Icons.inventory_2, size: 32, color: Colors.red), ), const SizedBox(width: 24), ], ), ), ), ); } } // BREADCRUMB ITEM Widget _buildBreadcrumbItem(String label, {VoidCallback? onPressed}) { return MouseRegion( cursor: SystemMouseCursors.click, child: InkWell( onTap: onPressed, borderRadius: BorderRadius.circular(4), hoverColor: Colors.green[100], splashColor: Colors.transparent, highlightColor: Colors.transparent, child: Padding( padding: const EdgeInsets.symmetric(vertical: 2), child: Text( label, style: const TextStyle( fontSize: 14, fontWeight: FontWeight.w500, color: Colors.green, ), ), ), ), ); } // BREADCRUMBS BAR WIDGET class BreadcrumbsBar extends StatelessWidget { final String currentPage; const BreadcrumbsBar({ super.key, required this.currentPage, }); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16), margin: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.green[50], borderRadius: BorderRadius.circular(12), ), child: Stack( children: [ const DecorativeIcons(), Row( children: [ Expanded( child: Text( currentPage, style: const TextStyle( fontSize: 32, fontWeight: FontWeight.bold, color: Colors.black87, ), ), ), Row( children: [ _buildBreadcrumbItem('Home', onPressed: () { Navigator.popUntil(context, (route) => route.isFirst); }), const SizedBox(width: 8), const Icon(Icons.chevron_right, size: 16, color: Colors.grey), const SizedBox(width: 8), _buildBreadcrumbItem('Shop', onPressed: () { Navigator.pushNamedAndRemoveUntil( context, '/shop', (route) => route.isFirst, ); }), const SizedBox(width: 8), const Icon(Icons.chevron_right, size: 16, color: Colors.grey), const SizedBox(width: 8), Text( currentPage, style: const TextStyle( fontSize: 14, fontWeight: FontWeight.w500, color: Colors.black54, ), ), ], ), ], ), ], ), ); } } // MAIN HOME PAGE class MyHomePage extends StatelessWidget { const MyHomePage({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: const CustomAppBar(title: 'Home'), drawer: const AppDrawer(), body: const Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text('Hello from Home!'), Text( '0', style: TextStyle(fontSize: 48), ), ], ), ), ); } } // SHOP LANDING PAGE (e.g., /shop) class ShopLandingPage extends StatelessWidget { const ShopLandingPage({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: const CustomAppBar(title: 'Shop'), drawer: const AppDrawer(), body: const Center( child: Text( 'Welcome to the Shop!', style: TextStyle(fontSize: 24), ), ), ); } } // PRODUCT CARD WIDGET class ProductCard extends StatelessWidget { final String imageUrl; final String category; final String title; final double rating; final String brand; final double currentPrice; final double originalPrice; final VoidCallback? onAdd; const ProductCard({ super.key, required this.imageUrl, required this.category, required this.title, required this.rating, required this.brand, required this.currentPrice, required this.originalPrice, this.onAdd, }); @override Widget build(BuildContext context) { return Container( margin: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(12.0), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.grey[200]!), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ ClipRRect( borderRadius: BorderRadius.circular(8), child: Image.network( imageUrl.trim(), // ✅ Trim spaces height: 200, width: double.infinity, fit: BoxFit.cover, errorBuilder: (context, error, stackTrace) => Container( height: 200, color: Colors.grey[200], child: const Center(child: Text('Image')), ), ), ), const SizedBox(height: 12), Text(category, style: const TextStyle(color: Colors.grey, fontSize: 12)), const SizedBox(height: 4), Text(title, style: const TextStyle(fontWeight: FontWeight.bold)), const SizedBox(height: 8), Text('By $brand', style: const TextStyle(color: Colors.green, fontSize: 12)), const SizedBox(height: 8), Row( children: [ Text('\$${currentPrice.toStringAsFixed(2)}', style: const TextStyle(color: Colors.green, fontSize: 18, fontWeight: FontWeight.bold)), const SizedBox(width: 8), Text('\$${originalPrice.toStringAsFixed(2)}', style: const TextStyle( color: Colors.grey, decoration: TextDecoration.lineThrough, )), ], ), const SizedBox(height: 12), ElevatedButton( onPressed: onAdd, style: ElevatedButton.styleFrom( backgroundColor: Colors.green[100], foregroundColor: Colors.green[800], ), child: const Text('Add'), ), ], ), ); } } // SHOP GRID - RIGHT SIDEBAR PAGE ✅ FIXED class ShopGridRightSidebarPage extends StatelessWidget { const ShopGridRightSidebarPage({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: const CustomAppBar(title: 'Shop Grid - Right Sidebar'), drawer: const AppDrawer(), // ✅ FIXED: Wrap Column in SingleChildScrollView body: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ BreadcrumbsBar(currentPage: 'Snack'), Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), child: Text( 'We found 29 items for you!', style: const TextStyle(fontSize: 14), ), ), Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), child: Row( children: [ ConstrainedBox( constraints: const BoxConstraints(maxWidth: 160), child: Container( decoration: BoxDecoration( color: Colors.grey[100], border: Border.all(color: Colors.grey[300]!), borderRadius: BorderRadius.circular(8), ), child: DropdownButtonHideUnderline( child: DropdownButton( value: '50', onChanged: (String? newValue) {}, items: ['50', '100', '150', '200', 'All'].map((String value) { return DropdownMenuItem( value: value, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), child: Row( children: [ if (value == '50') ...[ Icon(Icons.check, size: 16, color: Colors.green), const SizedBox(width: 8), ] else const SizedBox(width: 24), Text(value), ], ), ), ); }).toList(), isExpanded: true, icon: const Icon(Icons.arrow_drop_down, color: Colors.grey, size: 18), style: const TextStyle(fontSize: 13, color: Colors.black), selectedItemBuilder: (context) { return [ Padding( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), child: Row( children: [ Icon(Icons.view_agenda, size: 16, color: Colors.grey), const SizedBox(width: 6), Text('Show: 50', style: const TextStyle(fontSize: 13)), ], ), ), ]; }, ), ), ), ), const SizedBox(width: 12), ConstrainedBox( constraints: const BoxConstraints(maxWidth: 180), child: Container( decoration: BoxDecoration( color: Colors.grey[100], border: Border.all(color: Colors.grey[300]!), borderRadius: BorderRadius.circular(8), ), child: DropdownButtonHideUnderline( child: DropdownButton( value: 'Featured', onChanged: (String? newValue) {}, items: [ 'Featured', 'Price: Low to High', 'Price: High to Low', 'Release Date', 'Avg. Rating' ].map((String value) { return DropdownMenuItem( value: value, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), child: Row( children: [ if (value == 'Featured') ...[ Icon(Icons.check, size: 16, color: Colors.green), const SizedBox(width: 8), ] else const SizedBox(width: 24), Text(value), ], ), ), ); }).toList(), isExpanded: true, icon: const Icon(Icons.arrow_drop_down, color: Colors.grey, size: 18), style: const TextStyle(fontSize: 13, color: Colors.black), selectedItemBuilder: (context) { return [ Padding( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), child: Row( children: [ Icon(Icons.sort, size: 16, color: Colors.grey), const SizedBox(width: 6), Text('Sort by: Featured', style: const TextStyle(fontSize: 13)), ], ), ), ]; }, ), ), ), ), ], ), ), const SizedBox(height: 16), ProductCard( imageUrl: 'https://nest-frontend-v6.vercel.app/assets/imgs/shop/product-16-1.jpg', category: 'Snack', title: 'Seeds of Change Organic Quinoa', rating: 4.0, brand: 'NestFood', currentPrice: 28.85, originalPrice: 32.8, onAdd: () { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Added to cart!')), ); }, ), ProductCard( imageUrl: 'https://nest-frontend-v6.vercel.app/assets/imgs/shop/product-2-1.jpg', category: 'Hodo Foods', title: 'All Natural Italian-style Chicken Meatball', rating: 3.5, brand: 'Stouffer', currentPrice: 52.25, originalPrice: 55.6, onAdd: () { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Added to cart!')), ); }, ), ProductCard( imageUrl: 'https://nest-frontend-v6.vercel.app/assets/imgs/shop/product-3-1.jpg', category: 'Snack', title: "Angie’s Boomchickapop Sweet & Salty", rating: 4.0, brand: 'By StarKist', currentPrice: 48.85, originalPrice: 52.80, onAdd: () { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Added to cart!')), ); }, ), ProductCard( imageUrl: 'https://nest-frontend-v6.vercel.app/assets/imgs/shop/product-4-1.jpg', category: 'Vegetable', title: 'Foster Farms Takeout Crispy Classic', rating: 4.0, brand: 'NestFood', currentPrice: 17.85, originalPrice: 19.8, onAdd: (){ ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Added to card!')), ); }, ), ProductCard( imageUrl: 'https://nest-frontend-v6.vercel.app/assets/imgs/shop/product-5-1.jpg', category: 'Pet Foods', title: 'Blue Diamond Almonds Lightly', rating: 4.0, brand: "NestFood", currentPrice: 23.85, originalPrice: 25.8, onAdd: (){ ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Added to card!')), ); }, ), const SizedBox(height: 24), ], ), ), ); } }