Files
MookNote/lib/widgets/movie_list_item.dart
2026-03-12 16:13:13 +08:00

157 lines
4.2 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../models/data_models.dart';
import '../providers/app_provider.dart';
import '../utils/toast_util.dart';
/// 观影列表项组件 - 网格布局设计
class MovieListItem extends StatelessWidget {
final Movie movie;
const MovieListItem({super.key, required this.movie});
@override
Widget build(BuildContext context) {
return InkWell(
onTap: () {
Navigator.pushNamed(context, '/movie-detail', arguments: movie);
},
onLongPress: () => _showDeleteDialog(context),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 海报
Expanded(
child: _buildPoster(),
),
const SizedBox(height: 8),
// 影视名称
Text(
movie.title,
style: const TextStyle(
fontSize: 13,
fontWeight: FontWeight.w500,
color: Color(0xFF1A1A1A),
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 4),
// 评分 - 5星显示
if (movie.rating != null)
_buildStarRating(movie.rating!)
else
const SizedBox(height: 16),
],
),
);
}
/// 构建海报
Widget _buildPoster() {
return Container(
width: double.infinity,
decoration: BoxDecoration(
color: const Color(0xFFF5F5F5),
borderRadius: BorderRadius.circular(8),
border: Border.all(color: const Color(0xFFE5E5E5), width: 0.5),
),
clipBehavior: Clip.antiAlias,
child: movie.posterPath != null && movie.posterPath!.isNotEmpty
? Image.file(
File(movie.posterPath!),
fit: BoxFit.cover,
errorBuilder: (_, __, ___) => _buildPosterPlaceholder(),
)
: _buildPosterPlaceholder(),
);
}
Widget _buildPosterPlaceholder() {
return const Center(
child: Icon(
Icons.movie_outlined,
size: 24,
color: Color(0xFFCCCCCC),
),
);
}
/// 构建5星评分显示评分范围1-10每星2分
Widget _buildStarRating(double rating) {
// 将10分制转换为5星制
final starValue = rating / 2;
return Row(
mainAxisSize: MainAxisSize.min,
children: [
// 5个星星
...List.generate(5, (index) {
final starIndex = index + 1;
IconData iconData;
if (starValue >= starIndex) {
// 满星
iconData = Icons.star;
} else if (starValue >= starIndex - 0.5) {
// 半星
iconData = Icons.star_half;
} else {
// 空星
iconData = Icons.star_border;
}
return Icon(
iconData,
size: 12,
color: const Color(0xFFFFB800),
);
}),
const SizedBox(width: 4),
// 评分数字
Text(
rating.toStringAsFixed(1),
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFF666666),
),
),
],
);
}
/// 显示删除确认对话框
void _showDeleteDialog(BuildContext context) {
showDialog(
context: context,
builder: (context) => AlertDialog(
backgroundColor: Colors.white,
elevation: 0,
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.zero),
title: const Text('确认删除'),
content: Text('确定要删除《${movie.title}》吗?'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('取消', style: TextStyle(color: Color(0xFF666666))),
),
TextButton(
onPressed: () async {
await context.read<AppProvider>().removeMovie(movie.id);
Navigator.pop(context);
ToastUtil.show(context, '已删除');
},
child: const Text('删除', style: TextStyle(color: Colors.red)),
),
],
),
);
}
}