如何在应用内创建画廊并展示指定文件夹中的照片与视频
实现本地Documents文件夹多媒体画廊页面
1. 获取并筛选多媒体文件
首先读取getApplicationDocumentsDirectory()下的所有文件,筛选出图片和视频格式的文件。使用path_provider包获取目录路径,再通过Directory类遍历文件:
import 'dart:io'; import 'package:path_provider/path_provider.dart'; Future<List<File>> getMediaFiles() async { final documentsDir = await getApplicationDocumentsDirectory(); // 遍历目录下所有文件 final allFiles = documentsDir.listSync().where((entity) => entity is File).cast<File>().toList(); // 定义支持的多媒体格式 const supportedImageExts = {'jpg', 'jpeg', 'png', 'gif', 'bmp'}; const supportedVideoExts = {'mp4', 'mov', 'avi', 'mkv', 'flv'}; return allFiles.where((file) { final fileExt = file.path.split('.').last.toLowerCase(); return supportedImageExts.contains(fileExt) || supportedVideoExts.contains(fileExt); }).toList(); }
2. 构建画廊UI
用GridView展示缩略图,图片直接用Image.file加载,视频则通过video_thumbnail生成缩略图并叠加播放图标:
import 'package:flutter/material.dart'; import 'package:video_thumbnail/video_thumbnail.dart'; class MediaGallery extends StatefulWidget { const MediaGallery({super.key}); @override State<MediaGallery> createState() => _MediaGalleryState(); } class _MediaGalleryState extends State<MediaGallery> { late Future<List<File>> _mediaFilesFuture; @override void initState() { super.initState(); _mediaFilesFuture = getMediaFiles(); } bool _isVideo(File file) { final ext = file.path.split('.').last.toLowerCase(); return {'mp4', 'mov', 'avi', 'mkv', 'flv'}.contains(ext); } Future<String?> _getVideoThumbnail(File videoFile) async { return VideoThumbnail.thumbnailFile( video: videoFile.path, imageFormat: ImageFormat.JPEG, maxHeight: 150, quality: 75, ); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('本地多媒体画廊')), body: FutureBuilder<List<File>>( future: _mediaFilesFuture, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } if (snapshot.hasError) { return Center(child: Text('加载失败: ${snapshot.error}')); } final mediaFiles = snapshot.data ?? []; if (mediaFiles.isEmpty) { return const Center(child: Text('未找到多媒体文件')); } return GridView.builder( padding: const EdgeInsets.all(8), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 3, crossAxisSpacing: 8, mainAxisSpacing: 8, ), itemCount: mediaFiles.length, itemBuilder: (context, index) { final file = mediaFiles[index]; return GestureDetector( onTap: () => _showMediaDetail(context, file), child: Stack( fit: StackFit.expand, children: [ // 加载图片或视频缩略图 if (_isVideo(file)) FutureBuilder<String?>( future: _getVideoThumbnail(file), builder: (context, thumbSnap) { if (thumbSnap.hasData && thumbSnap.data != null) { return Image.file( File(thumbSnap.data!), fit: BoxFit.cover, ); } return const Center(child: Icon(Icons.video_library, size: 40)); }, ) else Image.file(file, fit: BoxFit.cover), // 视频添加播放标识 if (_isVideo(file)) const Center( child: Icon(Icons.play_circle, color: Colors.white, size: 32), ), ], ), ); }, ); }, ), ); } // 点击查看媒体详情 void _showMediaDetail(BuildContext context, File file) { showDialog( context: context, builder: (context) => Dialog( child: _isVideo(file) ? AspectRatio( aspectRatio: 16/9, child: VideoPlayerWidget(filePath: file.path), ) : Image.file(file), ), ); } }
3. 实现视频播放功能
使用video_player包实现视频播放组件,支持播放/暂停控制:
import 'package:video_player/video_player.dart'; class VideoPlayerWidget extends StatefulWidget { final String filePath; const VideoPlayerWidget({super.key, required this.filePath}); @override State<VideoPlayerWidget> createState() => _VideoPlayerWidgetState(); } class _VideoPlayerWidgetState extends State<VideoPlayerWidget> { late VideoPlayerController _controller; @override void initState() { super.initState(); _controller = VideoPlayerController.file(File(widget.filePath)) ..initialize().then((_) { setState(() {}); _controller.play(); }); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return _controller.value.isInitialized ? Stack( children: [ VideoPlayer(_controller), Center( child: IconButton( icon: Icon( _controller.value.isPlaying ? Icons.pause : Icons.play_arrow, color: Colors.white, size: 48, ), onPressed: () { setState(() { _controller.value.isPlaying ? _controller.pause() : _controller.play(); }); }, ), ), ], ) : const Center(child: CircularProgressIndicator()); } }
必要依赖配置
在pubspec.yaml中添加以下依赖:
dependencies: flutter: sdk: flutter path_provider: ^2.1.1 video_thumbnail: ^0.5.3 video_player: ^2.7.0
注意事项
- Android端:访问自身App的
Documents目录无需额外权限,若需访问外部存储需在AndroidManifest.xml添加对应权限; - iOS端:仅访问自身
Documents目录无需配置权限,若涉及系统相册访问需在Info.plist添加NSPhotoLibraryUsageDescription; - 大量文件时建议添加分页加载或缩略图缓存,优化页面加载性能。
内容的提问来源于stack exchange,提问作者Burak
相关产品推荐
相关产品推荐

