Initial commit: Eventify frontend
This commit is contained in:
382
lib/screens/booking_screen.dart
Normal file
382
lib/screens/booking_screen.dart
Normal file
@@ -0,0 +1,382 @@
|
||||
// lib/screens/booking_screen.dart
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class BookingScreen extends StatefulWidget {
|
||||
// Keep onBook in the constructor if you want to use it later, but we won't call it here.
|
||||
final VoidCallback? onBook;
|
||||
final String image;
|
||||
|
||||
const BookingScreen({
|
||||
Key? key,
|
||||
this.onBook,
|
||||
this.image = 'assets/images/event1.jpg',
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<BookingScreen> createState() => _BookingScreenState();
|
||||
}
|
||||
|
||||
class _BookingScreenState extends State<BookingScreen> {
|
||||
// small gallery placeholder
|
||||
final List<String> gallery = [
|
||||
'assets/images/event1.jpg',
|
||||
'assets/images/event2.jpg',
|
||||
'assets/images/event3.jpeg',
|
||||
'assets/images/event1.jpg',
|
||||
];
|
||||
|
||||
// long about text
|
||||
final String aboutText = '''
|
||||
Some voices don't just sing, they awaken something deep within. Sid Sriram is one of those rare artists who doesn't just perform music... he becomes it.
|
||||
|
||||
This concert isn't just about a setlist or a stage, it's a journey. A moment suspended in time. A powerful collective experience where every note pulls at memory, every silence says what words never could, and every crescendo feels like it was meant just for you.
|
||||
|
||||
From the moment the lights go down, you're no longer in the real world — you're in his world: a place shaped by melody and intimacy, where familiar songs are reborn and new ones carve out space in your memory. Expect an award-calibre voice that melts into orchestration, fragile acoustic passages that make the room hold its breath, and thunderous climaxes that lift everyone to their feet.
|
||||
|
||||
Whether you're a longtime fan or hearing him live for the first time, this evening promises to linger long after the last chord fades — a night of shared emotion, artistry, and music that stays with you.
|
||||
''';
|
||||
|
||||
bool _booked = false;
|
||||
|
||||
void _performLocalBooking() {
|
||||
// mark locally booked (do NOT call widget.onBook())
|
||||
if (!_booked) {
|
||||
setState(() => _booked = true);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Tickets booked (demo)')),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _openLearnMoreSheet() {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
builder: (ctx) {
|
||||
return DraggableScrollableSheet(
|
||||
expand: false,
|
||||
initialChildSize: 0.78,
|
||||
minChildSize: 0.35,
|
||||
maxChildSize: 0.95,
|
||||
builder: (context, scrollController) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(24.0)),
|
||||
boxShadow: [BoxShadow(color: Colors.black26, blurRadius: 8)],
|
||||
),
|
||||
child: SingleChildScrollView(
|
||||
controller: scrollController,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(20, 18, 20, 24),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// drag handle
|
||||
Center(
|
||||
child: Container(
|
||||
width: 48,
|
||||
height: 6,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[300],
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
|
||||
Text(
|
||||
"The Homecoming Tour | Sid Sriram Live in Concert",
|
||||
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
|
||||
Row(
|
||||
children: [
|
||||
Icon(Icons.calendar_today, size: 16, color: Colors.blue),
|
||||
SizedBox(width: 8),
|
||||
Text("22 Jan - 24 Jan, 2025", style: TextStyle(color: Colors.black54)),
|
||||
SizedBox(width: 14),
|
||||
Icon(Icons.access_time, size: 16, color: Colors.blue),
|
||||
SizedBox(width: 8),
|
||||
Text("04:00 PM - 11:00 PM", style: TextStyle(color: Colors.black54)),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 14),
|
||||
|
||||
Container(
|
||||
height: 120,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[200],
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Center(child: Text('Map placeholder — Cultural Hall', style: TextStyle(color: Colors.black45))),
|
||||
),
|
||||
SizedBox(height: 18),
|
||||
|
||||
Text('About the Event', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
|
||||
SizedBox(height: 8),
|
||||
Text(aboutText, style: TextStyle(color: Colors.black54, height: 1.45)),
|
||||
SizedBox(height: 18),
|
||||
|
||||
Text('Gallery', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
|
||||
SizedBox(height: 12),
|
||||
SizedBox(
|
||||
height: 78,
|
||||
child: ListView.separated(
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: gallery.length,
|
||||
separatorBuilder: (_, __) => SizedBox(width: 12),
|
||||
itemBuilder: (context, idx) {
|
||||
final path = gallery[idx];
|
||||
return ClipRRect(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: Container(
|
||||
width: 78,
|
||||
height: 78,
|
||||
color: Colors.grey[200],
|
||||
child: Image.asset(
|
||||
path,
|
||||
fit: BoxFit.cover,
|
||||
errorBuilder: (ctx, err, st) => Center(child: Icon(Icons.image, color: Colors.grey)),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(height: 22),
|
||||
|
||||
// If already booked, show booked UI inside the sheet too (optional)
|
||||
if (_booked) ...[
|
||||
_bookedRow(),
|
||||
SizedBox(height: 12),
|
||||
] else ...[
|
||||
// Book button inside sheet — now does in-place booking (no navigation)
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: EdgeInsets.symmetric(vertical: 16),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
backgroundColor: Color(0xFF0B63D6),
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
onPressed: () {
|
||||
// mark booked and close the sheet (no removal from home)
|
||||
_performLocalBooking();
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: Text('Book Your Spot', style: TextStyle(fontSize: 16, color: Colors.white)),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
SizedBox(height: 12),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// row shown after booking: left green pill + three action icons on right
|
||||
Widget _bookedRow() {
|
||||
final Color primary = Color(0xFF0B63D6);
|
||||
return Row(
|
||||
children: [
|
||||
// Tickets Booked pill (left)
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 18, vertical: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFFDFF5DF), // light green background
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Text('Tickets Booked', style: TextStyle(color: Color(0xFF2E7D32), fontWeight: FontWeight.bold)),
|
||||
),
|
||||
|
||||
Spacer(),
|
||||
|
||||
// action icons (scanner / chat / call)
|
||||
_iconSquare(primary, Icons.qr_code_scanner, onTap: () {
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Scanner tapped (demo)')));
|
||||
}),
|
||||
SizedBox(width: 12),
|
||||
_iconSquare(primary, Icons.chat, onTap: () {
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Chat tapped (demo)')));
|
||||
}),
|
||||
SizedBox(width: 12),
|
||||
_iconSquare(primary, Icons.call, onTap: () {
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Call tapped (demo)')));
|
||||
}),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _iconSquare(Color bg, IconData icon, {required VoidCallback onTap}) {
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: Container(
|
||||
width: 52,
|
||||
height: 52,
|
||||
decoration: BoxDecoration(
|
||||
color: bg,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: [BoxShadow(color: Colors.black12, blurRadius: 6, offset: Offset(0, 4))],
|
||||
),
|
||||
child: Icon(icon, color: Colors.white, size: 24),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _confirmBookingFromMain() {
|
||||
// book in-place (no navigation and no removal from home)
|
||||
_performLocalBooking();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// full-bleed image behind status bar
|
||||
return Scaffold(
|
||||
extendBodyBehindAppBar: true,
|
||||
backgroundColor: Colors.black,
|
||||
body: Stack(
|
||||
children: [
|
||||
// full background image
|
||||
Positioned.fill(
|
||||
child: Image.asset(
|
||||
widget.image,
|
||||
fit: BoxFit.cover,
|
||||
errorBuilder: (ctx, err, st) => Container(color: Colors.grey[900]),
|
||||
),
|
||||
),
|
||||
|
||||
// subtle gradient to make bottom white text readable
|
||||
Positioned.fill(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [Colors.transparent, Colors.black87],
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
stops: [0.45, 1.0],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// top safe area controls
|
||||
SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
_smallRoundedButton(icon: Icons.arrow_back, onTap: () => Navigator.pop(context)),
|
||||
_smallRoundedButton(icon: Icons.favorite_border, onTap: () {}),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// bottom anchored overlay: title, details, either book button or booked row, learn more
|
||||
Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: SafeArea(
|
||||
bottom: true,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(20, 12, 20, 16),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// Title & details
|
||||
Container(
|
||||
width: double.infinity,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'The Homecoming Tour | Sid Sriram Live in Concert',
|
||||
style: TextStyle(color: Colors.white, fontSize: 22, fontWeight: FontWeight.bold),
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
Row(
|
||||
children: [
|
||||
Icon(Icons.calendar_today, size: 16, color: Colors.white70),
|
||||
SizedBox(width: 8),
|
||||
Text('22 Jan - 24 Jan', style: TextStyle(color: Colors.white70)),
|
||||
SizedBox(width: 12),
|
||||
Icon(Icons.location_on, size: 16, color: Colors.white70),
|
||||
SizedBox(width: 8),
|
||||
Expanded(child: Text('Cultural Hall, Brookefield', style: TextStyle(color: Colors.white70))),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(height: 16),
|
||||
|
||||
// If booked: show green pill + icons in-place.
|
||||
// Otherwise show the large Book button.
|
||||
if (_booked) ...[
|
||||
_bookedRow(),
|
||||
SizedBox(height: 12),
|
||||
] else ...[
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: ElevatedButton(
|
||||
onPressed: _confirmBookingFromMain,
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: EdgeInsets.symmetric(vertical: 16),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
backgroundColor: Color(0xFF0B63D6),
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
child: Text('Book Your Spot', style: TextStyle(fontSize: 16, color: Colors.white)),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
],
|
||||
|
||||
// Learn more handle (tappable)
|
||||
GestureDetector(
|
||||
onTap: _openLearnMoreSheet,
|
||||
child: Column(
|
||||
children: [
|
||||
Text('Learn More', style: TextStyle(color: Colors.white70)),
|
||||
SizedBox(height: 6),
|
||||
Icon(Icons.keyboard_double_arrow_up, color: Colors.white70),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _smallRoundedButton({required IconData icon, required VoidCallback onTap}) {
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: Container(
|
||||
width: 44,
|
||||
height: 44,
|
||||
decoration: BoxDecoration(color: Colors.white24, borderRadius: BorderRadius.circular(12)),
|
||||
child: Icon(icon, color: Colors.white),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user