diff --git a/example/lib/pages/home_page.dart b/example/lib/pages/home_page.dart index d8019f43..6c1aa87d 100644 --- a/example/lib/pages/home_page.dart +++ b/example/lib/pages/home_page.dart @@ -156,6 +156,8 @@ class _HomePageState extends State { onVideoPickCallback: _onVideoPickCallback, // uncomment to provide a custom "pick from" dialog. // mediaPickSettingSelector: _selectMediaPickSetting, + // uncomment to provide a custom "pick from" dialog. + // cameraPickSettingSelector: _selectCameraPickSetting, showAlignmentButtons: true, ); if (kIsWeb) { @@ -271,6 +273,33 @@ class _HomePageState extends State { ), ); + // ignore: unused_element + Future _selectCameraPickSetting(BuildContext context) => + showDialog( + context: context, + builder: (ctx) => + AlertDialog( + contentPadding: EdgeInsets.zero, + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextButton.icon( + icon: const Icon(Icons.camera), + label: const Text('Capture a photo'), + onPressed: () => + Navigator.pop(ctx, MediaPickSetting.Camera), + ), + TextButton.icon( + icon: const Icon(Icons.video_call), + label: const Text('Capture a video'), + onPressed: () => + Navigator.pop(ctx, MediaPickSetting.Video), + ) + ], + ), + ), + ); + Widget _buildMenuBar(BuildContext context) { final size = MediaQuery.of(context).size; const itemStyle = TextStyle( @@ -400,4 +429,4 @@ class NotesBlockEmbed extends CustomBlockEmbed { NotesBlockEmbed(jsonEncode(document.toDelta().toJson())); Document get document => Document.fromJson(jsonDecode(data)); -} +} \ No newline at end of file diff --git a/lib/src/translations/toolbar.i18n.dart b/lib/src/translations/toolbar.i18n.dart index e4869b75..c2dd8200 100644 --- a/lib/src/translations/toolbar.i18n.dart +++ b/lib/src/translations/toolbar.i18n.dart @@ -33,6 +33,8 @@ extension Localization on String { 'showing match': 'showing match', 'Prev': 'Prev', 'Next': 'Next', + 'Camera': 'Camera', + 'Video': 'Video', }, 'en_us': { 'Paste a link': 'Paste a link', @@ -64,6 +66,8 @@ extension Localization on String { 'showing match': 'showing match', 'Prev': 'Prev', 'Next': 'Next', + 'Camera': 'Camera', + 'Video': 'Video', }, 'ar': { 'Paste a link': 'نسخ الرابط', @@ -95,6 +99,8 @@ extension Localization on String { 'showing match': 'showing match', 'Prev': 'Prev', 'Next': 'Next', + 'Camera': 'Camera', + 'Video': 'Video', }, 'da': { 'Paste a link': 'Indsæt link', @@ -126,6 +132,8 @@ extension Localization on String { 'showing match': 'showing match', 'Prev': 'Prev', 'Next': 'Next', + 'Camera': 'Camera', + 'Video': 'Video', }, 'de': { 'Paste a link': 'Link hinzufügen', @@ -189,6 +197,8 @@ extension Localization on String { 'showing match': 'showing match', 'Prev': 'Prev', 'Next': 'Next', + 'Camera': 'Camera', + 'Video': 'Video', }, 'zh_CN': { 'Paste a link': '粘贴链接', @@ -220,6 +230,8 @@ extension Localization on String { 'showing match': 'showing match', 'Prev': 'Prev', 'Next': 'Next', + 'Camera': 'Camera', + 'Video': 'Video', }, 'ko': { 'Paste a link': '링크를 붙여넣어 주세요.', @@ -250,6 +262,8 @@ extension Localization on String { 'showing match': 'showing match', 'Prev': 'Prev', 'Next': 'Next', + 'Camera': 'Camera', + 'Video': 'Video', }, 'ru': { 'Paste a link': 'Вставить ссылку', @@ -281,6 +295,8 @@ extension Localization on String { 'showing match': 'showing match', 'Prev': 'Prev', 'Next': 'Next', + 'Camera': 'Camera', + 'Video': 'Video', }, 'es': { 'Paste a link': 'Pega un enlace', @@ -313,6 +329,8 @@ extension Localization on String { 'showing match': 'showing match', 'Prev': 'Prev', 'Next': 'Next', + 'Camera': 'Camera', + 'Video': 'Video', }, 'tr': { 'Paste a link': 'Bağlantıyı Yapıştır', @@ -344,6 +362,8 @@ extension Localization on String { 'showing match': 'showing match', 'Prev': 'Prev', 'Next': 'Next', + 'Camera': 'Camera', + 'Video': 'Video', }, 'uk': { 'Paste a link': 'Вставити посилання', @@ -375,6 +395,8 @@ extension Localization on String { 'showing match': 'showing match', 'Prev': 'Prev', 'Next': 'Next', + 'Camera': 'Camera', + 'Video': 'Video', }, 'pt': { 'Paste a link': 'Colar um link', @@ -407,6 +429,8 @@ extension Localization on String { 'showing match': 'showing match', 'Prev': 'Prev', 'Next': 'Next', + 'Camera': 'Camera', + 'Video': 'Video', }, 'pl': { 'Paste a link': 'Wklej link', @@ -439,6 +463,8 @@ extension Localization on String { 'showing match': 'showing match', 'Prev': 'Prev', 'Next': 'Next', + 'Camera': 'Camera', + 'Video': 'Video', }, 'vi': { 'Paste a link': 'Chèn liên kết', @@ -471,6 +497,8 @@ extension Localization on String { 'showing match': 'showing match', 'Prev': 'Prev', 'Next': 'Next', + 'Camera': 'Camera', + 'Video': 'Video', }, 'ur': { 'Paste a link': 'لنک پیسٹ کریں', @@ -502,6 +530,8 @@ extension Localization on String { 'showing match': 'showing match', 'Prev': 'Prev', 'Next': 'Next', + 'Camera': 'Camera', + 'Video': 'Video', }, 'id': { 'Paste a link': 'Tempel tautan', @@ -533,6 +563,8 @@ extension Localization on String { 'showing match': 'showing match', 'Prev': 'Prev', 'Next': 'Next', + 'Camera': 'Camera', + 'Video': 'Video', }, 'no': { 'Paste a link': 'Lim inn lenke', @@ -564,6 +596,8 @@ extension Localization on String { 'showing match': 'showing match', 'Prev': 'Prev', 'Next': 'Next', + 'Camera': 'Camera', + 'Video': 'Video', }, 'fa': { 'Paste a link': 'جایگذاری لینک', @@ -595,6 +629,8 @@ extension Localization on String { 'showing match': 'showing match', 'Prev': 'Prev', 'Next': 'Next', + 'Camera': 'Camera', + 'Video': 'Video', }, 'hi': { 'Paste a link': 'लिंक पेस्ट करें', @@ -626,6 +662,8 @@ extension Localization on String { 'showing match': 'showing match', 'Prev': 'Prev', 'Next': 'Next', + 'Camera': 'Camera', + 'Video': 'Video', }, 'nl': { 'Paste a link': 'Plak een link', @@ -657,6 +695,8 @@ extension Localization on String { 'showing match': 'showing match', 'Prev': 'Prev', 'Next': 'Next', + 'Camera': 'Camera', + 'Video': 'Video', }, 'zh_HK': { 'Paste a link': '貼上連結', @@ -688,6 +728,8 @@ extension Localization on String { 'showing match': 'showing match', 'Prev': 'Prev', 'Next': 'Next', + 'Camera': 'Camera', + 'Video': 'Video', }, 'sr': { 'Paste a link': 'Nalepi vezu', @@ -719,6 +761,8 @@ extension Localization on String { 'showing match': 'showing match', 'Prev': 'Prev', 'Next': 'Next', + 'Camera': 'Camera', + 'Video': 'Video', }, }; diff --git a/lib/src/widgets/toolbar.dart b/lib/src/widgets/toolbar.dart index 40cffdf0..7a99de5a 100644 --- a/lib/src/widgets/toolbar.dart +++ b/lib/src/widgets/toolbar.dart @@ -54,6 +54,8 @@ typedef WebVideoPickImpl = Future Function( OnVideoPickCallback onImagePickCallback); typedef MediaPickSettingSelector = Future Function( BuildContext context); +typedef CameraPickSettingSelector = Future Function( + BuildContext context); // The default size of the icon of a button. const double kDefaultIconSize = 18; @@ -117,6 +119,7 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { OnImagePickCallback? onImagePickCallback, OnVideoPickCallback? onVideoPickCallback, MediaPickSettingSelector? mediaPickSettingSelector, + CameraPickSettingSelector? cameraPickSettingSelector, FilePickImpl? filePickImpl, WebImagePickImpl? webImagePickImpl, WebVideoPickImpl? webVideoPickImpl, @@ -364,6 +367,7 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { filePickImpl: filePickImpl, webImagePickImpl: webImagePickImpl, webVideoPickImpl: webVideoPickImpl, + cameraPickSettingSelector: cameraPickSettingSelector, iconTheme: iconTheme, ), if (showFormulaButton) diff --git a/lib/src/widgets/toolbar/camera_button.dart b/lib/src/widgets/toolbar/camera_button.dart index 184200c9..fcb6e3c1 100644 --- a/lib/src/widgets/toolbar/camera_button.dart +++ b/lib/src/widgets/toolbar/camera_button.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; import '../../models/themes/quill_icon_theme.dart'; +import '../../translations/toolbar.i18n.dart'; import '../controller.dart'; import '../toolbar.dart'; @@ -16,6 +17,7 @@ class CameraButton extends StatelessWidget { this.filePickImpl, this.webImagePickImpl, this.webVideoPickImpl, + this.cameraPickSettingSelector, this.iconTheme, Key? key, }) : super(key: key); @@ -37,6 +39,8 @@ class CameraButton extends StatelessWidget { final FilePickImpl? filePickImpl; + final CameraPickSettingSelector? cameraPickSettingSelector; + final QuillIconTheme? iconTheme; @override @@ -69,48 +73,55 @@ class CameraButton extends StatelessWidget { FilePickImpl? filePickImpl, WebImagePickImpl? webImagePickImpl}) async { if (onImagePickCallback != null && onVideoPickCallback != null) { - // Show dialog to choose Photo or Video - return await showDialog( - context: context, - builder: (context) { - return AlertDialog( - contentPadding: const EdgeInsets.all(0), - backgroundColor: Colors.transparent, - content: Column(mainAxisSize: MainAxisSize.min, children: [ - TextButton.icon( - icon: const Icon(Icons.photo, color: Colors.cyanAccent), - label: const Text('Photo'), - onPressed: () { - ImageVideoUtils.handleImageButtonTap(context, controller, - ImageSource.camera, onImagePickCallback, - filePickImpl: filePickImpl, - webImagePickImpl: webImagePickImpl); - }, + final selector = cameraPickSettingSelector ?? + (context) => showDialog( + context: context, + builder: (ctx) => AlertDialog( + contentPadding: EdgeInsets.zero, + backgroundColor: Colors.transparent, + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextButton.icon( + icon: const Icon( + Icons.camera, + color: Colors.orangeAccent, + ), + label: Text('Camera'.i18n), + onPressed: () => + Navigator.pop(ctx, MediaPickSetting.Camera), + ), + TextButton.icon( + icon: const Icon( + Icons.video_call, + color: Colors.cyanAccent, + ), + label: Text('Video'.i18n), + onPressed: () => + Navigator.pop(ctx, MediaPickSetting.Video), + ) + ], ), - TextButton.icon( - icon: const Icon(Icons.movie_creation, - color: Colors.orangeAccent), - label: const Text('Video'), - onPressed: () { - ImageVideoUtils.handleVideoButtonTap(context, controller, - ImageSource.camera, onVideoPickCallback, - filePickImpl: filePickImpl, - webVideoPickImpl: webVideoPickImpl); - }, - ) - ])); - }); - } - - if (onImagePickCallback != null) { - return ImageVideoUtils.handleImageButtonTap( - context, controller, ImageSource.camera, onImagePickCallback, - filePickImpl: filePickImpl, webImagePickImpl: webImagePickImpl); + ), + ); + + final source = await selector(context); + if (source != null) { + switch (source) { + case MediaPickSetting.Camera: + await ImageVideoUtils.handleImageButtonTap( + context, controller, ImageSource.camera, onImagePickCallback, + filePickImpl: filePickImpl, webImagePickImpl: webImagePickImpl); + break; + case MediaPickSetting.Video: + await ImageVideoUtils.handleVideoButtonTap( + context, controller, ImageSource.camera, onVideoPickCallback, + filePickImpl: filePickImpl, webVideoPickImpl: webVideoPickImpl); + break; + default: + throw ArgumentError('Invalid MediaSetting'); + } + } } - - assert(onVideoPickCallback != null, 'onVideoPickCallback must not be null'); - return ImageVideoUtils.handleVideoButtonTap( - context, controller, ImageSource.camera, onVideoPickCallback!, - filePickImpl: filePickImpl, webVideoPickImpl: webVideoPickImpl); } } diff --git a/lib/src/widgets/toolbar/image_video_utils.dart b/lib/src/widgets/toolbar/image_video_utils.dart index cd05de95..eca9dc9c 100644 --- a/lib/src/widgets/toolbar/image_video_utils.dart +++ b/lib/src/widgets/toolbar/image_video_utils.dart @@ -78,6 +78,8 @@ class LinkDialogState extends State { enum MediaPickSetting { Gallery, Link, + Camera, + Video, } class ImageVideoUtils {