458 lines
11 KiB
Dart
458 lines
11 KiB
Dart
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,
|
||
),
|
||
),
|
||
),
|
||
);
|
||
}
|
||
} |