拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Flutter---相册

效果图代码实例local_album_page UI主页面import dart:math; import package:flutter/material.dart; import package:my_flutter/photo_detail_page.dart; import ../local_photos.dart; ///本地相册页面 class LocalAlbumPage extends StatefulWidget { const LocalAlbumPage({super.key}); override StateStatefulWidget createState() _LocalAlbumPageState(); } class _LocalAlbumPageState extends StateLocalAlbumPage { late ListLocalPhotos photoList;// 照片数据列表 bool isDeleteState false; //全局是否处于删除状态 // 文本资源 String localAlbum 本地相册; override void initState() { super.initState(); // 生成模拟照片数据 photoList generateMockPhotos(); } // 生成模拟照片数据 ListLocalPhotos generateMockPhotos() { final ListLocalPhotos photos []; final Random random Random(); // 模拟照片路径使用网络图片或本地占位图 final ListString mockImageUrls [ https://picsum.photos/seed/1/400/400, https://picsum.photos/seed/2/400/400, https://picsum.photos/seed/3/400/400, https://picsum.photos/seed/4/400/400, https://picsum.photos/seed/5/400/400, https://picsum.photos/seed/6/400/400, https://picsum.photos/seed/7/400/400, https://picsum.photos/seed/8/400/400, https://picsum.photos/seed/9/400/400, https://picsum.photos/seed/10/400/400, https://picsum.photos/seed/11/400/400, https://picsum.photos/seed/12/400/400, https://picsum.photos/seed/13/400/400, https://picsum.photos/seed/14/400/400, https://picsum.photos/seed/15/400/400, ]; // 生成过去30天的随机日期 final DateTime now DateTime.now(); for (int i 0; i 30; i) { // 随机选择过去30天内的某一天 final int daysAgo random.nextInt(30); final DateTime photoDate now.subtract(Duration(days: daysAgo)); // 格式化日期为 2024-01-15 final String dateStr ${photoDate.year}-${photoDate.month.toString().padLeft(2, 0)}-${photoDate.day.toString().padLeft(2, 0)}; // 随机选择图片可以重复 final String imageUrl mockImageUrls[random.nextInt(mockImageUrls.length)]; photos.add(LocalPhotos( date: dateStr, photoPath: imageUrl, isSelected:false, )); } // 按日期排序最新在前面 photos.sort((a, b) b.date.compareTo(a.date)); return photos; } // 删除选中的照片 void deleteSelectedPhotos() { setState(() { photoList.removeWhere((photo) photo.isSelected); isDeleteState false; }); } //////////////////////////////////////////////////////////////////////////////////////// override Widget build(BuildContext context) { return Scaffold( backgroundColor: Color(0xFFF5FCFF), appBar: AppBar( backgroundColor: Color(0xFFF5FCFF), leading: IconButton( onPressed: () { Navigator.pop(context); }, icon: Icon(Icons.arrow_back_ios,color: Colors.black,), ), title: Text( localAlbum, style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold), ), centerTitle: true, actions: [ isDeleteState? Text(全选) :IconButton( onPressed: () { //进入全选状态 setState(() { isDeleteState true; }); }, icon: Icon(Icons.select_all), ), ], ), body: Stack( children: [ Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox(height: 20), // 照片网格 Expanded( child: _buildPhotoGrid(), ), ], ), //删除悬浮按钮 isDeleteState? Positioned( bottom: 30, left: 0, right: 0, child: Center( child:Container( height: 52, width: 127, padding: EdgeInsets.symmetric(vertical: 5), decoration: BoxDecoration( color: Colors.black.withOpacity(0.3), borderRadius: BorderRadius.circular(26), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, crossAxisAlignment: CrossAxisAlignment.center, children: [ //分享 GestureDetector( onTap: (){ }, child: Column( children: [ Icon(Icons.share,color: Colors.white,), SizedBox(height: 4,), Text(分享,style: TextStyle(color: Colors.white,fontSize: 8),), ], ), ), //删除 GestureDetector( onTap: (){ deleteSelectedPhotos(); }, child: Column( children: [ Icon(Icons.delete,color: Colors.white,), SizedBox(height: 4,), Text(删除,style: TextStyle(color: Colors.white,fontSize: 8),), ], ), ), ], ), ), ) ):SizedBox.shrink() ], ) ); } //构建照片网格 Widget _buildPhotoGrid() { // 按日期分组 final MapString, ListLocalPhotos groupedPhotos {}; for (var photo in photoList) { if (!groupedPhotos.containsKey(photo.date)) { groupedPhotos[photo.date] []; } groupedPhotos[photo.date]!.add(photo); } // 获取排序后的日期列表最新在前面 final ListString sortedDates groupedPhotos.keys.toList()..sort((a, b) b.compareTo(a)); return ListView.builder( padding: EdgeInsets.symmetric(horizontal: 16), itemCount: sortedDates.length, itemBuilder: (context, index) { final String date sortedDates[index]; final ListLocalPhotos photosOfDay groupedPhotos[date]!; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 日期标题 Padding( padding: EdgeInsets.symmetric(vertical: 12), child: Text( _formatDateDisplay(date), style: TextStyle( fontSize: 20, fontWeight: FontWeight.w600, color: Colors.black, ), ), ), // 该日期的照片网格 GridView.builder( shrinkWrap: true, // 让 GridView 在 ListView 中自适应 physics: NeverScrollableScrollPhysics(), // 禁止内部滚动 gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 3, // 每行3列 crossAxisSpacing: 4, mainAxisSpacing: 4, childAspectRatio: 1, // 正方形 ), itemCount: photosOfDay.length, itemBuilder: (context, photoIndex) { return _buildPhotoItem(photosOfDay[photoIndex]); }, ), SizedBox(height: 8), ], ); }, ); } //构建单张照片 Widget _buildPhotoItem(LocalPhotos photo) { return GestureDetector( onTap: (){ //单击进入详细页面 if (!isDeleteState) { Navigator.push( context, MaterialPageRoute( builder: (context) PhotoDetailPage(photo: photo), ), ); } }, child: ClipRRect( borderRadius: BorderRadius.circular(26), child: Stack( fit: StackFit.expand, children: [ // 底层图片 Image.network( photo.photoPath, fit: BoxFit.cover, loadingBuilder: (context, child, loadingProgress) { if (loadingProgress null) { return child; } return Container( color: Colors.grey[200], child: Center( child: CircularProgressIndicator( value: loadingProgress.expectedTotalBytes ! null ? loadingProgress.cumulativeBytesLoaded / loadingProgress.expectedTotalBytes! : null, strokeWidth: 2, ), ), ); }, errorBuilder: (context, error, stackTrace) { return Container( color: Colors.grey[300], child: Icon(Icons.broken_image, color: Colors.grey[600]), ); }, ), //圆环图标始终显示 isDeleteState?Positioned( right: 8, top: 8, child:GestureDetector( onTap: (){ setState(() { photo.isSelected !photo.isSelected; }); }, child: Container( height: 20, width: 20, child: photo.isSelected? Icon(Icons.check_circle,color: Colors.white,):Icon(Icons.circle_outlined,color: Colors.white) ), ) ):SizedBox.shrink() ], ), ), ); } // 格式化日期显示 String _formatDateDisplay(String dateStr) { final parts dateStr.split(-); if (parts.length ! 3) return dateStr; final year parts[0]; final month parts[1]; final day parts[2]; // 判断是否是今天、昨天等 final DateTime now DateTime.now(); final DateTime today DateTime(now.year, now.month, now.day); final DateTime photoDate DateTime( int.parse(year), int.parse(month), int.parse(day) ); final int difference today.difference(photoDate).inDays; if (difference 0) { return 今天; } else if (difference 1) { return 昨天; } else if (difference 7) { return ${difference}天前; } else { // 返回 2024年1月15日 格式 return ${year}年${int.parse(month)}月${int.parse(day)}日; } } }photo_detail_page 照片详情页面import package:flutter/material.dart; import local_photos.dart; /// 照片展示页面 - 显示照片原始尺寸 class PhotoDetailPage extends StatefulWidget { const PhotoDetailPage({ super.key, required this.photo, }); final LocalPhotos photo; override StateStatefulWidget createState() _PhotoDetailPageState(); } class _PhotoDetailPageState extends StatePhotoDetailPage { override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.white, appBar: AppBar( backgroundColor: Colors.white, elevation: 0, leading: IconButton( onPressed: () { Navigator.pop(context); }, icon: Icon(Icons.arrow_back_ios, color: Colors.black), ), actions: [ Text( _formatDate(widget.photo.date), style: TextStyle(color: Colors.black, fontSize: 20), ), ], ), body: Stack( children: [ //背景图 Center( child: Image.network( widget.photo.photoPath, fit: BoxFit.fitWidth, loadingBuilder: (context, child, loadingProgress) { if (loadingProgress null) return child; return Container( width: 300, height: 300, color: Colors.grey[900], child: Center( child: CircularProgressIndicator( value: loadingProgress.expectedTotalBytes ! null ? loadingProgress.cumulativeBytesLoaded / loadingProgress.expectedTotalBytes! : null, color: Colors.white, ), ), ); }, errorBuilder: (context, error, stackTrace) { return Container( width: 300, height: 300, color: Colors.grey[900], child: Center( child: Icon(Icons.broken_image, color: Colors.grey[600], size: 64), ), ); }, ), ), //底部按钮 Positioned( bottom: 20, right: 0, left: 0, child: Center( child: Container( height: 52, width: 270, padding: EdgeInsets.symmetric(vertical: 5), decoration: BoxDecoration( color: Color(0xFF000000).withOpacity(0.3), borderRadius: BorderRadius.circular(26), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ //智能分析 GestureDetector( child: Column( children: [ Icon(Icons.analytics,color: Colors.white,), SizedBox(height: 4,), Text(智能分析,style: TextStyle(color: Colors.white,fontSize: 8),) ], ) ), //下载 GestureDetector( child: Column( children: [ Icon(Icons.download,color: Colors.white,), SizedBox(height: 4,), Text(下载,style: TextStyle(color: Colors.white,fontSize: 8),) ], ) ), //分析 GestureDetector( child: Column( children: [ Icon(Icons.share,color: Colors.white,), SizedBox(height: 4,), Text(分享,style: TextStyle(color: Colors.white,fontSize: 8),) ], ) ), //删除 GestureDetector( child: Column( children: [ Icon(Icons.delete,color: Colors.white,), SizedBox(height: 4,), Text(删除,style: TextStyle(color: Colors.white,fontSize: 8),) ], ) ), ], ), ), ), ) ], ) ); } // 格式化日期为 2024年1月15日 格式 String _formatDate(String dateStr) { final parts dateStr.split(-); if (parts.length ! 3) return dateStr; final year parts[0]; final month int.parse(parts[1]); // 转成数字去掉前导0 final day int.parse(parts[2]); return ${year}年${month}月${day}日; } }照片类///本地照片类 class LocalPhotos { String date;//日期 String photoPath; //照片路径 bool isSelected; //是否被选中 LocalPhotos({ required this.date, required this.photoPath, this.isSelected false, //默认未选中 }); //复制方法 LocalPhotos copyWith({ String? date, String? photoPath, bool? isSelected, }){ return LocalPhotos( date: date ?? this.date, photoPath: photoPath ?? this.photoPath, isSelected: isSelected ?? this.isSelected, ); } }
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门