You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在应用内创建画廊并展示指定文件夹中的照片与视频

实现本地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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 21:39:18