MoodCanvas/lib/screens/favorites/favorites_screen.dart
fengshengxiong 91b7eebbf2 接入TopON
2026-01-22 16:34:55 +08:00

278 lines
8.7 KiB
Dart

import 'package:aesthetica_wallpaper/providers/recipe_provider.dart';
import 'package:aesthetica_wallpaper/providers/editor_provider.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:aesthetica_wallpaper/models/recipe.dart';
class FavoritesScreen extends StatelessWidget {
const FavoritesScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Favorites'),
backgroundColor: Colors.grey[900],
elevation: 0,
),
body: Consumer<RecipeProvider>(
builder: (context, provider, child) {
if (provider.recipes.isEmpty) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.favorite_border,
size: 80,
color: Colors.grey[600],
),
const SizedBox(height: 16),
Text(
'No Favorites Yet',
style: TextStyle(
color: Colors.grey[400],
fontSize: 20,
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: 8),
Text(
'Start creating and saving your favorite wallpapers!',
style: TextStyle(color: Colors.grey[500], fontSize: 14),
textAlign: TextAlign.center,
),
],
),
);
}
return GridView.builder(
padding: const EdgeInsets.fromLTRB(
16,
16,
16,
100,
), // 增加底部内边距避免被底部导航栏遮挡
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 12,
mainAxisSpacing: 12,
childAspectRatio: 0.8,
),
itemCount: provider.recipes.length,
itemBuilder: (context, index) {
final recipe = provider.recipes[index];
return _buildFavoriteCard(context, recipe, provider);
},
);
},
),
);
}
Widget _buildFavoriteCard(
BuildContext context,
Recipe recipe,
RecipeProvider provider,
) {
return Container(
decoration: BoxDecoration(
color: Colors.grey[800],
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.2),
blurRadius: 8,
offset: const Offset(0, 4),
),
],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(16),
child: InkWell(
onTap: () {
// 加载配方到编辑器
Provider.of<EditorProvider>(
context,
listen: false,
).loadFromRecipe(recipe);
Navigator.pushNamed(context, '/editor');
},
child: Stack(
fit: StackFit.expand,
children: [
// 壁纸预览
Image.asset(
recipe.baseImagePath,
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) {
return Container(
color: Colors.grey[700],
child: const Icon(
Icons.image_not_supported,
color: Colors.grey,
size: 40,
),
);
},
),
// 渐变蒙版
Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.transparent,
Colors.black.withValues(alpha: 0.7),
],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
),
),
// 收藏按钮
Positioned(
top: 8,
right: 8,
child: Container(
padding: const EdgeInsets.all(6),
decoration: BoxDecoration(
color: Colors.black.withValues(alpha: 0.5),
shape: BoxShape.circle,
),
child: const Icon(
Icons.favorite,
color: Colors.pinkAccent,
size: 20,
),
),
),
// 底部信息
Positioned(
bottom: 0,
left: 0,
right: 0,
child: Container(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
_getRecipeName(recipe.baseImagePath),
style: const TextStyle(
color: Colors.white,
fontSize: 14,
fontWeight: FontWeight.bold,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 4),
Text(
_getRecipeDescription(recipe),
style: TextStyle(color: Colors.grey[300], fontSize: 12),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
],
),
),
),
// 删除按钮
Positioned(
top: 8,
left: 8,
child: GestureDetector(
onTap: () => _showDeleteDialog(context, recipe, provider),
child: Container(
padding: const EdgeInsets.all(6),
decoration: BoxDecoration(
color: Colors.red.withValues(alpha: 0.8),
shape: BoxShape.circle,
),
child: const Icon(
Icons.delete_outline,
color: Colors.white,
size: 16,
),
),
),
),
],
),
),
),
);
}
String _getRecipeName(String imagePath) {
final fileName = imagePath.split('/').last;
// 移除文件扩展名
return fileName.split('.').first;
}
String _getRecipeDescription(Recipe recipe) {
final effects = <String>[];
if (recipe.brightness != 0.0) {
effects.add('Brightness: ${recipe.brightness.toStringAsFixed(1)}');
}
if (recipe.contrast != 1.0) {
effects.add('Contrast: ${recipe.contrast.toStringAsFixed(1)}');
}
if (recipe.saturation != 1.0) {
effects.add('Saturation: ${recipe.saturation.toStringAsFixed(1)}');
}
if (recipe.blur > 0.0) {
effects.add('Blur: ${recipe.blur.toStringAsFixed(1)}');
}
if (recipe.pixelate > 1.0) {
effects.add('Pixelate: ${recipe.pixelate.toStringAsFixed(1)}');
}
if (recipe.overlayText.isNotEmpty) {
effects.add('Text: ${recipe.overlayText}');
}
if (effects.isEmpty) {
return 'Original image';
}
return effects.take(2).join(', ');
}
void _showDeleteDialog(
BuildContext context,
Recipe recipe,
RecipeProvider provider,
) {
showDialog(
context: context,
builder: (context) => AlertDialog(
backgroundColor: Colors.grey[800],
title: const Text(
'Remove from Favorites',
style: TextStyle(color: Colors.white),
),
content: const Text(
'Are you sure you want to remove this wallpaper from your favorites?',
style: TextStyle(color: Colors.white),
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Cancel', style: TextStyle(color: Colors.grey)),
),
TextButton(
onPressed: () {
provider.deleteRecipe(recipe.id);
Navigator.of(context).pop();
},
child: const Text('Remove', style: TextStyle(color: Colors.red)),
),
],
),
);
}
}