Flutter媒体选择器插件vs_media_picker的使用

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

Flutter媒体选择器插件vs_media_picker的使用

简介

VS_Media_Picker 是基于 photo_widget 包开发的,其概念与 image_picker 类似,但提供了更吸引人的界面来从设备图库中选择图片或视频,支持Android和iOS平台。

功能

  • [✔] 选择图片
  • [✔] 选择视频
  • [✔] 多选图片/视频
  • [✔] 封面缩略图(预览图库中的第一张图片)
  • [❌] 从相机拍摄图片或视频(不支持)

示例演示

示例演示

安装

  1. pubspec.yaml 文件中添加 vs_media_picker: 0.0.2
  2. 导入 vs_media_picker 包:
    import 'package:vs_media_picker/vs_media_picker.dart';
    

配置

Android
  1. 更新 build.gradle 文件中的 Kotlin 版本为 1.6.0classpath 'com.android.tools.build:gradle:7.0.4'
  2. 设置 minSdkVersion25
  3. AndroidManifest.xml 文件中添加权限:
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    
iOS

info.plist 文件中添加以下配置:

<key>NSPhotoLibraryUsageDescription</key>
<string>访问相册权限说明</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>添加到相册权限说明</string>

使用方法

创建一个 VSMediaPicker 小部件:

Material(
  child: VSMediaPicker(
    pathList: (pathList) {
      // pathList 是一个包含选定媒体元数据的列表
      // 每个元素是一个 PickedAssetModel 对象
      // 可以根据需要处理这些数据
    },
    maxPickImages: 5, // 最多选择5张图片
    singlePick: false, // 是否单选
    appBarColor: Colors.blue, // 标题栏颜色
    albumBackGroundColor: Colors.grey[800], // 相册背景颜色
    albumDividerColor: Colors.grey[700], // 相册分隔线颜色
    albumTextColor: Colors.white, // 相册文字颜色
    appBarIconColor: Colors.white, // 标题栏图标颜色
    appBarTextColor: Colors.white, // 标题栏文字颜色
    crossAxisCount: 3, // 每行显示的图片数量
    gridViewBackgroundColor: Colors.grey[900], // 图片网格背景颜色
    childAspectRatio: 1, // 图片宽高比
    imageBackgroundColor: Colors.black, // 图片背景颜色
    selectedBackgroundColor: Colors.black, // 选中项背景颜色
    selectedCheckColor: Colors.white, // 选中项勾选颜色
    thumbnailBoxFix: BoxFit.cover, // 缩略图填充方式
    onlyImages: true, // 仅显示图片
    thumbnailQuality: 200, // 缩略图质量
  ),
);

完整示例Demo

以下是一个完整的示例代码,展示了如何使用 VS_Media_Picker 插件来选择图片并显示在应用中:

import 'dart:io';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:share_plus/share_plus.dart';
import 'package:vs_media_picker/vs_media_picker.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
  SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(statusBarColor: Colors.black));
  runApp(MultiProvider(
    providers: [ChangeNotifierProvider(create: (_) => PickerDataProvider())],
    child: const MyApp(),
  ));
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: const ViewMedia(),
    );
  }
}

class ViewMedia extends StatefulWidget {
  const ViewMedia({Key? key}) : super(key: key);

  [@override](/user/override)
  State<ViewMedia> createState() => _ViewMediaState();
}

class _ViewMediaState extends State<ViewMedia> {
  PickedAssetModel? data;

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: ListView(
        children: [
          TextButton(
            onPressed: () {
              Navigator.of(context)
                  .push(MaterialPageRoute<PickedAssetModel>(
                builder: (BuildContext context) => const Example(),
              ))
                  .then((value) {
                if (value != null) {
                  setState(() {
                    data = value;
                  });
                }
              });
            },
            child: const Text(
              "点击选择图片",
              style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
            ),
          ),
          const SizedBox(height: 30),
          data == null
              ? const SizedBox()
              : SizedBox(
                  height: 500,
                  child: Image.file(File(data!.path!)),
                )
        ],
      ),
    );
  }
}

class Example extends StatefulWidget {
  const Example({Key? key}) : super(key: key);

  [@override](/user/override)
  State<Example> createState() => _ExampleState();
}

class _ExampleState extends State<Example> {
  bool _singlePick = false;

  [@override](/user/override)
  Widget build(BuildContext context) {
    return SafeArea(
      child: Consumer<PickerDataProvider>(
        builder: (context, media, _) {
          return Scaffold(
            appBar: AppBar(
              actions: [
                IconButton(
                  onPressed: () {
                    if (media.pickedFile.isEmpty) {
                      ScaffoldMessenger.of(context).showSnackBar(
                        const SnackBar(content: Text("未选择媒体文件")),
                      );
                    } else {
                      Navigator.of(context).pop(media.pickedFile[0]);
                    }
                  },
                  icon: const Icon(Icons.done),
                )
              ],
            ),
            body: Column(
              children: [
                Container(
                  height: 300,
                  color: Colors.black,
                  child: media.pickedFile.isEmpty
                      ? Container(
                          alignment: Alignment.center,
                          child: Column(
                            mainAxisAlignment: MainAxisAlignment.center,
                            children: [
                              Transform.scale(
                                scale: 8,
                                child: const Icon(
                                  Icons.image_outlined,
                                  color: Colors.white,
                                  size: 10,
                                ),
                              ),
                              const SizedBox(height: 50),
                              const Text(
                                '未选择图片',
                                style: TextStyle(
                                    fontSize: 14,
                                    fontWeight: FontWeight.w600,
                                    color: Colors.white70),
                              )
                            ],
                          ),
                        )
                      : PageView(
                          children: media.pickedFile.map((data) {
                            if (data.type == 'image') {
                              return Center(
                                child: PhotoView.customChild(
                                  enablePanAlways: true,
                                  maxScale: 2.0,
                                  minScale: 1.0,
                                  child: Image.file(File(data.path!)),
                                ),
                              );
                            } else {
                              return Container();
                            }
                          }).toList(),
                        ),
                ),
                Expanded(
                  child: VSMediaPicker(
                    childAspectRatio: 1,
                    crossAxisCount: 3,
                    thumbnailQuality: 200,
                    onlyImages: true,
                    thumbnailBoxFix: BoxFit.cover,
                    singlePick: _singlePick,
                    gridViewBackgroundColor: Colors.grey[900]!,
                    imageBackgroundColor: Colors.black,
                    maxPickImages: 5,
                    appBarHeight: 60,
                    selectedBackgroundColor: Colors.black,
                    selectedCheckColor: Colors.black87,
                    selectedCheckBackgroundColor: Colors.white10,
                    pathList: (paths) {
                      setState(() {
                        media.pickedFile = paths;
                      });
                    },
                    appBarLeadingWidget: Align(
                      alignment: Alignment.bottomRight,
                      child: Padding(
                        padding: const EdgeInsets.only(right: 15, bottom: 12),
                        child: Row(
                          mainAxisSize: MainAxisSize.min,
                          children: [
                            SizedBox(
                              height: 30,
                              child: Container(
                                alignment: Alignment.centerRight,
                                child: Row(
                                  mainAxisSize: MainAxisSize.min,
                                  children: [
                                    GestureDetector(
                                      onTap: () {
                                        setState(() {
                                          _singlePick = !_singlePick;
                                        });
                                      },
                                      child: AnimatedContainer(
                                        duration: const Duration(milliseconds: 300),
                                        decoration: BoxDecoration(
                                          borderRadius: BorderRadius.circular(6),
                                          border: Border.all(color: Colors.blue, width: 1.5),
                                        ),
                                        child: Padding(
                                          padding: const EdgeInsets.all(8.0),
                                          child: Row(
                                            mainAxisSize: MainAxisSize.min,
                                            crossAxisAlignment: CrossAxisAlignment.center,
                                            mainAxisAlignment: MainAxisAlignment.center,
                                            children: [
                                              const Text(
                                                '多选',
                                                style: TextStyle(
                                                    color: Colors.white,
                                                    fontWeight: FontWeight.w500,
                                                    fontSize: 10),
                                              ),
                                              const SizedBox(width: 7),
                                              Transform.scale(
                                                scale: 1.5,
                                                child: Icon(
                                                  _singlePick
                                                      ? Icons.check_box_outline_blank
                                                      : Icons.check_box_outlined,
                                                  color: Colors.blue,
                                                  size: 10,
                                                ),
                                              )
                                            ],
                                          ),
                                        ),
                                      ),
                                    ),
                                  ],
                                ),
                              ),
                            ),
                            const Spacer(),
                            GestureDetector(
                              onTap: () async {
                                List<XFile> mediaPath = [];
                                for (var p in media.pickedFile) {
                                  mediaPath.add(XFile(p.path!));
                                }
                                if (mediaPath.isNotEmpty) {
                                  await Share.shareXFiles(mediaPath);
                                }
                                mediaPath.clear();
                              },
                              child: Container(
                                height: 30,
                                width: 30,
                                decoration: BoxDecoration(
                                  color: Colors.transparent,
                                  borderRadius: BorderRadius.circular(10),
                                  border: Border.all(color: Colors.blue, width: 1.5),
                                ),
                                child: Transform.scale(
                                  scale: 2,
                                  child: const Icon(
                                    Icons.share_outlined,
                                    color: Colors.blue,
                                    size: 10,
                                  ),
                                ),
                              ),
                            )
                          ],
                        ),
                      ),
                    ),
                  ),
                )
              ],
            ),
          );
        },
      ),
    );
  }
}

class PickerDataProvider with ChangeNotifier {
  List<PickedAssetModel> pickedFile = [];

  void updatePickedFile(List<PickedAssetModel> newFiles) {
    pickedFile = newFiles;
    notifyListeners();
  }
}

更多关于Flutter媒体选择器插件vs_media_picker的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter媒体选择器插件vs_media_picker的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何使用Flutter中的vs_media_picker插件来选择媒体文件(图片和视频)的代码示例。vs_media_picker是一个功能强大的媒体选择器插件,允许用户从设备图库中选择媒体文件。

首先,确保你已经在pubspec.yaml文件中添加了vs_media_picker依赖:

dependencies:
  flutter:
    sdk: flutter
  vs_media_picker: ^latest_version  # 请替换为最新的版本号

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

接下来,在你的Flutter项目中,你可以按照以下步骤使用vs_media_picker

  1. 导入必要的包
import 'package:flutter/material.dart';
import 'package:vs_media_picker/vs_media_picker.dart';
  1. 配置和使用VsMediaPicker
void main() {
  runApp(MyApp());
}

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

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  List<VsMedia?>? selectedMedia;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('vs_media_picker Demo'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            ElevatedButton(
              onPressed: () async {
                final result = await VsMediaPicker.pickMedia(
                  context: context,
                  maxVideos: 1,
                  maxImages: 5,
                  enableCamera: true,
                  videoQuality: CameraVideoQuality.high,
                  selectedAssets: selectedMedia?.cast<VsMedia>(),
                );

                if (result != null) {
                  setState(() {
                    selectedMedia = result;
                  });
                }
              },
              child: Text('Select Media'),
            ),
            SizedBox(height: 20),
            if (selectedMedia != null)
              GridView.builder(
                gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                  crossAxisCount: 3,
                  crossAxisSpacing: 4.0,
                  mainAxisSpacing: 4.0,
                ),
                itemCount: selectedMedia!.length,
                itemBuilder: (context, index) {
                  final media = selectedMedia![index];
                  if (media == null) return Container();

                  return Image.file(
                    File(media.path),
                    fit: BoxFit.cover,
                  );
                },
              ),
          ],
        ),
      ),
    );
  }
}

在这个示例中,我们创建了一个简单的Flutter应用,其中包含一个按钮用于启动媒体选择器。用户可以选择图片和视频,选中的媒体文件将显示在一个GridView中。

代码解释:

  • 依赖导入:我们导入了flutter/material.dartvs_media_picker/vs_media_picker.dart
  • UI结构:我们使用了一个Scaffold,其中包含一个AppBar和一个Column。Column中有一个按钮用于启动媒体选择器,以及一个GridView用于显示选中的媒体文件。
  • 媒体选择器:我们使用VsMediaPicker.pickMedia方法来启动媒体选择器。这个方法允许我们配置一些参数,比如最大视频和图片数量、是否启用相机、视频质量等。
  • 显示选中的媒体:选中的媒体文件保存在selectedMedia列表中,我们在GridView中遍历这个列表并显示每张图片。

请确保你的应用有读取存储权限(特别是在Android上),这通常需要在AndroidManifest.xml中添加相关权限声明,并在运行时请求权限(如果需要的话)。

希望这个示例能帮你更好地理解如何使用vs_media_picker插件!

回到顶部