Flutter图片浏览插件photo_gallery的使用

发布于 1周前 作者 ionicwang 来自 Flutter

Flutter图片浏览插件photo_gallery的使用

Photo Gallery

pub package

photo_gallery是一个Flutter插件,可以从移动设备的本地相册中检索图片和视频。

安装

iOS

<project root>/ios/Runner/Info.plist文件中添加以下键:

<key>NSPhotoLibraryUsageDescription</key>
<string>Example usage description</string>

这是在可视化编辑器中称为“隐私 - 相册使用说明”。

Android

<project root>/android/app/src/main/AndroidManifest.xml中添加以下权限:

<manifest ...>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
    <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
</manifest>

使用

以下是photo_gallery的一些基本用法:

  • 列出相册中的专辑

    final List<Album> imageAlbums = await PhotoGallery.listAlbums();
    final List<Album> videoAlbums = await PhotoGallery.listAlbums(
        mediumType: MediumType.video,
        newest: false,
        hideIfEmpty: false,
    );
    
  • 列出专辑中的媒体文件

    final MediaPage imagePage = await imageAlbum.listMedia();
    final MediaPage videoPage = await imageAlbum.listMedia(
        skip: 5,
        take: 10,
    );
    final List<Medium> allMedia = [
        ...imagePage.items,
        ...videoPage.items,
    ];
    
  • 加载更多媒体文件

    if (!imagePage.isLast) {
        final nextImagePage = await imagePage.nextPage();
        // ...
    }
    
  • 获取单个媒体文件

    final Medium medium = await PhotoGallery.getMedium(
      mediumId: "10",
      mediumType: MediumType.image
    );
    
  • 获取文件

    final File file = await medium.getFile();
    
  • 获取缩略图数据

    final List<int> data = await medium.getThumbnail(
        width: 128,
        height: 128,
        highQuality: true,
    );
    
  • 显示媒体缩略图

    FadeInImage(
        fit: BoxFit.cover,
        placeholder: MemoryImage(kTransparentImage),
        image: ThumbnailProvider(
            mediumId: mediumId,
            mediumType: MediumType.image,
            width: 128,
            height: 128,
            hightQuality: true,
        ),
    )
    
  • 显示完整尺寸的图片

    FadeInImage(
        fit: BoxFit.cover,
        placeholder: MemoryImage(kTransparentImage),
        image: PhotoProvider(
            mediumId: mediumId,
        ),
    )
    

示例代码

这是一个完整的示例应用,展示了如何使用photo_gallery插件来浏览相册、查看专辑内容以及展示媒体文件(包括图片和视频)。

import 'dart:async';
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:photo_gallery/photo_gallery.dart';
import 'package:transparent_image/transparent_image.dart';
import 'package:video_player/video_player.dart';

void main() {
  runApp(MyApp());
}

/// The main widget of example app
class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  List<Album>? _albums;
  bool _loading = false;

  @override
  void initState() {
    super.initState();
    _loading = true;
    initAsync();
  }

  Future<void> initAsync() async {
    if (await _promptPermissionSetting()) {
      List<Album> albums = await PhotoGallery.listAlbums();
      setState(() {
        _albums = albums;
        _loading = false;
      });
    }
    setState(() {
      _loading = false;
    });
  }

  Future<bool> _promptPermissionSetting() async {
    if (Platform.isIOS) {
      if (await Permission.photos.request().isGranted || await Permission.storage.request().isGranted) {
        return true;
      }
    }
    if (Platform.isAndroid) {
      if (await Permission.storage.request().isGranted ||
          await Permission.photos.request().isGranted && await Permission.videos.request().isGranted) {
        return true;
      }
    }
    return false;
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Photo gallery example'),
        ),
        body: _loading
            ? Center(
                child: CircularProgressIndicator(),
              )
            : LayoutBuilder(
                builder: (context, constraints) {
                  double gridWidth = (constraints.maxWidth - 20) / 3;
                  double gridHeight = gridWidth + 33;
                  double ratio = gridWidth / gridHeight;
                  return Container(
                    padding: EdgeInsets.all(5),
                    child: GridView.count(
                      childAspectRatio: ratio,
                      crossAxisCount: 3,
                      mainAxisSpacing: 5.0,
                      crossAxisSpacing: 5.0,
                      children: <Widget>[
                        ...?_albums?.map(
                          (album) => GestureDetector(
                            onTap: () => Navigator.of(context).push(
                              MaterialPageRoute(builder: (context) => AlbumPage(album)),
                            ),
                            child: Column(
                              children: <Widget>[
                                ClipRRect(
                                  borderRadius: BorderRadius.circular(5.0),
                                  child: Container(
                                    color: Colors.grey[300],
                                    height: gridWidth,
                                    width: gridWidth,
                                    child: FadeInImage(
                                      fit: BoxFit.cover,
                                      placeholder: MemoryImage(kTransparentImage),
                                      image: AlbumThumbnailProvider(
                                        album: album,
                                        highQuality: true,
                                      ),
                                    ),
                                  ),
                                ),
                                Container(
                                  alignment: Alignment.topLeft,
                                  padding: EdgeInsets.only(left: 2.0),
                                  child: Text(
                                    album.name ?? "Unnamed Album",
                                    maxLines: 1,
                                    textAlign: TextAlign.start,
                                    style: TextStyle(
                                      height: 1.2,
                                      fontSize: 16,
                                    ),
                                  ),
                                ),
                                Container(
                                  alignment: Alignment.topLeft,
                                  padding: EdgeInsets.only(left: 2.0),
                                  child: Text(
                                    album.count.toString(),
                                    textAlign: TextAlign.start,
                                    style: TextStyle(
                                      height: 1.2,
                                      fontSize: 12,
                                    ),
                                  ),
                                ),
                              ],
                            ),
                          ),
                        ),
                      ],
                    ),
                  );
                },
              ),
      ),
    );
  }
}

/// The album page widget
class AlbumPage extends StatefulWidget {
  /// Album object to show in the page
  final Album album;

  /// The constructor of AlbumPage
  AlbumPage(Album album) : album = album;

  @override
  State<StatefulWidget> createState() => _AlbumPageState();
}

class _AlbumPageState extends State<AlbumPage> {
  List<Medium>? _media;

  @override
  void initState() {
    super.initState();
    initAsync();
  }

  void initAsync() async {
    MediaPage mediaPage = await widget.album.listMedia();
    setState(() {
      _media = mediaPage.items;
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          leading: IconButton(
            icon: Icon(Icons.arrow_back_ios),
            onPressed: () => Navigator.of(context).pop(),
          ),
          title: Text(widget.album.name ?? "Unnamed Album"),
        ),
        body: GridView.count(
          crossAxisCount: 3,
          mainAxisSpacing: 1.0,
          crossAxisSpacing: 1.0,
          children: <Widget>[
            ...?_media?.map(
              (medium) => GestureDetector(
                onTap: () => Navigator.of(context).push(
                  MaterialPageRoute(builder: (context) => ViewerPage(medium)),
                ),
                child: Container(
                  color: Colors.grey[300],
                  child: FadeInImage(
                    fit: BoxFit.cover,
                    placeholder: MemoryImage(kTransparentImage),
                    image: ThumbnailProvider(
                      mediumId: medium.id,
                      mediumType: medium.mediumType,
                      highQuality: true,
                    ),
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

/// The viewer page widget
class ViewerPage extends StatelessWidget {
  /// The medium object to show in the page
  final Medium medium;

  /// The constructor of ViewerPage
  ViewerPage(Medium medium) : medium = medium;

  @override
  Widget build(BuildContext context) {
    DateTime? date = medium.creationDate ?? medium.modifiedDate;
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          leading: IconButton(
            onPressed: () => Navigator.of(context).pop(),
            icon: Icon(Icons.arrow_back_ios),
          ),
          title: date != null ? Text(date.toLocal().toString()) : null,
        ),
        body: Container(
          alignment: Alignment.center,
          child: medium.mediumType == MediumType.image
              ? GestureDetector(
                  onTap: () async {
                    PhotoGallery.deleteMedium(mediumId: medium.id);
                  },
                  child: FadeInImage(
                    fit: BoxFit.cover,
                    placeholder: MemoryImage(kTransparentImage),
                    image: PhotoProvider(mediumId: medium.id),
                  ),
                )
              : VideoProvider(
                  mediumId: medium.id,
                ),
        ),
      ),
    );
  }
}

/// The video provider widget
class VideoProvider extends StatefulWidget {
  /// The identifier of medium
  final String mediumId;

  /// The constructor of VideoProvider
  const VideoProvider({
    required this.mediumId,
  });

  @override
  _VideoProviderState createState() => _VideoProviderState();
}

class _VideoProviderState extends State<VideoProvider> {
  VideoPlayerController? _controller;
  File? _file;

  @override
  void initState() {
    WidgetsBinding.instance.addPostFrameCallback((_) {
      initAsync();
    });
    super.initState();
  }

  Future<void> initAsync() async {
    try {
      _file = await PhotoGallery.getFile(mediumId: widget.mediumId);
      _controller = VideoPlayerController.file(_file!);
      _controller?.initialize().then((_) {
        // Ensure the first frame is shown after the video is initialized, even before the play button has been pressed.
        setState(() {});
      });
    } catch (e) {
      print("Failed : $e");
    }
  }

  @override
  Widget build(BuildContext context) {
    return _controller == null || !_controller!.value.isInitialized
        ? Container()
        : Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              AspectRatio(
                aspectRatio: _controller!.value.aspectRatio,
                child: VideoPlayer(_controller!),
              ),
              TextButton(
                onPressed: () {
                  setState(() {
                    _controller!.value.isPlaying ? _controller!.pause() : _controller!.play();
                  });
                },
                child: Icon(
                  _controller!.value.isPlaying ? Icons.pause : Icons.play_arrow,
                ),
              ),
            ],
          );
  }
}

这个示例应用程序首先会请求访问相册的权限,然后列出所有相册,并允许用户点击进入每个相册查看其中的媒体文件。对于每张图片或视频,用户可以进一步点击查看大图或播放视频。


更多关于Flutter图片浏览插件photo_gallery的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter图片浏览插件photo_gallery的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何在Flutter项目中使用photo_gallery插件来实现图片浏览功能的示例代码。photo_gallery插件允许你展示一个图片画廊,并支持图片的滑动浏览。

首先,你需要在你的pubspec.yaml文件中添加photo_gallery依赖:

dependencies:
  flutter:
    sdk: flutter
  photo_gallery: ^x.y.z  # 请将x.y.z替换为最新版本号

然后运行flutter pub get来安装依赖。

接下来,你可以在你的Flutter项目中创建一个简单的画廊应用。以下是一个完整的示例代码:

import 'package:flutter/material.dart';
import 'package:photo_gallery/photo_gallery.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Photo Gallery Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: PhotoGalleryScreen(),
    );
  }
}

class PhotoGalleryScreen extends StatelessWidget {
  final List<String> imageUrls = [
    'https://example.com/image1.jpg',
    'https://example.com/image2.jpg',
    'https://example.com/image3.jpg',
    // 添加更多图片URL
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Photo Gallery'),
      ),
      body: Center(
        child: PhotoGallery(
          images: imageUrls.map((url) => NetworkImage(url)).toList(),
          pageController: PageController(),
          onPageChanged: (index) {
            print('Current image index: $index');
          },
        ),
      ),
    );
  }
}

class NetworkImage extends StatelessWidget {
  final String url;

  NetworkImage(this.url);

  @override
  Widget build(BuildContext context) {
    return Image.network(
      url,
      fit: BoxFit.cover,
      loadingBuilder: (context, child, loadingProgress) {
        if (loadingProgress == null) {
          return child;
        }
        return Center(
          child: CircularProgressIndicator(
            value: loadingProgress.expectedTotalBytes != null
                ? loadingProgress.cumulativeBytesLoaded.toDouble() /
                    loadingProgress.expectedTotalBytes!.toDouble()
                : null,
          ),
        );
      },
      errorWidget: (context, error, stackTrace) {
        return Icon(Icons.error);
      },
    );
  }
}

在这个示例中:

  1. MyApp 是应用程序的根widget,它定义了一个基本的Material应用,并设置了主题和主页。
  2. PhotoGalleryScreen 是主页widget,它包含了图片画廊。
  3. imageUrls 是一个包含图片URL的列表。
  4. PhotoGallery widget用于显示图片画廊,并接受一个图片列表、一个PageController以及一个页面改变时的回调函数。
  5. NetworkImage 是一个自定义widget,用于从网络加载图片,并处理加载进度和错误情况。

注意:

  • photo_gallery插件的具体实现可能有所不同,请确保参考插件的官方文档和示例代码进行适配。
  • 示例中的PhotoGallery widget是假设的,因为photo_gallery插件的具体API可能与此不同。在实际使用中,请查阅插件的最新版本和文档。
  • 如果photo_gallery插件没有直接提供PhotoGallery widget,你可能需要使用其他Flutter组件(如PageView)来实现图片画廊功能。
回到顶部