Files
sartron_Northblue 95afac750a first commit
2025-09-17 11:50:34 +04:00

475 lines
14 KiB
Dart

import 'package:flutter/material.dart';
import '../models/movie.dart';
import '../services/api_service.dart';
import 'movie_detail_page.dart';
import 'favorites_page.dart';
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
List<MovieCategory> categories = [
const MovieCategory(name: "TOUS", isSelected: true),
const MovieCategory(name: "POPULAIRES"),
const MovieCategory(name: "MIEUX NOTÉS"),
const MovieCategory(name: "EN COURS"),
const MovieCategory(name: "PROCHAINEMENT"),
];
List<Movie> movies = [];
bool isLoading = true;
String? errorMessage;
int currentFeaturedIndex = 0;
int _currentIndex = 1;
@override
void initState() {
super.initState();
_loadMovies();
}
Future<void> _loadMovies() async {
try {
setState(() {
isLoading = true;
errorMessage = null;
});
final popularMovies = await ApiService.getPopularMovies();
setState(() {
movies = popularMovies;
if (movies.isNotEmpty) {
movies[0] = Movie(
id: movies[0].id,
title: movies[0].title,
description: movies[0].description,
imageUrl: movies[0].imageUrl,
category: movies[0].category,
director: movies[0].director,
releaseDate: movies[0].releaseDate,
isFeatured: true,
rating: movies[0].rating,
originalTitle: movies[0].originalTitle,
);
}
isLoading = false;
});
} catch (e) {
setState(() {
errorMessage = e.toString();
isLoading = false;
});
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
backgroundColor: Colors.black,
appBar: AppBar(
backgroundColor: Colors.purple,
centerTitle: true,
toolbarHeight: 80,
title: const Text(
"MOVIES",
style: TextStyle(
fontSize: 32,
fontWeight: FontWeight.bold,
letterSpacing: 3,
color: Colors.white,
),
),
),
body: _getBody(),
bottomNavigationBar: _buildBottomNav(),
),
);
}
Widget _buildBody() {
if (isLoading) {
return const Center(
child: CircularProgressIndicator(
color: Colors.purple,
),
);
}
if (errorMessage != null) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(
Icons.error_outline,
color: Colors.red,
size: 64,
),
const SizedBox(height: 16),
Text(
'Erreur de chargement',
style: const TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
Text(
errorMessage!,
style: TextStyle(
color: Colors.grey[400],
fontSize: 14,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 16),
ElevatedButton(
onPressed: _loadMovies,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.purple,
foregroundColor: Colors.white,
),
child: const Text('Réessayer'),
),
],
),
);
}
return Column(
children: [
_buildCategoryBar(),
Expanded(
child: ListView(
children: [
if (movies.isNotEmpty) ...[
_buildFeaturedMovie(),
_buildFeaturedIndicators(),
],
...movies.skip(5).map((movie) => _buildMovieItem(movie)),
],
),
),
],
);
}
Widget _buildCategoryBar() {
return Container(
height: 50,
color: Colors.black,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: categories.length,
itemBuilder: (context, index) {
final isSelected = index == 0;
return Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
child: Text(
categories[index].name,
style: TextStyle(
color: isSelected ? Colors.white : Colors.grey,
fontSize: 16,
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
),
),
);
},
),
);
}
Widget _getBody() {
switch (_currentIndex) {
case 0:
return const FavoritesPage();
case 1:
return _buildBody();
case 2:
return _buildProfilePage();
default:
return _buildBody();
}
}
Widget _buildProfilePage() {
return Container(
color: Colors.black,
child: const Center(
child: Text(
'Profil',
style: TextStyle(
color: Colors.white,
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
),
);
}
Widget _buildBottomNav() {
return BottomNavigationBar(
backgroundColor: Colors.white,
selectedItemColor: Colors.purple,
unselectedItemColor: Colors.grey,
currentIndex: _currentIndex,
onTap: (index) {
setState(() {
_currentIndex = index;
});
},
items: const [
BottomNavigationBarItem(icon: Icon(Icons.favorite), label: 'Favoris'),
BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Accueil'),
BottomNavigationBarItem(icon: Icon(Icons.person), label: 'Profil'),
],
);
}
Widget _buildFeaturedMovie() {
final featuredMovies = movies.take(5).toList();
return Container(
height: 300,
margin: const EdgeInsets.all(16),
child: PageView.builder(
onPageChanged: (index) {
setState(() {
currentFeaturedIndex = index;
});
},
itemCount: featuredMovies.length,
itemBuilder: (context, index) {
final movie = featuredMovies[index];
return Container(
margin: const EdgeInsets.symmetric(horizontal: 4),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
color: Colors.grey[800],
),
child: Stack(
children: [
_buildMovieImage(movie.imageUrl, 12, movie: movie),
_buildGradientOverlay(),
_buildMovieDetails(movie),
_buildPlayButton(50, 30),
],
),
);
},
),
);
}
Widget _buildMovieItem(Movie movie) {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.grey[900],
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.grey[800]!, width: 1),
),
child: Row(
children: [
_buildMoviePoster(movie),
const SizedBox(width: 16),
Expanded(child: _buildMovieInfo(movie)),
_buildSimplePlayButton(40, 24),
],
),
);
}
Widget _buildMoviePoster(Movie movie) {
return Container(
width: 80,
height: 80,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: Colors.grey[700],
),
child: _buildMovieImage(movie.imageUrl, 8, movie: movie),
);
}
Widget _buildMovieInfo(Movie movie) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(movie.title, style: const TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.bold)),
const SizedBox(height: 4),
Row(
children: [
Icon(Icons.star, color: Colors.amber, size: 14),
const SizedBox(width: 4),
Text(movie.rating.toStringAsFixed(1), style: TextStyle(color: Colors.amber, fontSize: 12, fontWeight: FontWeight.w500)),
const SizedBox(width: 8),
Text(movie.category, style: TextStyle(color: Colors.purple[300], fontSize: 12, fontWeight: FontWeight.w500)),
],
),
const SizedBox(height: 4),
Text(movie.description, style: TextStyle(color: Colors.grey[400], fontSize: 12), maxLines: 2, overflow: TextOverflow.ellipsis),
const SizedBox(height: 4),
Text(movie.releaseDate, style: TextStyle(color: Colors.grey[500], fontSize: 10)),
],
);
}
Widget _buildMovieImage(String imageUrl, double radius, {Movie? movie}) {
return GestureDetector(
onTap: movie != null ? () => _navigateToMovieDetail(movie) : null,
child: ClipRRect(
borderRadius: BorderRadius.circular(radius),
child: imageUrl.isNotEmpty
? Image.network(
imageUrl,
width: radius == 12 ? double.infinity : 80,
height: radius == 12 ? double.infinity : 80,
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) => _buildFallbackImage(radius),
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return Container(
width: radius == 12 ? double.infinity : 80,
height: radius == 12 ? double.infinity : 80,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(radius),
color: Colors.grey[800],
),
child: const Center(
child: CircularProgressIndicator(
color: Colors.purple,
strokeWidth: 2,
),
),
);
},
)
: _buildFallbackImage(radius),
),
);
}
Widget _buildFallbackImage(double radius) {
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(radius),
gradient: LinearGradient(
colors: radius == 12
? [Colors.blue[900]!, Colors.purple[900]!]
: [Colors.grey[600]!, Colors.grey[800]!],
),
),
child: Center(
child: Icon(Icons.movie, color: Colors.white, size: radius == 12 ? 50 : 30),
),
);
}
Widget _buildGradientOverlay() {
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Colors.transparent, Colors.black.withOpacity(0.7)],
),
),
);
}
Widget _buildMovieDetails(Movie movie) {
return Positioned(
left: 20,
bottom: 20,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("FILM POPULAIRE", style: TextStyle(color: Colors.white.withOpacity(0.8), fontSize: 12)),
const SizedBox(height: 4),
Text(movie.category.toUpperCase(), style: TextStyle(color: Colors.purple[300], fontSize: 16, fontWeight: FontWeight.bold)),
const SizedBox(height: 8),
Text(movie.title, style: const TextStyle(color: Colors.white, fontSize: 32, fontWeight: FontWeight.bold)),
Row(
children: [
const Icon(Icons.star, color: Colors.amber, size: 16),
const SizedBox(width: 4),
Text("${movie.rating.toStringAsFixed(1)}/10", style: const TextStyle(color: Colors.white, fontSize: 16)),
],
),
const SizedBox(height: 8),
Text(movie.releaseDate, style: TextStyle(color: Colors.white.withOpacity(0.8), fontSize: 12)),
],
),
);
}
Widget _buildPlayButton(double size, double iconSize) {
return Positioned(
right: 20,
bottom: 20,
child: Container(
width: size,
height: size,
decoration: const BoxDecoration(color: Colors.red, shape: BoxShape.circle),
child: Icon(Icons.play_arrow, color: Colors.white, size: iconSize),
),
);
}
Widget _buildSimplePlayButton(double size, double iconSize) {
return Container(
width: size,
height: size,
decoration: const BoxDecoration(color: Colors.red, shape: BoxShape.circle),
child: Icon(Icons.play_arrow, color: Colors.white, size: iconSize),
);
}
Widget _buildFeaturedIndicators() {
final featuredCount = movies.take(5).length;
return Container(
height: 20,
margin: const EdgeInsets.symmetric(vertical: 8),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(
featuredCount,
(index) => Container(
margin: const EdgeInsets.symmetric(horizontal: 4),
width: 8,
height: 8,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: index == currentFeaturedIndex
? Colors.purple
: Colors.grey.withOpacity(0.5),
),
),
),
),
);
}
void _navigateToMovieDetail(Movie movie) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => MovieDetailPage(movie: movie),
),
);
}
}