diff --git a/lib/src/widgets/toolbar.dart b/lib/src/widgets/toolbar.dart index af36d706..69ce56b2 100644 --- a/lib/src/widgets/toolbar.dart +++ b/lib/src/widgets/toolbar.dart @@ -17,6 +17,7 @@ import 'toolbar/link_style_button.dart'; import 'toolbar/select_header_style_button.dart'; import 'toolbar/toggle_check_list_button.dart'; import 'toolbar/toggle_style_button.dart'; +import 'toolbar/video_button.dart'; export 'toolbar/clear_format_button.dart'; export 'toolbar/color_button.dart'; @@ -32,10 +33,12 @@ export 'toolbar/toggle_check_list_button.dart'; export 'toolbar/toggle_style_button.dart'; typedef OnImagePickCallback = Future Function(File file); -typedef ImagePickImpl = Future Function(ImageSource source); +typedef OnVideoPickCallback = Future Function(File file); typedef FilePickImpl = Future Function(BuildContext context); typedef WebImagePickImpl = Future Function( OnImagePickCallback onImagePickCallback); +typedef WebVideoPickImpl = Future Function( + OnVideoPickCallback onImagePickCallback); // The default size of the icon of a button. const double kDefaultIconSize = 18; @@ -76,6 +79,7 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { bool multiRowsDisplay = true, bool showCamera = true, OnImagePickCallback? onImagePickCallback, + OnVideoPickCallback? onVideoPickCallback, FilePickImpl? filePickImpl, WebImagePickImpl? webImagePickImpl, Key? key, @@ -89,7 +93,8 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { showColorButton || showBackgroundColorButton || showClearFormat || - onImagePickCallback != null, + onImagePickCallback != null || + onVideoPickCallback != null, showHeaderStyle, showListNumbers || showListBullets || showListCheck || showCodeBlock, showQuote || showIndent, @@ -183,6 +188,16 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { filePickImpl: filePickImpl, webImagePickImpl: webImagePickImpl, ), + if (onVideoPickCallback != null) + VideoButton( + icon: Icons.movie_creation, + iconSize: toolbarIconSize, + controller: controller, + videoSource: ImageSource.gallery, + onVideoPickCallback: onVideoPickCallback, + filePickImpl: filePickImpl, + webVideoPickImpl: webImagePickImpl, + ), if (isButtonGroupShown[0] && (isButtonGroupShown[1] || isButtonGroupShown[2] || diff --git a/lib/src/widgets/toolbar/image_button.dart b/lib/src/widgets/toolbar/image_button.dart index 1acfc40f..8b41d95c 100644 --- a/lib/src/widgets/toolbar/image_button.dart +++ b/lib/src/widgets/toolbar/image_button.dart @@ -17,7 +17,6 @@ class ImageButton extends StatelessWidget { this.iconSize = kDefaultIconSize, this.fillColor, this.onImagePickCallback, - this.imagePickImpl, this.filePickImpl, this.webImagePickImpl, Key? key, @@ -32,8 +31,6 @@ class ImageButton extends StatelessWidget { final OnImagePickCallback? onImagePickCallback; - final ImagePickImpl? imagePickImpl; - final WebImagePickImpl? webImagePickImpl; final ImageSource imageSource; @@ -60,22 +57,18 @@ class ImageButton extends StatelessWidget { final length = controller.selection.extentOffset - index; String? imageUrl; - if (imagePickImpl != null) { - imageUrl = await imagePickImpl!(imageSource); + if (kIsWeb) { + assert( + webImagePickImpl != null, + 'Please provide webImagePickImpl for Web ' + '(check out example directory for how to do it)'); + imageUrl = await webImagePickImpl!(onImagePickCallback!); + } else if (Platform.isAndroid || Platform.isIOS) { + imageUrl = await _pickImage(imageSource, onImagePickCallback!); } else { - if (kIsWeb) { - assert( - webImagePickImpl != null, - 'Please provide webImagePickImpl for Web ' - '(check out example directory for how to do it)'); - imageUrl = await webImagePickImpl!(onImagePickCallback!); - } else if (Platform.isAndroid || Platform.isIOS) { - imageUrl = await _pickImage(imageSource, onImagePickCallback!); - } else { - assert(filePickImpl != null, 'Desktop must provide filePickImpl'); - imageUrl = await _pickImageDesktop( - context, filePickImpl!, onImagePickCallback!); - } + assert(filePickImpl != null, 'Desktop must provide filePickImpl'); + imageUrl = + await _pickImageDesktop(context, filePickImpl!, onImagePickCallback!); } if (imageUrl != null) { diff --git a/lib/src/widgets/toolbar/video_button.dart b/lib/src/widgets/toolbar/video_button.dart new file mode 100644 index 00000000..f4c06f18 --- /dev/null +++ b/lib/src/widgets/toolbar/video_button.dart @@ -0,0 +1,99 @@ +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:image_picker/image_picker.dart'; + +import '../../models/documents/nodes/embed.dart'; +import '../controller.dart'; +import '../toolbar.dart'; +import 'quill_icon_button.dart'; + +class VideoButton extends StatelessWidget { + const VideoButton({ + required this.icon, + required this.controller, + required this.videoSource, + this.iconSize = kDefaultIconSize, + this.fillColor, + this.onVideoPickCallback, + this.filePickImpl, + this.webVideoPickImpl, + Key? key, + }) : super(key: key); + + final IconData icon; + final double iconSize; + + final Color? fillColor; + + final QuillController controller; + + final OnVideoPickCallback? onVideoPickCallback; + + final WebVideoPickImpl? webVideoPickImpl; + + final ImageSource videoSource; + + final FilePickImpl? filePickImpl; + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + + return QuillIconButton( + icon: Icon(icon, size: iconSize, color: theme.iconTheme.color), + highlightElevation: 0, + hoverElevation: 0, + size: iconSize * 1.77, + fillColor: fillColor ?? theme.canvasColor, + onPressed: () => _handleVideoButtonTap(context, filePickImpl), + ); + } + + Future _handleVideoButtonTap(BuildContext context, + [FilePickImpl? filePickImpl]) async { + final index = controller.selection.baseOffset; + final length = controller.selection.extentOffset - index; + + String? videoUrl; + if (kIsWeb) { + assert( + webVideoPickImpl != null, + 'Please provide webVideoPickImpl for Web ' + '(check out example directory for how to do it)'); + videoUrl = await webVideoPickImpl!(onVideoPickCallback!); + } else if (Platform.isAndroid || Platform.isIOS) { + videoUrl = await _pickVideo(videoSource, onVideoPickCallback!); + } else { + assert(filePickImpl != null, 'Desktop must provide filePickImpl'); + videoUrl = + await _pickVideoDesktop(context, filePickImpl!, onVideoPickCallback!); + } + + if (videoUrl != null) { + controller.replaceText(index, length, BlockEmbed.video(videoUrl), null); + } + } + + Future _pickVideo( + ImageSource source, OnVideoPickCallback onVideoPickCallback) async { + final pickedFile = await ImagePicker().getVideo(source: source); + if (pickedFile == null) { + return null; + } + + return onVideoPickCallback(File(pickedFile.path)); + } + + Future _pickVideoDesktop( + BuildContext context, + FilePickImpl filePickImpl, + OnVideoPickCallback onVideoPickCallback) async { + final filePath = await filePickImpl(context); + if (filePath == null || filePath.isEmpty) return null; + + final file = File(filePath); + return onVideoPickCallback(file); + } +}