From eeb720e097bd8d7642303a5926051918c9244840 Mon Sep 17 00:00:00 2001
From: Ellet <73608287+freshtechtips@users.noreply.github.com>
Date: Sun, 5 Nov 2023 21:38:13 +0300
Subject: [PATCH] Step 2
---
CHANGELOG.md | 3 +
example/ios/Runner/Info.plist | 4 +-
example/lib/pages/home_page.dart | 148 ++---
example/lib/widgets/demo_scaffold.dart | 7 +-
.../lib/flutter_quill_extensions.dart | 26 +-
.../lib/logic/extensions/controller.dart | 10 +-
.../embeds/embed_types/camera.dart | 36 ++
.../embeds/embed_types/image.dart | 37 +-
.../embeds/embed_types/video.dart | 66 +++
.../{ => camera_button}/camera_button.dart | 158 +++---
.../camera_button/select_camera_action.dart | 37 ++
.../toolbar/image_button/image_button.dart | 31 +-
.../image_button/select_image_source.dart | 2 +-
.../embeds/toolbar/media_button.dart | 533 ------------------
.../toolbar/media_button/media_button.dart | 533 ++++++++++++++++++
.../video_button/select_video_source.dart | 57 ++
.../{ => video_button}/video_button.dart | 89 ++-
.../models/config/toolbar/buttons/camera.dart | 13 +-
.../models/config/toolbar/buttons/image.dart | 25 +-
.../config/toolbar/buttons/media_button.dart | 2 -
.../models/config/toolbar/buttons/video.dart | 9 +-
.../models/config/shared_configurations.dart | 4 +
pubspec.yaml | 2 +-
23 files changed, 1049 insertions(+), 783 deletions(-)
create mode 100644 flutter_quill_extensions/lib/presentation/embeds/embed_types/camera.dart
create mode 100644 flutter_quill_extensions/lib/presentation/embeds/embed_types/video.dart
rename flutter_quill_extensions/lib/presentation/embeds/toolbar/{ => camera_button}/camera_button.dart (53%)
create mode 100644 flutter_quill_extensions/lib/presentation/embeds/toolbar/camera_button/select_camera_action.dart
delete mode 100644 flutter_quill_extensions/lib/presentation/embeds/toolbar/media_button.dart
create mode 100644 flutter_quill_extensions/lib/presentation/embeds/toolbar/media_button/media_button.dart
create mode 100644 flutter_quill_extensions/lib/presentation/embeds/toolbar/video_button/select_video_source.dart
rename flutter_quill_extensions/lib/presentation/embeds/toolbar/{ => video_button}/video_button.dart (62%)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 68f121a6..1153a751 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,6 @@
+## [8.1.12]
+- Add the option to add configurations for `fresh_quill_extensions` using `extraConfigurations`
+
## [8.1.11]
- Follow dart best practices by using `lints` and remove `pedantic` as well `platform` since they are not used
- Fix text direction bug
diff --git a/example/ios/Runner/Info.plist b/example/ios/Runner/Info.plist
index b8fc1f1d..4a49f260 100644
--- a/example/ios/Runner/Info.plist
+++ b/example/ios/Runner/Info.plist
@@ -21,7 +21,9 @@
CFBundleVersion
$(FLUTTER_BUILD_NUMBER)
NSPhotoLibraryUsageDescription
- Need to save image
+ The app will use it to pick images
+ NSCameraUsageDescription
+ The app will use it to capture a images, record videos.
LSRequiresIPhoneOS
UILaunchStoryboardName
diff --git a/example/lib/pages/home_page.dart b/example/lib/pages/home_page.dart
index 34567dbb..f5a4a4a5 100644
--- a/example/lib/pages/home_page.dart
+++ b/example/lib/pages/home_page.dart
@@ -13,6 +13,7 @@ import 'package:flutter/services.dart';
import 'package:flutter_quill/extensions.dart';
import 'package:flutter_quill/flutter_quill.dart';
import 'package:flutter_quill_extensions/flutter_quill_extensions.dart';
+import 'package:flutter_quill_extensions/presentation/embeds/embed_types/image.dart';
import 'package:flutter_quill_extensions/presentation/models/config/toolbar/buttons/video.dart';
import 'package:path/path.dart' as path;
import 'package:path_provider/path_provider.dart';
@@ -274,8 +275,12 @@ class _HomePageState extends State {
configurations: QuillToolbarConfigurations(
embedButtons: FlutterQuillEmbeds.toolbarButtons(
imageButtonOptions: QuillToolbarImageButtonOptions(
- onImagePickCallback: _onImagePickCallback,
- webImagePickImpl: _webImagePickImpl,
+ imageButtonConfigurations: QuillToolbarImageConfigurations(
+ onImageInsertedCallback: (image) async {
+ _onImagePickCallback(File(image));
+ },
+ ),
+ // webImagePickImpl: _webImagePickImpl,
),
),
buttonOptions: QuillToolbarButtonOptions(
@@ -290,11 +295,15 @@ class _HomePageState extends State {
if (_isDesktop()) {
return QuillToolbar(
configurations: QuillToolbarConfigurations(
- showDirection: true,
embedButtons: FlutterQuillEmbeds.toolbarButtons(
imageButtonOptions: QuillToolbarImageButtonOptions(
- onImagePickCallback: _onImagePickCallback,
- filePickImpl: openFileSystemPickerForDesktop,
+ imageButtonConfigurations: QuillToolbarImageConfigurations(
+ onImageInsertedCallback: (image) async {
+ _onImagePickCallback(File(image));
+ },
+ ),
+ // onImagePickCallback: _onImagePickCallback,
+ // filePickImpl: openFileSystemPickerForDesktop,
),
),
showAlignmentButtons: true,
@@ -311,18 +320,23 @@ class _HomePageState extends State {
configurations: QuillToolbarConfigurations(
embedButtons: FlutterQuillEmbeds.toolbarButtons(
imageButtonOptions: QuillToolbarImageButtonOptions(
+ imageButtonConfigurations: QuillToolbarImageConfigurations(
+ onImageInsertedCallback: (image) async {
+ _onImagePickCallback(File(image));
+ },
+ ),
// provide a callback to enable picking images from device.
// if omit, "image" button only allows adding images from url.
// same goes for videos.
- onImagePickCallback: _onImagePickCallback,
+ // onImagePickCallback: _onImagePickCallback,
// uncomment to provide a custom "pick from" dialog.
// mediaPickSettingSelector: _selectMediaPickSetting,
// uncomment to provide a custom "pick from" dialog.
// cameraPickSettingSelector: _selectCameraPickSetting,
),
- videoButtonOptions: QuillToolbarVideoButtonOptions(
- onVideoPickCallback: _onVideoPickCallback,
- ),
+ // videoButtonOptions: QuillToolbarVideoButtonOptions(
+ // onVideoPickCallback: _onVideoPickCallback,
+ // ),
),
showAlignmentButtons: true,
buttonOptions: QuillToolbarButtonOptions(
@@ -414,19 +428,19 @@ class _HomePageState extends State {
return copiedFile.path.toString();
}
- Future _webImagePickImpl(
- OnImagePickCallback onImagePickCallback) async {
- final result = await FilePicker.platform.pickFiles();
- if (result == null) {
- return null;
- }
+ // Future _webImagePickImpl(
+ // OnImagePickCallback onImagePickCallback) async {
+ // final result = await FilePicker.platform.pickFiles();
+ // if (result == null) {
+ // return null;
+ // }
- // Take first, because we don't allow picking multiple files.
- final fileName = result.files.first.name;
- final file = File(fileName);
+ // // Take first, because we don't allow picking multiple files.
+ // final fileName = result.files.first.name;
+ // final file = File(fileName);
- return onImagePickCallback(file);
- }
+ // return onImagePickCallback(file);
+ // }
// Renders the video picked by imagePicker from local file storage
// You can also upload the picked video to any server (eg : AWS s3
@@ -439,53 +453,53 @@ class _HomePageState extends State {
return copiedFile.path.toString();
}
- // ignore: unused_element
- Future _selectMediaPickSetting(BuildContext context) =>
- showDialog(
- context: context,
- builder: (ctx) => AlertDialog(
- contentPadding: EdgeInsets.zero,
- content: Column(
- mainAxisSize: MainAxisSize.min,
- children: [
- TextButton.icon(
- icon: const Icon(Icons.collections),
- label: const Text('Gallery'),
- onPressed: () => Navigator.pop(ctx, MediaPickSetting.gallery),
- ),
- TextButton.icon(
- icon: const Icon(Icons.link),
- label: const Text('Link'),
- onPressed: () => Navigator.pop(ctx, MediaPickSetting.link),
- )
- ],
- ),
- ),
- );
-
- // 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),
- )
- ],
- ),
- ),
- );
+ // // ignore: unused_element
+ // Future _selectMediaPickSetting(BuildContext context) =>
+ // showDialog(
+ // context: context,
+ // builder: (ctx) => AlertDialog(
+ // contentPadding: EdgeInsets.zero,
+ // content: Column(
+ // mainAxisSize: MainAxisSize.min,
+ // children: [
+ // TextButton.icon(
+ // icon: const Icon(Icons.collections),
+ // label: const Text('Gallery'),
+ // onPressed: () => Navigator.pop(ctx, MediaPickSetting.gallery),
+ // ),
+ // TextButton.icon(
+ // icon: const Icon(Icons.link),
+ // label: const Text('Link'),
+ // onPressed: () => Navigator.pop(ctx, MediaPickSetting.link),
+ // )
+ // ],
+ // ),
+ // ),
+ // );
+
+ // // 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.sizeOf(context);
diff --git a/example/lib/widgets/demo_scaffold.dart b/example/lib/widgets/demo_scaffold.dart
index 78c350fc..27c6ebb0 100644
--- a/example/lib/widgets/demo_scaffold.dart
+++ b/example/lib/widgets/demo_scaffold.dart
@@ -89,9 +89,10 @@ class _DemoScaffoldState extends State {
return QuillToolbar(
configurations: QuillToolbarConfigurations(
embedButtons: FlutterQuillEmbeds.toolbarButtons(
- imageButtonOptions: QuillToolbarImageButtonOptions(
- filePickImpl: openFileSystemPickerForDesktop,
- ),
+ // ignore: avoid_redundant_argument_values
+ imageButtonOptions: const QuillToolbarImageButtonOptions(
+ // filePickImpl: openFileSystemPickerForDesktop,
+ ),
),
),
);
diff --git a/flutter_quill_extensions/lib/flutter_quill_extensions.dart b/flutter_quill_extensions/lib/flutter_quill_extensions.dart
index 78e1bc77..bb617431 100644
--- a/flutter_quill_extensions/lib/flutter_quill_extensions.dart
+++ b/flutter_quill_extensions/lib/flutter_quill_extensions.dart
@@ -9,11 +9,13 @@ import 'presentation/embeds/editor/image/image.dart';
import 'presentation/embeds/editor/image/image_web.dart';
import 'presentation/embeds/editor/video.dart';
import 'presentation/embeds/editor/webview.dart';
-import 'presentation/embeds/toolbar/camera_button.dart';
+import 'presentation/embeds/toolbar/camera_button/camera_button.dart';
import 'presentation/embeds/toolbar/formula_button.dart';
import 'presentation/embeds/toolbar/image_button/image_button.dart';
-import 'presentation/embeds/toolbar/media_button.dart';
-import 'presentation/embeds/toolbar/video_button.dart';
+// TODO: Temporary
+// ignore: unused_import
+import 'presentation/embeds/toolbar/media_button/media_button.dart';
+import 'presentation/embeds/toolbar/video_button/video_button.dart';
import 'presentation/models/config/editor/image.dart';
import 'presentation/models/config/editor/video.dart';
import 'presentation/models/config/editor/webview.dart';
@@ -27,12 +29,12 @@ export '/presentation/models/config/editor/webview.dart';
export './logic/extensions/controller.dart';
export 'presentation/embeds/editor/unknown.dart';
export 'presentation/embeds/embed_types.dart';
-export 'presentation/embeds/toolbar/camera_button.dart';
+export 'presentation/embeds/toolbar/camera_button/camera_button.dart';
export 'presentation/embeds/toolbar/formula_button.dart';
export 'presentation/embeds/toolbar/image_button/image_button.dart';
-export 'presentation/embeds/toolbar/media_button.dart';
+export 'presentation/embeds/toolbar/media_button/media_button.dart';
export 'presentation/embeds/toolbar/utils/image_video_utils.dart';
-export 'presentation/embeds/toolbar/video_button.dart';
+export 'presentation/embeds/toolbar/video_button/video_button.dart';
export 'presentation/embeds/utils.dart';
export 'presentation/models/config/editor/image.dart';
export 'presentation/models/config/toolbar/buttons/image.dart';
@@ -173,12 +175,12 @@ class FlutterQuillEmbeds {
controller: cameraButtonOptions.controller ?? controller,
options: cameraButtonOptions,
),
- if (mediaButtonOptions != null)
- (controller, toolbarIconSize, iconTheme, dialogTheme) =>
- QuillToolbarMediaButton(
- controller: mediaButtonOptions.controller ?? controller,
- options: mediaButtonOptions,
- ),
+ // if (mediaButtonOptions != null)
+ // (controller, toolbarIconSize, iconTheme, dialogTheme) =>
+ // QuillToolbarMediaButton(
+ // controller: mediaButtonOptions.controller ?? controller,
+ // options: mediaButtonOptions,
+ // ),
if (formulaButtonOptions != null && !kIsWeb)
(controller, toolbarIconSize, iconTheme, dialogTheme) =>
QuillToolbarFormulaButton(
diff --git a/flutter_quill_extensions/lib/logic/extensions/controller.dart b/flutter_quill_extensions/lib/logic/extensions/controller.dart
index 856b87b7..2318e5f6 100644
--- a/flutter_quill_extensions/lib/logic/extensions/controller.dart
+++ b/flutter_quill_extensions/lib/logic/extensions/controller.dart
@@ -28,7 +28,7 @@ extension QuillControllerExt on QuillController {
required String imageUrl,
}) {
this
- ..skipRequestKeyboard = skipRequestKeyboard
+ ..skipRequestKeyboard = true
..replaceText(
index,
length,
@@ -36,4 +36,12 @@ extension QuillControllerExt on QuillController {
null,
);
}
+
+ void insertVideoBlock({
+ required String videoUrl,
+ }) {
+ this
+ ..skipRequestKeyboard = true
+ ..replaceText(index, length, BlockEmbed.video(videoUrl), null);
+ }
}
diff --git a/flutter_quill_extensions/lib/presentation/embeds/embed_types/camera.dart b/flutter_quill_extensions/lib/presentation/embeds/embed_types/camera.dart
new file mode 100644
index 00000000..7ce65321
--- /dev/null
+++ b/flutter_quill_extensions/lib/presentation/embeds/embed_types/camera.dart
@@ -0,0 +1,36 @@
+import 'package:flutter/widgets.dart' show BuildContext;
+import 'package:meta/meta.dart' show immutable;
+
+import 'image.dart';
+
+enum CameraAction {
+ video,
+ image,
+}
+
+/// When the user click the camera button, should we take a photo or record
+/// a video using the camera
+///
+/// by default will show a dialog that ask the user which option he/she wants
+typedef OnRequestCameraActionCallback = Future Function(
+ BuildContext context,
+);
+
+@immutable
+class QuillToolbarCameraConfigurations {
+ const QuillToolbarCameraConfigurations({
+ this.onRequestCameraActionCallback,
+ OnImageInsertCallback? onImageInsertCallback,
+ this.onImageInsertedCallback,
+ }) : _onImageInsertCallback = onImageInsertCallback;
+
+ final OnRequestCameraActionCallback? onRequestCameraActionCallback;
+
+ final OnImageInsertedCallback? onImageInsertedCallback;
+
+ final OnImageInsertCallback? _onImageInsertCallback;
+
+ OnImageInsertCallback get onImageInsertCallback {
+ return _onImageInsertCallback ?? defaultOnImageInsertCallback();
+ }
+}
diff --git a/flutter_quill_extensions/lib/presentation/embeds/embed_types/image.dart b/flutter_quill_extensions/lib/presentation/embeds/embed_types/image.dart
index 5f1bc001..e191c129 100644
--- a/flutter_quill_extensions/lib/presentation/embeds/embed_types/image.dart
+++ b/flutter_quill_extensions/lib/presentation/embeds/embed_types/image.dart
@@ -1,12 +1,13 @@
import 'package:flutter/widgets.dart' show BuildContext;
import 'package:flutter_quill/flutter_quill.dart';
+import 'package:meta/meta.dart' show immutable;
import '../../../logic/extensions/controller.dart';
import '../../../logic/services/image_picker/s_image_picker.dart';
/// When request picking an image, for example when the image button toolbar
/// clicked, it should be null in case the user didn't choose any image or
/// any other reasons, and it should be the image file path as string that is
-/// existied in case the user picked the image successfully
+/// exists in case the user picked the image successfully
///
/// by default we already have a default implementation that show a dialog
/// request the source for picking the image, from gallery, link or camera
@@ -15,13 +16,8 @@ typedef OnRequestPickImage = Future Function(
ImagePickerService imagePickerService,
);
-/// When a new image picked this callback will called and you might want to
-/// do some logic depending on your use case
-typedef OnImagePickedCallback = Future Function(
- String image,
-);
-
/// A callback will called when inserting a image in the editor
+/// it have the logic that will insert the image block using the controller
typedef OnImageInsertCallback = Future Function(
String image,
QuillController controller,
@@ -35,8 +31,35 @@ OnImageInsertCallback defaultOnImageInsertCallback() {
};
}
+/// When a new image picked this callback will called and you might want to
+/// do some logic depending on your use case
+typedef OnImageInsertedCallback = Future Function(
+ String image,
+);
+
enum InsertImageSource {
gallery,
camera,
link,
}
+
+/// Configurations for dealing with images, on insert a image
+/// on request picking a image
+@immutable
+class QuillToolbarImageConfigurations {
+ const QuillToolbarImageConfigurations({
+ this.onRequestPickImage,
+ this.onImageInsertedCallback,
+ OnImageInsertCallback? onImageInsertCallback,
+ }) : _onImageInsertCallback = onImageInsertCallback;
+
+ final OnRequestPickImage? onRequestPickImage;
+
+ final OnImageInsertedCallback? onImageInsertedCallback;
+
+ final OnImageInsertCallback? _onImageInsertCallback;
+
+ OnImageInsertCallback get onImageInsertCallback {
+ return _onImageInsertCallback ?? defaultOnImageInsertCallback();
+ }
+}
diff --git a/flutter_quill_extensions/lib/presentation/embeds/embed_types/video.dart b/flutter_quill_extensions/lib/presentation/embeds/embed_types/video.dart
new file mode 100644
index 00000000..d69ab5e0
--- /dev/null
+++ b/flutter_quill_extensions/lib/presentation/embeds/embed_types/video.dart
@@ -0,0 +1,66 @@
+import 'package:flutter/widgets.dart' show BuildContext;
+import 'package:flutter_quill/flutter_quill.dart';
+import 'package:meta/meta.dart' show immutable;
+
+import '../../../logic/extensions/controller.dart';
+import '../../../logic/services/image_picker/s_image_picker.dart';
+
+/// When request picking an video, for example when the video button toolbar
+/// clicked, it should be null in case the user didn't choose any video or
+/// any other reasons, and it should be the video file path as string that is
+/// exists in case the user picked the video successfully
+///
+/// by default we already have a default implementation that show a dialog
+/// request the source for picking the video, from gallery, link or camera
+typedef OnRequestPickVideo = Future Function(
+ BuildContext context,
+ ImagePickerService imagePickerService,
+);
+
+/// A callback will called when inserting a video in the editor
+/// it have the logic that will insert the video block using the controller
+typedef OnVideoInsertCallback = Future Function(
+ String video,
+ QuillController controller,
+);
+
+OnVideoInsertCallback defaultOnVideoInsertCallback() {
+ return (videoUrl, controller) async {
+ controller
+ ..skipRequestKeyboard = true
+ ..insertVideoBlock(videoUrl: videoUrl);
+ };
+}
+
+/// When a new video picked this callback will called and you might want to
+/// do some logic depending on your use case
+typedef OnVideoInsertedCallback = Future Function(
+ String video,
+);
+
+enum InsertVideoSource {
+ gallery,
+ camera,
+ link,
+}
+
+/// Configurations for dealing with videos, on insert a video
+/// on request picking a video
+@immutable
+class QuillToolbarVideoConfigurations {
+ const QuillToolbarVideoConfigurations({
+ this.onRequestPickVideo,
+ this.onVideoInsertedCallback,
+ OnVideoInsertCallback? onVideoInsertCallback,
+ }) : _onVideoInsertCallback = onVideoInsertCallback;
+
+ final OnRequestPickVideo? onRequestPickVideo;
+
+ final OnVideoInsertedCallback? onVideoInsertedCallback;
+
+ final OnVideoInsertCallback? _onVideoInsertCallback;
+
+ OnVideoInsertCallback get onVideoInsertCallback {
+ return _onVideoInsertCallback ?? defaultOnVideoInsertCallback();
+ }
+}
diff --git a/flutter_quill_extensions/lib/presentation/embeds/toolbar/camera_button.dart b/flutter_quill_extensions/lib/presentation/embeds/toolbar/camera_button/camera_button.dart
similarity index 53%
rename from flutter_quill_extensions/lib/presentation/embeds/toolbar/camera_button.dart
rename to flutter_quill_extensions/lib/presentation/embeds/toolbar/camera_button/camera_button.dart
index 25859832..1324c936 100644
--- a/flutter_quill_extensions/lib/presentation/embeds/toolbar/camera_button.dart
+++ b/flutter_quill_extensions/lib/presentation/embeds/toolbar/camera_button/camera_button.dart
@@ -3,9 +3,12 @@
import 'package:flutter/material.dart';
import 'package:flutter_quill/flutter_quill.dart';
import 'package:flutter_quill/translations.dart';
-import 'package:image_picker/image_picker.dart';
-import '../../models/config/toolbar/buttons/camera.dart';
+import '../../../../logic/models/config/configurations.dart';
+import '../../../../logic/services/image_picker/image_options.dart';
+import '../../../models/config/toolbar/buttons/camera.dart';
+import '../../embed_types/camera.dart';
+import 'select_camera_action.dart';
class QuillToolbarCameraButton extends StatelessWidget {
const QuillToolbarCameraButton({
@@ -70,17 +73,14 @@ class QuillToolbarCameraButton extends StatelessWidget {
if (childBuilder != null) {
childBuilder(
QuillToolbarCameraButtonOptions(
- onImagePickCallback: options.onImagePickCallback,
onVideoPickCallback: options.onVideoPickCallback,
afterButtonPressed: _afterButtonPressed(context),
- cameraPickSettingSelector: options.cameraPickSettingSelector,
- filePickImpl: options.filePickImpl,
iconData: options.iconData,
fillColor: options.fillColor,
iconSize: options.iconSize,
iconTheme: options.iconTheme,
tooltip: options.tooltip,
- webImagePickImpl: options.webImagePickImpl,
+ cameraConfigurations: options.cameraConfigurations,
webVideoPickImpl: options.webVideoPickImpl,
),
QuillToolbarCameraButtonExtraOptions(
@@ -109,84 +109,92 @@ class QuillToolbarCameraButton extends StatelessWidget {
);
}
+ Future _getCameraAction(BuildContext context) async {
+ final customCallback =
+ options.cameraConfigurations.onRequestCameraActionCallback;
+ if (customCallback != null) {
+ return await customCallback(context);
+ }
+ final cameraAction = await showDialog(
+ context: context,
+ builder: (ctx) => const SelectCameraActionDialog(),
+ );
+
+ return cameraAction;
+ }
+
Future _onPressedHandler(
BuildContext context,
QuillController controller,
) async {
- if (onVideoPickCallback == null && onImagePickCallback == null) {
- throw ArgumentError(
- 'onImagePickCallback and onVideoPickCallback are both null',
- );
- }
- final selector = options.cameraPickSettingSelector ??
- (context) => showDialog(
- context: context,
- builder: (ctx) => AlertDialog(
- contentPadding: EdgeInsets.zero,
- backgroundColor: Colors.transparent,
- content: Column(
- mainAxisSize: MainAxisSize.min,
- children: [
- if (onImagePickCallback != null)
- TextButton.icon(
- icon: const Icon(
- Icons.camera,
- color: Colors.orangeAccent,
- ),
- label: Text('Camera'.i18n),
- onPressed: () =>
- Navigator.pop(ctx, MediaPickSetting.camera),
- ),
- if (onVideoPickCallback != null)
- TextButton.icon(
- icon: const Icon(
- Icons.video_call,
- color: Colors.cyanAccent,
- ),
- label: Text('Video'.i18n),
- onPressed: () =>
- Navigator.pop(ctx, MediaPickSetting.video),
- )
- ],
- ),
- ),
- );
-
- final source = await selector(context);
- if (source == null) {
+ // if (onVideoPickCallback == null && onImagePickCallback == null) {
+ // throw ArgumentError(
+ // 'onImagePickCallback and onVideoPickCallback are both null',
+ // );
+ // }
+
+ final cameraAction = await _getCameraAction(context);
+
+ if (cameraAction == null) {
return;
}
- 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: options.webVideoPickImpl,
+
+ final imagePickerService =
+ QuillSharedExtensionsConfigurations.get(context: context)
+ .imagePickerService;
+
+ switch (cameraAction) {
+ case CameraAction.video:
+ final videoFile = await imagePickerService.pickVideo(
+ source: ImageSource.camera,
);
- break;
- case MediaPickSetting.gallery:
- throw ArgumentError(
- 'Invalid MediaSetting for the camera button.\n'
- 'gallery is not related to camera button',
+ if (videoFile == null) {
+ return;
+ }
+ // TODO: Implement this
+ case CameraAction.image:
+ final imageFile = await imagePickerService.pickImage(
+ source: ImageSource.camera,
);
- case MediaPickSetting.link:
- throw ArgumentError(
- 'Invalid MediaSetting for the camera button.\n'
- 'link is not related to camera button',
+ if (imageFile == null) {
+ return;
+ }
+ options.cameraConfigurations.onImageInsertCallback(
+ imageFile.path,
+ controller,
);
+ await options.cameraConfigurations.onImageInsertedCallback
+ ?.call(imageFile.path);
}
+
+ // final file = await switch (cameraAction) {
+ // CameraAction.image =>
+ // imagePickerService.pickImage(source: ImageSource.camera),
+ // CameraAction.video =>
+ // imagePickerService.pickVideo(source: ImageSource.camera),
+ // };
+
+ // 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: options.webVideoPickImpl,
+ // );
+ // break;
+ // }
}
}
diff --git a/flutter_quill_extensions/lib/presentation/embeds/toolbar/camera_button/select_camera_action.dart b/flutter_quill_extensions/lib/presentation/embeds/toolbar/camera_button/select_camera_action.dart
new file mode 100644
index 00000000..a79fa393
--- /dev/null
+++ b/flutter_quill_extensions/lib/presentation/embeds/toolbar/camera_button/select_camera_action.dart
@@ -0,0 +1,37 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_quill/translations.dart';
+
+import '../../embed_types/camera.dart';
+
+class SelectCameraActionDialog extends StatelessWidget {
+ const SelectCameraActionDialog({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return 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('Photo'.i18n),
+ onPressed: () => Navigator.pop(context, CameraAction.image),
+ ),
+ TextButton.icon(
+ icon: const Icon(
+ Icons.video_call,
+ color: Colors.cyanAccent,
+ ),
+ label: Text('Video'.i18n),
+ onPressed: () => Navigator.pop(context, CameraAction.video),
+ )
+ ],
+ ),
+ );
+ }
+}
diff --git a/flutter_quill_extensions/lib/presentation/embeds/toolbar/image_button/image_button.dart b/flutter_quill_extensions/lib/presentation/embeds/toolbar/image_button/image_button.dart
index fbc7d361..7451a435 100644
--- a/flutter_quill_extensions/lib/presentation/embeds/toolbar/image_button/image_button.dart
+++ b/flutter_quill_extensions/lib/presentation/embeds/toolbar/image_button/image_button.dart
@@ -115,6 +115,7 @@ class QuillToolbarImageButton extends StatelessWidget {
final imagePickerService =
QuillSharedExtensionsConfigurations.get(context: context)
.imagePickerService;
+
final onRequestPickImage =
options.imageButtonConfigurations.onRequestPickImage;
if (onRequestPickImage != null) {
@@ -125,6 +126,8 @@ class QuillToolbarImageButton extends StatelessWidget {
if (imageUrl != null) {
await options.imageButtonConfigurations
.onImageInsertCallback(imageUrl, controller);
+ await options.imageButtonConfigurations.onImageInsertedCallback
+ ?.call(imageUrl);
}
return;
}
@@ -134,27 +137,26 @@ class QuillToolbarImageButton extends StatelessWidget {
if (source == null) {
return;
}
- final String? imageUrl;
- switch (source) {
- case InsertImageSource.gallery:
- imageUrl = (await imagePickerService.pickImage(
+
+ final imageUrl = switch (source) {
+ InsertImageSource.gallery => (await imagePickerService.pickImage(
source: ImageSource.gallery,
))
- ?.path;
- break;
- case InsertImageSource.link:
- imageUrl = await _typeLink(context);
- break;
- case InsertImageSource.camera:
- imageUrl = (await imagePickerService.pickImage(
+ ?.path,
+ InsertImageSource.link => await _typeLink(context),
+ InsertImageSource.camera => (await imagePickerService.pickImage(
source: ImageSource.camera,
))
- ?.path;
- break;
+ ?.path,
+ };
+ if (imageUrl == null) {
+ return;
}
- if (imageUrl != null && imageUrl.trim().isNotEmpty) {
+ if (imageUrl.trim().isNotEmpty) {
await options.imageButtonConfigurations
.onImageInsertCallback(imageUrl, controller);
+ await options.imageButtonConfigurations.onImageInsertedCallback
+ ?.call(imageUrl);
}
}
@@ -164,6 +166,7 @@ class QuillToolbarImageButton extends StatelessWidget {
builder: (_) => TypeLinkDialog(
dialogTheme: options.dialogTheme,
linkRegExp: options.linkRegExp,
+ linkType: LinkType.image,
),
);
return value;
diff --git a/flutter_quill_extensions/lib/presentation/embeds/toolbar/image_button/select_image_source.dart b/flutter_quill_extensions/lib/presentation/embeds/toolbar/image_button/select_image_source.dart
index ab18fb28..3c838934 100644
--- a/flutter_quill_extensions/lib/presentation/embeds/toolbar/image_button/select_image_source.dart
+++ b/flutter_quill_extensions/lib/presentation/embeds/toolbar/image_button/select_image_source.dart
@@ -32,7 +32,7 @@ class SelectImageSourceDialog extends StatelessWidget {
ListTile(
title: const Text('Link'),
subtitle: const Text(
- 'Paste a photo using https link',
+ 'Paste a photo using a link',
),
leading: const Icon(Icons.link),
onTap: () => Navigator.of(context).pop(InsertImageSource.link),
diff --git a/flutter_quill_extensions/lib/presentation/embeds/toolbar/media_button.dart b/flutter_quill_extensions/lib/presentation/embeds/toolbar/media_button.dart
deleted file mode 100644
index 212bd39e..00000000
--- a/flutter_quill_extensions/lib/presentation/embeds/toolbar/media_button.dart
+++ /dev/null
@@ -1,533 +0,0 @@
-// ignore_for_file: use_build_context_synchronously
-
-import 'dart:math' as math;
-import 'dart:ui';
-
-import 'package:flutter/foundation.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_quill/extensions.dart';
-import 'package:flutter_quill/flutter_quill.dart';
-import 'package:flutter_quill/translations.dart';
-import 'package:image_picker/image_picker.dart';
-
-import '../../models/config/toolbar/buttons/media_button.dart';
-import '../embed_types.dart';
-import 'utils/image_video_utils.dart';
-
-/// Widget which combines [ImageButton] and [VideButton] widgets. This widget
-/// has more customization and uses dialog similar to one which is used
-/// on [http://quilljs.com].
-class QuillToolbarMediaButton extends StatelessWidget {
- QuillToolbarMediaButton({
- required this.controller,
- required this.options,
- super.key,
- }) : assert(options.type == QuillMediaType.image,
- 'Video selection is not supported yet');
-
- final QuillController controller;
- final QuillToolbarMediaButtonOptions options;
-
- double _iconSize(BuildContext context) {
- final baseFontSize = baseButtonExtraOptions(context).globalIconSize;
- final iconSize = options.iconSize;
- return iconSize ?? baseFontSize;
- }
-
- VoidCallback? _afterButtonPressed(BuildContext context) {
- return options.afterButtonPressed ??
- baseButtonExtraOptions(context).afterButtonPressed;
- }
-
- QuillIconTheme? _iconTheme(BuildContext context) {
- return options.iconTheme ?? baseButtonExtraOptions(context).iconTheme;
- }
-
- QuillToolbarBaseButtonOptions baseButtonExtraOptions(BuildContext context) {
- return context.requireQuillToolbarBaseButtonOptions;
- }
-
- (IconData, String) get _defaultData {
- switch (options.type) {
- case QuillMediaType.image:
- return (Icons.perm_media, 'Photo media button');
- case QuillMediaType.video:
- throw UnsupportedError('The video is not supported yet.');
- }
- }
-
- IconData _iconData(BuildContext context) {
- return options.iconData ??
- baseButtonExtraOptions(context).iconData ??
- _defaultData.$1;
- }
-
- String _tooltip(BuildContext context) {
- return options.tooltip ??
- baseButtonExtraOptions(context).tooltip ??
- _defaultData.$2;
- // ('Camera'.i18n);
- }
-
- void _sharedOnPressed(BuildContext context) {
- _onPressedHandler(context);
- _afterButtonPressed(context);
- }
-
- @override
- Widget build(BuildContext context) {
- final tooltip = _tooltip(context);
- final iconSize = _iconSize(context);
- final iconData = _iconData(context);
- final childBuilder =
- options.childBuilder ?? baseButtonExtraOptions(context).childBuilder;
- final iconTheme = _iconTheme(context);
-
- if (childBuilder != null) {
- return childBuilder(
- QuillToolbarMediaButtonOptions(
- type: options.type,
- onMediaPickedCallback: options.onMediaPickedCallback,
- onImagePickCallback: options.onImagePickCallback,
- onVideoPickCallback: options.onVideoPickCallback,
- iconData: iconData,
- afterButtonPressed: _afterButtonPressed(context),
- autovalidateMode: options.autovalidateMode,
- childrenSpacing: options.childrenSpacing,
- dialogBarrierColor: options.dialogBarrierColor,
- dialogTheme: options.dialogTheme,
- filePickImpl: options.filePickImpl,
- fillColor: options.fillColor,
- galleryButtonText: options.galleryButtonText,
- iconTheme: iconTheme,
- iconSize: iconSize,
- hintText: options.hintText,
- labelText: options.labelText,
- submitButtonSize: options.submitButtonSize,
- linkButtonText: options.linkButtonText,
- mediaFilePicker: options.mediaFilePicker,
- submitButtonText: options.submitButtonText,
- validationMessage: options.validationMessage,
- webImagePickImpl: options.webImagePickImpl,
- webVideoPickImpl: options.webVideoPickImpl,
- tooltip: options.tooltip,
- ),
- QuillToolbarMediaButtonExtraOptions(
- context: context,
- controller: controller,
- onPressed: () => _sharedOnPressed(context),
- ),
- );
- }
-
- final theme = Theme.of(context);
-
- final iconColor =
- options.iconTheme?.iconUnselectedColor ?? theme.iconTheme.color;
- final iconFillColor = options.iconTheme?.iconUnselectedFillColor ??
- options.fillColor ??
- theme.canvasColor;
-
- return QuillToolbarIconButton(
- icon: Icon(iconData, size: iconSize, color: iconColor),
- tooltip: tooltip,
- highlightElevation: 0,
- hoverElevation: 0,
- size: iconSize * 1.77,
- fillColor: iconFillColor,
- borderRadius: iconTheme?.borderRadius ?? 2,
- onPressed: () => _sharedOnPressed(context),
- );
- }
-
- Future _onPressedHandler(BuildContext context) async {
- if (options.onMediaPickedCallback == null) {
- _inputLink(context);
- return;
- }
- final mediaSource = await showDialog(
- context: context,
- builder: (_) => MediaSourceSelectorDialog(
- dialogTheme: options.dialogTheme,
- galleryButtonText: options.galleryButtonText,
- linkButtonText: options.linkButtonText,
- ),
- );
- if (mediaSource == null) {
- return;
- }
- switch (mediaSource) {
- case MediaPickSetting.gallery:
- await _pickImage();
- break;
- case MediaPickSetting.link:
- _inputLink(context);
- break;
- case MediaPickSetting.camera:
- await ImageVideoUtils.handleImageButtonTap(
- context,
- controller,
- ImageSource.camera,
- options.onImagePickCallback,
- filePickImpl: options.filePickImpl,
- webImagePickImpl: options.webImagePickImpl,
- );
- break;
- case MediaPickSetting.video:
- await ImageVideoUtils.handleVideoButtonTap(
- context,
- controller,
- ImageSource.camera,
- options.onVideoPickCallback,
- filePickImpl: options.filePickImpl,
- webVideoPickImpl: options.webVideoPickImpl,
- );
- break;
- }
- }
-
- Future _pickImage() async {
- if (!(kIsWeb || isMobile() || isDesktop())) {
- throw UnsupportedError(
- 'Unsupported target platform: ${defaultTargetPlatform.name}',
- );
- }
-
- final mediaFileUrl = await _pickMediaFileUrl();
-
- if (mediaFileUrl != null) {
- final index = controller.selection.baseOffset;
- final length = controller.selection.extentOffset - index;
- controller.replaceText(
- index,
- length,
- BlockEmbed.image(mediaFileUrl),
- null,
- );
- }
- }
-
- Future _pickMediaFileUrl() async {
- final mediaFile = await options.mediaFilePicker?.call(options.type);
- return mediaFile != null
- ? options.onMediaPickedCallback?.call(mediaFile)
- : null;
- }
-
- void _inputLink(BuildContext context) {
- showDialog(
- context: context,
- builder: (_) => MediaLinkDialog(
- dialogTheme: options.dialogTheme,
- labelText: options.labelText,
- hintText: options.hintText,
- buttonText: options.submitButtonText,
- buttonSize: options.submitButtonSize,
- childrenSpacing: options.childrenSpacing,
- autovalidateMode: options.autovalidateMode,
- validationMessage: options.validationMessage,
- ),
- ).then(_linkSubmitted);
- }
-
- void _linkSubmitted(String? value) {
- if (value != null && value.isNotEmpty) {
- final index = controller.selection.baseOffset;
- final length = controller.selection.extentOffset - index;
- final data = options.type.isImage
- ? BlockEmbed.image(value)
- : BlockEmbed.video(value);
- controller.replaceText(index, length, data, null);
- }
- }
-}
-
-/// Provides a dialog for input link to media resource.
-class MediaLinkDialog extends StatefulWidget {
- const MediaLinkDialog({
- super.key,
- this.link,
- this.dialogTheme,
- this.childrenSpacing = 16.0,
- this.labelText,
- this.hintText,
- this.buttonText,
- this.buttonSize,
- this.autovalidateMode = AutovalidateMode.disabled,
- this.validationMessage,
- }) : assert(childrenSpacing > 0);
-
- final String? link;
- final QuillDialogTheme? dialogTheme;
-
- /// The margin between child widgets in the dialog.
- final double childrenSpacing;
-
- /// The text of label in link add mode.
- final String? labelText;
-
- /// The hint text for link [TextField].
- final String? hintText;
-
- /// The text of the submit button.
- final String? buttonText;
-
- /// The size of dialog buttons.
- final Size? buttonSize;
-
- final AutovalidateMode autovalidateMode;
- final String? validationMessage;
-
- @override
- State createState() => _MediaLinkDialogState();
-}
-
-class _MediaLinkDialogState extends State {
- final _linkFocus = FocusNode();
- final _linkController = TextEditingController();
-
- @override
- void dispose() {
- _linkFocus.dispose();
- _linkController.dispose();
- super.dispose();
- }
-
- @override
- Widget build(BuildContext context) {
- final constraints = widget.dialogTheme?.linkDialogConstraints ??
- () {
- final size = MediaQuery.sizeOf(context);
- final maxWidth = kIsWeb ? size.width / 4 : size.width - 80;
- return BoxConstraints(maxWidth: maxWidth, maxHeight: 80);
- }();
-
- final buttonStyle = widget.buttonSize != null
- ? Theme.of(context)
- .elevatedButtonTheme
- .style
- ?.copyWith(fixedSize: MaterialStatePropertyAll(widget.buttonSize))
- : widget.dialogTheme?.buttonStyle;
-
- final isWrappable = widget.dialogTheme?.isWrappable ?? false;
-
- final children = [
- Text(widget.labelText ?? 'Enter media'.i18n),
- UtilityWidgets.maybeWidget(
- enabled: !isWrappable,
- wrapper: (child) => Expanded(
- child: child,
- ),
- child: Padding(
- padding: EdgeInsets.symmetric(horizontal: widget.childrenSpacing),
- child: TextFormField(
- controller: _linkController,
- focusNode: _linkFocus,
- style: widget.dialogTheme?.inputTextStyle,
- keyboardType: TextInputType.url,
- textInputAction: TextInputAction.done,
- decoration: InputDecoration(
- labelStyle: widget.dialogTheme?.labelTextStyle,
- hintText: widget.hintText,
- ),
- autofocus: true,
- autovalidateMode: widget.autovalidateMode,
- validator: _validateLink,
- onChanged: _linkChanged,
- ),
- ),
- ),
- ElevatedButton(
- onPressed: _canPress() ? _submitLink : null,
- style: buttonStyle,
- child: Text(widget.buttonText ?? 'Ok'.i18n),
- ),
- ];
-
- return Dialog(
- backgroundColor: widget.dialogTheme?.dialogBackgroundColor,
- shape: widget.dialogTheme?.shape ??
- DialogTheme.of(context).shape ??
- RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)),
- child: ConstrainedBox(
- constraints: constraints,
- child: Padding(
- padding:
- widget.dialogTheme?.linkDialogPadding ?? const EdgeInsets.all(16),
- child: Form(
- child: isWrappable
- ? Wrap(
- alignment: WrapAlignment.center,
- crossAxisAlignment: WrapCrossAlignment.center,
- runSpacing: widget.dialogTheme?.runSpacing ?? 0.0,
- children: children,
- )
- : Row(
- children: children,
- ),
- ),
- ),
- ),
- );
- }
-
- bool _canPress() => _validateLink(_linkController.text) == null;
-
- void _linkChanged(String value) {
- setState(() {
- _linkController.text = value;
- });
- }
-
- void _submitLink() => Navigator.pop(context, _linkController.text);
-
- String? _validateLink(String? value) {
- if ((value?.isEmpty ?? false) ||
- !AutoFormatMultipleLinksRule.oneLineLinkRegExp.hasMatch(value!)) {
- return widget.validationMessage ?? 'That is not a valid URL';
- }
-
- return null;
- }
-}
-
-/// Media souce selector.
-class MediaSourceSelectorDialog extends StatelessWidget {
- const MediaSourceSelectorDialog({
- super.key,
- this.dialogTheme,
- this.galleryButtonText,
- this.linkButtonText,
- });
-
- final QuillDialogTheme? dialogTheme;
-
- /// The text of the gallery button [MediaSourceSelectorDialog].
- final String? galleryButtonText;
-
- /// The text of the link button [MediaSourceSelectorDialog].
- final String? linkButtonText;
-
- @override
- Widget build(BuildContext context) {
- final constraints = dialogTheme?.mediaSelectorDialogConstraints ??
- () {
- final size = MediaQuery.sizeOf(context);
- double maxWidth, maxHeight;
- if (kIsWeb) {
- maxWidth = size.width / 7;
- maxHeight = size.height / 7;
- } else {
- maxWidth = size.width - 80;
- maxHeight = maxWidth / 2;
- }
- return BoxConstraints(maxWidth: maxWidth, maxHeight: maxHeight);
- }();
-
- final shape = dialogTheme?.shape ??
- DialogTheme.of(context).shape ??
- RoundedRectangleBorder(borderRadius: BorderRadius.circular(4));
-
- return Dialog(
- backgroundColor: dialogTheme?.dialogBackgroundColor,
- shape: shape,
- child: ConstrainedBox(
- constraints: constraints,
- child: Padding(
- padding: dialogTheme?.mediaSelectorDialogPadding ??
- const EdgeInsets.all(16),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Expanded(
- child: TextButtonWithIcon(
- icon: Icons.collections,
- label: galleryButtonText ?? 'Gallery'.i18n,
- onPressed: () =>
- Navigator.pop(context, MediaPickSetting.gallery),
- ),
- ),
- const SizedBox(width: 10),
- Expanded(
- child: TextButtonWithIcon(
- icon: Icons.link,
- label: linkButtonText ?? 'Link'.i18n,
- onPressed: () =>
- Navigator.pop(context, MediaPickSetting.link),
- ),
- )
- ],
- ),
- ),
- ),
- );
- }
-}
-
-class TextButtonWithIcon extends StatelessWidget {
- const TextButtonWithIcon({
- required this.label,
- required this.icon,
- required this.onPressed,
- this.textStyle,
- super.key,
- });
-
- final String label;
- final IconData icon;
- final VoidCallback onPressed;
- final TextStyle? textStyle;
-
- @override
- Widget build(BuildContext context) {
- final theme = Theme.of(context);
- final scale = MediaQuery.maybeOf(context)?.textScaleFactor ?? 1;
- final gap = scale <= 1 ? 8.0 : lerpDouble(8, 4, math.min(scale - 1, 1))!;
- final buttonStyle = TextButtonTheme.of(context).style;
- final shape = buttonStyle?.shape?.resolve({}) ??
- const RoundedRectangleBorder(
- borderRadius: BorderRadius.all(
- Radius.circular(4),
- ),
- );
- return Material(
- shape: shape,
- textStyle: textStyle ??
- theme.textButtonTheme.style?.textStyle?.resolve({}) ??
- theme.textTheme.labelLarge,
- elevation: buttonStyle?.elevation?.resolve({}) ?? 0,
- child: InkWell(
- customBorder: shape,
- onTap: onPressed,
- child: Padding(
- padding: const EdgeInsets.all(16),
- child: Column(
- mainAxisSize: MainAxisSize.min,
- children: [
- Icon(icon),
- SizedBox(height: gap),
- Flexible(child: Text(label)),
- ],
- ),
- ),
- ),
- );
- }
-}
-
-/// Default file picker.
-// Future _defaultMediaPicker(QuillMediaType mediaType) async {
-// final pickedFile = mediaType.isImage
-// ? await ImagePicker().pickImage(source: ImageSource.gallery)
-// : await ImagePicker().pickVideo(source: ImageSource.gallery);
-
-// if (pickedFile != null) {
-// return QuillFile(
-// name: pickedFile.name,
-// path: pickedFile.path,
-// bytes: await pickedFile.readAsBytes(),
-// );
-// }
-
-// return null;
-// }
diff --git a/flutter_quill_extensions/lib/presentation/embeds/toolbar/media_button/media_button.dart b/flutter_quill_extensions/lib/presentation/embeds/toolbar/media_button/media_button.dart
new file mode 100644
index 00000000..1d8e5fcf
--- /dev/null
+++ b/flutter_quill_extensions/lib/presentation/embeds/toolbar/media_button/media_button.dart
@@ -0,0 +1,533 @@
+// // ignore_for_file: use_build_context_synchronously
+
+// import 'dart:math' as math;
+// import 'dart:ui';
+
+// import 'package:flutter/foundation.dart';
+// import 'package:flutter/material.dart';
+// import 'package:flutter_quill/extensions.dart';
+// import 'package:flutter_quill/flutter_quill.dart';
+// import 'package:flutter_quill/translations.dart';
+// import 'package:image_picker/image_picker.dart';
+
+// import '../../../models/config/toolbar/buttons/media_button.dart';
+// import '../../embed_types.dart';
+// import '../utils/image_video_utils.dart';
+
+// /// Widget which combines [ImageButton] and [VideButton] widgets. This widget
+// /// has more customization and uses dialog similar to one which is used
+// /// on [http://quilljs.com].
+// class QuillToolbarMediaButton extends StatelessWidget {
+// QuillToolbarMediaButton({
+// required this.controller,
+// required this.options,
+// super.key,
+// }) : assert(options.type == QuillMediaType.image,
+// 'Video selection is not supported yet');
+
+// final QuillController controller;
+// final QuillToolbarMediaButtonOptions options;
+
+// double _iconSize(BuildContext context) {
+// final baseFontSize = baseButtonExtraOptions(context).globalIconSize;
+// final iconSize = options.iconSize;
+// return iconSize ?? baseFontSize;
+// }
+
+// VoidCallback? _afterButtonPressed(BuildContext context) {
+// return options.afterButtonPressed ??
+// baseButtonExtraOptions(context).afterButtonPressed;
+// }
+
+// QuillIconTheme? _iconTheme(BuildContext context) {
+// return options.iconTheme ?? baseButtonExtraOptions(context).iconTheme;
+// }
+
+// QuillToolbarBaseButtonOptions baseButtonExtraOptions(BuildContext context) {
+// return context.requireQuillToolbarBaseButtonOptions;
+// }
+
+// (IconData, String) get _defaultData {
+// switch (options.type) {
+// case QuillMediaType.image:
+// return (Icons.perm_media, 'Photo media button');
+// case QuillMediaType.video:
+// throw UnsupportedError('The video is not supported yet.');
+// }
+// }
+
+// IconData _iconData(BuildContext context) {
+// return options.iconData ??
+// baseButtonExtraOptions(context).iconData ??
+// _defaultData.$1;
+// }
+
+// String _tooltip(BuildContext context) {
+// return options.tooltip ??
+// baseButtonExtraOptions(context).tooltip ??
+// _defaultData.$2;
+// // ('Camera'.i18n);
+// }
+
+// void _sharedOnPressed(BuildContext context) {
+// _onPressedHandler(context);
+// _afterButtonPressed(context);
+// }
+
+// @override
+// Widget build(BuildContext context) {
+// final tooltip = _tooltip(context);
+// final iconSize = _iconSize(context);
+// final iconData = _iconData(context);
+// final childBuilder =
+// options.childBuilder ?? baseButtonExtraOptions(context).childBuilder;
+// final iconTheme = _iconTheme(context);
+
+// if (childBuilder != null) {
+// return childBuilder(
+// QuillToolbarMediaButtonOptions(
+// type: options.type,
+// onMediaPickedCallback: options.onMediaPickedCallback,
+// onImagePickCallback: options.onImagePickCallback,
+// onVideoPickCallback: options.onVideoPickCallback,
+// iconData: iconData,
+// afterButtonPressed: _afterButtonPressed(context),
+// autovalidateMode: options.autovalidateMode,
+// childrenSpacing: options.childrenSpacing,
+// dialogBarrierColor: options.dialogBarrierColor,
+// dialogTheme: options.dialogTheme,
+// filePickImpl: options.filePickImpl,
+// fillColor: options.fillColor,
+// galleryButtonText: options.galleryButtonText,
+// iconTheme: iconTheme,
+// iconSize: iconSize,
+// hintText: options.hintText,
+// labelText: options.labelText,
+// submitButtonSize: options.submitButtonSize,
+// linkButtonText: options.linkButtonText,
+// mediaFilePicker: options.mediaFilePicker,
+// submitButtonText: options.submitButtonText,
+// validationMessage: options.validationMessage,
+// webImagePickImpl: options.webImagePickImpl,
+// webVideoPickImpl: options.webVideoPickImpl,
+// tooltip: options.tooltip,
+// ),
+// QuillToolbarMediaButtonExtraOptions(
+// context: context,
+// controller: controller,
+// onPressed: () => _sharedOnPressed(context),
+// ),
+// );
+// }
+
+// final theme = Theme.of(context);
+
+// final iconColor =
+// options.iconTheme?.iconUnselectedColor ?? theme.iconTheme.color;
+// final iconFillColor = options.iconTheme?.iconUnselectedFillColor ??
+// options.fillColor ??
+// theme.canvasColor;
+
+// return QuillToolbarIconButton(
+// icon: Icon(iconData, size: iconSize, color: iconColor),
+// tooltip: tooltip,
+// highlightElevation: 0,
+// hoverElevation: 0,
+// size: iconSize * 1.77,
+// fillColor: iconFillColor,
+// borderRadius: iconTheme?.borderRadius ?? 2,
+// onPressed: () => _sharedOnPressed(context),
+// );
+// }
+
+// Future _onPressedHandler(BuildContext context) async {
+// if (options.onMediaPickedCallback == null) {
+// _inputLink(context);
+// return;
+// }
+// final mediaSource = await showDialog(
+// context: context,
+// builder: (_) => MediaSourceSelectorDialog(
+// dialogTheme: options.dialogTheme,
+// galleryButtonText: options.galleryButtonText,
+// linkButtonText: options.linkButtonText,
+// ),
+// );
+// if (mediaSource == null) {
+// return;
+// }
+// switch (mediaSource) {
+// case MediaPickSetting.gallery:
+// await _pickImage();
+// break;
+// case MediaPickSetting.link:
+// _inputLink(context);
+// break;
+// case MediaPickSetting.camera:
+// await ImageVideoUtils.handleImageButtonTap(
+// context,
+// controller,
+// ImageSource.camera,
+// options.onImagePickCallback,
+// filePickImpl: options.filePickImpl,
+// webImagePickImpl: options.webImagePickImpl,
+// );
+// break;
+// case MediaPickSetting.video:
+// await ImageVideoUtils.handleVideoButtonTap(
+// context,
+// controller,
+// ImageSource.camera,
+// options.onVideoPickCallback,
+// filePickImpl: options.filePickImpl,
+// webVideoPickImpl: options.webVideoPickImpl,
+// );
+// break;
+// }
+// }
+
+// Future _pickImage() async {
+// if (!(kIsWeb || isMobile() || isDesktop())) {
+// throw UnsupportedError(
+// 'Unsupported target platform: ${defaultTargetPlatform.name}',
+// );
+// }
+
+// final mediaFileUrl = await _pickMediaFileUrl();
+
+// if (mediaFileUrl != null) {
+// final index = controller.selection.baseOffset;
+// final length = controller.selection.extentOffset - index;
+// controller.replaceText(
+// index,
+// length,
+// BlockEmbed.image(mediaFileUrl),
+// null,
+// );
+// }
+// }
+
+// Future _pickMediaFileUrl() async {
+// final mediaFile = await options.mediaFilePicker?.call(options.type);
+// return mediaFile != null
+// ? options.onMediaPickedCallback?.call(mediaFile)
+// : null;
+// }
+
+// void _inputLink(BuildContext context) {
+// showDialog(
+// context: context,
+// builder: (_) => MediaLinkDialog(
+// dialogTheme: options.dialogTheme,
+// labelText: options.labelText,
+// hintText: options.hintText,
+// buttonText: options.submitButtonText,
+// buttonSize: options.submitButtonSize,
+// childrenSpacing: options.childrenSpacing,
+// autovalidateMode: options.autovalidateMode,
+// validationMessage: options.validationMessage,
+// ),
+// ).then(_linkSubmitted);
+// }
+
+// void _linkSubmitted(String? value) {
+// if (value != null && value.isNotEmpty) {
+// final index = controller.selection.baseOffset;
+// final length = controller.selection.extentOffset - index;
+// final data = options.type.isImage
+// ? BlockEmbed.image(value)
+// : BlockEmbed.video(value);
+// controller.replaceText(index, length, data, null);
+// }
+// }
+// }
+
+// /// Provides a dialog for input link to media resource.
+// class MediaLinkDialog extends StatefulWidget {
+// const MediaLinkDialog({
+// super.key,
+// this.link,
+// this.dialogTheme,
+// this.childrenSpacing = 16.0,
+// this.labelText,
+// this.hintText,
+// this.buttonText,
+// this.buttonSize,
+// this.autovalidateMode = AutovalidateMode.disabled,
+// this.validationMessage,
+// }) : assert(childrenSpacing > 0);
+
+// final String? link;
+// final QuillDialogTheme? dialogTheme;
+
+// /// The margin between child widgets in the dialog.
+// final double childrenSpacing;
+
+// /// The text of label in link add mode.
+// final String? labelText;
+
+// /// The hint text for link [TextField].
+// final String? hintText;
+
+// /// The text of the submit button.
+// final String? buttonText;
+
+// /// The size of dialog buttons.
+// final Size? buttonSize;
+
+// final AutovalidateMode autovalidateMode;
+// final String? validationMessage;
+
+// @override
+// State createState() => _MediaLinkDialogState();
+// }
+
+// class _MediaLinkDialogState extends State {
+// final _linkFocus = FocusNode();
+// final _linkController = TextEditingController();
+
+// @override
+// void dispose() {
+// _linkFocus.dispose();
+// _linkController.dispose();
+// super.dispose();
+// }
+
+// @override
+// Widget build(BuildContext context) {
+// final constraints = widget.dialogTheme?.linkDialogConstraints ??
+// () {
+// final size = MediaQuery.sizeOf(context);
+// final maxWidth = kIsWeb ? size.width / 4 : size.width - 80;
+// return BoxConstraints(maxWidth: maxWidth, maxHeight: 80);
+// }();
+
+// final buttonStyle = widget.buttonSize != null
+// ? Theme.of(context)
+// .elevatedButtonTheme
+// .style
+// ?.copyWith(fixedSize: MaterialStatePropertyAll(widget.buttonSize))
+// : widget.dialogTheme?.buttonStyle;
+
+// final isWrappable = widget.dialogTheme?.isWrappable ?? false;
+
+// final children = [
+// Text(widget.labelText ?? 'Enter media'.i18n),
+// UtilityWidgets.maybeWidget(
+// enabled: !isWrappable,
+// wrapper: (child) => Expanded(
+// child: child,
+// ),
+// child: Padding(
+// padding: EdgeInsets.symmetric(horizontal: widget.childrenSpacing),
+// child: TextFormField(
+// controller: _linkController,
+// focusNode: _linkFocus,
+// style: widget.dialogTheme?.inputTextStyle,
+// keyboardType: TextInputType.url,
+// textInputAction: TextInputAction.done,
+// decoration: InputDecoration(
+// labelStyle: widget.dialogTheme?.labelTextStyle,
+// hintText: widget.hintText,
+// ),
+// autofocus: true,
+// autovalidateMode: widget.autovalidateMode,
+// validator: _validateLink,
+// onChanged: _linkChanged,
+// ),
+// ),
+// ),
+// ElevatedButton(
+// onPressed: _canPress() ? _submitLink : null,
+// style: buttonStyle,
+// child: Text(widget.buttonText ?? 'Ok'.i18n),
+// ),
+// ];
+
+// return Dialog(
+// backgroundColor: widget.dialogTheme?.dialogBackgroundColor,
+// shape: widget.dialogTheme?.shape ??
+// DialogTheme.of(context).shape ??
+// RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)),
+// child: ConstrainedBox(
+// constraints: constraints,
+// child: Padding(
+// padding:
+// widget.dialogTheme?.linkDialogPadding ?? const EdgeInsets.all(16),
+// child: Form(
+// child: isWrappable
+// ? Wrap(
+// alignment: WrapAlignment.center,
+// crossAxisAlignment: WrapCrossAlignment.center,
+// runSpacing: widget.dialogTheme?.runSpacing ?? 0.0,
+// children: children,
+// )
+// : Row(
+// children: children,
+// ),
+// ),
+// ),
+// ),
+// );
+// }
+
+// bool _canPress() => _validateLink(_linkController.text) == null;
+
+// void _linkChanged(String value) {
+// setState(() {
+// _linkController.text = value;
+// });
+// }
+
+// void _submitLink() => Navigator.pop(context, _linkController.text);
+
+// String? _validateLink(String? value) {
+// if ((value?.isEmpty ?? false) ||
+// !AutoFormatMultipleLinksRule.oneLineLinkRegExp.hasMatch(value!)) {
+// return widget.validationMessage ?? 'That is not a valid URL';
+// }
+
+// return null;
+// }
+// }
+
+// /// Media souce selector.
+// class MediaSourceSelectorDialog extends StatelessWidget {
+// const MediaSourceSelectorDialog({
+// super.key,
+// this.dialogTheme,
+// this.galleryButtonText,
+// this.linkButtonText,
+// });
+
+// final QuillDialogTheme? dialogTheme;
+
+// /// The text of the gallery button [MediaSourceSelectorDialog].
+// final String? galleryButtonText;
+
+// /// The text of the link button [MediaSourceSelectorDialog].
+// final String? linkButtonText;
+
+// @override
+// Widget build(BuildContext context) {
+// final constraints = dialogTheme?.mediaSelectorDialogConstraints ??
+// () {
+// final size = MediaQuery.sizeOf(context);
+// double maxWidth, maxHeight;
+// if (kIsWeb) {
+// maxWidth = size.width / 7;
+// maxHeight = size.height / 7;
+// } else {
+// maxWidth = size.width - 80;
+// maxHeight = maxWidth / 2;
+// }
+// return BoxConstraints(maxWidth: maxWidth, maxHeight: maxHeight);
+// }();
+
+// final shape = dialogTheme?.shape ??
+// DialogTheme.of(context).shape ??
+// RoundedRectangleBorder(borderRadius: BorderRadius.circular(4));
+
+// return Dialog(
+// backgroundColor: dialogTheme?.dialogBackgroundColor,
+// shape: shape,
+// child: ConstrainedBox(
+// constraints: constraints,
+// child: Padding(
+// padding: dialogTheme?.mediaSelectorDialogPadding ??
+// const EdgeInsets.all(16),
+// child: Row(
+// mainAxisAlignment: MainAxisAlignment.spaceBetween,
+// children: [
+// Expanded(
+// child: TextButtonWithIcon(
+// icon: Icons.collections,
+// label: galleryButtonText ?? 'Gallery'.i18n,
+// onPressed: () =>
+// Navigator.pop(context, MediaPickSetting.gallery),
+// ),
+// ),
+// const SizedBox(width: 10),
+// Expanded(
+// child: TextButtonWithIcon(
+// icon: Icons.link,
+// label: linkButtonText ?? 'Link'.i18n,
+// onPressed: () =>
+// Navigator.pop(context, MediaPickSetting.link),
+// ),
+// )
+// ],
+// ),
+// ),
+// ),
+// );
+// }
+// }
+
+// class TextButtonWithIcon extends StatelessWidget {
+// const TextButtonWithIcon({
+// required this.label,
+// required this.icon,
+// required this.onPressed,
+// this.textStyle,
+// super.key,
+// });
+
+// final String label;
+// final IconData icon;
+// final VoidCallback onPressed;
+// final TextStyle? textStyle;
+
+// @override
+// Widget build(BuildContext context) {
+// final theme = Theme.of(context);
+// final scale = MediaQuery.maybeOf(context)?.textScaleFactor ?? 1;
+// final gap = scale <= 1 ? 8.0 : lerpDouble(8, 4, math.min(scale - 1, 1))!;
+// final buttonStyle = TextButtonTheme.of(context).style;
+// final shape = buttonStyle?.shape?.resolve({}) ??
+// const RoundedRectangleBorder(
+// borderRadius: BorderRadius.all(
+// Radius.circular(4),
+// ),
+// );
+// return Material(
+// shape: shape,
+// textStyle: textStyle ??
+// theme.textButtonTheme.style?.textStyle?.resolve({}) ??
+// theme.textTheme.labelLarge,
+// elevation: buttonStyle?.elevation?.resolve({}) ?? 0,
+// child: InkWell(
+// customBorder: shape,
+// onTap: onPressed,
+// child: Padding(
+// padding: const EdgeInsets.all(16),
+// child: Column(
+// mainAxisSize: MainAxisSize.min,
+// children: [
+// Icon(icon),
+// SizedBox(height: gap),
+// Flexible(child: Text(label)),
+// ],
+// ),
+// ),
+// ),
+// );
+// }
+// }
+
+// /// Default file picker.
+// // Future _defaultMediaPicker(QuillMediaType mediaType) async {
+// // final pickedFile = mediaType.isImage
+// // ? await ImagePicker().pickImage(source: ImageSource.gallery)
+// // : await ImagePicker().pickVideo(source: ImageSource.gallery);
+
+// // if (pickedFile != null) {
+// // return QuillFile(
+// // name: pickedFile.name,
+// // path: pickedFile.path,
+// // bytes: await pickedFile.readAsBytes(),
+// // );
+// // }
+
+// // return null;
+// // }
diff --git a/flutter_quill_extensions/lib/presentation/embeds/toolbar/video_button/select_video_source.dart b/flutter_quill_extensions/lib/presentation/embeds/toolbar/video_button/select_video_source.dart
new file mode 100644
index 00000000..7187f10b
--- /dev/null
+++ b/flutter_quill_extensions/lib/presentation/embeds/toolbar/video_button/select_video_source.dart
@@ -0,0 +1,57 @@
+import 'package:flutter/material.dart';
+
+import '../../embed_types/video.dart';
+
+class SelectVideoSourceDialog extends StatelessWidget {
+ const SelectVideoSourceDialog({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return SizedBox(
+ height: 230,
+ width: double.infinity,
+ child: SingleChildScrollView(
+ child: Column(
+ children: [
+ ListTile(
+ title: const Text('Gallery'),
+ subtitle: const Text(
+ 'Pick a video from your gallery',
+ ),
+ leading: const Icon(Icons.photo_sharp),
+ onTap: () => Navigator.of(context).pop(InsertVideoSource.gallery),
+ ),
+ ListTile(
+ title: const Text('Camera'),
+ subtitle: const Text(
+ 'Record a video using your phone camera',
+ ),
+ leading: const Icon(Icons.camera),
+ onTap: () => Navigator.of(context).pop(InsertVideoSource.camera),
+ ),
+ ListTile(
+ title: const Text('Link'),
+ subtitle: const Text(
+ 'Paste a video using a link',
+ ),
+ leading: const Icon(Icons.link),
+ onTap: () => Navigator.of(context).pop(InsertVideoSource.link),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
+
+Future showSelectVideoSourceDialog({
+ required BuildContext context,
+}) async {
+ final imageSource = await showModalBottomSheet(
+ showDragHandle: true,
+ context: context,
+ constraints: const BoxConstraints(maxWidth: 640),
+ builder: (context) => const SelectVideoSourceDialog(),
+ );
+ return imageSource;
+}
diff --git a/flutter_quill_extensions/lib/presentation/embeds/toolbar/video_button.dart b/flutter_quill_extensions/lib/presentation/embeds/toolbar/video_button/video_button.dart
similarity index 62%
rename from flutter_quill_extensions/lib/presentation/embeds/toolbar/video_button.dart
rename to flutter_quill_extensions/lib/presentation/embeds/toolbar/video_button/video_button.dart
index 2bed465d..2b9bace3 100644
--- a/flutter_quill_extensions/lib/presentation/embeds/toolbar/video_button.dart
+++ b/flutter_quill_extensions/lib/presentation/embeds/toolbar/video_button/video_button.dart
@@ -2,11 +2,14 @@
import 'package:flutter/material.dart';
import 'package:flutter_quill/flutter_quill.dart';
-import 'package:image_picker/image_picker.dart';
-import '../../models/config/toolbar/buttons/video.dart';
-import '../embed_types.dart';
-import 'utils/image_video_utils.dart';
+import '../../../../logic/models/config/configurations.dart';
+import '../../../../logic/services/image_picker/image_options.dart';
+import '../../../models/config/toolbar/buttons/video.dart';
+import '../../embed_types.dart';
+import '../../embed_types/video.dart';
+import '../utils/image_video_utils.dart';
+import 'select_video_source.dart';
class QuillToolbarVideoButton extends StatelessWidget {
const QuillToolbarVideoButton({
@@ -78,15 +81,13 @@ class QuillToolbarVideoButton extends StatelessWidget {
afterButtonPressed: _afterButtonPressed(context),
iconData: iconData,
dialogTheme: options.dialogTheme,
- filePickImpl: options.filePickImpl,
fillColor: iconFillColor,
iconSize: options.iconSize,
linkRegExp: options.linkRegExp,
tooltip: options.tooltip,
- mediaPickSettingSelector: options.mediaPickSettingSelector,
iconTheme: options.iconTheme,
- onVideoPickCallback: options.onVideoPickCallback,
webVideoPickImpl: options.webVideoPickImpl,
+ videoConfigurations: options.videoConfigurations,
),
QuillToolbarVideoButtonExtraOptions(
context: context,
@@ -109,39 +110,67 @@ class QuillToolbarVideoButton extends StatelessWidget {
}
Future _onPressedHandler(BuildContext context) async {
- if (options.onVideoPickCallback != null) {
- final selector = options.mediaPickSettingSelector ??
- ImageVideoUtils.selectMediaPickSetting;
- final source = await selector(context);
- if (source != null) {
- if (source == MediaPickSetting.gallery) {
- _pickVideo(context);
- } else {
- await _typeLink(context);
- }
+ final imagePickerService =
+ QuillSharedExtensionsConfigurations.get(context: context)
+ .imagePickerService;
+
+ final onRequestPickVideo = options.videoConfigurations.onRequestPickVideo;
+ if (onRequestPickVideo != null) {
+ final videoUrl = await onRequestPickVideo(context, imagePickerService);
+ if (videoUrl != null) {
+ await options.videoConfigurations
+ .onVideoInsertCallback(videoUrl, controller);
+ await options.videoConfigurations.onVideoInsertedCallback
+ ?.call(videoUrl);
}
- } else {
- await _typeLink(context);
+ return;
}
- }
- void _pickVideo(BuildContext context) => ImageVideoUtils.handleVideoButtonTap(
- context,
- controller,
- ImageSource.gallery,
- options.onVideoPickCallback!,
- filePickImpl: options.filePickImpl,
- webVideoPickImpl: options.webVideoPickImpl,
- );
+ final imageSource = await showSelectVideoSourceDialog(context: context);
+
+ if (imageSource == null) {
+ return;
+ }
+
+ final videoUrl = switch (imageSource) {
+ InsertVideoSource.gallery =>
+ (await imagePickerService.pickVideo(source: ImageSource.gallery))?.path,
+ InsertVideoSource.camera =>
+ (await imagePickerService.pickVideo(source: ImageSource.camera))?.path,
+ InsertVideoSource.link => await _typeLink(context),
+ };
+ if (videoUrl == null) {
+ return;
+ }
+
+ if (videoUrl.trim().isNotEmpty) {
+ await options.videoConfigurations
+ .onVideoInsertCallback(videoUrl, controller);
+ await options.videoConfigurations.onVideoInsertedCallback?.call(videoUrl);
+ }
+
+ // if (options.onVideoPickCallback != null) {
+ // final selector = options.mediaPickSettingSelector ??
+ // ImageVideoUtils.selectMediaPickSetting;
+ // final source = await selector(context);
+ // if (source != null) {
+ // if (source == MediaPickSetting.gallery) {
+ // } else {
+ // await _typeLink(context);
+ // }
+ // }
+ // } else {}
+ }
- Future _typeLink(BuildContext context) async {
+ Future _typeLink(BuildContext context) async {
final value = await showDialog(
context: context,
builder: (_) => TypeLinkDialog(
dialogTheme: options.dialogTheme,
+ linkType: LinkType.video,
),
);
- _linkSubmitted(value);
+ return value;
}
void _linkSubmitted(String? value) {
diff --git a/flutter_quill_extensions/lib/presentation/models/config/toolbar/buttons/camera.dart b/flutter_quill_extensions/lib/presentation/models/config/toolbar/buttons/camera.dart
index b1b5bd8e..c685bb4a 100644
--- a/flutter_quill_extensions/lib/presentation/models/config/toolbar/buttons/camera.dart
+++ b/flutter_quill_extensions/lib/presentation/models/config/toolbar/buttons/camera.dart
@@ -2,8 +2,7 @@ import 'package:flutter/widgets.dart' show Color;
import 'package:flutter_quill/flutter_quill.dart';
import '../../../../embeds/embed_types.dart';
-import '../../../../embeds/embed_types/image.dart';
-import 'image.dart';
+import '../../../../embeds/embed_types/camera.dart';
class QuillToolbarCameraButtonExtraOptions
extends QuillToolbarBaseButtonExtraOptions {
@@ -18,7 +17,7 @@ class QuillToolbarCameraButtonOptions extends QuillToolbarBaseButtonOptions<
QuillToolbarCameraButtonOptions, QuillToolbarCameraButtonExtraOptions> {
const QuillToolbarCameraButtonOptions({
required this.onVideoPickCallback,
- this.imageConfigurations = const QuillToolbarImageButtonConfigurations(),
+ this.cameraConfigurations = const QuillToolbarCameraConfigurations(),
this.webVideoPickImpl,
this.iconSize,
this.fillColor,
@@ -30,13 +29,13 @@ class QuillToolbarCameraButtonOptions extends QuillToolbarBaseButtonOptions<
super.controller,
});
- final QuillToolbarImageButtonConfigurations imageConfigurations;
+ final double? iconSize;
+
+ final Color? fillColor;
final OnVideoPickCallback onVideoPickCallback;
final WebVideoPickImpl? webVideoPickImpl;
- final double? iconSize;
-
- final Color? fillColor;
+ final QuillToolbarCameraConfigurations cameraConfigurations;
}
diff --git a/flutter_quill_extensions/lib/presentation/models/config/toolbar/buttons/image.dart b/flutter_quill_extensions/lib/presentation/models/config/toolbar/buttons/image.dart
index d6195c4c..b0084eeb 100644
--- a/flutter_quill_extensions/lib/presentation/models/config/toolbar/buttons/image.dart
+++ b/flutter_quill_extensions/lib/presentation/models/config/toolbar/buttons/image.dart
@@ -2,8 +2,6 @@ import 'package:flutter/widgets.dart' show Color;
import 'package:flutter_quill/flutter_quill.dart';
import 'package:meta/meta.dart' show immutable;
-import '../../../../../logic/extensions/controller.dart';
-import '../../../../embeds/embed_types.dart';
import '../../../../embeds/embed_types/image.dart';
class QuillToolbarImageButtonExtraOptions
@@ -31,8 +29,7 @@ class QuillToolbarImageButtonOptions extends QuillToolbarBaseButtonOptions<
this.fillColor,
this.dialogTheme,
this.linkRegExp,
- this.imageButtonConfigurations =
- const QuillToolbarImageButtonConfigurations(),
+ this.imageButtonConfigurations = const QuillToolbarImageConfigurations(),
});
final double? iconSize;
@@ -43,23 +40,5 @@ class QuillToolbarImageButtonOptions extends QuillToolbarBaseButtonOptions<
/// [imageLinkRegExp] is a regular expression to identify image links.
final RegExp? linkRegExp;
- final QuillToolbarImageButtonConfigurations imageButtonConfigurations;
-}
-
-class QuillToolbarImageButtonConfigurations {
- const QuillToolbarImageButtonConfigurations({
- this.onRequestPickImage,
- this.onImagePickedCallback,
- OnImageInsertCallback? onImageInsertCallback,
- }) : _onImageInsertCallback = onImageInsertCallback;
-
- final OnRequestPickImage? onRequestPickImage;
-
- final OnImagePickedCallback? onImagePickedCallback;
-
- final OnImageInsertCallback? _onImageInsertCallback;
-
- OnImageInsertCallback get onImageInsertCallback {
- return _onImageInsertCallback ?? defaultOnImageInsertCallback();
- }
+ final QuillToolbarImageConfigurations imageButtonConfigurations;
}
diff --git a/flutter_quill_extensions/lib/presentation/models/config/toolbar/buttons/media_button.dart b/flutter_quill_extensions/lib/presentation/models/config/toolbar/buttons/media_button.dart
index e35d59e9..a1c6fca9 100644
--- a/flutter_quill_extensions/lib/presentation/models/config/toolbar/buttons/media_button.dart
+++ b/flutter_quill_extensions/lib/presentation/models/config/toolbar/buttons/media_button.dart
@@ -18,7 +18,6 @@ class QuillToolbarMediaButtonOptions extends QuillToolbarBaseButtonOptions<
const QuillToolbarMediaButtonOptions({
required this.type,
required this.onMediaPickedCallback,
- required this.onImagePickCallback,
required this.onVideoPickCallback,
this.dialogBarrierColor,
this.mediaFilePicker,
@@ -35,7 +34,6 @@ class QuillToolbarMediaButtonOptions extends QuillToolbarBaseButtonOptions<
this.linkButtonText,
this.validationMessage,
this.filePickImpl,
- this.webImagePickImpl,
this.webVideoPickImpl,
super.iconData,
super.afterButtonPressed,
diff --git a/flutter_quill_extensions/lib/presentation/models/config/toolbar/buttons/video.dart b/flutter_quill_extensions/lib/presentation/models/config/toolbar/buttons/video.dart
index 96f121ec..4e030192 100644
--- a/flutter_quill_extensions/lib/presentation/models/config/toolbar/buttons/video.dart
+++ b/flutter_quill_extensions/lib/presentation/models/config/toolbar/buttons/video.dart
@@ -2,6 +2,7 @@ import 'package:flutter/widgets.dart' show Color;
import 'package:flutter_quill/flutter_quill.dart';
import '../../../../embeds/embed_types.dart';
+import '../../../../embeds/embed_types/video.dart';
class QuillToolbarVideoButtonExtraOptions
extends QuillToolbarBaseButtonExtraOptions {
@@ -17,10 +18,7 @@ class QuillToolbarVideoButtonOptions extends QuillToolbarBaseButtonOptions<
const QuillToolbarVideoButtonOptions({
this.linkRegExp,
this.dialogTheme,
- this.onVideoPickCallback,
this.webVideoPickImpl,
- this.filePickImpl,
- this.mediaPickSettingSelector,
this.fillColor,
this.iconSize,
super.iconData,
@@ -29,16 +27,15 @@ class QuillToolbarVideoButtonOptions extends QuillToolbarBaseButtonOptions<
super.iconTheme,
super.childBuilder,
super.controller,
+ this.videoConfigurations = const QuillToolbarVideoConfigurations(),
});
final RegExp? linkRegExp;
final QuillDialogTheme? dialogTheme;
- final OnVideoPickCallback? onVideoPickCallback;
+ final QuillToolbarVideoConfigurations videoConfigurations;
final WebVideoPickImpl? webVideoPickImpl;
- final FilePickImpl? filePickImpl;
-
final Color? fillColor;
final double? iconSize;
diff --git a/lib/src/models/config/shared_configurations.dart b/lib/src/models/config/shared_configurations.dart
index 78dd6b06..98a87e03 100644
--- a/lib/src/models/config/shared_configurations.dart
+++ b/lib/src/models/config/shared_configurations.dart
@@ -18,6 +18,7 @@ class QuillSharedConfigurations extends Equatable {
this.animationConfigurations = const QuillAnimationConfigurations(
checkBoxPointItem: false,
),
+ this.extraConfigurations = const {},
});
// This is just example or showcase of this major update to make the library
@@ -36,6 +37,9 @@ class QuillSharedConfigurations extends Equatable {
/// To configure which animations you want to be enabled
final QuillAnimationConfigurations animationConfigurations;
+ /// Store custom configurations in here and use it in the widget tree
+ final Map extraConfigurations;
+
@override
List