Main
Main
dart';
import 'package:carousel_slider/carousel_slider.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:url_launcher/url_launcher.dart';
void main() {
runApp(const GrilliApp());
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Grilli - Amazing & Delicious Food',
theme: ThemeData(
primaryColor: const Color(0xFFDBA159), // gold-crayola
scaffoldBackgroundColor: const Color(0xFF1A2526), // eerie-black-1
textTheme: TextTheme(
displayLarge: GoogleFonts.forum(
fontSize: 48,
fontWeight: FontWeight.w400,
color: Colors.white,
),
headlineLarge: GoogleFonts.forum(
fontSize: 36,
fontWeight: FontWeight.w400,
color: Colors.white,
),
headlineMedium: GoogleFonts.forum(
fontSize: 28,
fontWeight: FontWeight.w400,
color: Colors.white,
),
titleLarge: GoogleFonts.dmSans(
fontSize: 22,
fontWeight: FontWeight.w700,
color: Colors.white,
),
titleMedium: GoogleFonts.dmSans(
fontSize: 21,
fontWeight: FontWeight.w400,
color: Colors.white,
),
bodyLarge: GoogleFonts.dmSans(
fontSize: 24,
color: Colors.white,
height: 1.85,
),
bodyMedium: GoogleFonts.dmSans(
fontSize: 16,
color: const Color(0xFFA6A6A6), // quick-silver
height: 1.6,
),
bodySmall: GoogleFonts.dmSans(
fontSize: 14,
color: const Color(0xFFA6A6A6),
height: 1.4,
),
labelLarge: GoogleFonts.dmSans(
fontSize: 14,
fontWeight: FontWeight.w700,
color: const Color(0xFFDBA159),
letterSpacing: 0.4,
),
labelMedium: GoogleFonts.dmSans(
fontSize: 12,
fontWeight: FontWeight.w700,
color: const Color(0xFFDBA159),
letterSpacing: 0.4,
),
),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFFDBA159),
foregroundColor: Colors.black,
padding: const EdgeInsets.symmetric(horizontal: 45, vertical: 12),
textStyle: GoogleFonts.dmSans(
fontSize: 12,
fontWeight: FontWeight.w700,
letterSpacing: 3,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24),
side: const BorderSide(color: Color(0xFFDBA159), width: 2),
),
),
),
inputDecorationTheme: const InputDecorationTheme(
filled: true,
fillColor: Color(0xFF1D2728), // eerie-black-2
contentPadding: EdgeInsets.symmetric(horizontal: 20, vertical: 10),
border: OutlineInputBorder(
borderSide: BorderSide(color: Colors.white10),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Color(0xFFDBA159)),
),
),
),
home: const HomeScreen(),
);
}
}
@override
State<HomeScreen> createState() => _HomeScreenState();
}
@override
void initState() {
super.initState();
_preloaderController = AnimationController(
duration: const Duration(seconds: 1),
vsync: this,
)..repeat();
_preloaderAnimation = Tween<double>(begin: 0, end:
360).animate(_preloaderController);
// Simulate preloader
Future.delayed(const Duration(seconds: 2), () {
setState(() {
_isLoaded = true;
});
});
}
@override
void dispose() {
_preloaderController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Stack(
children: [
if (!_isLoaded)
_buildPreloader()
else
Scaffold(
appBar: _buildTopBar(context),
drawer: _buildDrawer(context),
body: NotificationListener<ScrollNotification>(
onNotification: (scrollNotification) {
if (scrollNotification is ScrollUpdateNotification) {
setState(() {
if (scrollNotification.metrics.pixels >= 50) {
_isHeaderActive = true;
if (_lastScrollPos < scrollNotification.metrics.pixels) {
_isHeaderActive = false; // Hide header when scrolling down
}
} else {
_isHeaderActive = false;
}
_lastScrollPos = scrollNotification.metrics.pixels;
});
}
return true;
},
child: SingleChildScrollView(
child: Column(
children: [
_buildHeroSlider(context),
_buildServiceSection(context),
_buildAboutSection(context),
_buildSpecialDishSection(context),
_buildMenuSection(context),
_buildTestimonialsSection(context),
_buildReservationSection(context),
_buildFeaturesSection(context),
_buildEventSection(context),
_buildFooterSection(context),
],
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
Scrollable.ensureVisible(context, duration: const
Duration(milliseconds: 500));
},
backgroundColor: const Color(0xFFDBA159),
child: const Icon(Icons.arrow_upward, color: Colors.black),
),
),
],
);
}
Widget _buildPreloader() {
return Container(
color: const Color(0xFFDBA159),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
AnimatedBuilder(
animation: _preloaderAnimation,
builder: (context, child) {
return Transform.rotate(
angle: _preloaderAnimation.value * 3.14159 / 180,
child: Container(
width: 112,
height: 112,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: Colors.white,
width: 3,
),
),
child: Container(
margin: const EdgeInsets.all(3),
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Color(0xFF1C1810),
),
),
),
);
},
),
const SizedBox(height: 45),
Text(
'Grilli',
style: GoogleFonts.dmSans(
fontSize: 40,
fontWeight: FontWeight.w700,
letterSpacing: 16,
foreground: Paint()
..shader = const LinearGradient(
colors: [Colors.transparent, Color(0xFF1C1810),
Colors.transparent],
).createShader(const Rect.fromLTWH(0, 0, 500, 40)),
),
),
],
),
),
);
}
return Stack(
children: [
CarouselSlider(
carouselController: _carouselController,
options: CarouselOptions(
height: MediaQuery.of(context).size.height * 0.7,
autoPlay: true,
autoPlayInterval: const Duration(seconds: 7),
viewportFraction: 1.0,
onPageChanged: (index, reason) {
setState(() {
_currentSlide = index;
});
},
),
items: slides.map((slide) {
return Stack(
fit: StackFit.expand,
children: [
Image.asset(
slide['image']!,
fit: BoxFit.cover,
),
Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color.fromRGBO(0, 0, 0, 0.9),
Color.fromRGBO(0, 0, 0, 0.7),
Colors.transparent,
],
),
),
),
Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
slide['subtitle']!.toUpperCase(),
style: Theme.of(context).textTheme.labelMedium,
),
const SizedBox(height: 20),
Text(
slide['title']!,
style: Theme.of(context).textTheme.displayLarge,
textAlign: TextAlign.center,
),
const SizedBox(height: 10),
Text(
slide['text']!,
style: Theme.of(context).textTheme.bodyLarge,
textAlign: TextAlign.center,
),
const SizedBox(height: 40),
ElevatedButton(
onPressed: () {},
child: const Text('VIEW OUR MENU'),
),
],
),
),
],
);
}).toList(),
),
Positioned(
left: 30,
top: MediaQuery.of(context).size.height * 0.35,
child: IconButton(
icon: const Icon(Icons.chevron_left, color: Color(0xFFDBA159)),
onPressed: () => _carouselController.previousPage(),
),
),
Positioned(
right: 30,
top: MediaQuery.of(context).size.height * 0.35,
child: IconButton(
icon: const Icon(Icons.chevron_right, color: Color(0xFFDBA159)),
onPressed: () => _carouselController.nextPage(),
),
),
Positioned(
bottom: 15,
right: 15,
child: FloatingActionButton(
onPressed: () {},
backgroundColor: const Color(0xFFDBA159),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset('assets/images/hero-icon.png', height: 48, width: 48),
const Text(
'Book A Table',
style: TextStyle(fontSize: 10, color: Colors.black, fontWeight:
FontWeight.w700),
textAlign: TextAlign.center,
),
],
),
),
),
],
);
}
return Container(
color: const Color(0xFF1C1810),
padding: const EdgeInsets.symmetric(vertical: 70),
child: Column(
children: [
Text(
'Flavors For Royalty',
style: Theme.of(context).textTheme.labelMedium,
),
const SizedBox(height: 12),
Text(
'We Offer Top Notch',
style: Theme.of(context).textTheme.headlineLarge,
),
const SizedBox(height: 16),
Text(
'Lorem Ipsum is simply dummy text of the printing and typesetting
industry lorem Ipsum has been the industrys standard dummy text ever.',
style: Theme.of(context).textTheme.bodyMedium,
textAlign: TextAlign.center,
),
const SizedBox(height: 40),
GridView.count(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
crossAxisCount: MediaQuery.of(context).size.width > 992 ? 3 : 2,
crossAxisSpacing: 40,
mainAxisSpacing: 40,
childAspectRatio: 0.85,
children: services.map((service) {
return Card(
color: const Color(0xFF1C1810),
child: Column(
children: [
GestureDetector(
onTap: () {},
child: ClipRRect(
borderRadius: BorderRadius.circular(24),
child: Image.asset(
service['image']!,
width: 285,
height: 336,
fit: BoxFit.cover,
),
),
),
const SizedBox(height: 12),
Text(
service['title']!,
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 12),
TextButton(
onPressed: () {},
child: Text(
'View Menu',
style: Theme.of(context).textTheme.labelMedium,
),
),
],
),
);
}).toList(),
),
Positioned(
bottom: 0,
left: 0,
child: Image.asset('assets/images/shape-1.png', width: 246, height:
412),
),
Positioned(
top: 0,
right: 0,
child: Image.asset('assets/images/shape-2.png', width: 343, height:
345),
),
],
),
);
}
return Container(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
const SizedBox(height: 70),
Text(
'Special Selection',
style: Theme.of(context).textTheme.labelMedium,
),
const SizedBox(height: 12),
Text(
'Delicious Menu',
style: Theme.of(context).textTheme.headlineLarge,
),
const SizedBox(height: 40),
GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: MediaQuery.of(context).size.width > 992 ? 3 : 2,
crossAxisSpacing: 40,
mainAxisSpacing: 40,
childAspectRatio: 0.7,
),
itemCount: menuItems.length,
itemBuilder: (context, index) {
final item = menuItems[index];
return Card(
color: const Color(0xFF1C1810),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ClipRRect(
borderRadius: BorderRadius.circular(24),
child: Image.asset(
item['image'],
width: 100,
height: 100,
fit: BoxFit.cover,
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
item['title'],
style: Theme.of(context).textTheme.titleMedium,
),
if (item['badge'] != null) ...[
const SizedBox(width: 10),
Container(
padding: const EdgeInsets.symmetric(horizontal:
10, vertical: 2),
color: const Color(0xFFDBA159),
child: Text(
item['badge'],
style: const TextStyle(
fontSize: 12,
color: Colors.black,
fontFamily: 'Forum',
),
),
),
],
],
),
const SizedBox(height: 10),
Text(
item['price'],
style: const TextStyle(
fontSize: 16,
color: Color(0xFFDBA159),
),
),
const SizedBox(height: 10),
Text(
item['description'],
style: Theme.of(context).textTheme.bodyMedium,
),
],
),
),
),
],
),
);
},
),
const SizedBox(height: 50),
Text(
'During winter daily from 7:00 pm to 9:00 pm',
style: Theme.of(context).textTheme.bodyLarge,
),
const SizedBox(height: 26),
ElevatedButton(
onPressed: () {},
child: const Text('VIEW ALL MENU'),
),
Positioned(
top: 0,
left: 0,
child: Image.asset('assets/images/shape-5.png', width: 921, height:
1036),
),
Positioned(
bottom: 0,
right: 0,
child: Image.asset('assets/images/shape-6.png', width: 343, height:
345),
),
],
),
);
}
return Container(
padding: const EdgeInsets.symmetric(vertical: 100),
child: Column(
children: [
Text(
'Why Choose Us',
style: Theme.of(context).textTheme.labelMedium,
),
const SizedBox(height: 12),
Text(
'Our Strength',
style: Theme.of(context).textTheme.headlineLarge,
),
const SizedBox(height: 40),
GridView.count(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
crossAxisCount: MediaQuery.of(context).size.width > 1200 ? 4 : 2,
crossAxisSpacing: 40,
mainAxisSpacing: 40,
childAspectRatio: 0.85,
children: features.asMap().entries.map((entry) {
final index = entry.key;
final feature = entry.value;
return Card(
color: index % 2 == 0 ? const Color(0xFF151D1E) : const
Color(0xFF1C1810),
child: Column(
children: [
Image.asset(feature['icon']!, width: 100, height: 80),
const SizedBox(height: 20),
Text(
feature['title']!,
style: Theme.of(context).textTheme.titleLarge,
),
const SizedBox(height: 10),
Text(
feature['text']!,
style: Theme.of(context).textTheme.bodyMedium,
textAlign: TextAlign.center,
),
],
),
);
}).toList(),
),
Positioned(
top: -100,
right: 0,
child: Image.asset('assets/images/shape-7.png', width: 208, height:
178),
),
Positioned(
bottom: 80,
left: 0,
child: Image.asset('assets/images/shape-8.png', width: 120, height:
115),
),
],
),
);
}
return Container(
color: const Color(0xFF1C1810),
padding: const EdgeInsets.symmetric(vertical: 100),
child: Column(
children: [
Text(
'Recent Updates',
style: Theme.of(context).textTheme.labelMedium,
),
const SizedBox(height: 12),
Text(
'Upcoming Event',
style: Theme.of(context).textTheme.headlineLarge,
),
const SizedBox(height: 40),
GridView.count(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
crossAxisCount: MediaQuery.of(context).size.width > 992 ? 3 : 2,
crossAxisSpacing: 40,
mainAxisSpacing: 40,
childAspectRatio: 0.7,
children: events.map((event) {
return Card(
color: const Color(0xFF1C1810),
child: Stack(
children: [
Image.asset(
event['image']!,
width: 350,
height: 450,
fit: BoxFit.cover,
),
Positioned(
top: 30,
left: 25,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 10,
vertical: 5),
color: Colors.black,
child: Text(
event['date']!,
style: Theme.of(context).textTheme.labelMedium,
),
),
),
Positioned(
bottom: 0,
left: 0,
right: 0,
child: Container(
padding: const EdgeInsets.fromLTRB(35, 35, 35, 25),
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color.fromRGBO(0, 0, 0, 0.9),
Color.fromRGBO(0, 0, 0, 0.7),
Colors.transparent,
],
),
),
child: Column(
children: [
Text(
event['subtitle']!,
style: Theme.of(context).textTheme.labelMedium,
),
const SizedBox(height: 5),
Text(
event['title']!,
style: Theme.of(context).textTheme.titleLarge,
textAlign: TextAlign.center,
),
],
),
),
),
],
),
);
}).toList(),
),
const SizedBox(height: 40),
ElevatedButton(
onPressed: () {},
child: const Text('VIEW OUR BLOG'),
),
],
),
);
}