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

672 lines
19 KiB
Dart

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:aesthetica_wallpaper/models/image_category.dart';
import 'package:aesthetica_wallpaper/providers/editor_provider.dart';
import 'package:aesthetica_wallpaper/screens/puzzle/drag_puzzle_menu_screen.dart';
import 'package:aesthetica_wallpaper/screens/home/recommendation_list_screen.dart';
import 'dart:math' as math;
import '../aigenerate/ai_generate.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen>
with SingleTickerProviderStateMixin {
late Future<List<ImageCategory>> _categoriesFuture;
late PageController _bannerController;
late AnimationController _animationController;
@override
void initState() {
super.initState();
_categoriesFuture = _loadCategories();
_bannerController = PageController();
_animationController = AnimationController(
vsync: this,
duration: const Duration(seconds: 2),
)..repeat();
// 自动轮播Banner
Future.delayed(const Duration(seconds: 3), _autoPlayBanner);
}
void _autoPlayBanner() {
if (!mounted) return;
_bannerController.nextPage(
duration: const Duration(milliseconds: 500),
curve: Curves.easeInOut,
);
Future.delayed(const Duration(seconds: 3), _autoPlayBanner);
}
@override
void dispose() {
_bannerController.dispose();
_animationController.dispose();
super.dispose();
}
// 直接读取assets文件夹中的图片
Future<List<ImageCategory>> _loadCategories() async {
final categories = <ImageCategory>[];
// 定义类别配置
final categoryConfigs = [
{'name': 'Nature', 'folder': 'nature'},
{'name': 'Abstract', 'folder': 'abstract'},
{'name': 'Architecture', 'folder': 'architecture'},
{'name': 'Animals', 'folder': 'animals'},
{'name': 'Food', 'folder': 'food'},
{'name': 'Travel', 'folder': 'travel'},
];
for (final config in categoryConfigs) {
try {
// 尝试读取文件夹中的图片
final images = await _getImagesFromFolder(config['folder']!);
if (images.isNotEmpty) {
categories.add(
ImageCategory(
name: config['name']!,
folder: config['folder']!,
images: images,
),
);
}
} catch (e) {
debugPrint('Error loading category ${config['name']}: $e');
}
}
return categories;
}
// 获取指定文件夹中的所有图片
Future<List<String>> _getImagesFromFolder(String folderName) async {
// 根据实际assets文件夹中的图片文件列表
final knownImages = {
'nature': [
'nature1.png',
'nature2.png',
'nature3.png',
'nature4.png',
'nature5.png',
'nature6.png',
'nature7.png',
'nature8.png',
'nature9.png',
],
'abstract': [
'abstract1.png',
'abstract2.png',
'abstract3.png',
'abstract4.png',
'abstract5.png',
'abstract6.png',
'abstract7.png',
'abstract8.png',
'abstract9.png',
'abstract10.png',
'abstract11.png',
'abstract12.png',
'abstract13.png',
'abstract14.png',
'abstract15.png',
],
'architecture': [
'architecture1.png',
'architecture2.png',
'architecture3.png',
'architecture4.png',
'architecture5.png',
'architecture6.png',
'architecture7.png',
'architecture8.png',
'architecture9.png',
'architecture10.png',
],
'animals': [
'animals1.png',
'animals2.png',
'animals3.png',
'animals4.png',
'animals5.png',
'animals6.png',
'animals7.png',
],
'food': [
'food1.png',
'food2.png',
'food3.png',
'food4.png',
'food5.png',
'food6.png',
'food7.png',
'food8.png',
'food9.png',
'food10.png',
'food11.png',
'food12.png',
'food13.png',
],
'travel': <String>[
'travel5.png',
'travel1.png',
'travel2.png',
'travel3.png',
'travel4.png',
'travel6.png',
'travel7.png',
'travel8.png',
'travel9.png',
],
};
return knownImages[folderName] ?? [];
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder<List<ImageCategory>>(
future: _categoriesFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
}
if (snapshot.hasError) {
return Center(
child: Text('Error loading categories: ${snapshot.error}'),
);
}
if (!snapshot.hasData || snapshot.data!.isEmpty) {
return const Center(child: Text('No categories found.'));
}
final categories = snapshot.data!;
return CustomScrollView(
slivers: [
// 美化的AppBar
_buildSliverAppBar(),
// Banner轮播
SliverToBoxAdapter(child: _buildBannerSection(categories)),
// 快速功能入口
SliverToBoxAdapter(child: _buildQuickActions()),
// 每日推荐
SliverToBoxAdapter(child: _buildDailyRecommendation(categories)),
// 热门分类标题
SliverToBoxAdapter(
child: _buildSectionTitle(
'Categories',
'Explore beautiful wallpapers',
),
),
// 分类网格
SliverPadding(
padding: const EdgeInsets.symmetric(horizontal: 16),
sliver: SliverGrid(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisSpacing: 12,
crossAxisSpacing: 12,
childAspectRatio: 0.75,
),
delegate: SliverChildBuilderDelegate((context, index) {
return _buildCategoryGridItem(context, categories[index]);
}, childCount: categories.length),
),
),
// 底部间距
const SliverToBoxAdapter(child: SizedBox(height: 100)),
],
);
},
),
);
}
// 美化的SliverAppBar
Widget _buildSliverAppBar() {
return SliverAppBar(
expandedHeight: 120,
floating: false,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
title: const Text(
'MoodCanvas',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 24),
),
background: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [Colors.purple.shade400, Colors.blue.shade400],
),
),
),
),
);
}
// Banner轮播区域 - 只保留两个:拼图和推荐
Widget _buildBannerSection(List<ImageCategory> categories) {
final bannerItems = [
{
'title': '🎮 Puzzle Game',
'subtitle': 'Challenge your mind with wall papers',
'color': Colors.purple,
'icon': Icons.extension,
'action': 'puzzle',
},
{
'title': '⭐ Daily Picks',
'subtitle': 'Discover today\'s best wallpapers',
'color': Colors.orange,
'icon': Icons.star,
'action': 'recommendation',
},
];
return Container(
height: 145,
margin: const EdgeInsets.symmetric(vertical: 16),
child: PageView.builder(
controller: _bannerController,
itemCount: bannerItems.length,
itemBuilder: (context, index) {
final item = bannerItems[index];
return _buildBannerItem(
item['title'] as String,
item['subtitle'] as String,
item['color'] as Color,
item['icon'] as IconData,
item['action'] as String,
);
},
),
);
}
Widget _buildBannerItem(
String title,
String subtitle,
Color color,
IconData icon,
String action,
) {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 16),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [color, color.withValues(alpha: 0.7)],
),
boxShadow: [
BoxShadow(
color: color.withValues(alpha: 0.3),
blurRadius: 15,
offset: const Offset(0, 8),
),
],
),
child: Material(
color: Colors.transparent,
child: InkWell(
borderRadius: BorderRadius.circular(20),
onTap: () {
if (action == 'puzzle') {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const DragPuzzleMenuScreen(),
),
);
} else if (action == 'recommendation') {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const RecommendationListScreen(),
),
);
}
},
child: Padding(
padding: const EdgeInsets.all(24),
child: Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
title,
style: const TextStyle(
color: Colors.white,
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
Text(
subtitle,
style: TextStyle(
color: Colors.white.withValues(alpha: 0.9),
fontSize: 14,
),
),
],
),
),
AnimatedBuilder(
animation: _animationController,
builder: (context, child) {
return Transform.rotate(
angle: _animationController.value * 2 * math.pi,
child: Icon(
icon,
size: 60,
color: Colors.white.withValues(alpha: 0.3),
),
);
},
),
],
),
),
),
),
);
}
// 快速功能入口 - 只保留拼图和AI生成
Widget _buildQuickActions() {
final actions = [
{'icon': Icons.extension, 'label': 'Puzzle', 'color': Colors.purple},
{
'icon': Icons.auto_awesome,
'label': 'AI Generate',
'color': Colors.blue,
},
];
return Container(
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Row(
children: [
Expanded(
child: _buildQuickActionItem(
actions[0]['icon'] as IconData,
actions[0]['label'] as String,
actions[0]['color'] as Color,
),
),
const SizedBox(width: 12),
Expanded(
child: _buildQuickActionItem(
actions[1]['icon'] as IconData,
actions[1]['label'] as String,
actions[1]['color'] as Color,
),
),
],
),
);
}
Widget _buildQuickActionItem(IconData icon, String label, Color color) {
return InkWell(
onTap: () {
// 根据不同功能跳转
if (label == 'Puzzle') {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const DragPuzzleMenuScreen(),
),
);
} else {
//AI Generate
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const ParticleHomePage()),
);
}
},
borderRadius: BorderRadius.circular(16),
child: Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
color.withValues(alpha: 0.2),
color.withValues(alpha: 0.1),
],
),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: color.withValues(alpha: 0.3), width: 1),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: color.withValues(alpha: 0.15),
shape: BoxShape.circle,
),
child: Icon(icon, color: color, size: 36),
),
const SizedBox(height: 12),
Text(
label,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
color: color,
),
textAlign: TextAlign.center,
),
],
),
),
);
}
// 每日推荐区域
Widget _buildDailyRecommendation(List<ImageCategory> categories) {
if (categories.isEmpty) return const SizedBox.shrink();
final random = math.Random();
final randomCategory = categories[random.nextInt(categories.length)];
final randomImages = randomCategory.images.take(5).toList();
return Column(
children: [
_buildSectionTitle('Daily Picks', 'Curated wallpapers for you'),
SizedBox(
height: 150,
child: ListView.builder(
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.symmetric(horizontal: 16),
itemCount: randomImages.length,
itemBuilder: (context, index) {
return _buildRecommendationItem(
randomCategory.folder,
randomImages[index],
);
},
),
),
],
);
}
Widget _buildRecommendationItem(String folder, String imageName) {
final imagePath = 'assets/images/$folder/$imageName';
return GestureDetector(
onTap: () {
// 初始化编辑器并跳转
Provider.of<EditorProvider>(
context,
listen: false,
).startEditing(imagePath);
Navigator.pushNamed(context, '/editor');
},
child: Container(
width: 140,
margin: const EdgeInsets.only(right: 12),
child: ClipRRect(
borderRadius: BorderRadius.circular(16),
child: Stack(
fit: StackFit.expand,
children: [
Image.asset(imagePath, fit: BoxFit.cover),
Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.transparent,
Colors.black.withValues(alpha: 0.5),
],
),
),
),
const Positioned(
bottom: 8,
left: 8,
right: 8,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Icon(Icons.favorite_border, color: Colors.white, size: 20),
Icon(Icons.edit, color: Colors.white, size: 20),
],
),
),
],
),
),
),
);
}
// 区域标题
Widget _buildSectionTitle(String title, String subtitle) {
return Padding(
padding: const EdgeInsets.fromLTRB(16, 24, 16, 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
Text(
subtitle,
style: TextStyle(fontSize: 14, color: Colors.grey.shade600),
),
],
),
],
),
);
}
// 分类网格项
Widget _buildCategoryGridItem(BuildContext context, ImageCategory category) {
return InkWell(
onTap: () {
Navigator.pushNamed(context, '/gallery', arguments: category);
},
borderRadius: BorderRadius.circular(16),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.1),
blurRadius: 10,
offset: const Offset(0, 4),
),
],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(16),
child: Stack(
fit: StackFit.expand,
children: [
Image.asset(category.getThumbnailPath(), fit: BoxFit.cover),
Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.transparent,
Colors.black.withValues(alpha: 0.7),
],
),
),
),
Positioned(
bottom: 12,
left: 12,
right: 12,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
category.name,
style: const TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
Text(
'${category.images.length} wallpapers',
style: TextStyle(
color: Colors.white.withValues(alpha: 0.8),
fontSize: 12,
),
),
],
),
),
],
),
),
),
);
}
}