snap_wish/lib/pages/photo_page.dart
2025-08-30 18:11:40 +08:00

458 lines
11 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 'package:flutter/material.dart';
/// 照片页面 - 应用主页面
/// 使用CustomScrollView实现弹性滚动和复杂布局
class PhotoPage extends StatelessWidget {
const PhotoPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
// 透明AppBar区域让内容延伸到状态栏
extendBodyBehindAppBar: true,
body: CustomScrollView(
slivers: [
const PhotoAppBar(),
const PhotoFeed(),
],
),
);
}
}
/// 自定义SliverAppBar
/// 技巧使用SliverAppBar实现滚动时的动态效果
class PhotoAppBar extends StatelessWidget {
const PhotoAppBar({super.key});
@override
Widget build(BuildContext context) {
return SliverAppBar(
backgroundColor: Colors.transparent,
elevation: 0,
pinned: true, // 滚动时固定在顶部
floating: true, // 向下滚动时立即显示
snap: false,
expandedHeight: 0,
title: ShaderMask(
// 技巧使用ShaderMask实现文字渐变效果
shaderCallback: (bounds) => const LinearGradient(
colors: [Color(0xFFFF8A1F), Color(0xFFFFBE3D)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
).createShader(bounds),
child: const Text(
'SnapWish,',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.white, // 会被shader覆盖
),
),
),
);
}
}
/// 照片信息流
/// 使用SliverList实现高效的长列表渲染
class PhotoFeed extends StatelessWidget {
const PhotoFeed({super.key});
@override
Widget build(BuildContext context) {
// 模拟数据 - 实际应用中应从API获取
final mockPhotos = []; // 空数组触发空状态
if (mockPhotos.isEmpty) {
return const SliverFillRemaining(
child: EmptyState(),
);
}
return SliverPadding(
// 水平内边距,确保内容不贴边
padding: const EdgeInsets.symmetric(horizontal: 16),
sliver: SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
return const PhotoCard();
},
childCount: mockPhotos.length,
),
),
);
}
}
/// 空状态组件
/// 良好的空状态设计提升用户体验
class EmptyState extends StatelessWidget {
const EmptyState({super.key});
@override
Widget build(BuildContext context) {
return const Center(
child: Text(
'还没有添加任何图片',
style: TextStyle(
fontSize: 14,
color: Color(0xFF9CA3AF),
letterSpacing: 0.5, // 字间距让文字更美观
),
),
);
}
}
/// 照片卡片组件 - 核心UI元素
/// 使用StatefulWidget管理收藏状态
class PhotoCard extends StatefulWidget {
const PhotoCard({super.key});
@override
State<PhotoCard> createState() => _PhotoCardState();
}
/// 照片卡片状态管理
/// 技巧使用AnimationController实现按压动画
class _PhotoCardState extends State<PhotoCard>
with SingleTickerProviderStateMixin {
// 收藏状态
bool _isFavorite = false;
// 动画控制器
late AnimationController _controller;
late Animation<double> _scaleAnimation;
@override
void initState() {
super.initState();
// 初始化动画控制器时长200ms
_controller = AnimationController(
duration: const Duration(milliseconds: 200),
vsync: this,
);
// 创建缩放动画从1.0到0.98
_scaleAnimation = Tween<double>(
begin: 1.0,
end: 0.98,
).animate(CurvedAnimation(
parent: _controller,
curve: Curves.easeInOut,
));
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
/// 切换收藏状态
void _toggleFavorite() {
setState(() {
_isFavorite = !_isFavorite;
});
}
/// 处理卡片点击
void _handleCardTap() {
// TODO: 实现跳转到详情页
debugPrint('跳转到照片详情页');
}
@override
Widget build(BuildContext context) {
final screenWidth = MediaQuery.of(context).size.width;
final cardWidth = screenWidth - 32; // 考虑左右16dp内边距
final cardHeight = cardWidth * 4 / 5; // 4:5的宽高比
return GestureDetector(
// 按压动画触发
onTapDown: (_) => _controller.forward(),
onTapUp: (_) {
_controller.reverse();
_handleCardTap();
},
onTapCancel: () => _controller.reverse(),
// 双击收藏
onDoubleTap: _toggleFavorite,
child: ScaleTransition(
scale: _scaleAnimation,
child: Container(
margin: const EdgeInsets.only(bottom: 14),
width: cardWidth,
height: cardHeight,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(24),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.35),
blurRadius: 24,
offset: const Offset(0, 8),
),
],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(24),
child: Stack(
children: [
// 图片占位符
_buildImagePlaceholder(),
// 底部渐变遮罩,提升文字可读性
_buildGradientOverlay(),
// 收藏按钮
_buildFavoriteButton(),
// 书签装饰
_buildBookmarkDecoration(),
// 标签文字
_buildTags(),
],
),
),
),
),
);
}
/// 构建图片占位符
Widget _buildImagePlaceholder() {
return Container(
color: Colors.grey[800],
child: const Center(
child: Icon(Icons.photo, size: 80, color: Colors.grey),
),
);
}
/// 构建底部渐变遮罩
Widget _buildGradientOverlay() {
return Positioned(
bottom: 0,
left: 0,
right: 0,
height: 80,
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.black.withOpacity(0), // 透明
Colors.black.withOpacity(0.6), // 60%不透明
],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
),
),
);
}
/// 构建收藏按钮
Widget _buildFavoriteButton() {
return Positioned(
top: 12,
right: 12,
child: GestureDetector(
onTap: _toggleFavorite,
child: Container(
width: 32,
height: 32,
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.35),
shape: BoxShape.circle,
),
child: Icon(
_isFavorite ? Icons.favorite : Icons.favorite_border,
color: _isFavorite ? const Color(0xFFFF5A5F) : Colors.white,
size: 20,
),
),
),
);
}
/// 构建书签装饰
Widget _buildBookmarkDecoration() {
return Positioned(
bottom: 12,
left: 12,
child: Container(
width: 18,
height: 18,
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(4),
border: Border.all(
color: Colors.white.withOpacity(0.4),
width: 1,
),
),
),
);
}
/// 构建标签文字
Widget _buildTags() {
return const Positioned(
bottom: 12,
right: 12,
child: Text(
'#家居 #盆栽 #庭院',
style: TextStyle(
fontSize: 12,
color: Colors.white,
fontWeight: FontWeight.w600,
shadows: [
Shadow(
color: Colors.black54,
offset: Offset(0, 1),
blurRadius: 2,
),
],
),
),
);
}
}
/// 照片详情页面
/// 提供全屏浏览和交互功能
class PhotoDetailPage extends StatefulWidget {
final List<String> photos;
final int initialIndex;
const PhotoDetailPage({
super.key,
required this.photos,
required this.initialIndex,
});
@override
State<PhotoDetailPage> createState() => _PhotoDetailPageState();
}
class _PhotoDetailPageState extends State<PhotoDetailPage> {
// 当前照片索引
late int _currentIndex;
// 页面控制器用于控制PageView
late PageController _pageController;
@override
void initState() {
super.initState();
_currentIndex = widget.initialIndex;
_pageController = PageController(initialPage: _currentIndex);
}
@override
void dispose() {
_pageController.dispose();
super.dispose();
}
/// 显示操作反馈
void _showActionFeedback(String message) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(message)),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
actions: [
_buildActionButton(Icons.favorite_border, '收藏功能待实现'),
_buildActionButton(Icons.share, '分享功能待实现'),
_buildActionButton(Icons.delete, '删除功能待实现'),
],
),
body: Stack(
children: [
// 照片浏览区域
_buildPhotoViewer(),
// 页码指示器
_buildPageIndicator(),
],
),
);
}
/// 构建操作按钮
Widget _buildActionButton(IconData icon, String feedback) {
return IconButton(
icon: Icon(icon),
onPressed: () => _showActionFeedback(feedback),
);
}
/// 构建照片查看器
Widget _buildPhotoViewer() {
return PageView.builder(
controller: _pageController,
itemCount: widget.photos.length,
onPageChanged: (index) {
setState(() {
_currentIndex = index;
});
},
itemBuilder: (context, index) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height * 0.7,
color: Colors.grey[800],
child: const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.photo, size: 100, color: Colors.white),
SizedBox(height: 20),
Text(
'照片占位符',
style: TextStyle(
color: Colors.white,
fontSize: 24,
),
),
],
),
),
),
],
),
);
},
);
}
/// 构建页码指示器
Widget _buildPageIndicator() {
return Positioned(
bottom: 20,
left: 0,
right: 0,
child: Center(
child: Text(
'${_currentIndex + 1} / ${widget.photos.length}',
style: const TextStyle(
color: Colors.white,
fontSize: 16,
),
),
),
);
}
}