Commit b81d26cc08ee22a30587370e6c624351d52037ba

Authored by weixin_45529955
1 parent 70fd5331

多图选择适配鸿蒙,选择数量控制

Showing 83 changed files with 231 additions and 4241 deletions

Too many changes to show.

To preserve performance only 83 of 83+ files are displayed.

import 'dart:io';
class MediaDN {
File? file;
}
\ No newline at end of file
... ...
... ... @@ -5,7 +5,9 @@ import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_native_image/flutter_native_image.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:one_key/models/mediaDN.dart';
import 'package:one_key/models/upload_img_model.dart';
import 'package:one_key/pages/online_check/image_select_ohos_util.dart';
import 'package:one_key/pages/online_check/photo_view_wiget.dart';
import 'package:one_key/pages/online_check/progress.dart';
... ... @@ -196,19 +198,43 @@ class _VideoPickerWidgetState extends DisposableState<VideoPickerWidget> {
// pickType: type,
// photoPathList: pathList,
// );
List<AssetEntity>? imgList = await AssetPicker.pickAssets(context,
List<MediaDN>? imgList = [];
if (Platform.isAndroid || Platform.isIOS) {
List<AssetEntity>? imagListTemp = await AssetPicker.pickAssets(context,
pickerConfig: AssetPickerConfig(
maxAssets: widget.maxImageNumber -
widget.model!.imageListDenvironment.length,
// selectedAssets: assets,
requestType: type,
themeColor: MyColours.color_199095));
if (imgList == null || imgList.isEmpty) {
imagListTemp?.forEach((elenemt) async {
MediaDN mediaDN = MediaDN();
mediaDN.file = await elenemt.file;
imgList.add(mediaDN);
});
}else if(Platform.isOhos){
List<XFile> imagListTemp = await ImageSelectOhosUtil.onImageButtonPressed(
ImageSource.gallery,
context: context,
isMultiImage: true,
isVideo: false,
limit: widget.maxImageNumber -
widget.model!.imageListDenvironment.length,);
imagListTemp.forEach((elenemt) async {
MediaDN mediaDN = MediaDN();
mediaDN.file = File(elenemt.path);
imgList.add(mediaDN);
});
}
if (imgList.length == 0 || imgList.isEmpty) {
print("No pick item.");
return;
} else {
}else {
for (int i = 0; i < imgList.length; i++) {
var file = (await imgList[i].file)!;
var file = ( imgList[i].file)!;
setState(() {
UploadImgModel imgModel = UploadImgModel();
imgModel.file = File(file.absolute.path);
... ...
import 'package:flutter/material.dart';
import 'package:image_picker_ohos/image_picker_ohos.dart';
import 'package:image_picker_platform_interface/image_picker_platform_interface.dart';
class ImageSelectOhosUtil{
static Future<List<XFile>> onImageButtonPressed(ImageSource source,
{required BuildContext context,
bool isMultiImage = false,
bool isMedia = false,
bool isVideo = false,
int limit = 9}) async {
// if (_controller != null) {
// await _controller!.setVolume(0.0);
// }
final ImagePickerPlatform _picker = new ImagePickerOhos();
if (context.mounted) {
if (isVideo) {
List<XFile> temp = [];
final XFile? file = await _picker.getVideo(
source: source, maxDuration: const Duration(seconds: 10));
if (file != null && context.mounted) {
// _showPickedSnackBar(context, <XFile>[file]);
temp.add(file as XFile);
}
return temp;
} else if (isMultiImage) {
double? maxWidth = 300.00;
double? maxHeight = 500;
int? quality = 90;
try {
final ImageOptions imageOptions = ImageOptions(
maxWidth: maxWidth,
maxHeight: maxHeight,
imageQuality: quality,
);
final List<XFile> pickedFileList = isMedia
? await _picker.getMedia(
options: MediaOptions(
allowMultiple: isMultiImage,
imageOptions: imageOptions,
limit: limit),
)
: await _picker.getMultiImageWithOptions(
options: MultiImagePickerOptions(
imageOptions: imageOptions, limit: limit),
);
if (pickedFileList.isNotEmpty && context.mounted) {
return pickedFileList;
} else {
return [];
}
} catch (e) {
print(e.toString());
}
} else if (isMedia) {
// double? maxWidth = 300.00;
// double? maxHeight = 500;
// int? quality = 90;
try {
final List<XFile> pickedFileList = <XFile>[];
final XFile? media = firstOrNull(await _picker.getMedia(
options: MediaOptions(
allowMultiple: isMultiImage,
// imageOptions: ImageOptions(
// maxWidth: maxWidth,
// maxHeight: maxHeight,
// imageQuality: quality,
// )
),
));
if (media != null) {
pickedFileList.add(media);
print(pickedFileList);
return pickedFileList;
}
} catch (e) {
print(e.toString());
}
} else {
double? maxWidth = 300.00;
double? maxHeight = 500;
int? quality = 90;
try {
final XFile? pickedFile = await _picker.getImageFromSource(
source: source,
options: ImagePickerOptions(
maxWidth: maxWidth,
maxHeight: maxHeight,
imageQuality: quality,
),
);
List<XFile> temp = [];
if (pickedFile != null && context.mounted) {
temp.add(pickedFile as XFile);
}
print(pickedFile);
return temp;
// <XFile>[pickedFile]
} catch (e) {
print(e);
}
}
}
return [];
}
static T? firstOrNull<T>(List<T> list) {
return list.isEmpty ? null : list.first;
}
}
\ No newline at end of file
... ...
... ... @@ -7,7 +7,9 @@ import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:one_key/models/mediaDN.dart';
import 'package:one_key/models/upload_img_model.dart';
import 'package:one_key/pages/online_check/image_select_ohos_util.dart';
import 'package:one_key/pages/online_check/photo_view_wiget.dart';
import 'package:one_key/pages/online_check/progress.dart';
... ... @@ -34,12 +36,9 @@ import './dispose.dart';
// import 'package:photo_manager/photo_manager.dart';
import 'package:flutter_native_image/flutter_native_image.dart';
// import 'package:flutter_image_compress/flutter_image_compress.dart';
import 'package:image_picker_ohos/image_picker_ohos.dart';
import 'package:image_picker_platform_interface/image_picker_platform_interface.dart';
class MediaDN {
File? file;
}
typedef void OnPickImageCallback(
double maxWidth, double maxHeight, int quality);
... ... @@ -91,7 +90,7 @@ class _ImgPickerWidgetState extends DisposableState<ImgPickerWidget> {
// 控制器,可以用于取消任务、获取上述的状态,进度等信息
// PutController putController = PutController();
final ImagePickerPlatform _picker = new ImagePickerOhos();
// 当前的任务状态
@override
... ... @@ -249,116 +248,9 @@ class _ImgPickerWidgetState extends DisposableState<ImgPickerWidget> {
// });
// }
T? _firstOrNull<T>(List<T> list) {
return list.isEmpty ? null : list.first;
}
Future<List<XFile>> _onImageButtonPressed(
ImageSource source, {
required BuildContext context,
bool isMultiImage = false,
bool isMedia = false,
bool isVideo = false,
}) async {
// if (_controller != null) {
// await _controller!.setVolume(0.0);
// }
if (context.mounted) {
if (isVideo) {
List<XFile> temp = [];
final XFile? file = await _picker.getVideo(
source: source, maxDuration: const Duration(seconds: 10));
if (file != null && context.mounted) {
// _showPickedSnackBar(context, <XFile>[file]);
temp.add(file as XFile);
}
return temp;
} else if (isMultiImage) {
double? maxWidth = 300.00;
double? maxHeight = 500;
int? quality = 90;
try {
final ImageOptions imageOptions = ImageOptions(
maxWidth: maxWidth,
maxHeight: maxHeight,
imageQuality: quality,
);
final List<XFile> pickedFileList = isMedia
? await _picker.getMedia(
options: MediaOptions(
allowMultiple: isMultiImage, imageOptions: imageOptions),
)
: await _picker.getMultiImageWithOptions(
options: MultiImagePickerOptions(
imageOptions: imageOptions,
),
);
if (pickedFileList.isNotEmpty && context.mounted) {
return pickedFileList;
} else {
return [];
}
} catch (e) {
print(e.toString());
}
} else if (isMedia) {
// double? maxWidth = 300.00;
// double? maxHeight = 500;
// int? quality = 90;
try {
final List<XFile> pickedFileList = <XFile>[];
final XFile? media = _firstOrNull(await _picker.getMedia(
options: MediaOptions(
allowMultiple: isMultiImage,
// imageOptions: ImageOptions(
// maxWidth: maxWidth,
// maxHeight: maxHeight,
// imageQuality: quality,
// )
),
));
if (media != null) {
pickedFileList.add(media);
print(pickedFileList);
return pickedFileList;
}
} catch (e) {
print(e.toString());
}
} else {
double? maxWidth = 300.00;
double? maxHeight = 500;
int? quality = 90;
try {
final XFile? pickedFile = await _picker.getImageFromSource(
source: source,
options: ImagePickerOptions(
maxWidth: maxWidth,
maxHeight: maxHeight,
imageQuality: quality,
),
);
List<XFile> temp = [];
if (pickedFile != null && context.mounted) {
temp.add(pickedFile as XFile);
}
print(pickedFile);
return temp;
// <XFile>[pickedFile]
} catch (e) {
print(e);
}
}
}
return [];
}
///多图片选择
void pickAsset(RequestType type, {List<AssetPathEntity>? pathList}) async {
... ... @@ -393,11 +285,12 @@ class _ImgPickerWidgetState extends DisposableState<ImgPickerWidget> {
imgList.add(mediaDN);
});
} else if (Platform.isOhos) {
List<XFile> imagListTemp = await _onImageButtonPressed(
List<XFile> imagListTemp = await ImageSelectOhosUtil.onImageButtonPressed(
ImageSource.gallery,
context: context,
isMultiImage: true,
isVideo: false);
isVideo: false,
limit: widget.maxImageNumber - temp!.length);
imagListTemp.forEach((elenemt) async {
MediaDN mediaDN = MediaDN();
... ...
... ... @@ -6,7 +6,9 @@ import 'dart:async';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:one_key/models/mediaDN.dart';
import 'package:one_key/models/upload_img_model.dart';
import 'package:one_key/pages/online_check/image_select_ohos_util.dart';
import 'package:one_key/pages/online_check/photo_view_wiget.dart';
import 'package:one_key/pages/online_check/progress.dart';
... ... @@ -200,61 +202,39 @@ class _ImgPickerFWidgetState extends DisposableState<ImgPickerFWidget> {
} else if (widget.uploadType == UploadType.FeastRemarkInvoiceType) {
temp = widget.model!.feastRemarkInvoiceImageList;
}
List<AssetEntity>? imgList = await AssetPicker.pickAssets(context,
pickerConfig: AssetPickerConfig(
maxAssets: widget.maxImageNumber - temp!.length,
// selectedAssets: assets,
requestType: type,
themeColor: MyColours.color_199095));
/// context is required, other params is optional.
// List<AssetEntity> imgList = await PhotoPicker.pickAsset(
// // BuildContext required
// context: context,
List<MediaDN>? imgList = [];
if (Platform.isAndroid || Platform.isIOS) {
List<AssetEntity>? imagListTemp = await AssetPicker.pickAssets(context,
pickerConfig: AssetPickerConfig(
maxAssets: widget.maxImageNumber - temp!.length,
// selectedAssets: assets,
requestType: type,
themeColor: MyColours.color_199095));
imagListTemp?.forEach((elenemt) async {
MediaDN mediaDN = MediaDN();
mediaDN.file = await elenemt.file;
imgList.add(mediaDN);
});
} else if (Platform.isOhos) {
List<XFile> imagListTemp = await ImageSelectOhosUtil.onImageButtonPressed(
ImageSource.gallery,
context: context,
isMultiImage: true,
isVideo: false,
limit: widget.maxImageNumber - temp!.length);
// /// The following are optional parameters.
// themeColor: MyColours.color_199095,
// // the title color and bottom color
// textColor: Colors.white,
// // text color
// padding: 1.0,
// // item padding
// dividerColor: Colors.grey,
// // divider color
// disableColor: Colors.grey.shade300,
// // the check box disable color
// itemRadio: 0.88,
// // the content item radio
// maxSelected: widget.maxImageNumber - temp.length,
// // max picker image count
// // provider: I18nProvider.english,
// provider: I18nProvider.chinese,
// // i18n provider ,default is chinese. , you can custom I18nProvider or use ENProvider()
// rowCount: 3,
// // item row count
// thumbSize: 150,
// // preview thumb size , default is 64
// sortDelegate: SortDelegate.common,
// // default is common ,or you make custom delegate to sort your gallery
// checkBoxBuilderDelegate: DefaultCheckBoxBuilderDelegate(
// activeColor: MyColours.color_353535,
// unselectedColor: Colors.white,
// checkColor: MyColours.color_199095,
// ),
// // default is DefaultCheckBoxBuilderDelegate ,or you make custom delegate to create checkbox
// // loadingDelegate: this,
// // if you want to build custom loading widget,extends LoadingDelegate, [see example/lib/main.dart]
// badgeDelegate: const DurationBadgeDelegate(),
// // badgeDelegate to show badge widget
// pickType: type,
// photoPathList: pathList,
// );
if (imgList == null || imgList.isEmpty) {
imagListTemp.forEach((elenemt) async {
MediaDN mediaDN = MediaDN();
mediaDN.file = File(elenemt.path);
imgList.add(mediaDN);
});
}
if (imgList.length == 0 || imgList.isEmpty) {
print("No pick item.");
return;
} else {
for (int i = 0; i < imgList.length; i++) {
var file = (await imgList[i].file)!;
var file = ( imgList[i].file)!;
// File compressedFile = await FlutterNativeImage.compressImage(
// file.absolute.path,
// quality: 50,
... ... @@ -272,7 +252,7 @@ class _ImgPickerFWidgetState extends DisposableState<ImgPickerFWidget> {
if (need) {
file = File(compressedFile!.absolute.path);
}
UploadImageUtil().startUpload(context, file, temp.length - 1, false,
UploadImageUtil().startUpload(context, file, temp!.length - 1, false,
widget.model!, widget.uploadType,
countAll: imgList.length,
callback: widget.callback,
... ...
... ... @@ -134,7 +134,8 @@ class UploadImageUtil {
String? mimeType = Utils.getMimeType(selectedFile.path);
print(mimeType);
String imageName = Utils.getImageNameByDateTime(selectedFile.path);
String imageName =
Utils.getImageNameByDateTime(selectedFile.path, extend: '_${index}');
print(imageName);
storage
... ... @@ -280,6 +281,7 @@ class UploadImageUtil {
// 取消当前任务
putController.cancel();
uploadImageProvider.countUpload = 0;
uploadImageProvider.notifyListeners();
if (error is StorageError) {
switch (error.type) {
... ... @@ -372,7 +374,8 @@ class UploadImageUtil {
String? mimeType = Utils.getMimeType(selectedFile!.path);
print(mimeType);
String imageName = Utils.getImageNameByDateTime(selectedFile.path);
String imageName =
Utils.getImageNameByDateTime(selectedFile.path, extend: '_${index}');
print(imageName);
storage
... ... @@ -535,7 +538,8 @@ class UploadImageUtil {
String? mimeType = Utils.getMimeType(selectedFile.path);
print(mimeType);
String imageName = Utils.getImageNameByDateTime(selectedFile.path);
String imageName =
Utils.getImageNameByDateTime(selectedFile.path, extend: '_${index}');
print(imageName);
storage
... ...
... ... @@ -69,58 +69,39 @@ import 'package:app_update_check_plugin/app_update_check_plugin.dart';
typedef BookTimeSetCallback = void Function(bool needChange);
class Utils {
static Future<bool> checkUpdateOhos() async {
// 鸿蒙商城是否有新版本
final _appUpdateCheckPlugin = AppUpdateCheckPlugin();
try {
bool hasUpdate = await _appUpdateCheckPlugin.checkAppUpdate();
if (hasUpdate) {
//跳转应用商城
static Future<bool> checkUpdateOhos()async{
// 鸿蒙商城是否有新版本
final _appUpdateCheckPlugin = AppUpdateCheckPlugin();
try {
bool hasUpdate = await _appUpdateCheckPlugin.checkAppUpdate();
if (hasUpdate) {
//跳转应用商城
return true;
} else {
return false;
}
} on PlatformException {
return true;
} else {
return false;
// platformVersion = 'Failed to get platform version.';
}
} on PlatformException {
return false;
// platformVersion = 'Failed to get platform version.';
}
}
// 跳转到华为应用商城更新的方法
static void launchAppGallery() async{
final appid =Constant.ohos_app_id; //可以是应用id、也可以是鸿蒙应用包名
// final url='store://appgallery.huawei.com/app/detail?id=$appid';
// 跳转到华为应用商城更新的方法
static void launchAppGallery() async {
final appid = Constant.ohos_app_id; //可以是应用id、也可以是鸿蒙应用包名
// final url='store://appgallery.huawei.com/app/detail?id=$appid';
final url= 'store://appgallery.huawei.com/app/detail?id=com.jd.hm.mall&channelId=LAUNCHERSHARE';
final url =
'store://appgallery.huawei.com/app/detail?id=com.jd.hm.mall&channelId=LAUNCHERSHARE';
try{
try {
await launchUrl(Uri.parse(url));
print('打开成功');
}catch(err){
print('打开失败');
} catch (err) {
print('打开失败');
}
}
}
static String extractFileNameFromUrl(String url) {
try {
... ... @@ -131,16 +112,17 @@ static void launchAppGallery() async{
return ""; // 或者返回null或抛出异常,根据你的需求处理错误情况
}
}
static String? getMimeType(String filePath) {
return lookupMimeType(filePath); // 使用文件扩展名查找,或者传入文件头字节查找更准确的结果
}
static String getImageNameByDateTime(String filePath) {
static String getImageNameByDateTime(String filePath, {String extend = ''}) {
// 获取当前时间的毫秒数
int timestamp = DateTime.now().millisecondsSinceEpoch;
// return lookupMimeType(filePath);
String extension = path.extension(filePath); // 获取文件扩展名,可选步骤
return timestamp.toString() + extension;
return timestamp.toString() + extend + extension;
// 格式化时间戳为字符串(例如:1633072800000.png)
// String imageName = '$timestamp.png';
}
... ... @@ -1361,7 +1343,11 @@ static void launchAppGallery() async{
static getPickPermission(BuildContext context, VoidCallback callback,
Permission permissionGroup) async {
if (!(await checkPermission(permissionGroup))) {
String title = Platform.isAndroid ? '存储' : Platform.isIOS ? '相册':'图片和视频';
String title = Platform.isAndroid
? '存储'
: Platform.isIOS
? '相册'
: '图片和视频';
if (permissionGroup == Permission.camera) {
title = '相机';
} else if (permissionGroup == Permission.location) {
... ...
<!-- Copyright 2019 The FlutterCandies author. All rights reserved.
Use of this source code is governed by an Apache license
that can be found in the LICENSE file. -->
# Changelog
> [!IMPORTANT]
> See the [Migration Guide](guides/migration_guide.md) for the details of breaking changes between versions.
## Unreleased
*None.*
## 9.8.0
> [!NOTE]
> Be aware of potential minor theme changes since the base theme has taken place in the picker's theme.
**Improvements**
- Improve themes by inheriting the base theme rather than standalone constructors.
**Fixes**
- Enabling using the package on Flutter 3.35.
## 9.7.0
**Improvements**
- Allows specifying the fallback text delegate through `assetPickerTextDelegateFromLocale`.
## 9.6.0
**New features**
- Expose more route settings.
Users will now be able to control the route settings for the picker and viewer.
- Add Persian (Farsi) language support. (Thanks to @Ho3einTahan)
**Improvements**
- Improve how `MediaQuery` is being listened.
- Stop enabling drag to select when `maxAssets == 1`.
- Extract `DefaultAssetPickerProvider.init`.
**Fixes**
- Fix semantics with the close button.
- Fix `selectPredicate` not being called when selecting the video asset under the WeChat Moment mode.
- Manually dispose memory leak objects.
## 9.5.1
**Improvements**
- Allow `extended_image` v10.
## 9.5.0
**New features**
- Support drag to select.
**Fixes**
- Fixes semantics issues.
## 9.4.2
**Improvements**
- Add Traditional Chinese language text delegate. (Thanks to @Gasol)
- Improves the default sort conditions on Android.
## 9.4.1
**Improvements**
- Improves the default sort conditions.
**Fixes**
- Fixes selecting when reached the max asset limit in the preview.
## 9.4.0
**Improvements**
- Allows `extended_image: ^9.0.0`.
**Fixes**
- Allows assets changing when no path previously.
## 9.3.3
**Fixes**
- Recovers the compatibility with Flutter 3.16.
## 9.3.2
**Improvements**
- Adapt the file type getter from the image provider to get a precise file type in grid.
- Adds the identifier for grid item's semantics.
- Improves preview page back button.
## 9.3.1
**Improvements**
- Do not mute the Live Photo during the preview.
**Fixes**
- Fix the GIF indicator's layout.
- Fix the directionality with the reverted grid item.
## 9.3.0
**New features**
- Add explicit Live Photos indicator for assets.
**Improvements**
- Make Live Photos gesture consistent when scaling and panning.
- Integrate `LocallyAvailableBuilder` with thumbnail options to improve the thumbnail loading speed.
- Use `visibility_detector` and scroll observer to improve media playing experiences.
**Fixes**
- Fix the bottom actions bar display conditions.
## 9.2.2
- Uses correct `isOriginal` for the `LocallyAvailableBuilder`.
## 9.2.1
- Improve changes when limited on iOS.
- Use `LocallyAvailableBuilder` in the grid to provide better user awareness.
## 9.2.0
**New features**
- Introduce `AssetsChangeCallback` and `AssetsChangeRefreshPredicate`
to help users act according to asset changes.
- Add `shouldAutoplayPreview` to the picker config.
**Fixes**
- Raise detailed negative range error.
- Fix viewer confirm button predication.
- Enlarge GIF gradients.
- Fix potential paths assets count unexpected merging behaviors.
## 9.1.0
**Improvements**
- Support limited permission displays on Android.
- Improves the limited overlay padding on Android.
- Adds permission request lock for the picker state.
- Speeding up by splitting asset loading into separate steps.
- Speeding up using `AdvancedCustomFilter` rather than `FilterOptionGroup` by default.
**Fixes**
- Fix reverted index when previewing assets on Android.
- Requests with the correct options with the picker.
## 9.0.4
**Fixes**
- Fix the app bar of the viewer that is not animating.
- Fix loading when no assets are in the path.
- Reset the has more to load flag between path switching.
## 9.0.3
**Fixes**
- Fix index reverting in `viewAsset`.
## 9.0.2
**Fixes**
- Fix the index with bottom items in the preview.
## 9.0.1
**Fixes**
- Fix the current asset in the picker viewer.
**Improvements**
- Improve code formatting.
## 9.0.0
**Breaking changes**
- Migrate to Flutter 3.16, and drop supports for previous Flutter versions.
- Bump `photo_manager` to v3.x.
- Export `photo_manager_image_provider`.
- Integrate `PermissionRequestOption` for callers.
**Improvements**
- Adapt `ThemeData` usages.
- Use `wechat_picker_library`.
- Make the first asset count not blocking loads.
**Fixes**
- Fix unhandled child semantics with the app bar title.
- Fix styles around the app bar and other widgets.
- Fix previewing selected assets' behavior.
- Use `PermissionRequestOption` as much as possible.
- Raise more errors for non-synced paths.
- Fix the experience with `shouldRevertGrid`.
## 8.8.0
**New features**
- Add Korean language support. (Thanks to @LIMMIHEE)
**Improvements**
- Use `viewAsset` in the preview button.
**Fixes**
- Fix disposed provider throwing error when fetching assets. (#493)
## 8.7.1
**Improvements**
- Improve README docs and add topics for pub.
## 8.7.0
**Breaking changes**
- Migrate to Flutter 3.13, and drop supports for previous Flutter versions.
## 8.6.3
**Improvements**
- Improve `AssetPickerProvider.paths`.
## 8.6.2
**Improvements**
- Add `appBarPreferredSize` in the picker delegate to help with padding calculations.
- Improve the performance with `MediaQuery` callers.
## 8.6.1
**Improvements**
- Remove `needTitle` for image filter options.
**Fixes**
- Fix hit test when previewing videos.
## 8.6.0
**Breaking changes**
- Use `TargetPlatform` for the `isAppleOS` method in delegates, which relies on a `BuildContext`.
**New features**
- Sync all UI details from WeChat 8.3.x. (#458)
- Add Turkish language text delegate. (Thanks to @cevheri).
- Allow to confirm 0 assets if there are selected assets previously. (#461)
**Improvements**
- Silent part of thumbnail request exceptions.
**Fixes**
- Fix semantics interactions for video preview. (#458)
- Be able to update the items builder page. (#417)
## 8.5.0
**Breaking changes**
- Migrate to Flutter 3.10, and drop supports for previous Flutter versions.
## 8.4.3
**New features**
- Add Vietnamese language text delegate. (Thanks to @nploi).
**Improvements**
- Expand `FilterOptionGroup` to `PMFilter`. (#436)
## 8.4.2
**Fixes**
- Avoid clearing selected assets when disposing the provider. (#428)
## 8.4.1
**Fixes**
- Call `AssetPickerProvider.dispose` when disposing the builder delegate. (#421)
## 8.4.0
**Breaking changes**
- Migrate to Flutter 3.7, and drop supports for previous Flutter versions.
## 8.3.2+1
**Improvements**
- Add more assertions. (#411)
## 8.3.2
**Fixes**
- Use `Completer` and more accurate conditions to avoid duplicate load assets
when `AssetPickerConfig.pageSize` is smaller than a complete page. (#407)
## 8.3.1+1
**Improvements**
- Fix images and descriptions in `README`s.
## 8.3.1
**New features**
- Add `didUpdateViewer` and `initAnimations` in the `AssetPickerViewerBuilderDelegate`. (#403)
- Add insta_assets_picker as a custom delegate example. (#403)
## 8.3.0
**New features**
- Add `index` argument to `selectAsset` in the `AssetPickerBuilderDelegate`. (#399)
**Improvements**
- Improve UI details in the `AssetPickerAppBar`. (#400)
## 8.2.0
**New features**
- Allow overrides `viewAsset` in the `AssetPickerBuilderDelegate`. (#391)
**Fixes**
- Correct behaviors when the access is limited on iOS. (#392)
## 8.1.4
**Fixes**
- Fix conditions with the confirm button on iOS. (#376)
## 8.1.3
**Improvements**
- Bump `photo_manager` to explicitly remove the requirements of `requiredLegacyExternalStorage`.
## 8.1.2
**Fixes**
- Fix conditions with the confirm button. (#371)
## 8.1.1
**Fixes**
- Fix conditions with the confirm button. (#367)
## 8.1.0
**New features**
- Upgrade `photo_manager` for Android 13. (#365)
**Improvements**
- Improve `BuildContext` usages to obtain the correct directionality for the assets grid. (#359)
- Provide a better condition to the confirm button
to make sure it displays correctly in all cases on iOS/macOS. (#359)
- Improve `bottomActionBar` in `DefaultAssetPickerBuilderDelegate`. (#359)
**Fixes**
- Fix invalid path sort. (#364)
## 8.0.2
**Improvements**
- Adapt Flutter 3.3. (#354)
## 8.0.1
**Fixes**
- Fix not updated empty flag in `DefaultAssetPickerProvider`. (#353)
## 8.0.0
To know more about breaking changes, see [Migration Guide][].
**New features**
- Introduce `PathWrapper` in delegates to improve the overall loading speed. (#338)
- Allow using `Key` during picking. (#339)
- Add `initializeDelayDuration` for `DefaultAssetPickerProvider`. (#341)
- Prevent race conditions with paths. (#342)
- Expose `sortPathsByModifiedDate`. (#343)
**Fixes**
- Unify indicator usage to avoid accidental indicator switching. (#344)
## 7.3.2
**Improvements**
- Improve `onChangingSelected` in `AssetPickerViewerBuilderDelegate`. (#332)
- Fix typo in `README.md`. (#333)
**Fixes**
- Fix behaviors when unselecting all assets in the viewer. (#335)
## 7.3.1
**Improvements**
- Improve selection callers between picker and viewer. (#327)
## 7.3.0
**Breaking changes**
- Migrate to Flutter 3, and drop supports for previous Flutter versions.
## 7.2.0
**New features**
- Separate `AssetPickerDelegate` (#315),
which provides the ability to override methods within the `AssetPicker`.
**Improvements**
- Improve `specialItemBuilder`. (#314)
## 7.1.2
**Fixes**
- Fix context usages for inherited theme data.
- Fix unlinked path name builder.
**Improvements**
- Update how paths get updated. (#312)
- Expose `DefaultAssetPickerProvider.forTest`.
## 7.1.1
**Fixes**
- Fix `selectPredicate` with the viewer. (#307)
## 7.1.0
**New features**
- Add `PathNameBuilder`. (#303)
- Add `LimitedPermissionOverlayPredicate`. (#287)
## 7.0.5
**Improvements**
- Support compile on the Web. (#273)
## 7.0.4
**Improvements**
- Make all text delegates const.
## 7.0.3
**Fixes**
- Fix the broken semantics on iOS/macOS. (#272)
## 7.0.2
**Fixes**
- Fix wrong conditions judging when obtaining path thumbnails.
## 7.0.1
**Improvements**
- Support semantics with Japanese text delegates. (#266).
**Fixes**
- Obtain the path thumbnail only when the asset is an image or video.
## 7.0.0
To know more about breaking changes, see [Migration Guide][].
**New features**
- Support predictable special item display. (#264)
- Support Live-Photos display. (#251)
- Expose `AssetPickerPageRoute` for customization. (#248)
- Add full semantics support. (#232, #235, #240, #242, #243, #245, #254)
**Improvements**
- Improve scaling with select indicators and numbers.
- Implement the default light theme. (#234)
**Fixes**
- Fix `LocallyAvailableBuilder` with more edge conditions. (#263)
- Fix potential "No elements" error with thumbnails.
## 6.3.1
- Improve image type determined when resolving image data.
This mostly resolved the occasional HEIC loading issue when
apps are running under the release mode.
## 6.3.0
- Support for Flutter 2.8.0, also drop supports for Flutter below 2.8.0.
## 6.2.4
- Improve audio item layout.
- Improve workflows.
- Indicate the path getter more precisely.
- Improve Arabic text delegate.
## 6.2.3
- Pass through `selectPredicate` to `AssetPickerViewer`. (#211)
- Bump the `sdk` constraints (since 6.2.1).
## 6.2.2
- Use `.contentUri` for video preview only on Android.
## 6.2.1
- Migrate to `extended_image` 5.x.
- Use `.contentUri` constructor for `VideoPlayerController`.
## 6.2.0
- Introduce `shouldRevertGrid` to determine whether the assets grid should be reverted.
- Upgrade `photo_manager` to resolve issues against `AssetEntity`s comparison.
## 6.1.2
- Fix the limited resources refresh issue.
- Update callers to avoid deprecated usage.
## 6.1.1
- Handle iCloud video more gracefully.
## 6.1.0
- Introduce `selectPredicate` to predicate asset when picking.
## 6.0.6
- Use the correct index reference with `selectedBackdrop`. (#195)
## 6.0.5
- Upgrade dependencies.
## 6.0.4
- Add French language text delegate. (Thanks to @didiosn)
- Export "Nothing here." as a field to text delegate. (#190)
- Fix selected backdrop not synced all the time.
- Improve the instructions for the example.
## 6.0.3
- Fix path entity nullable issues with the asset grid.
- Fix assets displaying conditions missing with the iOS layout.
## 6.0.2
- Expose `shouldReversePreview` for `AssetPickerViewerBuilderDelegate`.
## 6.0.1
- Improve total count calculation with `AssetPathEntity`. (#187)
- Obtain `AssetPathEntity.isAll` fix from `photo_manager`.
- Documents update.
## 6.0.0
**New features**
- Sync all UI details from WeChat 8.0.x.
- Integrate iCloud progress overview in previews.
- Change the permission from the app settings when it's limited.
- Request more assets on iOS when the permission is limited.
- Fit the assets' grid's layout as the iOS `Photos` app (reverted and started from the bottom).
- Add Arabic language text delegate.
- Allow using `AssetPicker` and `AssetPickerViewer` directly with delegates.
- Add `keepScrollOffset` feature for the `AssetPickerBuilderDelegate`.
**Improvements**
- Items that are being banned from select (reached max assets or type conflict)
will have a stronger color cover to indicate.
- The video preview in the [SpecialPickerType.wechatMoment] is completely different from other previews.
- Grid items have removed the fade builder for more straight feedback after it gets loaded.
- Better interaction when jumping between previewing assets.
- Path entities list layout structure performance & structure improved.
- More precise thumbnail option for iOS.
- Improve text scale handling. (#177)
- Reduce font size for a couple of texts.
To know more about breaking changes, see [Migration Guide][].
## 5.5.8
- Fix the viewer's select button issue with WeChat Moment on Android.
## 5.5.7
- Make `switchPath` method in `AssetPickerProvider` async.
## 5.5.6
- Add Japanese language text delegate. (Thanks to @KosukeSaigusa)
- Fix video judge condition with WeChat Moment mode.
- Fix the wrong position for the confirm button on iOS with WeChat Moment mode.
## 5.5.5
- Migrate permissions check to the new API in `photo_manager`.
## 5.5.4
- Fix assets reduced under the WeChat Moment mode when previewing.
## 5.5.3+1
- Fix static analysis issue.
## 5.5.3
- Use `SystemUiOverlayStyle` from the provided theme first.
## 5.5.2
- Add German language text delegate. (Thanks to @Letalus)
- Add Russian language text delegate. (Thanks to @greymag)
## 5.5.1
- Implement `RenderToggleable` to support other channels of flutter (2.1.x - 2.3.x). (Thanks to @Letalus)
## 5.5.0
- Add `findChildIndexBuilder` to indicate grid find reusable `RenderObject`.
- Fix the padding issue for the assets' grid.
## 5.4.3
- Fix the missing `ScrollController` in the scroll view.
## 5.4.2
- Split `AssetGridItemBuidler` to solve the rebuild issue.
- Fix the GIF indicator stretched issue.
## 5.4.1
- Export `AssetPickerPageRoute`.
## 5.4.0+1
- Fix the format that pub.dev suggests.
## 5.4.0
- Fully support RTL languages.
- Add control for root navigator.
- Improve `AssetEntityImageProvider`'s constructor and decode.
- Grab the iOS/macOS thumbnail's size fix from `photo_manager`.
## 5.3.0
- Add `gridThumbSize` to control thumbnails in the select grid.
- Applies a further fix to the WeChat Moment video preview.
- Fix unlimited assets choose in preview mode.
## 5.2.1
- Fix the WeChat Moment preview issue.
## 5.2.0
- Add `SpecialPickerType.noPreview` to disable the preview when picking. (Thanks to @yanivshaked)
## 5.1.4
- Add Hebrew language text delegate. (Thanks to @yanivshaked)
- Fix the slide page route issue when integrated with the `get` package.
## 5.1.3
- Fix not synced issue when the picker is under single pick mode.
## 5.1.2
- Fix selected assets not syncing between thumbnail preview mode and grid view.
- Fix the wrong index displays in thumbnail preview mode.
- Dependencies upgrade roll.
## 5.1.1
- Integrate `lastModified` to sort path entities by default.
## 5.1.0
- Reset to the top for the asset grid view after switching paths.
- Add the ability to select assets in any position of the picker.
- Implement mime type for image type judgment.
- Fix the disappeared GIFs indicator.
## 5.0.5
- Fix force cast null type issue with WeChat moment special pick type.
- Improve the app bar's type definition.
## 5.0.4
- Fix path thumb data's display issue.
- Fix the default preview thumb size issue with the image preview builder.
## 5.0.3
- Remove required annotation for selected assets.
## 5.0.2
- Fix the wrong viewer provider state.
- Enhance page stream controller sink close.
- Fix `dartdoc` generate issue for pub.
## 5.0.1
- Fix the video indicator layout issue.
- Prevent video selection in WeChat moment mode for edge cases.
- Pickup fixes from `photo_manager`.
## 5.0.0
- Add the ability to show the special item when the device has no assets.
- Allow users to build their own picker with custom asset types. (See example for custom delegate.)
- Slightly reduce performance consumption with layout.
**Breaking changes**
- Migrate to non-nullable by default.
- `CustomItemBuilder` -> `SpecialItemBuilder`, `CustomItemPosition` -> `SpecialItemPosition`.
- Abstract `AssetPickerBuilderDelegate<A, P>`, `AssetPickerViewerBuilderDelegate<A, P>`,
`AssetPickerProvider<A, P>`, and minify the `AssetPickerViewerProvider<A>`. Support custom types by generic type.
- The `assets` param in `AssetPickerViewer.pushToViewer` is now `previewAssets`.
## 4.2.2
- Suppress deprecated usage for example.
- Improve code format with dart format.
- Raise the dependencies version.
## 4.2.1
- Fix arguments judging condition with preview thumb size.
- Remove common exports and split out constants.
## 4.2.0
- Add `previewThumbSize` for the viewer.
## 4.1.0+4
- Upgrade `extended_image`.
## 4.1.0+3
- Make widgets constant.
- Remove system UI overlays update.
- Migrate files to compatible with Flutter `1.20.0`.
- Sync analysis options.
## 4.1.0+2
- Ignore size constraints for image assets.
- Format code using `dartfmt`.
- Upgrade `photo_manager`.
## 4.1.0+1
- Adjust AppleOS layout.
## 4.1.0
- Add a backdrop widget in the picker, which makes the picker more like the one in WeChat.
## 4.0.0
- Drop `asset_audio_player`.
- Experimenting status bar hidden on iOS.
- Remove the video player listener before pausing.
**Breaking changes**
- `TextDelegate` -> `AssetsPickerTextDelegate`.
## 3.0.0+1
- Constraint dependencies version.
## 3.0.0
- Add `FilterOptionGroup`. (#41)
- Add `SpecialPickerType`. (#37)
- Add custom item build mode. (#39)
## 2.2.1
- Introduce `ColorScheme` for theme details. (#32)
- Enhance RTL compatibility.
- Enlarge select indicator's size. (#33)
## 2.2.0+2
- Remove path entity properties refresh.
## 2.2.0+1
- Fix the preview widget for audio assets in the picker viewer.
## 2.2.0
- A brand-new example.
- Add `SortPathDelegate`.
- Using `ZoomPageTransition` for viewers.
- Slightly add padding to the viewer's assets list view.
- Migrate elevation and color to material rendering.
- Request thumb only when the request type is not audio.
- Force request title for audio type asset.
- Expose `AssetType` enum.
- Fix the issue with the request type.
## 2.1.0
- Add present English text delegate.
- Refactored theme constructor and getter with theme capability. (#22)
- Update color scheme usage for the assets grid. (#23)
- Update picker viewer style for Apple OS.
- Fix the bottom bar disappearing on Apple OS when it's single asset mode.
## 2.0.2
- Fix audio paused accidentally when the app is launching on Android. (#18)
## 2.0.1
- Expose observe register methods.
## 2.0.0
- Support audio assets.
- Support single asset mode.
- Enlarge the preview button's detector area size.
- Fix wrong properties' usage causing infinite build when the page reaches the end.
**Breaking changes**
- `videoIndicatorBuilder` -> `durationIndicatorBuilder`.
## 1.7.0
- Hide detail display when the video starts to play.
- Switch to `ExtendedImageGesturePageView`. (#16)
- Add full theme support.
- Add macOS support.
- Add delay for the first init method to prevent stuck in page routing. (#13)
- Update widgets style on iOS. (#14)
- Fix the state of the example not updated after the result was returned without the input method activated.
## 1.6.0
- Support HEIC/HEIF image type.
## 1.5.0+1
- Upgrade `photo_manager` to `0.5.1`.
- Replace deprecate `TextTheme.title` API usage.
- Document (`dartdoc`) update.
## 1.5.0
- Bumping flutter SDK minimum version to `1.17.0`.
- Declare API stability and compatibility with 1.0.0
( more details at: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0 ).
## 1.4.1
- Remove the loading indicator for the image widget.
- Refactor the video page's initialization for ratio update.
- Using constants to store text delegate.
- Add error catching for main methods.
- Update text delegate management.
## 1.4.0+1
- Fix call on null with `currentlySelectedAssets`.
## 1.4.0
- Support paging assets load.
- Fix selected assets not synced with the picker provider.
- Bump `photo_manager` to `0.5.1-dev.5`
## 1.3.2
- Expose page transition curve and duration to a static method.
- Fix theme color not passed to a static method.
## 1.3.1+1
- Fix the `pageBuilder` null issue.
## 1.3.1
- Add upwards slide page transition.
- Add padding to the bottom action bar in the picker.
## 1.3.0
- Add iOS style.
- Add cancel field to text delegate.
- `Set` -> `List`.
## 1.2.1
- Fix the missing aspect ratio for the video player.
- Using common request type in example.
## 1.2.0
- Add text delegate support. (Also with i18n support using delegate).
## 1.1.0
- Support video assets. You can use `requestType` to select video or video+image.
- Hide system ui overlays according to flag and system.
- Update the GIF indicator and add a video indicator.
## 1.0.0
- Initial release.
[Migration Guide]: guides/migration_guide.md
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [2019] [FlutterCandies]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
<!-- Copyright 2019 The FlutterCandies author. All rights reserved.
Use of this source code is governed by an Apache license
that can be found in the LICENSE file. -->
# Flutter WeChat Assets Picker
[![pub package](https://img.shields.io/pub/v/wechat_assets_picker?label=%E7%A8%B3%E5%AE%9A%E7%89%88&logo=dart&style=flat-square)](https://pub.flutter-io.cn/packages/wechat_assets_picker)
[![pub package](https://img.shields.io/pub/v/wechat_assets_picker?color=9d00ff&include_prereleases&label=%E5%BC%80%E5%8F%91%E7%89%88&logo=dart&style=flat-square)](https://pub.flutter-io.cn/packages/wechat_assets_picker)
[![CodeFactor](https://img.shields.io/codefactor/grade/github/fluttercandies/flutter_wechat_assets_picker?label=%E4%BB%A3%E7%A0%81%E8%B4%A8%E9%87%8F&logo=codefactor&logoColor=%23ffffff&style=flat-square)](https://www.codefactor.io/repository/github/fluttercandies/flutter_wechat_assets_picker)
[![Build status](https://img.shields.io/github/actions/workflow/status/fluttercandies/flutter_wechat_assets_picker/runnable.yml?branch=main&label=CI&logo=github&style=flat-square)](https://github.com/fluttercandies/flutter_wechat_assets_picker/actions/workflows/runnable.yml)
[![GitHub license](https://img.shields.io/github/license/fluttercandies/flutter_wechat_assets_picker?label=%E5%8D%8F%E8%AE%AE&style=flat-square)](https://github.com/fluttercandies/flutter_wechat_assets_picker/blob/main/LICENSE)
[![GitHub stars](https://img.shields.io/github/stars/fluttercandies/flutter_wechat_assets_picker?logo=github&style=flat-square)](https://github.com/fluttercandies/flutter_wechat_assets_picker/stargazers)
[![GitHub forks](https://img.shields.io/github/forks/fluttercandies/flutter_wechat_assets_picker?logo=github&style=flat-square)](https://github.com/fluttercandies/flutter_wechat_assets_picker/network)
[![Awesome Flutter](https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg)](https://github.com/Solido/awesome-flutter)
<a href="https://qm.qq.com/q/ZyJbSVjfSU"><img src="https://img.shields.io/badge/dynamic/yaml?url=https%3A%2F%2Fraw.githubusercontent.com%2Ffluttercandies%2F.github%2Frefs%2Fheads%2Fmain%2Fdata.yml&query=%24.qq_group_number&label=QQ%E7%BE%A4&logo=qq&style=flat&color=1DACE8" /></a>
Language: [English](README.md) | 中文
基于 **微信 UI** 的 Flutter **图片选择器(同时支持视频和音频)**。
当前的界面设计基于的微信版本:**8.0.51**
界面更新将在微信版本更新后随时进行跟进。
如果你需要拍照及录制视频,请先查看示例的详细用法,
并前往 [wechat_camera_picker][wechat_camera_picker pub]。
该插件是独立扩展,需要结合使用。
查看 [迁移指南][] 了解如何从破坏性改动中迁移为可用代码。
## 版本兼容
该插件仅保证能与 **stable 渠道的 Flutter SDK** 配合使用。
我们不会为其他渠道的 Flutter SDK 做实时支持。
| | 3.7 | 3.10 | 3.13 | 3.16 | 3.22 |
|--------|:---:|:----:|:----:|:----:|:----:|
| 9.5.0+ | ❌ | ❌ | ❌ | ❌ | ✅ |
| 8.9.0+ | ❌ | ❌ | ❌ | ✅ | ❌ |
| 8.7.0+ | ❌ | ❌ | ✅ | ❌ | ❌ |
| 8.5.0+ | ❌ | ✅ | ❌ | ❌ | ❌ |
| 8.4.0+ | ✅ | ❌ | ❌ | ❌ | ❌ |
如果在 `flutter pub get` 时遇到了 `resolve conflict` 失败问题,
请使用 `dependency_overrides` 解决。
## 主要使用的 package
该插件基于这些优秀的 package 构建:
| Name | Features |
|:-------------------------------------|:--------------|
| [photo_manager][photo_manager pub] | 资源的基础抽象和管理。 |
| [extended_image][extended_image pub] | 以熟悉的操作预览所有资源。 |
| [provider][provider pub] | 协助选择器管理器内部状态。 |
| [video_player][video_player pub] | 播放对应的视频和音频。 |
这些 package 在该插件中的实现已相对稳定。
如果你在使用中发现于它们相关的问题,
请先在本插件的问题跟踪中报告相关问题。
<details>
<summary>目录列表</summary>
<!-- TOC -->
* [Flutter WeChat Assets Picker](#flutter-wechat-assets-picker)
* [版本兼容](#版本兼容)
* [主要使用的 package](#主要使用的-package)
* [特性 ✨](#特性-)
* [特别提醒 📝](#特别提醒-)
* [项目展柜 🖼️](#项目展柜-)
* [截图 📸](#截图-)
* [开始前的注意事项 ‼️](#开始前的注意事项-)
* [准备工作 🍭](#准备工作-)
* [Flutter](#flutter)
* [Android](#android)
* [权限](#权限)
* [iOS](#ios)
* [macOS](#macos)
* [使用方法 📖](#使用方法-)
* [国际化](#国际化)
* [简单的使用方法](#简单的使用方法)
* [更详细的使用方法](#更详细的使用方法)
* [展示选中的资源](#展示选中的资源)
* [注册资源变化回调](#注册资源变化回调)
* [在表单数据中上传 `AssetEntity`](#在表单数据中上传-assetentity)
* [使用 `http`](#使用-http)
* [使用 `dio`](#使用-dio)
* [自定义选择器](#自定义选择器)
* [常见问题 ❔](#常见问题-)
* [修改默认相册名称(将 `Recent` 改为其他)](#修改默认相册名称将-recent-改为其他)
* [Execution failed for task ':photo_manager:compileDebugKotlin'](#execution-failed-for-task-photo_managercompiledebugkotlin)
* [从 `File` 或 `Uint8List` 创建 `AssetEntity` 的方法](#从-file-或-uint8list-创建-assetentity-的方法)
* [控制台提示 'Failed to find GeneratedAppGlideModule'](#控制台提示-failed-to-find-generatedappglidemodule)
* [致谢](#致谢)
<!-- TOC -->
</details>
## 特性 ✨
- ♿ 完整的无障碍支持,包括 **TalkBack** 和 **VoiceOver**
- ♻️ 支持基于代理重载的全量自定义
- 🎏 完全可自定义的基于 `ThemeData` 的主题
- 💚 复刻微信风格(甚至优化了更多的细节)
- ⚡️ 根据配置调节的性能优化
- 📷 图片资源支持
- 🔬 HEIF 格式图片支持 <a href="#特别提醒-"><sup>(1)</sup></a>
- 🎥 视频资源支持
- 🎶 音频资源支持 <a href="#notes-"><sup>(2)</sup></a>
- 1️⃣ 单选模式模式
- 💱 国际化 (i18n) 支持
- ⏪ RTL 语言支持
- ➕ 特殊 widget 构建支持
- 🗂 自定义路径排序支持
- 📝 自定义文本构建支持
- ⏳ 自定义筛选规则支持
- 💻 支持 MacOS
### 特别提醒 📝
1. HEIF (HEIC) 图片支持获取和转换,但是它们的显示依托于 Flutter 的图片解析。
在此 issue 中 [flutter/flutter#20522](https://github.com/flutter/flutter/issues/20522) 有所说明。
若要用于显示,请使用 `entity.file` 或 `AssetEntityImage` 进行处理。
2. 由于 iOS 和 macOS 的系统限制,在获取音频时只能获取应用沙盒环境内的音频。
## 项目展柜 🖼️
| name | pub | github |
|:--------------------|:---------------------------------------------------------------------------------------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------|
| insta_assets_picker | [![pub package](https://img.shields.io/pub/v/insta_assets_picker)](https://pub.flutter-io.cn/packages/insta_assets_picker) | [![star](https://img.shields.io/github/stars/LeGoffMael/insta_assets_picker?style=social)](https://github.com/LeGoffMael/insta_assets_picker) |
## 截图 📸
| ![1](screenshots/README_1.webp) | ![2](screenshots/README_2.webp) | ![3](screenshots/README_3.webp) |
|-----------------------------------|-----------------------------------|-----------------------------------|
| ![4](screenshots/README_4.webp) | ![5](screenshots/README_5.webp) | ![6](screenshots/README_6.webp) |
| ![7](screenshots/README_7.webp) | ![8](screenshots/README_8.webp) | ![9](screenshots/README_9.webp) |
| ![10](screenshots/README_10.webp) | ![11](screenshots/README_11.webp) | ![12](screenshots/README_12.webp) |
## 开始前的注意事项 ‼️
在开始一切之前,请明确以下两点:
- 由于理解差异和篇幅限制,并不是所有的内容都会明确地在文档中指出。
当你遇到没有找到需求和无法理解的概念时,请先运行项目的示例 example,
它可以解决 90% 的常见需求。
- 该库与 [photo_manager][photo_manager pub] 有强关联性,
大部分方法的行为是由 photo_manager 进行控制的,
所以请尽可能地确保你了解以下两个类的概念:
- 资源(图片/视频/音频) [`AssetEntity`](https://pub.flutter-io.cn/documentation/photo_manager/latest/photo_manager/AssetEntity-class.html)
- 资源合集(相册或集合概念) [`AssetPathEntity`](https://pub.flutter-io.cn/documentation/photo_manager/latest/photo_manager/AssetPathEntity-class.html)
当你有与相关的 API 和行为的疑问时,你可以查看
[photo_manager API 文档][] 了解更多细节。
众多使用场景都已包含在示例中。
在你提出任何问题之前,请仔细并完整地查看和使用示例。
## 准备工作 🍭
### Flutter
执行 `flutter pub add wechat_assets_picker`,
或者将 `wechat_assets_picker` 手动添加至 `pubspec.yaml` 引用。
```yaml
dependencies:
wechat_assets_picker: ^latest_version
```
最新的 **稳定** 版本是:
[![pub package](https://img.shields.io/pub/v/wechat_assets_picker?logo=dart&label=stable&style=flat-square)](https://pub.flutter-io.cn/packages/wechat_assets_picker)
最新的 **开发** 版本是:
[![pub package](https://img.shields.io/pub/v/wechat_assets_picker?color=9d00ff&include_prereleases&label=dev&logo=dart&style=flat-square)](https://pub.flutter-io.cn/packages/wechat_assets_picker)
在你的代码中导入:
```dart
import 'package:wechat_assets_picker/wechat_assets_picker.dart';
```
### Android
在使用这个 package 时,请确保
`compileSdkVersion` 和 `targetSdkVersion` 升级到 `33`。
否则,在 Android 13 设备上将有可能无法加载任何资源。
#### 权限
| Name | 必需 | 已声明 | 最高 API 版本 | 其他 |
|--------------------------|-----|-----|-----------|-------------|
| `READ_EXTERNAL_STORAGE` | 是 | 是 | 32 | |
| `WRITE_EXTERNAL_STORAGE` | 否 | 否 | 29 | |
| `ACCESS_MEDIA_LOCATION` | 是* | 否 | N/A | 读取 EXIF 时必需 |
| `READ_MEDIA_IMAGES` | 是* | 是 | N/A | 读取图片时必需 |
| `READ_MEDIA_VIDEO` | 是* | 是 | N/A | 读取视频时必需 |
| `READ_MEDIA_AUDIO` | 是* | 是 | N/A | 读取音频时必需 |
如果你的目标 SDK 版本大于 33,且你不需要获取图片、视频或者音频,
你可以考虑只声明需要的权限,具体如下:
```xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.your.app">
<!--请求图片和视频权限-->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<!--如果不需要获取音频,移除或者注释 READ_MEDIA_AUDIO-->
<!--<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />-->
</manifest>
```
### iOS
1. 在 `ios/Podfile` 中指定最低构建版本至 **11.0**。
```ruby
platform :ios, '11.0'
```
如果该行以 `#` (注释) 开头,请把 `#` 删除。
2. 将以下内容添加至 `Info.plist`。
```
<key>NSPhotoLibraryUsageDescription</key>
<string>你的相册权限描述</string>
```
### macOS
1. 在 `macos/Podfile` 中指定最低构建版本至 **10.15**。
```Podfile
platform :osx, '10.15'
```
如果该行以 `#` (注释) 开头,请把 `#` 删除。
2. 使用 **Xcode** 打开 `macos/Runner.xcworkspace`。
接着将 macOS 的最低构建版本提升至 **10.15**。
3. 与 [iOS](#iOS) 一样,添加相同的内容到 `Info.plist` 里。
## 使用方法 📖
### 国际化
当你在选择资源的时候,package 会通过你的 `BuildContext`
读取 `Locale?`,返回对应语言的文字代理实现。
请确保你可以通过 `BuildContext` 获取到 `Locale`,否则将会 **默认展示中文文字**。
内置的语言文字实现有:
* 简体中文 (默认)
* English
* העברית
* Deutsche
* Локализация
* 日本語
* مة العربية
* Délégué
* Tiếng Việt
* Türkçe Yerelleştirme
如果你想使用自定义或固定的文字实现,请通过
`AssetPickerConfig.textDelegate` 传递调用。
### 简单的使用方法
```dart
final List<AssetEntity>? result = await AssetPicker.pickAssets(context);
```
你可以使用 `AssetPickerConfig` 来调整选择时的行为。
```dart
final List<AssetEntity>? result = await AssetPicker.pickAssets(
context,
pickerConfig: const AssetPickerConfig(),
);
```
`AssetPickerConfig` 的成员说明:
| 参数名 | 类型 | 描述 | 默认值 |
|-----------------------------------|--------------------------------------------------|------------------------------------------------------|-----------------------------|
| selectedAssets | `List<AssetEntity>?` | 已选的资源。确保不重复选择。 | `null` |
| maxAssets | `int` | 最多选择的图片数量 | 9 |
| pageSize | `int` | 分页加载时每页加载的资源数量。**必须为网格数的倍数。 | 80 |
| gridThumbnailSize | `ThumbnailSize` | 预览网格的缩略图大小 | `ThumbnailSize.square(200)` |
| pathThumbnailSize | `ThumbnailSize` | 路径选择器的缩略图大小 | `ThumbnailSize.square(80)` |
| previewThumbnailSize | `ThumbnailSize?` | 预览时图片的缩略图大小 | `null` |
| requestType | `RequestType` | 选择器选择资源的类型 | `RequestType.common` |
| specialPickerType | `SpecialPickerType?` | 提供一些特殊的选择器类型以整合非常规的选择行为 | `null` |
| keepScrollOffset | `bool` | 选择器是否可以从同样的位置开始选择 | `null` |
| sortPathDelegate | `SortPathDelegate<AssetPathEntity>?` | 资源路径的排序实现,可自定义路径排序方法 | `CommonSortPathDelegate` |
| sortPathsByModifiedDate | `bool` | 是否结合 `FilterOptionGroup.containsPathModified` 进行路径排序 | `false` |
| filterOptions | `PMFilter?` | 允许用户自定义资源过滤条件 | `null` |
| gridCount | `int` | 选择器网格数量 | 4 |
| themeColor | `Color?` | 选择器的主题色 | `Color(0xff00bc56)` |
| pickerTheme | `ThemeData?` | 选择器的主题提供,包括查看器 | `null` |
| textDelegate | `AssetPickerTextDelegate?` | 选择器的文本代理构建,用于自定义文本 | `AssetPickerTextDelegate()` |
| specialItemPosition | `SpecialItemPosition` | 允许用户在选择器中添加一个自定义item,并指定位置。 | `SpecialPosition.none` |
| specialItemBuilder | `SpecialItemBuilder?` | 自定义item的构造方法 | `null` |
| loadingIndicatorBuilder | `IndicatorBuilder?` | 加载器的实现 | `null` |
| selectPredicate | `AssetSelectPredicate` | 判断资源可否被选择 | `null` |
| shouldRevertGrid | `bool?` | 判断资源网格是否需要倒序排列 | `null` |
| limitedPermissionOverlayPredicate | `LimitedPermissionOverlayPredicate?` | 判断有限的权限情况下是否展示提示页面 | `null` |
| pathNameBuilder | `PathNameBuilder<AssetPathEntity>?` | 基于路径(相册)构建自定义名称的方法 | `null` |
| assetsChangeCallback | `AssetsChangeCallback<AssetPathEntity>?` | 当系统通知资源变化时将调用的回调 | `null` |
| assetsChangeRefreshPredicate | `AssetsChangeRefreshPredicate<AssetPathEntity>?` | 判断资源变化是否根据 call 和当前选中的路径进行更新 | `null` |
| shouldAutoplayPreview | `bool` | 预览是否应自动播放 | `false` |
| dragToSelect | `bool` | 是否开启拖拽选择 | `true` |
- 当 `maxAssets` 等于 `1`(即单选模式),搭配
`SpecialPickerType.noPreview` 使用会在用户点选资源换时立刻选中并返回。
- `limitedPermissionOverlayPredicate` 不是持久化的,
如果你需要在应用下次启动时不再显示权限受限的页面,请自主实现持久化的控制。
### 更详细的使用方法
我们已将常用的调用方法封装在 [example](example) 中。
你可以在 `example/lib/pages/multi_assets_page.dart` 和
`example/lib/pages/single_assets_page.dart`
找到 `List<PickMethod> pickMethods`,
它分别定义了多选和单选可用的选择模式。
在选择资源后,资源会暂存并展示在页面下方。
#### 展示选中的资源
`AssetEntityImage` 和 `AssetEntityImageProvider`
可以为 **图片 & 视频** 展示缩略图,以及展示 **图片的原图**。
它的使用方法与常见的 `Image` 和 `ImageProvider` 一致。
```dart
AssetEntityImage(asset, isOriginal: false);
```
或:
```dart
/// AssetEntityImageProvider
Image(image: AssetEntityImageProvider(asset, isOriginal: false));
```
#### 注册资源变化回调
```dart
/// 注册回调
AssetPicker.registerObserve();
/// 取消注册回调
AssetPicker.unregisterObserve();
```
#### 在表单数据中上传 `AssetEntity`
`AssetEntity` 包含有多种 I/O 相关的方法可以用于上传。
**请注意,I/O 相关的方法会消耗性能(通常是时间和内存),它们不应该被频繁调用。**
##### 使用 `http`
`http` package: https://pub.flutter-io.cn/packages/http
`http` package 使用v
[`MultipartFile`](https://pub.flutter-io.cn/documentation/http/latest/http/MultipartFile-class.html)
来在请求中处理文件。
示例代码如下:
```dart
import 'package:http/http.dart' as http;
Future<void> upload() async {
final entity = await obtainYourEntity();
final uri = Uri.https('example.com', 'create');
final request = http.MultipartRequest('POST', uri)
..fields['test_field'] = 'test_value'
..files.add(await multipartFileFromAssetEntity(entity));
final response = await request.send();
if (response.statusCode == 200) {
print('Uploaded!');
}
}
Future<http.MultipartFile> multipartFileFromAssetEntity(AssetEntity entity) async {
http.MultipartFile mf;
// Using the file path.
final file = await entity.file;
if (file == null) {
throw StateError('Unable to obtain file of the entity ${entity.id}.');
}
mf = await http.MultipartFile.fromPath('test_file', file.path);
// Using the bytes.
final bytes = await entity.originBytes;
if (bytes == null) {
throw StateError('Unable to obtain bytes of the entity ${entity.id}.');
}
mf = http.MultipartFile.fromBytes('test_file', bytes);
return mf;
}
```
##### 使用 `dio`
`dio` package: https://pub.flutter-io.cn/packages/dio
`dio` package 同样使用了
[`MultipartFile`](https://pub.flutter-io.cn/documentation/dio/latest/dio/MultipartFile-class.html)
来在请求中处理文件。
示例代码:
```dart
import 'package:dio/dio.dart' as dio;
Future<void> upload() async {
final entity = await obtainYourEntity();
final uri = Uri.https('example.com', 'create');
final response = dio.Dio().requestUri(
uri,
data: dio.FormData.fromMap({
'test_field': 'test_value',
'test_file': await multipartFileFromAssetEntity(entity),
}),
);
print('Uploaded!');
}
Future<dio.MultipartFile> multipartFileFromAssetEntity(AssetEntity entity) async {
dio.MultipartFile mf;
// Using the file path.
final file = await entity.file;
if (file == null) {
throw StateError('Unable to obtain file of the entity ${entity.id}.');
}
mf = await dio.MultipartFile.fromFile(file.path);
// Using the bytes.
final bytes = await entity.originBytes;
if (bytes == null) {
throw StateError('Unable to obtain bytes of the entity ${entity.id}.');
}
mf = dio.MultipartFile.fromBytes(bytes);
return mf;
}
```
### 自定义选择器
`AssetPickerBuilderDelegate`、`AssetPickerViewerBuilderDelegate`、
`AssetPickerProvider` 及 `AssetPickerViewerProvider` 均已暴露且可重载。
使用者可以使用自定义的泛型类型 `<A: 资源, P: 路径>`,
配合继承与重载,实现对应抽象类和类中的方法。
更多用法请查看示例中的 `Custom` 页面,
该页面包含一个以 `<File, Directory>` 为类型基础的选择器。
想要了解如何完全自定义主题、widget 和布局,前往 [示例](example/lib/customs/pickers)
查看已有的自定义选择器的实现。
如果你觉得你的实现有价值或能帮助到其他人,欢迎以 PR 的形式进行提交。
更多细节请阅读 [贡献自定义实现][]。
## 常见问题 ❔
### 修改默认相册名称(将 `Recent` 改为其他)
在 Android 上 `Recent` 是总相册的默认名称,
总相册是一个实际不存在的概念,它只是原始媒体数据的记录集合。
想要在 Android 上解决这个问题,你可以像这样使用 `pathNameBuilder`:
```dart
AssetPickerConfig(
pathNameBuilder: (AssetPathEntity path) => switch (path) {
final p when p.isAll => '最近',
// 你也可以将类似的逻辑应用在其他常见的相册上。
_ => path.name,
},
)
```
其他相册或者其他平台 (iOS/macOS) 上的相册会根据系统语言和配置支持的语言来进行展示。
`pathNameBuilder` 可以用于任何的相册。
### Execution failed for task ':photo_manager:compileDebugKotlin'
查看 [photo_manager#561][] 了解详细的解决方法。
### 从 `File` 或 `Uint8List` 创建 `AssetEntity` 的方法
如果需要使用此库结合一些拍照需求,
可通过以下方法将 `File` 或 `Uint8List` 转为 `AssetEntity`。
```dart
final File file = your_file; // 你的 File 对象
final String path = file.path;
final AssetEntity fileEntity = await PhotoManager.editor.saveImageWithPath(
path,
title: basename(path),
); // 存入手机并生成 AssetEntity
final Uint8List data = your_data; // 你的 Uint8List 对象
final AssetEntity imageEntity = await PhotoManager.editor.saveImage(
file.path,
title: '带有后缀的名称.jpg',
); // 存入手机并生成 AssetEntity
```
**注意:如果不想保留文件,请尽量用 `File` 承载中间操作,**
否则在调用 `AssetEntity` 的删除时,系统可能会触发弹窗:
```dart
final List<String> result = await PhotoManager.editor.deleteWithIds(
<String>[entity.id],
);
```
你可以阅读 [photo_manager#from-raw-data][]
和 [photo_manager#delete-entities][]
了解更多细节。
### 控制台提示 'Failed to find GeneratedAppGlideModule'
```
W/Glide (21133): Failed to find GeneratedAppGlideModule.
You should include an annotationProcessor compile dependency on com.github.bumptech.glide:compiler
in you application ana a @GlideModule annotated AppGlideModule implementation
or LibraryGlideModules will be silently ignored.
```
`Glide` 通过注解来保证单例,防止单例或版本之间的冲突,
而因为 `photo_manager` 使用了 `Glide` 提供部分图片功能,
所以使用它的项目必须实现自己的 `AppGlideModule`。
请移步 [Glide Generated API 文档][] 了解如何实现。
## 致谢
> IntelliJ IDEA 的每个方面都旨在最大化开发者生产力。结合智能编码辅助与符合人体工程学的设计,让开发不仅高效,更成为一种享受。
感谢 [JetBrains](https://www.jetbrains.com/?from=fluttercandies)
为开源项目提供免费的
[IntelliJ IDEA](https://www.jetbrains.com/idea/?from=fluttercandies)
等 IDE 的授权。
[<img src="https://github.com/fluttercandies/flutter_wechat_assets_picker/raw/main/.github/jetbrains-variant.png" width="200"/>](https://www.jetbrains.com/?from=fluttercandies)
[photo_manager pub]: https://pub.flutter-io.cn/packages/photo_manager
[extended_image pub]: https://pub.flutter-io.cn/packages/extended_image
[provider pub]: https://pub.flutter-io.cn/packages/provider
[video_player pub]: https://pub.flutter-io.cn/packages/video_player
[wechat_camera_picker pub]: https://pub.flutter-io.cn/packages/wechat_camera_picker
[迁移指南]: https://github.com/fluttercandies/flutter_wechat_assets_picker/blob/main/guides/migration_guide.md
[photo_manager API 文档]: https://pub.flutter-io.cn/documentation/photo_manager/latest/
[Glide Generated API 文档]: https://muyangmin.github.io/glide-docs-cn/doc/generatedapi.html
[贡献自定义实现]: https://github.com/fluttercandies/flutter_wechat_assets_picker/blob/main/example/lib/customs/CONTRIBUTING.md
[photo_manager#561]: https://github.com/fluttercandies/flutter_photo_manager/issues/561
[photo_manager#from-raw-data]: https://github.com/fluttercandies/flutter_photo_manager#from-raw-data
[photo_manager#delete-entities]: https://github.com/fluttercandies/flutter_photo_manager#delete-entities
<!-- Copyright 2019 The FlutterCandies author. All rights reserved.
Use of this source code is governed by an Apache license
that can be found in the LICENSE file. -->
# Flutter WeChat Assets Picker
[![pub package](https://img.shields.io/pub/v/wechat_assets_picker?logo=dart&label=stable&style=flat-square)](https://pub.dev/packages/wechat_assets_picker)
[![pub package](https://img.shields.io/pub/v/wechat_assets_picker?color=9d00ff&include_prereleases&label=dev&logo=dart&style=flat-square)](https://pub.dev/packages/wechat_assets_picker)
[![CodeFactor](https://img.shields.io/codefactor/grade/github/fluttercandies/flutter_wechat_assets_picker?logo=codefactor&logoColor=%23ffffff&style=flat-square)](https://www.codefactor.io/repository/github/fluttercandies/flutter_wechat_assets_picker)
[![Build status](https://img.shields.io/github/actions/workflow/status/fluttercandies/flutter_wechat_assets_picker/runnable.yml?branch=main&label=CI&logo=github&style=flat-square)](https://github.com/fluttercandies/flutter_wechat_assets_picker/actions/workflows/runnable.yml)
[![GitHub license](https://img.shields.io/github/license/fluttercandies/flutter_wechat_assets_picker?style=flat-square)](https://github.com/fluttercandies/flutter_wechat_assets_picker/blob/main/LICENSE)
[![GitHub stars](https://img.shields.io/github/stars/fluttercandies/flutter_wechat_assets_picker?logo=github&style=flat-square)](https://github.com/fluttercandies/flutter_wechat_assets_picker/stargazers)
[![GitHub forks](https://img.shields.io/github/forks/fluttercandies/flutter_wechat_assets_picker?logo=github&style=flat-square)](https://github.com/fluttercandies/flutter_wechat_assets_picker/network)
[![Awesome Flutter](https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg)](https://github.com/Solido/awesome-flutter)
<a href="https://qm.qq.com/q/ZyJbSVjfSU"><img src="https://img.shields.io/badge/dynamic/yaml?url=https%3A%2F%2Fraw.githubusercontent.com%2Ffluttercandies%2F.github%2Frefs%2Fheads%2Fmain%2Fdata.yml&query=%24.qq_group_number&label=QQ%E7%BE%A4&logo=qq&style=flat&color=1DACE8" /></a>
Language: English | [中文](README-ZH.md)
An **image picker (also with videos and audios)**
for Flutter projects based on the WeChat's UI.
Current WeChat version that UI based on: **8.0.51**
UI designs will be updated following the WeChat update in anytime.
To take a photo or a video for assets,
please check the detailed usage in the example,
and head over to [wechat_camera_picker][wechat_camera_picker pub].
The package is a standalone extension that can to be used with combination.
See the [Migration Guide][] to learn how to migrate between breaking changes.
## Versions compatibility
The package only guarantees to be working on **the stable version of Flutter**.
We won't update it in real-time to align with other channels of Flutter.
| | 3.7 | 3.10 | 3.13 | 3.16 | 3.22 |
|--------|:---:|:----:|:----:|:----:|:----:|
| 9.5.0+ | ❌ | ❌ | ❌ | ❌ | ✅ |
| 8.9.0+ | ❌ | ❌ | ❌ | ✅ | ❌ |
| 8.7.0+ | ❌ | ❌ | ✅ | ❌ | ❌ |
| 8.5.0+ | ❌ | ✅ | ❌ | ❌ | ❌ |
| 8.4.0+ | ✅ | ❌ | ❌ | ❌ | ❌ |
If you got a `resolve conflict` error when running `flutter pub get`,
please use `dependency_overrides` to fix it.
## Package credits
The package is built from these wonderful packages.
| Name | Features |
|:-------------------------------------|:-----------------------------------------------------|
| [photo_manager][photo_manager pub] | The basic abstractions and management for assets. |
| [extended_image][extended_image pub] | Preview assets with expected behaviors. |
| [provider][provider pub] | Helps to manage the interaction state of the picker. |
| [video_player][video_player pub] | Plays videos and audios correspondingly. |
Their implementation should be relatively stable in the package.
If you've found any issues related to them when using the picker,
submit issues to our issue tracker first.
<details>
<summary>Table of content</summary>
<!-- TOC -->
* [Flutter WeChat Assets Picker](#flutter-wechat-assets-picker)
* [Versions compatibility](#versions-compatibility)
* [Package credits](#package-credits)
* [Features ✨](#features-)
* [Notes 📝](#notes-)
* [Projects using this plugin 🖼️](#projects-using-this-plugin-)
* [Screenshots 📸](#screenshots-)
* [READ THIS FIRST ‼️](#read-this-first-)
* [Preparing for use 🍭](#preparing-for-use-)
* [Flutter](#flutter)
* [Android](#android)
* [Permissions](#permissions)
* [iOS](#ios)
* [macOS](#macos)
* [Usage 📖](#usage-)
* [Localizations](#localizations)
* [Simple usage](#simple-usage)
* [Detailed usage](#detailed-usage)
* [Display selected assets](#display-selected-assets)
* [Register assets change observe callback](#register-assets-change-observe-callback)
* [Upload an `AssetEntity` with a form data](#upload-an-assetentity-with-a-form-data)
* [With `http`](#with-http)
* [With `dio`](#with-dio)
* [Custom pickers](#custom-pickers)
* [Frequently asked question ❔](#frequently-asked-question-)
* [Changing the default album name (`Recent` to others)](#changing-the-default-album-name-recent-to-others)
* [Execution failed for task ':photo_manager:compileDebugKotlin'](#execution-failed-for-task-photo_managercompiledebugkotlin)
* [Create `AssetEntity` from `File` or `Uint8List` (rawData)](#create-assetentity-from-file-or-uint8list-rawdata)
* [Glide warning 'Failed to find GeneratedAppGlideModule'](#glide-warning-failed-to-find-generatedappglidemodule)
* [Contributors ✨](#contributors-)
* [Credits](#credits)
<!-- TOC -->
</details>
## Features ✨
- ♿ Complete a11y support with _TalkBack_ and _VoiceOver_
- ♻️ Fully customizable with delegates override
- 🎏 Fully customizable theme based on `ThemeData`
- 💚 Completely WeChat style (even more)
- ⚡️ Adjustable performance with different configurations
- 📷 Image support
- 🔬 HEIF Image type support <a href="#notes-"><sup>(1)</sup></a>
- 🎥 Video support
- 🎶 Audio support <a href="#notes-"><sup>(2)</sup></a>
- 1️⃣ Single picking mode
- 💱 Internationalization (i18n) support
- ⏪ RTL language support
- ➕ Special item builder support
- 🗂 Custom sort path delegate support
- 📝 Custom text delegate support
- ⏳ Custom filter options support
- 💻 macOS support
### Notes 📝
1. HEIF (HEIC) images are support to obtain and conversion,
but the display with them are based on Flutter's image decoder.
See [flutter/flutter#20522](https://github.com/flutter/flutter/issues/20522).
Use `entity.file` or `AssetEntityImage` for them when displays.
2. Due to limitations on iOS and macOS, audio can only be fetched within the sandbox.
## Projects using this plugin 🖼️
| name | pub | github |
|:--------------------|:-----------------------------------------------------------------------------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------|
| insta_assets_picker | [![pub package](https://img.shields.io/pub/v/insta_assets_picker)](https://pub.dev/packages/insta_assets_picker) | [![star](https://img.shields.io/github/stars/LeGoffMael/insta_assets_picker?style=social)](https://github.com/LeGoffMael/insta_assets_picker) |
## Screenshots 📸
| ![1](screenshots/README_1.webp) | ![2](screenshots/README_2.webp) | ![3](screenshots/README_3.webp) |
|-----------------------------------|-----------------------------------|-----------------------------------|
| ![4](screenshots/README_4.webp) | ![5](screenshots/README_5.webp) | ![6](screenshots/README_6.webp) |
| ![7](screenshots/README_7.webp) | ![8](screenshots/README_8.webp) | ![9](screenshots/README_9.webp) |
| ![10](screenshots/README_10.webp) | ![11](screenshots/README_11.webp) | ![12](screenshots/README_12.webp) |
## READ THIS FIRST ‼️
Be aware of below notices before you started anything:
- Due to understanding differences and the limitation of a single document,
documents will not cover all the contents.
If you find nothing related to your expected features and cannot understand about concepts,
run the example project and check every options first.
It has covered 90% of regular requests with the package.
- The package deeply integrates with the [photo_manager][photo_manager pub] plugin,
make sure you understand these two concepts as much as possible:
- Asset (photos/videos/audio) - [`AssetEntity`](https://pub.dev/documentation/photo_manager/latest/photo_manager/AssetEntity-class.html)
- Assets collection (albums/libraries) - [`AssetPathEntity`](https://pub.dev/documentation/photo_manager/latest/photo_manager/AssetPathEntity-class.html)
When you have questions about related APIs and behaviors,
check [photo_manager's API docs][] for more details.
Most usages are detailed covered by the [example](example).
Please walk through the [example](example) carefully
before you have any questions.
## Preparing for use 🍭
### Flutter
Run `flutter pub add wechat_assets_picker`,
or add `wechat_assets_picker` to `pubspec.yaml` dependencies manually.
```yaml
dependencies:
wechat_assets_picker: ^latest_version
```
The latest **stable** version is:
[![pub package](https://img.shields.io/pub/v/wechat_assets_picker?logo=dart&label=stable&style=flat-square)](https://pub.dev/packages/wechat_assets_picker)
The latest **dev** version is:
[![pub package](https://img.shields.io/pub/v/wechat_assets_picker?color=9d00ff&include_prereleases&label=dev&logo=dart&style=flat-square)](https://pub.dev/packages/wechat_assets_picker)
Then import the package in your code:
```dart
import 'package:wechat_assets_picker/wechat_assets_picker.dart';
```
### Android
When using the package, please upgrade
`targetSdkVersion` and `compileSdkVersion` to `33`.
Otherwise, no assets can be fetched on Android 13.
#### Permissions
| Name | Required | Declared | Max API Level | Others |
|--------------------------|----------|----------|---------------|------------------------------|
| `READ_EXTERNAL_STORAGE` | YES | YES | 32 | |
| `WRITE_EXTERNAL_STORAGE` | NO | NO | 29 | |
| `ACCESS_MEDIA_LOCATION` | YES* | NO | N/A | Required when reading EXIF |
| `READ_MEDIA_IMAGES` | YES* | YES | N/A | Required when reading images |
| `READ_MEDIA_VIDEO` | YES* | YES | N/A | Required when reading videos |
| `READ_MEDIA_AUDIO` | YES* | YES | N/A | Required when reading audios |
If you're targeting Android SDK 33+,
and you don't need to load photos, videos or audios,
consider declare only relevant permission in your apps, more specifically:
```xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.your.app">
<!--Requesting access to images and videos.-->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<!--When your app has no need to access audio, remove it or comment it out.-->
<!--<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />-->
</manifest>
```
### iOS
1. Platform version has to be at least *11.0*.
Modify `ios/Podfile` and update accordingly.
```Podfile
platform :ios, '11.0'
```
Remove the `#` heading if the line starts with it.
2. Add the following content to `Info.plist`.
```
<key>NSPhotoLibraryUsageDescription</key>
<string>Replace with your permission description.</string>
```
### macOS
1. Platform version has to be at least *10.15*.
Modify `macos/Podfile` and update accordingly.
```ruby
platform :osx, '10.15'
```
Remove the `#` heading if the line starts with it.
2. Set the minimum deployment target of the macOS to *10.15*.
Use XCode to open `macos/Runner.xcworkspace` .
3. Follow the [iOS](#iOS) instructions and modify `Info.plist` accordingly.
## Usage 📖
### Localizations
When you're picking assets, the package will obtain the `Locale?`
from your `BuildContext`, and return the corresponding text delegate
of the current language.
Make sure you have a valid `Locale` in your widget tree that can be accessed
from the `BuildContext`. **Otherwise, the default Chinese delegate will be used.**
Embedded text delegates languages are:
* 简体中文 (default)
* English
* העברית
* Deutsche
* Локализация
* 日本語
* مة العربية
* Délégué
* Tiếng Việt
* Türkçe Yerelleştirme
If you want to use a custom/fixed text delegate, pass it through the
`AssetPickerConfig.textDelegate`.
### Simple usage
```dart
final List<AssetEntity>? result = await AssetPicker.pickAssets(context);
```
Use `AssetPickerConfig` for more picking behaviors.
```dart
final List<AssetEntity>? result = await AssetPicker.pickAssets(
context,
pickerConfig: const AssetPickerConfig(),
);
```
Fields in `AssetPickerConfig`:
| Name | Type | Description | Default |
|-----------------------------------|--------------------------------------------------|------------------------------------------------------------------------------------------------|-----------------------------|
| selectedAssets | `List<AssetEntity>?` | Selected assets. Prevent duplicate selection. | `null` |
| maxAssets | `int` | Maximum asset that the picker can pick. | 9 |
| pageSize | `int?` | Number of assets per page. **Must be a multiple of `gridCount`**. | 80 |
| gridThumbnailSize | `ThumbnailSize` | Thumbnail size for the grid's item. | `ThumbnailSize.square(200)` |
| pathThumbnailSize | `ThumbnailSize` | Thumbnail size for the path selector. | `ThumbnailSize.square(80)` |
| previewThumbnailSize | `ThumbnailSize?` | Preview thumbnail size in the viewer. | `null` |
| requestType | `RequestType` | Request type for picker. | `RequestType.common` |
| specialPickerType | `SpecialPickerType?` | Provides the option to integrate a custom picker type. | `null` |
| keepScrollOffset | `bool` | Whether the picker should save the scroll offset between pushes and pops. | `null` |
| sortPathDelegate | `SortPathDelegate<AssetPathEntity>?` | Path entities sort delegate for the picker, sort paths as you want. | `CommonSortPathDelegate` |
| sortPathsByModifiedDate | `bool` | Whether to allow sort delegates to sort paths with `FilterOptionGroup.containsPathModified`. | `false` |
| filterOptions | `PMFilter?` | Allow users to customize assets filter options. | `null` |
| gridCount | `int` | Grid count in picker. | 4 |
| themeColor | `Color?` | Main theme color for the picker. | `Color(0xff00bc56)` |
| pickerTheme | `ThemeData?` | Theme data provider for the picker and the viewer. | `null` |
| textDelegate | `AssetPickerTextDelegate?` | Text delegate for the picker, for customize the texts. | `AssetPickerTextDelegate()` |
| specialItemPosition | `SpecialItemPosition` | Allow users set a special item in the picker with several positions. | `SpecialItemPosition.none` |
| specialItemBuilder | `SpecialItemBuilder?` | The widget builder for the special item. | `null` |
| loadingIndicatorBuilder | `IndicatorBuilder?` | Indicates the loading status for the builder. | `null` |
| selectPredicate | `AssetSelectPredicate` | Predicate whether an asset can be selected or unselected. | `null` |
| shouldRevertGrid | `bool?` | Whether the assets grid should revert. | `null` |
| limitedPermissionOverlayPredicate | `LimitedPermissionOverlayPredicate?` | Predicate whether the limited permission overlay should be displayed. | `null` |
| pathNameBuilder | `PathNameBuilder<AssetPathEntity>?` | Build customized path (album) name with the given path entity. | `null` |
| assetsChangeCallback | `AssetsChangeCallback<AssetPathEntity>?` | The callback that will be called when the system notifies assets changes. | `null` |
| assetsChangeRefreshPredicate | `AssetsChangeRefreshPredicate<AssetPathEntity>?` | Whether assets changing should call refresh with the given call and the current selected path. | `null` |
| shouldAutoPlayPreview | `bool` | Whether the preview should auto play. | `false` |
| dragToSelect | `bool` | Whether assets selection can be done with drag gestures. | `true` |
- When `maxAssets` equals to `1` (a.k.a. single picking mode),
use `SpecialPickerType.noPreview` will immediately select asset
clicked (pressed) by the user and popped.
- `limitedPermissionOverlayPredicate` lives without persistence,
if you want to ignore the limited preview after restart,
you'll need to integrate with your own saving methods.
### Detailed usage
We've put multiple common usage
with the packages in the [example](example).
You can both found `List<PickMethod> pickMethods` in
[here](example/lib/pages/multi_assets_page.dart)
and [here](example/lib/pages/single_assets_page.dart),
which provide methods in multiple picking and single picking mode.
Assets will be stored temporary and displayed at the below of the page.
#### Display selected assets
The `AssetEntityImage` and `AssetEntityImageProvider`
can display the thumb image of _images & videos_,
and the original data of _image_.
Use it like a common `Image` and `ImageProvider`.
```dart
AssetEntityImage(asset, isOriginal: false);
```
Or:
```dart
Image(image: AssetEntityImageProvider(asset, isOriginal: false));
```
#### Register assets change observe callback
```dart
// Register callback.
AssetPicker.registerObserve();
// Unregister callback.
AssetPicker.unregisterObserve();
```
#### Upload an `AssetEntity` with a form data
There are multiple ways to upload an `AssetEntity` with I/O related methods.
**Be aware, I/O related methods will consume performance
(typically time and memory), they should not be called frequently.**
##### With `http`
`http` package: https://pub.dev/packages/http
The `http` package uses
[`MultipartFile`](https://pub.dev/documentation/http/latest/http/MultipartFile-class.html)
to handle files in requests.
Pseudo code:
```dart
import 'package:http/http.dart' as http;
Future<void> upload() async {
final entity = await obtainYourEntity();
final uri = Uri.https('example.com', 'create');
final request = http.MultipartRequest('POST', uri)
..fields['test_field'] = 'test_value'
..files.add(await multipartFileFromAssetEntity(entity));
final response = await request.send();
if (response.statusCode == 200) {
print('Uploaded!');
}
}
Future<http.MultipartFile> multipartFileFromAssetEntity(AssetEntity entity) async {
http.MultipartFile mf;
// Using the file path.
final file = await entity.file;
if (file == null) {
throw StateError('Unable to obtain file of the entity ${entity.id}.');
}
mf = await http.MultipartFile.fromPath('test_file', file.path);
// Using the bytes.
final bytes = await entity.originBytes;
if (bytes == null) {
throw StateError('Unable to obtain bytes of the entity ${entity.id}.');
}
mf = http.MultipartFile.fromBytes('test_file', bytes);
return mf;
}
```
##### With `dio`
`dio` package: https://pub.dev/packages/dio
The `dio` package also uses
[`MultipartFile`](https://pub.dev/documentation/dio/latest/dio/MultipartFile-class.html)
to handle files in requests.
Pseudo code:
```dart
import 'package:dio/dio.dart' as dio;
Future<void> upload() async {
final entity = await obtainYourEntity();
final uri = Uri.https('example.com', 'create');
final response = dio.Dio().requestUri(
uri,
data: dio.FormData.fromMap({
'test_field': 'test_value',
'test_file': await multipartFileFromAssetEntity(entity),
}),
);
print('Uploaded!');
}
Future<dio.MultipartFile> multipartFileFromAssetEntity(AssetEntity entity) async {
dio.MultipartFile mf;
// Using the file path.
final file = await entity.file;
if (file == null) {
throw StateError('Unable to obtain file of the entity ${entity.id}.');
}
mf = await dio.MultipartFile.fromFile(file.path);
// Using the bytes.
final bytes = await entity.originBytes;
if (bytes == null) {
throw StateError('Unable to obtain bytes of the entity ${entity.id}.');
}
mf = dio.MultipartFile.fromBytes(bytes);
return mf;
}
```
### Custom pickers
`AssetPickerBuilderDelegate`, `AssetPickerViewerBuilderDelegate`,
`AssetPickerProvider` and `AssetPickerViewerProvider`
are all exposed and overridable.
You can extend them and use your own
type with generic type `<A: Asset, P: Path>`,
then implement abstract methods.
To know about how to fully customize themes, widgets or layouts.
See how to customize delegates in the custom pickers page in the
[example](example/lib/customs/pickers).
You can submit PRs to create your own implementation
if you found your implementation might be useful for others.
See [Contribute custom implementations][] for more details.
## Frequently asked question ❔
### Changing the default album name (`Recent` to others)
`Recent` is the fix album name for the ALL assets on Android
since the all assets' album is not an actual album, it only represents all media data records.
To solve that on Android, use `pathNameBuilder`, for example:
```dart
AssetPickerConfig(
pathNameBuilder: (AssetPathEntity path) => switch (path) {
final p when p.isAll => '最近',
// You can apply similar conditions to other common paths.
_ => path.name,
},
)
```
Other albums or albums on other platforms (iOS/macOS) will follow
the configured system localization and supported localizations.
`pathNameBuilder` is available for all albums.
### Execution failed for task ':photo_manager:compileDebugKotlin'
See [photo_manager#561][] for more details.
### Create `AssetEntity` from `File` or `Uint8List` (rawData)
In order to combine this package with camera shooting or something related,
there's a solution about how to create an `AssetEntity`
with `File` or `Uint8List` object.
```dart
final File file = your_file; // Your `File` object
final String path = file.path;
final AssetEntity fileEntity = await PhotoManager.editor.saveImageWithPath(
path,
title: basename(path),
); // Saved in the device then create an AssetEntity
final Uint8List data = your_data; // Your `Uint8List` object
final AssetEntity imageEntity = await PhotoManager.editor.saveImage(
file.path,
title: 'title_with_extension.jpg',
); // Saved in the device then create an AssetEntity
```
**Notice: If you don't want to keep the file in your device,
use `File` for operations as much as possible.**
Deleting an `AssetEntity` might cause system popups show:
```dart
final List<String> result = await PhotoManager.editor.deleteWithIds(
<String>[entity.id],
);
```
See [photo_manager#from-raw-data][]
and [photo_manager#delete-entities][]
for more details.
### Glide warning 'Failed to find GeneratedAppGlideModule'
```
W/Glide (21133): Failed to find GeneratedAppGlideModule.
You should include an annotationProcessor compile dependency on com.github.bumptech.glide:compiler
in you application ana a @GlideModule annotated AppGlideModule implementation
or LibraryGlideModules will be silently ignored.
```
`Glide` needs annotation to keep singleton,
prevent conflict between instances and versions,
so while the photo manager uses `Glide` to implement image features,
the project which import this should define its own `AppGlideModule`.
See [Glide Generated API docs][] for implementation.
## Contributors ✨
Many thanks to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<table>
<tbody>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://blog.alexv525.com"><img src="https://avatars1.githubusercontent.com/u/15884415?v=4?s=50" width="50px;" alt="Alex Li"/><br /><sub><b>Alex Li</b></sub></a><br /><a href="https://github.com/fluttercandies/flutter_wechat_assets_picker/commits?author=AlexV525" title="Code">💻</a> <a href="#design-AlexV525" title="Design">🎨</a> <a href="https://github.com/fluttercandies/flutter_wechat_assets_picker/commits?author=AlexV525" title="Documentation">📖</a> <a href="#example-AlexV525" title="Examples">💡</a> <a href="#ideas-AlexV525" title="Ideas, Planning, & Feedback">🤔</a> <a href="#maintenance-AlexV525" title="Maintenance">🚧</a> <a href="#question-AlexV525" title="Answering Questions">💬</a> <a href="https://github.com/fluttercandies/flutter_wechat_assets_picker/pulls?q=is%3Apr+reviewed-by%3AAlexV525" title="Reviewed Pull Requests">👀</a> <a href="#a11y-AlexV525" title="Accessibility">️️️️♿️</a> <a href="#translation-AlexV525" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.kikt.top"><img src="https://avatars0.githubusercontent.com/u/14145407?v=4?s=50" width="50px;" alt="Caijinglong"/><br /><sub><b>Caijinglong</b></sub></a><br /><a href="#example-CaiJingLong" title="Examples">💡</a> <a href="#ideas-CaiJingLong" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/SchnMar"><img src="https://avatars3.githubusercontent.com/u/12902321?v=4?s=50" width="50px;" alt="Marcel Schneider"/><br /><sub><b>Marcel Schneider</b></sub></a><br /><a href="https://github.com/fluttercandies/flutter_wechat_assets_picker/issues?q=author%3ASchnMar" title="Bug reports">🐛</a> <a href="https://github.com/fluttercandies/flutter_wechat_assets_picker/commits?author=SchnMar" title="Code">💻</a> <a href="#ideas-SchnMar" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ganlanshu0211"><img src="https://avatars0.githubusercontent.com/u/9670379?v=4?s=50" width="50px;" alt="ganlanshu0211"/><br /><sub><b>ganlanshu0211</b></sub></a><br /><a href="https://github.com/fluttercandies/flutter_wechat_assets_picker/issues?q=author%3Aganlanshu0211" title="Bug reports">🐛</a> <a href="#ideas-ganlanshu0211" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/JasonHezz"><img src="https://avatars3.githubusercontent.com/u/15358765?v=4?s=50" width="50px;" alt="JasonHezz"/><br /><sub><b>JasonHezz</b></sub></a><br /><a href="https://github.com/fluttercandies/flutter_wechat_assets_picker/issues?q=author%3AJasonHezz" title="Bug reports">🐛</a> <a href="https://github.com/fluttercandies/flutter_wechat_assets_picker/commits?author=JasonHezz" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/yanivshaked"><img src="https://avatars.githubusercontent.com/u/13107481?v=4?s=50" width="50px;" alt="Yaniv Shaked"/><br /><sub><b>Yaniv Shaked</b></sub></a><br /><a href="#translation-yanivshaked" title="Translation">🌍</a> <a href="https://github.com/fluttercandies/flutter_wechat_assets_picker/commits?author=yanivshaked" title="Code">💻</a> <a href="https://github.com/fluttercandies/flutter_wechat_assets_picker/issues?q=author%3Ayanivshaked" title="Bug reports">🐛</a> <a href="#maintenance-yanivshaked" title="Maintenance">🚧</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/avi-yadav"><img src="https://avatars.githubusercontent.com/u/7314430?v=4?s=50" width="50px;" alt="avi-yadav"/><br /><sub><b>avi-yadav</b></sub></a><br /><a href="https://github.com/fluttercandies/flutter_wechat_assets_picker/commits?author=avi-yadav" title="Code">💻</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Letalus"><img src="https://avatars.githubusercontent.com/u/41230136?v=4?s=50" width="50px;" alt="Letalus"/><br /><sub><b>Letalus</b></sub></a><br /><a href="https://github.com/fluttercandies/flutter_wechat_assets_picker/issues?q=author%3ALetalus" title="Bug reports">🐛</a> <a href="#translation-Letalus" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/greymag"><img src="https://avatars.githubusercontent.com/u/1502131?v=4?s=50" width="50px;" alt="greymag"/><br /><sub><b>greymag</b></sub></a><br /><a href="#translation-greymag" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/NaikSoftware"><img src="https://avatars.githubusercontent.com/u/4218994?v=4?s=50" width="50px;" alt="Nickolay Savchenko"/><br /><sub><b>Nickolay Savchenko</b></sub></a><br /><a href="#design-NaikSoftware" title="Design">🎨</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/KosukeSaigusa"><img src="https://avatars.githubusercontent.com/u/13669049?v=4?s=50" width="50px;" alt="Kosuke Saigusa"/><br /><sub><b>Kosuke Saigusa</b></sub></a><br /><a href="#translation-KosukeSaigusa" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Jon-Millent"><img src="https://avatars.githubusercontent.com/u/17584565?v=4?s=50" width="50px;" alt="三闻书店"/><br /><sub><b>三闻书店</b></sub></a><br /><a href="https://github.com/fluttercandies/flutter_wechat_assets_picker/commits?author=Jon-Millent" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/didiosn"><img src="https://avatars.githubusercontent.com/u/15895051?v=4?s=50" width="50px;" alt="DidiosFaust"/><br /><sub><b>DidiosFaust</b></sub></a><br /><a href="#translation-didiosn" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ConanXie"><img src="https://avatars.githubusercontent.com/u/10040846?v=4?s=50" width="50px;" alt="xiejie"/><br /><sub><b>xiejie</b></sub></a><br /><a href="https://github.com/fluttercandies/flutter_wechat_assets_picker/issues?q=author%3AConanXie" title="Bug reports">🐛</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/maxzod"><img src="https://avatars.githubusercontent.com/u/47630729?v=4?s=50" width="50px;" alt="Ahmed Masoud "/><br /><sub><b>Ahmed Masoud </b></sub></a><br /><a href="#translation-maxzod" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/luomo-pro"><img src="https://avatars.githubusercontent.com/u/41097395?v=4?s=50" width="50px;" alt="luomo-pro"/><br /><sub><b>luomo-pro</b></sub></a><br /><a href="#a11y-luomo-pro" title="Accessibility">️️️️♿️</a> <a href="https://github.com/fluttercandies/flutter_wechat_assets_picker/issues?q=author%3Aluomo-pro" title="Bug reports">🐛</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/paigupai"><img src="https://avatars.githubusercontent.com/u/44311361?v=4?s=50" width="50px;" alt="paigupai"/><br /><sub><b>paigupai</b></sub></a><br /><a href="#translation-paigupai" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/taqiabdulaziz"><img src="https://avatars.githubusercontent.com/u/30410316?v=4?s=50" width="50px;" alt="Muhammad Taqi Abdul Aziz"/><br /><sub><b>Muhammad Taqi Abdul Aziz</b></sub></a><br /><a href="https://github.com/fluttercandies/flutter_wechat_assets_picker/commits?author=taqiabdulaziz" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/hellohejinyu"><img src="https://avatars.githubusercontent.com/u/8766034?v=4?s=50" width="50px;" alt="何锦余"/><br /><sub><b>何锦余</b></sub></a><br /><a href="https://github.com/fluttercandies/flutter_wechat_assets_picker/issues?q=author%3Ahellohejinyu" title="Bug reports">🐛</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/leonpesdk"><img src="https://avatars.githubusercontent.com/u/57394644?v=4?s=50" width="50px;" alt="Leon Dudlik"/><br /><sub><b>Leon Dudlik</b></sub></a><br /><a href="https://github.com/fluttercandies/flutter_wechat_assets_picker/issues?q=author%3Aleonpesdk" title="Bug reports">🐛</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.legoffmael.fr"><img src="https://avatars.githubusercontent.com/u/22376981?v=4?s=50" width="50px;" alt="Maël"/><br /><sub><b>Maël</b></sub></a><br /><a href="https://github.com/fluttercandies/flutter_wechat_assets_picker/commits?author=LeGoffMael" title="Code">💻</a> <a href="#maintenance-LeGoffMael" title="Maintenance">🚧</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/dddrop"><img src="https://avatars.githubusercontent.com/u/5361175?v=4?s=50" width="50px;" alt="dddrop"/><br /><sub><b>dddrop</b></sub></a><br /><a href="https://github.com/fluttercandies/flutter_wechat_assets_picker/commits?author=dddrop" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.linkedin.com/in/loinp"><img src="https://avatars.githubusercontent.com/u/34020090?v=4?s=50" width="50px;" alt="Nguyen Phuc Loi"/><br /><sub><b>Nguyen Phuc Loi</b></sub></a><br /><a href="#translation-nploi" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://sqlturk.wordpress.com/"><img src="https://avatars.githubusercontent.com/u/12383547?v=4?s=50" width="50px;" alt="Cevheri"/><br /><sub><b>Cevheri</b></sub></a><br /><a href="#translation-cevheri" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://velog.io/@hee_mm_"><img src="https://avatars.githubusercontent.com/u/48482259?v=4?s=50" width="50px;" alt="mirimhee"/><br /><sub><b>mirimhee</b></sub></a><br /><a href="#translation-LIMMIHEE" title="Translation">🌍</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://amoshk.top"><img src="https://avatars.githubusercontent.com/u/32262985?v=4?s=50" width="50px;" alt="Amos"/><br /><sub><b>Amos</b></sub></a><br /><a href="https://github.com/fluttercandies/flutter_wechat_assets_picker/issues?q=author%3AAmosHuKe" title="Bug reports">🐛</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Dimilkalathiya"><img src="https://avatars.githubusercontent.com/u/102401667?v=4?s=50" width="50px;" alt="Dimil Kalathiya"/><br /><sub><b>Dimil Kalathiya</b></sub></a><br /><a href="https://github.com/fluttercandies/flutter_wechat_assets_picker/commits?author=Dimilkalathiya" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://about.me/gasol"><img src="https://avatars.githubusercontent.com/u/108053?v=4?s=50" width="50px;" alt="Gasol Wu"/><br /><sub><b>Gasol Wu</b></sub></a><br /><a href="#translation-Gasol" title="Translation">🌍</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/WeiJun0507"><img src="https://avatars.githubusercontent.com/u/66726409?v=4?s=50" width="50px;" alt="Wei Jun"/><br /><sub><b>Wei Jun</b></sub></a><br /><a href="#business-WeiJun0507" title="Business development">💼</a> <a href="https://github.com/fluttercandies/flutter_wechat_assets_picker/commits?author=WeiJun0507" title="Code">💻</a> <a href="#ideas-WeiJun0507" title="Ideas, Planning, & Feedback">🤔</a></td>
</tr>
</tbody>
</table>
<!-- markdownlint-restore -->
<!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the
[all-contributors](https://github.com/all-contributors/all-contributors) specification.
Contributions of any kind welcomed!!
## Credits
> Every aspect of IntelliJ IDEA has been designed to maximize developer productivity.
Together, intelligent coding assistance and ergonomic design make development not only productive but also enjoyable.
Thanks to [JetBrains](https://www.jetbrains.com/?from=fluttercandies)
for allocating free open-source licenses for IDEs
such as [IntelliJ IDEA](https://www.jetbrains.com/idea/?from=fluttercandies).
[<img src="https://github.com/fluttercandies/flutter_wechat_assets_picker/raw/main/.github/jetbrains-variant.png" width="200"/>](https://www.jetbrains.com/?from=fluttercandies)
[photo_manager pub]: https://pub.dev/packages/photo_manager
[extended_image pub]: https://pub.dev/packages/extended_image
[provider pub]: https://pub.dev/packages/provider
[video_player pub]: https://pub.dev/packages/video_player
[wechat_camera_picker pub]: https://pub.dev/packages/wechat_camera_picker
[Migration Guide]: https://github.com/fluttercandies/flutter_wechat_assets_picker/blob/main/guides/migration_guide.md
[photo_manager's API docs]: https://pub.dev/documentation/photo_manager/latest/
[Glide Generated API docs]: https://sjudd.github.io/glide/doc/generatedapi.html
[Contribute custom implementations]: https://github.com/fluttercandies/flutter_wechat_assets_picker/blob/main/example/lib/customs/CONTRIBUTING.md
[photo_manager#561]: https://github.com/fluttercandies/flutter_photo_manager/issues/561
[photo_manager#from-raw-data]: https://github.com/fluttercandies/flutter_photo_manager#from-raw-data
[photo_manager#delete-entities]: https://github.com/fluttercandies/flutter_photo_manager#delete-entities
include: package:flutter_lints/flutter.yaml
analyzer:
errors:
deprecated_member_use: ignore
deprecated_member_use_from_same_package: ignore
linter:
rules:
always_declare_return_types: true
always_put_control_body_on_new_line: true
avoid_print: true
avoid_renaming_method_parameters: true
avoid_unnecessary_containers: true
avoid_void_async: true
curly_braces_in_flow_control_structures: true
directives_ordering: true
flutter_style_todos: true
library_private_types_in_public_api: false
overridden_fields: false
prefer_const_constructors: true
prefer_const_constructors_in_immutables: false
prefer_final_fields: true
prefer_final_in_for_each: true
prefer_final_locals: true
prefer_single_quotes: true
require_trailing_commas: true
sort_child_properties_last: true
sort_constructors_first: true
sort_unnamed_constructors_first: true
unnecessary_await_in_return: true
unnecessary_breaks: true
unnecessary_late: true
unnecessary_parenthesis: true
use_build_context_synchronously: false
void_checks: true
<!-- Copyright 2019 The FlutterCandies author. All rights reserved.
Use of this source code is governed by an Apache license
that can be found in the LICENSE file. -->
# WeChat Assets Picker example
This is the example for the `wechat_assets_picker` package.
We've put multiple common usage
with the packages in the example.
You can both found `List<PickMethod> pickMethods` in
[here](lib/pages/multi_assets_page.dart)
and [here](lib/pages/single_assets_page.dart),
which provide methods in multiple picking and single picking mode.
Assets will be stored temporary and displayed at the below of the page.
include: ../analysis_options.yaml
analyzer:
exclude:
- lib/l10n/gen/*.dart
linter:
rules:
use_build_context_synchronously: false
\ No newline at end of file
plugins {
id "com.android.application"
id "kotlin-android"
id "kotlin-kapt"
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
id "dev.flutter.flutter-gradle-plugin"
}
def flutterVersionCode = null
def flutterVersionName = null
if (flutter.hasProperty('versionCode')) {
flutterVersionCode = flutter.versionCode
}
if (flutter.hasProperty('versionName')) {
flutterVersionName = flutter.versionName
}
if (flutterVersionCode == null || flutterVersionName == null) {
def localProperties = new Properties()
rootProject.file('local.properties').withReader('UTF-8') { reader ->
localProperties.load(reader)
}
if (flutterVersionCode == null) {
flutterVersionCode = localProperties.getProperty('flutter.versionCode')
}
if (flutterVersionName == null) {
flutterVersionName = localProperties.getProperty('flutter.versionName')
}
}
android {
namespace "com.fluttercandies.wechatAssetsPickerExample"
compileSdk flutter.compileSdkVersion
ndkVersion flutter.ndkVersion
defaultConfig {
applicationId = "com.fluttercandies.wechatAssetsPickerExample"
minSdk 21
targetSdk flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
kotlinOptions {
jvmTarget = '17'
}
compileOptions {
// Sets Java compatibility to Java 17
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
signingConfigs {
forAll {
storeFile file("${rootDir.absolutePath}/key.jks")
storePassword 'picker'
keyAlias 'picker'
keyPassword 'picker'
}
}
buildTypes {
debug {
signingConfig signingConfigs.forAll
}
profile {
signingConfig signingConfigs.forAll
}
release {
signingConfig signingConfigs.forAll
}
}
}
flutter {
source '../..'
}
dependencies {
implementation 'com.github.bumptech.glide:glide:4.15.0'
kapt 'com.github.bumptech.glide:compiler:4.15.0'
}
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28"/>
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" />
<application
android:label="Wechat Assets Picker Example"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"
android:allowBackup="false">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
<!-- Required to query activities that can process text, see:
https://developer.android.com/training/package-visibility?hl=en and
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
<queries>
<intent>
<action android:name="android.intent.action.PROCESS_TEXT"/>
<data android:mimeType="text/plain"/>
</intent>
</queries>
</manifest>
package io.flutter.plugins;
import androidx.annotation.Keep;
import androidx.annotation.NonNull;
import io.flutter.Log;
import io.flutter.embedding.engine.FlutterEngine;
/**
* Generated file. Do not edit.
* This file is generated by the Flutter tool based on the
* plugins that support the Android platform.
*/
@Keep
public final class GeneratedPluginRegistrant {
private static final String TAG = "GeneratedPluginRegistrant";
public static void registerWith(@NonNull FlutterEngine flutterEngine) {
try {
flutterEngine.getPlugins().add(new io.flutter.plugins.camera.CameraPlugin());
} catch (Exception e) {
Log.e(TAG, "Error registering plugin camera_android, io.flutter.plugins.camera.CameraPlugin", e);
}
try {
flutterEngine.getPlugins().add(new io.flutter.plugins.flutter_plugin_android_lifecycle.FlutterAndroidLifecyclePlugin());
} catch (Exception e) {
Log.e(TAG, "Error registering plugin flutter_plugin_android_lifecycle, io.flutter.plugins.flutter_plugin_android_lifecycle.FlutterAndroidLifecyclePlugin", e);
}
try {
flutterEngine.getPlugins().add(new dev.fluttercommunity.plus.packageinfo.PackageInfoPlugin());
} catch (Exception e) {
Log.e(TAG, "Error registering plugin package_info_plus, dev.fluttercommunity.plus.packageinfo.PackageInfoPlugin", e);
}
try {
flutterEngine.getPlugins().add(new io.flutter.plugins.pathprovider.PathProviderPlugin());
} catch (Exception e) {
Log.e(TAG, "Error registering plugin path_provider_android, io.flutter.plugins.pathprovider.PathProviderPlugin", e);
}
try {
flutterEngine.getPlugins().add(new com.fluttercandies.photo_manager.PhotoManagerPlugin());
} catch (Exception e) {
Log.e(TAG, "Error registering plugin photo_manager, com.fluttercandies.photo_manager.PhotoManagerPlugin", e);
}
try {
flutterEngine.getPlugins().add(new dev.fluttercommunity.plus.sensors.SensorsPlugin());
} catch (Exception e) {
Log.e(TAG, "Error registering plugin sensors_plus, dev.fluttercommunity.plus.sensors.SensorsPlugin", e);
}
try {
flutterEngine.getPlugins().add(new io.flutter.plugins.videoplayer.VideoPlayerPlugin());
} catch (Exception e) {
Log.e(TAG, "Error registering plugin video_player_android, io.flutter.plugins.videoplayer.VideoPlayerPlugin", e);
}
}
}
package com.fluttercandies.wechatAssetsPickerExample;
import com.bumptech.glide.annotation.GlideModule;
import com.bumptech.glide.module.AppGlideModule;
@GlideModule
public class ExampleAppGlideModule extends AppGlideModule {
}
\ No newline at end of file
package com.fluttercandies.wechatAssetsPickerExample
import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity() {
// The default activity that extends flutter's activity.
}
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="?android:colorBackground" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/white" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>
allprojects {
repositories {
google()
mavenCentral()
}
}
rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(':app')
}
tasks.register("clean", Delete) {
delete rootProject.buildDir
}
org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true
android.enableJetifier=true
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
sdk.dir=/Users/wangdongni/Library/Android/sdk
flutter.sdk=/Users/wangdongni/fvm/versions/custom_3.27.4-ohos-dev
\ No newline at end of file
pluginManagement {
def flutterSdkPath = {
def properties = new Properties()
file("local.properties").withInputStream { properties.load(it) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
return flutterSdkPath
}()
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "8.9.1" apply false
id "com.android.library" version "8.9.1" apply false
id "org.jetbrains.kotlin.android" version "2.1.20" apply false
}
include ":app"
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>App</string>
<key>CFBundleIdentifier</key>
<string>io.flutter.flutter.app</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>App</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>12.0</string>
</dict>
</plist>
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"
// This is a generated file; do not edit or check into version control.
FLUTTER_ROOT=/Users/wangdongni/fvm/versions/custom_3.27.4-ohos-dev
FLUTTER_APPLICATION_PATH=/Users/wangdongni/Desktop/Flutter-ReserveApp/library/wechat_assets_picker-9.8.0/example
COCOAPODS_PARALLEL_CODE_SIGN=true
FLUTTER_TARGET=lib/main.dart
FLUTTER_BUILD_DIR=build
FLUTTER_BUILD_NAME=9.8.0
FLUTTER_BUILD_NUMBER=69
EXCLUDED_ARCHS[sdk=iphonesimulator*]=i386
EXCLUDED_ARCHS[sdk=iphoneos*]=armv7
DART_OBFUSCATION=false
TRACK_WIDGET_CREATION=true
TREE_SHAKE_ICONS=false
PACKAGE_CONFIG=.dart_tool/package_config.json
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"
#!/bin/sh
# This is a generated file; do not edit or check into version control.
export "FLUTTER_ROOT=/Users/wangdongni/fvm/versions/custom_3.27.4-ohos-dev"
export "FLUTTER_APPLICATION_PATH=/Users/wangdongni/Desktop/Flutter-ReserveApp/library/wechat_assets_picker-9.8.0/example"
export "COCOAPODS_PARALLEL_CODE_SIGN=true"
export "FLUTTER_TARGET=lib/main.dart"
export "FLUTTER_BUILD_DIR=build"
export "FLUTTER_BUILD_NAME=9.8.0"
export "FLUTTER_BUILD_NUMBER=69"
export "DART_OBFUSCATION=false"
export "TRACK_WIDGET_CREATION=true"
export "TREE_SHAKE_ICONS=false"
export "PACKAGE_CONFIG=.dart_tool/package_config.json"
# Uncomment this line to define a global platform for your project
# platform :ios, '12.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
flutter_ios_podfile_setup
target 'Runner' do
use_frameworks!
use_modular_headers!
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
end
end
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
513FA762C8C9C7254396DC7C /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 65B67F314727E97056D6FB02 /* Pods_Runner.framework */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
65B67F314727E97056D6FB02 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
6B0C8420452B45A5D1F1C0C7 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
6C1693957F33351E0284D719 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
FF5A0AA64F3B1C88D3552E39 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
513FA762C8C9C7254396DC7C /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
075E26373E1C2C8E97B8BDA5 /* Frameworks */ = {
isa = PBXGroup;
children = (
65B67F314727E97056D6FB02 /* Pods_Runner.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
9482DB91E3C799F148919200 /* Pods */ = {
isa = PBXGroup;
children = (
6C1693957F33351E0284D719 /* Pods-Runner.debug.xcconfig */,
FF5A0AA64F3B1C88D3552E39 /* Pods-Runner.release.xcconfig */,
6B0C8420452B45A5D1F1C0C7 /* Pods-Runner.profile.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
};
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
9740EEB31CF90195004384FC /* Generated.xcconfig */,
);
name = Flutter;
sourceTree = "<group>";
};
97C146E51CF9000F007C117D = {
isa = PBXGroup;
children = (
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
9482DB91E3C799F148919200 /* Pods */,
075E26373E1C2C8E97B8BDA5 /* Frameworks */,
);
sourceTree = "<group>";
};
97C146EF1CF9000F007C117D /* Products */ = {
isa = PBXGroup;
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
);
name = Products;
sourceTree = "<group>";
};
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
97C147021CF9000F007C117D /* Info.plist */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
);
path = Runner;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
97C146ED1CF9000F007C117D /* Runner */ = {
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
71ACE8DF3F7938AC5EF5C276 /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
950F336F7D4BCE35DBE7FE4C /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = Runner;
productName = Runner;
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = YES;
LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
LastSwiftMigration = 1100;
};
};
};
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 97C146E51CF9000F007C117D;
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
97C146ED1CF9000F007C117D /* Runner */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
97C146EC1CF9000F007C117D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
);
name = "Thin Binary";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
71ACE8DF3F7938AC5EF5C276 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
950F336F7D4BCE35DBE7FE4C /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
97C146EA1CF9000F007C117D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C146FB1CF9000F007C117D /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C147001CF9000F007C117D /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
249021D3217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Profile;
};
249021D4217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 64MAFNBS88;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.fluttercandies.wechatAssetsPickerDemo;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Profile;
};
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
97C147041CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
97C147061CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 64MAFNBS88;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.fluttercandies.wechatAssetsPickerDemo;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
};
97C147071CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 64MAFNBS88;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.fluttercandies.wechatAssetsPickerDemo;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147031CF9000F007C117D /* Debug */,
97C147041CF9000F007C117D /* Release */,
249021D3217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147061CF9000F007C117D /* Debug */,
97C147071CF9000F007C117D /* Release */,
249021D4217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
}
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1510"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "331C8080294A63A400263BE5"
BuildableName = "RunnerTests.xctest"
BlueprintName = "RunnerTests"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
enableGPUValidationMode = "1"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Profile"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>
import UIKit
import Flutter
@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
{
"images":[
{
"idiom":"iphone",
"size":"20x20",
"scale":"2x",
"filename":"Icon-App-20x20@2x.png"
},
{
"idiom":"iphone",
"size":"20x20",
"scale":"3x",
"filename":"Icon-App-20x20@3x.png"
},
{
"idiom":"iphone",
"size":"29x29",
"scale":"1x",
"filename":"Icon-App-29x29@1x.png"
},
{
"idiom":"iphone",
"size":"29x29",
"scale":"2x",
"filename":"Icon-App-29x29@2x.png"
},
{
"idiom":"iphone",
"size":"29x29",
"scale":"3x",
"filename":"Icon-App-29x29@3x.png"
},
{
"idiom":"iphone",
"size":"40x40",
"scale":"2x",
"filename":"Icon-App-40x40@2x.png"
},
{
"idiom":"iphone",
"size":"40x40",
"scale":"3x",
"filename":"Icon-App-40x40@3x.png"
},
{
"idiom":"iphone",
"size":"60x60",
"scale":"2x",
"filename":"Icon-App-60x60@2x.png"
},
{
"idiom":"iphone",
"size":"60x60",
"scale":"3x",
"filename":"Icon-App-60x60@3x.png"
},
{
"idiom":"iphone",
"size":"76x76",
"scale":"2x",
"filename":"Icon-App-76x76@2x.png"
},
{
"idiom":"ipad",
"size":"20x20",
"scale":"1x",
"filename":"Icon-App-20x20@1x.png"
},
{
"idiom":"ipad",
"size":"20x20",
"scale":"2x",
"filename":"Icon-App-20x20@2x.png"
},
{
"idiom":"ipad",
"size":"29x29",
"scale":"1x",
"filename":"Icon-App-29x29@1x.png"
},
{
"idiom":"ipad",
"size":"29x29",
"scale":"2x",
"filename":"Icon-App-29x29@2x.png"
},
{
"idiom":"ipad",
"size":"40x40",
"scale":"1x",
"filename":"Icon-App-40x40@1x.png"
},
{
"idiom":"ipad",
"size":"40x40",
"scale":"2x",
"filename":"Icon-App-40x40@2x.png"
},
{
"idiom":"ipad",
"size":"76x76",
"scale":"1x",
"filename":"Icon-App-76x76@1x.png"
},
{
"idiom":"ipad",
"size":"76x76",
"scale":"2x",
"filename":"Icon-App-76x76@2x.png"
},
{
"idiom":"ipad",
"size":"83.5x83.5",
"scale":"2x",
"filename":"Icon-App-83.5x83.5@2x.png"
},
{
"size" : "1024x1024",
"idiom" : "ios-marketing",
"scale" : "1x",
"filename" : "ItunesArtwork@2x.png"
}
],
"info":{
"version":1,
"author":"makeappicon"
}
}
{
"images" : [
{
"idiom" : "universal",
"filename" : "LaunchImage.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
# Launch Screen Assets
You can customize the launch screen with your own desired assets by replacing the image files in this directory.
You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
<viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
</imageView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<image name="LaunchImage" width="168" height="185"/>
</resources>
</document>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
</dependencies>
<scenes>
<!--Flutter View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>
//
// Generated file. Do not edit.
//
// clang-format off
#ifndef GeneratedPluginRegistrant_h
#define GeneratedPluginRegistrant_h
#import <Flutter/Flutter.h>
NS_ASSUME_NONNULL_BEGIN
@interface GeneratedPluginRegistrant : NSObject
+ (void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry;
@end
NS_ASSUME_NONNULL_END
#endif /* GeneratedPluginRegistrant_h */
//
// Generated file. Do not edit.
//
// clang-format off
#import "GeneratedPluginRegistrant.h"
#if __has_include(<camera_avfoundation/CameraPlugin.h>)
#import <camera_avfoundation/CameraPlugin.h>
#else
@import camera_avfoundation;
#endif
#if __has_include(<package_info_plus/FPPPackageInfoPlusPlugin.h>)
#import <package_info_plus/FPPPackageInfoPlusPlugin.h>
#else
@import package_info_plus;
#endif
#if __has_include(<path_provider_foundation/PathProviderPlugin.h>)
#import <path_provider_foundation/PathProviderPlugin.h>
#else
@import path_provider_foundation;
#endif
#if __has_include(<photo_manager/PhotoManagerPlugin.h>)
#import <photo_manager/PhotoManagerPlugin.h>
#else
@import photo_manager;
#endif
#if __has_include(<sensors_plus/FPPSensorsPlusPlugin.h>)
#import <sensors_plus/FPPSensorsPlusPlugin.h>
#else
@import sensors_plus;
#endif
#if __has_include(<video_player_avfoundation/FVPVideoPlayerPlugin.h>)
#import <video_player_avfoundation/FVPVideoPlayerPlugin.h>
#else
@import video_player_avfoundation;
#endif
@implementation GeneratedPluginRegistrant
+ (void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry {
[CameraPlugin registerWithRegistrar:[registry registrarForPlugin:@"CameraPlugin"]];
[FPPPackageInfoPlusPlugin registerWithRegistrar:[registry registrarForPlugin:@"FPPPackageInfoPlusPlugin"]];
[PathProviderPlugin registerWithRegistrar:[registry registrarForPlugin:@"PathProviderPlugin"]];
[PhotoManagerPlugin registerWithRegistrar:[registry registrarForPlugin:@"PhotoManagerPlugin"]];
[FPPSensorsPlusPlugin registerWithRegistrar:[registry registrarForPlugin:@"FPPSensorsPlusPlugin"]];
[FVPVideoPlayerPlugin registerWithRegistrar:[registry registrarForPlugin:@"FVPVideoPlayerPlugin"]];
}
@end
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Wechat Assets Picker Example</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>flutter_wechat_assets_picker_example</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSCameraUsageDescription</key>
<string>Take a photo for display</string>
<key>NSMicrophoneUsageDescription</key>
<string>Take a video for display</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Read your photos for display</string>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>
arb-dir: lib/l10n
format: true
output-class: AppLocalizations
output-dir: lib/l10n/gen
output-localization-file: app_localizations.dart
template-arb-file: app_en.arb
synthetic-package: false
untranslated-messages-file: lib/l10n/gen/app_localizations_untranslated.json
// Copyright 2019 The FlutterCandies author. All rights reserved.
// Use of this source code is governed by an Apache license that can be found
// in the LICENSE file.
import 'package:flutter/widgets.dart';
@immutable
class CustomPickMethod {
const CustomPickMethod({
required this.icon,
required this.name,
required this.description,
required this.method,
});
final String icon;
final String name;
final String description;
final void Function(BuildContext context) method;
}
// Copyright 2019 The FlutterCandies author. All rights reserved.
// Use of this source code is governed by an Apache license that can be found
// in the LICENSE file.
import 'package:flutter/material.dart';
import 'package:wechat_assets_picker_demo/l10n/gen/app_localizations.dart';
extension BuildContextExtension on BuildContext {
AppLocalizations get l10n => AppLocalizations.of(this)!;
}
extension BrightnessExtension on Brightness {
bool get isDark => this == Brightness.dark;
bool get isLight => this == Brightness.light;
Brightness get reverse =>
this == Brightness.light ? Brightness.dark : Brightness.light;
}
extension ColorExtension on Color {
MaterialColor get swatch => Colors.primaries.firstWhere(
(Color c) => c.value == value,
orElse: () => _swatch,
);
Map<int, Color> get getMaterialColorValues => <int, Color>{
50: _swatchShade(50),
100: _swatchShade(100),
200: _swatchShade(200),
300: _swatchShade(300),
400: _swatchShade(400),
500: _swatchShade(500),
600: _swatchShade(600),
700: _swatchShade(700),
800: _swatchShade(800),
900: _swatchShade(900),
};
MaterialColor get _swatch => MaterialColor(value, getMaterialColorValues);
Color _swatchShade(int swatchValue) => HSLColor.fromColor(this)
.withLightness(1 - (swatchValue / 1000))
.toColor();
}