import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'dart:convert'; import '../data/app_data.dart'; import '../models/film.dart'; import 'login_page.dart'; import 'detail_page.dart'; import 'my_bookings_page.dart'; class HomePage extends StatefulWidget { final String username; final bool isGuest; const HomePage({super.key, required this.username, required this.isGuest}); @override State createState() => _HomePageState(); } class _HomePageState extends State { int _selectedIndex = 0; Widget _buildFilmImage(String? imageUrl, {required double width, required double height}) { if (imageUrl == null || imageUrl.isEmpty) { return Center( child: Icon(Icons.movie, size: 80, color: Colors.grey), ); } if (imageUrl.startsWith('data:image')) { // Base64 image try { final base64String = imageUrl.split(',')[1]; final bytes = base64Decode(base64String); return ClipRRect( borderRadius: const BorderRadius.vertical(top: Radius.circular(12)), child: Image.memory( bytes, width: width, height: height, fit: BoxFit.cover, errorBuilder: (context, error, stackTrace) { return Center( child: Icon(Icons.movie, size: 80, color: Colors.grey), ); }, ), ); } catch (e) { return Center( child: Icon(Icons.movie, size: 80, color: Colors.grey), ); } } else { // Network image (URL) return ClipRRect( borderRadius: const BorderRadius.vertical(top: Radius.circular(12)), child: Image.network( imageUrl, width: width, height: height, fit: BoxFit.cover, errorBuilder: (context, error, stackTrace) { return Center( child: Icon(Icons.movie, size: 80, color: Colors.grey), ); }, ), ); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( backgroundColor: Colors.black, title: const Row( children: [ Icon(Icons.movie, color: Color(0xFFFBBF24)), SizedBox(width: 8), Text('CineSim'), ], ), actions: [ if (!widget.isGuest) IconButton( icon: const Icon(Icons.confirmation_number_outlined), onPressed: () { Navigator.push( context, MaterialPageRoute( builder: (_) => MyBookingsPage(username: widget.username), ), ); }, tooltip: 'Tiket Saya', ), Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Row( children: [ const Icon(Icons.person, size: 20), const SizedBox(width: 8), Text(widget.username), const SizedBox(width: 16), IconButton( icon: const Icon(Icons.logout), onPressed: () { Navigator.pushReplacement( context, MaterialPageRoute(builder: (_) => const LoginPage()), ); }, ), ], ), ), ], ), body: _selectedIndex == 0 ? _buildNowShowingTab() : _buildComingSoonTab(), bottomNavigationBar: BottomNavigationBar( currentIndex: _selectedIndex, onTap: (index) => setState(() => _selectedIndex = index), backgroundColor: const Color(0xFF1F2937), selectedItemColor: const Color(0xFFFBBF24), unselectedItemColor: Colors.grey, items: const [ BottomNavigationBarItem( icon: Icon(Icons.play_circle_outline), label: 'Sedang Tayang', ), BottomNavigationBarItem( icon: Icon(Icons.upcoming), label: 'Coming Soon', ), ], ), ); } Widget _buildNowShowingTab() { final nowShowingFilms = AppData.films .where((f) => f.releaseDate.isBefore(DateTime.now())) .toList(); return SingleChildScrollView( padding: const EdgeInsets.all(24), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'Film Sedang Tayang', style: TextStyle(fontSize: 28, fontWeight: FontWeight.bold), ), const SizedBox(height: 8), Text( widget.isGuest ? 'Login untuk memesan tiket' : 'Pilih film favorit Anda', style: const TextStyle(color: Colors.grey), ), const SizedBox(height: 24), _buildFilmGrid(nowShowingFilms), ], ), ); } Widget _buildComingSoonTab() { final comingSoonFilms = AppData.films .where((f) => f.releaseDate.isAfter(DateTime.now())) .toList(); return SingleChildScrollView( padding: const EdgeInsets.all(24), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'Coming Soon', style: TextStyle(fontSize: 28, fontWeight: FontWeight.bold), ), const SizedBox(height: 8), const Text( 'Film yang akan segera tayang', style: TextStyle(color: Colors.grey), ), const SizedBox(height: 24), _buildFilmGrid(comingSoonFilms), ], ), ); } Widget _buildFilmGrid(List films) { return LayoutBuilder( builder: (context, constraints) { int crossAxisCount = 4; if (constraints.maxWidth < 1200) crossAxisCount = 3; if (constraints.maxWidth < 900) crossAxisCount = 2; if (constraints.maxWidth < 600) crossAxisCount = 1; return GridView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: crossAxisCount, childAspectRatio: 0.65, crossAxisSpacing: 16, mainAxisSpacing: 16, ), itemCount: films.length, itemBuilder: (context, index) { final film = films[index]; final isComingSoon = film.releaseDate.isAfter(DateTime.now()); return InkWell( onTap: () { Navigator.push( context, MaterialPageRoute( builder: (_) => DetailPage( film: film, username: widget.username, isGuest: widget.isGuest, ), ), ); }, child: Container( decoration: BoxDecoration( color: const Color(0xFF1F2937), borderRadius: BorderRadius.circular(12), border: Border.all(color: const Color(0xFF374151)), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Stack( children: [ Container( height: 300, decoration: BoxDecoration( color: Colors.grey[800], borderRadius: const BorderRadius.vertical( top: Radius.circular(12), ), ), child: _buildFilmImage( film.imageUrl, width: double.infinity, height: 300, ), ), if (isComingSoon) Positioned( top: 8, right: 8, child: Container( padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 6), decoration: BoxDecoration( color: const Color(0xFFFBBF24), borderRadius: BorderRadius.circular(8), ), child: const Text( 'COMING SOON', style: TextStyle( color: Colors.black, fontWeight: FontWeight.bold, fontSize: 10, ), ), ), ), Positioned( top: 8, left: 8, child: Container( padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 4), decoration: BoxDecoration( color: Colors.black87, borderRadius: BorderRadius.circular(6), ), child: Text( film.ageRating, style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 12, ), ), ), ), ], ), Padding( padding: const EdgeInsets.all(12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( film.title, style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold), maxLines: 1, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 4), Text( film.genre, style: const TextStyle( fontSize: 12, color: Colors.grey), ), const SizedBox(height: 8), Row( children: [ const Icon(Icons.star, size: 16, color: Color(0xFFFBBF24)), const SizedBox(width: 4), Text( film.rating.toString(), style: const TextStyle( color: Color(0xFFFBBF24), fontWeight: FontWeight.bold, ), ), const SizedBox(width: 12), const Icon(Icons.access_time, size: 14, color: Colors.grey), const SizedBox(width: 4), Text( '${film.duration} min', style: const TextStyle( fontSize: 12, color: Colors.grey), ), ], ), if (isComingSoon) ...[ const SizedBox(height: 8), Text( 'Tayang: ${DateFormat('dd MMM yyyy').format(film.releaseDate)}', style: const TextStyle( fontSize: 11, color: Color(0xFFFBBF24), fontWeight: FontWeight.w600, ), ), ], ], ), ), ], ), ), ); }, ); }, ); } }