diff --git a/example/lib/pages/camera/camera_picker.dart b/example/lib/pages/camera/camera_picker.dart index cbc7613..a20a6ea 100644 --- a/example/lib/pages/camera/camera_picker.dart +++ b/example/lib/pages/camera/camera_picker.dart @@ -79,50 +79,53 @@ class _CameraPickerState extends State { context, onPressed: () => InstaAssetPicker.pickAssets( context, - title: widget.description.fullLabel, - maxAssets: 4, - pickerTheme: widget.getPickerTheme(context), - actionsBuilder: ( - BuildContext context, - ThemeData? pickerTheme, - double height, - VoidCallback unselectAll, - ) => - [ - InstaPickerCircleIconButton.unselectAll( - onTap: unselectAll, - theme: pickerTheme, - size: height, - ), - const SizedBox(width: 8), - InstaPickerCircleIconButton( - onTap: () => _pickFromCamera(context), - theme: pickerTheme, - icon: const Icon(Icons.camera_alt), - size: height, - ), - ], - specialItemBuilder: (BuildContext context, _, __) { - // return a button that open the camera - return ElevatedButton( - onPressed: () => _pickFromCamera(context), - style: ElevatedButton.styleFrom( - shape: const RoundedRectangleBorder(), - foregroundColor: Colors.white, - backgroundColor: Colors.transparent, + builderOptions: InstaAssetPickerBuilderOptions( + context, + title: widget.description.fullLabel, + pickerTheme: widget.getPickerTheme(context), + actionsBuilder: ( + BuildContext context, + ThemeData? pickerTheme, + double height, + VoidCallback unselectAll, + ) => + [ + InstaPickerCircleIconButton.unselectAll( + onTap: unselectAll, + theme: pickerTheme, + size: height, ), - child: FittedBox( - fit: BoxFit.cover, - child: Text( - InstaAssetPicker.defaultTextDelegate(context) - .sActionUseCameraHint, - textAlign: TextAlign.center, - ), + const SizedBox(width: 8), + InstaPickerCircleIconButton( + onTap: () => _pickFromCamera(context), + theme: pickerTheme, + icon: const Icon(Icons.camera_alt), + size: height, ), - ); - }, - // since the list is revert, use prepend to be at the top - specialItemPosition: SpecialItemPosition.prepend, + ], + specialItemBuilder: (BuildContext context, _, __) { + // return a button that open the camera + return ElevatedButton( + onPressed: () => _pickFromCamera(context), + style: ElevatedButton.styleFrom( + shape: const RoundedRectangleBorder(), + foregroundColor: Colors.white, + backgroundColor: Colors.transparent, + ), + child: FittedBox( + fit: BoxFit.cover, + child: Text( + InstaAssetPicker.defaultTextDelegate(context) + .sActionUseCameraHint, + textAlign: TextAlign.center, + ), + ), + ); + }, + // since the list is revert, use prepend to be at the top + specialItemPosition: SpecialItemPosition.prepend, + ), + maxAssets: 4, onCompleted: (cropStream) { Navigator.push( context, diff --git a/example/lib/pages/camera/wechat_camera_picker.dart b/example/lib/pages/camera/wechat_camera_picker.dart index cf3e8d4..343c5fd 100644 --- a/example/lib/pages/camera/wechat_camera_picker.dart +++ b/example/lib/pages/camera/wechat_camera_picker.dart @@ -28,62 +28,65 @@ class WeChatCameraPicker extends StatelessWidget with InstaPickerInterface { context, onPressed: () => InstaAssetPicker.pickAssets( context, - title: description.fullLabel, - maxAssets: 4, - pickerTheme: getPickerTheme(context), - actionsBuilder: ( - BuildContext context, - ThemeData? pickerTheme, - double height, - VoidCallback unselectAll, - ) => - [ - InstaPickerCircleIconButton.unselectAll( - onTap: unselectAll, - theme: pickerTheme, - size: height, - ), - const SizedBox(width: 8), - InstaPickerCircleIconButton( - onTap: () => _pickFromWeChatCamera(context), - theme: pickerTheme, - icon: const Icon(Icons.camera_alt), - size: height, - ), - ], - specialItemBuilder: (context, _, __) { - // return a button that open the camera - return ElevatedButton( - onPressed: () async { - Feedback.forTap(context); - final AssetEntity? entity = - await _pickFromWeChatCamera(context); - if (entity == null) return; - - if (context.mounted) { - await InstaAssetPicker.refreshAndSelectEntity( - context, - entity, - ); - } - }, - style: ElevatedButton.styleFrom( - shape: const RoundedRectangleBorder(), - foregroundColor: Colors.white, - backgroundColor: Colors.transparent, + builderOptions: InstaAssetPickerBuilderOptions( + context, + title: description.fullLabel, + pickerTheme: getPickerTheme(context), + actionsBuilder: ( + BuildContext context, + ThemeData? pickerTheme, + double height, + VoidCallback unselectAll, + ) => + [ + InstaPickerCircleIconButton.unselectAll( + onTap: unselectAll, + theme: pickerTheme, + size: height, ), - child: FittedBox( - fit: BoxFit.cover, - child: Text( - InstaAssetPicker.defaultTextDelegate(context) - .sActionUseCameraHint, - textAlign: TextAlign.center, - ), + const SizedBox(width: 8), + InstaPickerCircleIconButton( + onTap: () => _pickFromWeChatCamera(context), + theme: pickerTheme, + icon: const Icon(Icons.camera_alt), + size: height, ), - ); - }, - // since the list is revert, use prepend to be at the top - specialItemPosition: SpecialItemPosition.prepend, + ], + specialItemBuilder: (context, _, __) { + // return a button that open the camera + return ElevatedButton( + onPressed: () async { + Feedback.forTap(context); + final AssetEntity? entity = + await _pickFromWeChatCamera(context); + if (entity == null) return; + + if (context.mounted) { + await InstaAssetPicker.refreshAndSelectEntity( + context, + entity, + ); + } + }, + style: ElevatedButton.styleFrom( + shape: const RoundedRectangleBorder(), + foregroundColor: Colors.white, + backgroundColor: Colors.transparent, + ), + child: FittedBox( + fit: BoxFit.cover, + child: Text( + InstaAssetPicker.defaultTextDelegate(context) + .sActionUseCameraHint, + textAlign: TextAlign.center, + ), + ), + ); + }, + // since the list is revert, use prepend to be at the top + specialItemPosition: SpecialItemPosition.prepend, + ), + maxAssets: 4, onCompleted: (cropStream) { Navigator.push( context, diff --git a/example/lib/pages/restorable_picker.dart b/example/lib/pages/restorable_picker.dart index 63bb55a..fd2a981 100644 --- a/example/lib/pages/restorable_picker.dart +++ b/example/lib/pages/restorable_picker.dart @@ -36,10 +36,13 @@ class _PickerScreenState extends State { final List? result = await _instaAssetsPicker.restorableAssetsPicker( context, - title: widget.description.fullLabel, - closeOnComplete: true, + builderOptions: InstaAssetPickerBuilderOptions( + context, + title: widget.description.fullLabel, + closeOnComplete: true, + pickerTheme: _pickerTheme, + ), provider: () => _provider, - pickerTheme: _pickerTheme, onCompleted: (cropStream) { // example withtout StreamBuilder cropStream.listen((event) { diff --git a/example/lib/widgets/insta_picker_interface.dart b/example/lib/widgets/insta_picker_interface.dart index dbf40cc..a6c8a16 100644 --- a/example/lib/widgets/insta_picker_interface.dart +++ b/example/lib/widgets/insta_picker_interface.dart @@ -79,10 +79,13 @@ mixin InstaPickerInterface on Widget { void pickAssets(BuildContext context, {required int maxAssets}) => InstaAssetPicker.pickAssets( context, - title: description.fullLabel, - closeOnComplete: true, + builderOptions: InstaAssetPickerBuilderOptions( + context, + title: description.fullLabel, + closeOnComplete: true, + pickerTheme: getPickerTheme(context), + ), maxAssets: maxAssets, - pickerTheme: getPickerTheme(context), onCompleted: (Stream cropStream) { Navigator.push( context, diff --git a/lib/src/assets_picker.dart b/lib/src/assets_picker.dart index 96476bc..1d65474 100644 --- a/lib/src/assets_picker.dart +++ b/lib/src/assets_picker.dart @@ -33,6 +33,68 @@ class InstaAssetCropDelegate { final List cropRatios; } +class InstaAssetPickerBuilderOptions { + InstaAssetPickerBuilderOptions( + BuildContext context, { + ThemeData? pickerTheme, + AssetPickerTextDelegate? textDelegate, + Locale? locale, + this.gridCount = _kGridCount, + this.title, + this.closeOnComplete = false, + this.loadingIndicatorBuilder, + this.limitedPermissionOverlayPredicate, + this.specialItemBuilder, + this.specialItemPosition, + this.actionsBuilder, + }) : pickerTheme = pickerTheme ?? + InstaAssetPicker.themeData(Theme.of(context).primaryColor), + textDelegate = + textDelegate ?? InstaAssetPicker.defaultTextDelegate(context), + locale = locale ?? Localizations.maybeLocaleOf(context); + + /// Specifies the number of assets in the cross axis. + /// Defaults to [_kGridCount], like instagram. + final int gridCount; + + /// Specifies the theme to apply to the picker. + /// It is by default initialized with the `primaryColor` of the context theme. + final ThemeData? pickerTheme; + + /// Specifies the language to apply to the picker. + /// Default is the locale language from the context. + final AssetPickerTextDelegate? textDelegate; + + /// Specifies the text title in the picker [AppBar]. + final String? title; + + /// Specifies if the picker should be closed after assets selection confirmation. + /// Defaults to `false`. + final bool closeOnComplete; + + /// The loader indicator to display in the picker. + final LoadingIndicatorBuilder? loadingIndicatorBuilder; + + /// Specifies if the limited permission overlay should be displayed. + final LimitedPermissionOverlayPredicate? limitedPermissionOverlayPredicate; + + /// Specifies [Widget] for the the special item. + final SpecialItemBuilder? specialItemBuilder; + + /// Set a special item in the picker with several positions. + /// Since the grid view is reversed, [SpecialItemPosition.prepend] + /// will be at the top and [SpecialItemPosition.append] at the bottom. + /// Defaults to [SpecialItemPosition.none]. + final SpecialItemPosition? specialItemPosition; + + /// The [Widget] to display on top of the assets grid view. + /// Default is unselect all assets button. + final InstaPickerActionsBuilder? actionsBuilder; + + /// Identifier used to select picker language + final Locale? locale; +} + class InstaAssetPicker { InstaAssetPickerBuilder? builder; @@ -85,11 +147,13 @@ class InstaAssetPicker { /// Open a [ScaffoldMessenger] describing the reason why the picker cannot be opened. static void _openErrorPermission( BuildContext context, - AssetPickerTextDelegate textDelegate, + AssetPickerTextDelegate? textDelegate, Function(BuildContext context, String error)? customHandler, ) { + final text = textDelegate ?? defaultTextDelegate(context); + final defaultDescription = - '${textDelegate.unableToAccessAll}\n${textDelegate.goToSystemSettings}'; + '${text.unableToAccessAll}\n${text.goToSystemSettings}'; if (customHandler != null) { customHandler(context, defaultDescription); @@ -128,38 +192,10 @@ class InstaAssetPicker { /// Getter needed to initialize the provider state after permission check. /// This argument is required. /// - /// - Set [gridCount] to specifies the number of assets in the cross axis. - /// Defaults to [_kGridCount], like instagram. - /// - /// - Set [pickerTheme] to specifies the theme to apply to the picker. - /// It is by default initialized with the `primaryColor` of the context theme. - /// - /// - Set [textDelegate] to specifies the language to apply to the picker. - /// Default is the locale language from the context. - /// - /// - Set [title] to specifies the text title in the picker [AppBar]. - /// - /// - Set [closeOnComplete] to specifies if the picker should be closed - /// after assets selection confirmation. - /// /// - The [onCompleted] callback is called when the assets selection is confirmed. /// It will as argument a [Stream] with exportation details [InstaAssetsExportDetails]. /// - /// - Set [loadingIndicatorBuilder] to specifies the loader indicator - /// to display in the picker. - /// - /// - Set [limitedPermissionOverlayPredicate] to specifies if the limited - /// permission overlay should be displayed. - /// - /// - Set [specialItemPosition] to allows users to set a special item in the picker - /// with several positions. Since the grid view is reversed, [SpecialItemPosition.prepend] - /// will be at the top and [SpecialItemPosition.append] at the bottom. - /// Defaults to [SpecialItemPosition.none]. - /// - /// - Set [specialItemBuilder] to specifies [Widget] for the the special item. - /// - /// - Set [actionsBuilder] function to specifies the [Widget]s to display - /// on top of the assets grid view. Default is unselect all assets button. + /// - Set [builderOptions] to specifies more optional parameters for the picker. Future?> restorableAssetsPicker( BuildContext context, { Key? key, @@ -172,29 +208,22 @@ class InstaAssetPicker { InstaAssetCropDelegate cropDelegate = const InstaAssetCropDelegate(), /// InstaAssetPickerBuilder options - int gridCount = _kGridCount, required DefaultAssetPickerProvider Function() provider, - ThemeData? pickerTheme, - AssetPickerTextDelegate? textDelegate, - String? title, - bool closeOnComplete = false, required Function(Stream exportDetails) onCompleted, - Widget Function(BuildContext context, bool isAssetsEmpty)? - loadingIndicatorBuilder, - LimitedPermissionOverlayPredicate? limitedPermissionOverlayPredicate, - Widget? Function(BuildContext context, AssetPathEntity? path, int length)? - specialItemBuilder, - SpecialItemPosition? specialItemPosition, - InstaPickerActionsBuilder? actionsBuilder, + InstaAssetPickerBuilderOptions? builderOptions, }) async { - final text = textDelegate ?? defaultTextDelegate(context); + builderOptions ??= InstaAssetPickerBuilderOptions(context); PermissionState? ps; try { ps = await _permissionCheck(); } catch (e) { - _openErrorPermission(context, text, onPermissionDenied); + _openErrorPermission( + context, + builderOptions.textDelegate, + onPermissionDenied, + ); return []; } @@ -206,20 +235,10 @@ class InstaAssetPicker { builder ??= InstaAssetPickerBuilder( initialPermission: ps, provider: restoredProvider, - title: title, - gridCount: gridCount, - pickerTheme: pickerTheme ?? themeData(Theme.of(context).primaryColor), - locale: Localizations.maybeLocaleOf(context), keepScrollOffset: true, - textDelegate: text, - loadingIndicatorBuilder: loadingIndicatorBuilder, - limitedPermissionOverlayPredicate: limitedPermissionOverlayPredicate, - closeOnComplete: closeOnComplete, cropDelegate: cropDelegate, onCompleted: onCompleted, - specialItemBuilder: specialItemBuilder, - specialItemPosition: specialItemPosition, - actionsBuilder: actionsBuilder, + options: builderOptions, ); return AssetPicker.pickAssetsWithDelegate( @@ -246,31 +265,10 @@ class InstaAssetPicker { /// /// Those arguments are used by [InstaAssetPickerBuilder] /// - /// - Set [gridCount] to specifies the number of assets in the cross axis. - /// Defaults to [_kGridCount], like instagram. - /// - /// - Set [pickerTheme] to specifies the theme to apply to the picker. - /// It is by default initialized with the `primaryColor` of the context theme. - /// - /// - Set [textDelegate] to specifies the language to apply to the picker. - /// Default is the locale language from the context. - /// - /// - Set [title] to specifies the text title in the picker [AppBar]. - /// - /// - Set [closeOnComplete] to specifies if the picker should be closed - /// after assets selection confirmation. - /// /// - The [onCompleted] callback is called when the assets selection is confirmed. /// It will as argument a [Stream] with exportation details [InstaAssetsExportDetails]. /// - /// - Set [loadingIndicatorBuilder] to specifies the loader indicator - /// to display in the picker. - /// - /// - Set [limitedPermissionOverlayPredicate] to specifies if the limited - /// permission overlay should be displayed. - /// - /// - Set [actionsBuilder] function to specifies the [Widget]s to display - /// on top of the assets grid view. Default is unselect all assets button. + /// - Set [builderOptions] to specifies more optional parameters for the picker. /// /// Those arguments are used by [DefaultAssetPickerProvider] /// @@ -297,13 +295,6 @@ class InstaAssetPicker { /// /// - Set [initializeDelayDuration] to specifies the delay before loading the assets /// Defaults to [_kInitializeDelayDuration]. - /// - /// - Set [specialItemPosition] to allows users to set a special item in the picker - /// with several positions. Since the grid view is reversed, [SpecialItemPosition.prepend] - /// will be at the top and [SpecialItemPosition.append] at the bottom. - /// Defaults to [SpecialItemPosition.none]. - /// - /// - Set [specialItemBuilder] to specifies [Widget] for the the special item. static Future?> pickAssets( BuildContext context, { Key? key, @@ -316,16 +307,9 @@ class InstaAssetPicker { InstaAssetCropDelegate cropDelegate = const InstaAssetCropDelegate(), /// InstaAssetPickerBuilder options - int gridCount = _kGridCount, - ThemeData? pickerTheme, - AssetPickerTextDelegate? textDelegate, - String? title, - bool closeOnComplete = false, required Function(Stream exportDetails) onCompleted, - Widget Function(BuildContext context, bool isAssetsEmpty)? - loadingIndicatorBuilder, - LimitedPermissionOverlayPredicate? limitedPermissionOverlayPredicate, + InstaAssetPickerBuilderOptions? builderOptions, /// DefaultAssetPickerProvider options List? selectedAssets, @@ -337,19 +321,19 @@ class InstaAssetPicker { bool sortPathsByModifiedDate = false, PMFilter? filterOptions, Duration initializeDelayDuration = _kInitializeDelayDuration, - Widget? Function(BuildContext context, AssetPathEntity? path, int length)? - specialItemBuilder, - SpecialItemPosition? specialItemPosition, - InstaPickerActionsBuilder? actionsBuilder, }) async { - final text = textDelegate ?? defaultTextDelegate(context); + builderOptions ??= InstaAssetPickerBuilderOptions(context); // must be called before initializing any picker provider to avoid `PlatformException(PERMISSION_REQUESTING)` type exception PermissionState? ps; try { ps = await _permissionCheck(); } catch (e) { - _openErrorPermission(context, text, onPermissionDenied); + _openErrorPermission( + context, + builderOptions.textDelegate, + onPermissionDenied, + ); return []; } @@ -368,20 +352,10 @@ class InstaAssetPicker { final InstaAssetPickerBuilder builder = InstaAssetPickerBuilder( initialPermission: ps, provider: provider, - title: title, - gridCount: gridCount, - pickerTheme: pickerTheme ?? themeData(Theme.of(context).primaryColor), - locale: Localizations.maybeLocaleOf(context), keepScrollOffset: false, - textDelegate: text, - loadingIndicatorBuilder: loadingIndicatorBuilder, - limitedPermissionOverlayPredicate: limitedPermissionOverlayPredicate, - closeOnComplete: closeOnComplete, cropDelegate: cropDelegate, onCompleted: onCompleted, - specialItemBuilder: specialItemBuilder, - specialItemPosition: specialItemPosition, - actionsBuilder: actionsBuilder, + options: builderOptions, ); return AssetPicker.pickAssetsWithDelegate( diff --git a/lib/src/widget/insta_asset_picker_delegate.dart b/lib/src/widget/insta_asset_picker_delegate.dart index 89770ff..7751f7b 100644 --- a/lib/src/widget/insta_asset_picker_delegate.dart +++ b/lib/src/widget/insta_asset_picker_delegate.dart @@ -37,30 +37,37 @@ class InstaAssetPickerBuilder extends DefaultAssetPickerBuilderDelegate { required super.initialPermission, required super.provider, required this.onCompleted, - super.gridCount = 4, - super.pickerTheme, - super.textDelegate, - super.locale, - super.keepScrollOffset, - super.loadingIndicatorBuilder, - super.limitedPermissionOverlayPredicate, - super.specialItemBuilder, - SpecialItemPosition? specialItemPosition, - this.title, - this.closeOnComplete = false, - this.actionsBuilder, + required InstaAssetPickerBuilderOptions options, InstaAssetCropDelegate cropDelegate = const InstaAssetCropDelegate(), + super.keepScrollOffset, }) : _cropController = InstaAssetsCropController(keepScrollOffset, cropDelegate), + title = options.title, + closeOnComplete = options.closeOnComplete, + actionsBuilder = options.actionsBuilder, super( + gridCount: options.gridCount, + pickerTheme: options.pickerTheme, + textDelegate: options.textDelegate, + loadingIndicatorBuilder: options.loadingIndicatorBuilder, + limitedPermissionOverlayPredicate: + options.limitedPermissionOverlayPredicate, + specialItemBuilder: options.specialItemBuilder, + specialItemPosition: + options.specialItemPosition ?? SpecialItemPosition.none, + locale: options.locale, shouldRevertGrid: false, - specialItemPosition: specialItemPosition ?? SpecialItemPosition.none, ); + /// The text title in the picker [AppBar]. final String? title; + /// Callback called when the assets selection is confirmed. + /// It will as argument a [Stream] with exportation details [InstaAssetsExportDetails]. final Function(Stream) onCompleted; + /// The [Widget] to display on top of the assets grid view. + /// Default is unselect all assets button. final InstaPickerActionsBuilder? actionsBuilder; /// Should the picker be closed when the selection is confirmed