required changes

pull/1686/head
Mayank Variya 1 year ago
parent 65d1cb6d4a
commit 1548b4b537
  1. 3
      example/lib/screens/quill/my_quill_toolbar.dart
  2. 47
      example/lib/screens/quill/quill_screen.dart
  3. 2
      example/macos/Flutter/GeneratedPluginRegistrant.swift
  4. 11
      flutter_quill_extensions/lib/embeds/image/editor/image_embed.dart
  5. 1
      flutter_quill_extensions/lib/embeds/image/editor/image_embed_types.dart
  6. 15
      flutter_quill_extensions/lib/embeds/image/toolbar/image_button.dart
  7. 54
      flutter_quill_extensions/lib/embeds/image/toolbar/select_image_source.dart
  8. 13
      flutter_quill_extensions/lib/embeds/others/camera_button/camera_button.dart
  9. 15
      flutter_quill_extensions/lib/embeds/others/camera_button/camera_types.dart
  10. 9
      flutter_quill_extensions/lib/embeds/others/camera_button/select_camera_action.dart
  11. 277
      flutter_quill_extensions/lib/embeds/others/image_video_utils.dart
  12. 537
      flutter_quill_extensions/lib/embeds/others/media_button/media_button.dart
  13. 62
      flutter_quill_extensions/lib/embeds/video/editor/video_embed.dart
  14. 64
      flutter_quill_extensions/lib/embeds/video/editor/video_web_embed.dart
  15. 59
      flutter_quill_extensions/lib/embeds/video/toolbar/select_video_source.dart
  16. 188
      flutter_quill_extensions/lib/embeds/video/toolbar/video_button.dart
  17. 66
      flutter_quill_extensions/lib/embeds/video/video.dart
  18. 78
      flutter_quill_extensions/lib/embeds/widgets/youtube_video_app.dart
  19. 28
      flutter_quill_extensions/lib/flutter_quill_embeds.dart
  20. 7
      flutter_quill_extensions/lib/flutter_quill_extensions.dart
  21. 24
      flutter_quill_extensions/lib/models/config/video/editor/video_configurations.dart
  22. 11
      flutter_quill_extensions/lib/models/config/video/editor/video_web_configurations.dart
  23. 36
      flutter_quill_extensions/lib/models/config/video/toolbar/video_configurations.dart
  24. 1
      flutter_quill_extensions/pubspec.yaml
  25. 30
      quill_html_converter/.gitignore
  26. 10
      quill_html_converter/.metadata
  27. 1734
      quill_html_converter/CHANGELOG.md
  28. 21
      quill_html_converter/LICENSE
  29. 41
      quill_html_converter/README.md
  30. 36
      quill_html_converter/analysis_options.yaml
  31. 28
      quill_html_converter/lib/quill_html_converter.dart
  32. 38
      quill_html_converter/pubspec.yaml
  33. 3
      quill_html_converter/pubspec_overrides.yaml.disabled
  34. 7
      quill_html_converter/test/quill_html_converter.dart
  35. 29
      quill_pdf_converter/.gitignore
  36. 10
      quill_pdf_converter/.metadata
  37. 1734
      quill_pdf_converter/CHANGELOG.md
  38. 21
      quill_pdf_converter/LICENSE
  39. 34
      quill_pdf_converter/README.md
  40. 36
      quill_pdf_converter/analysis_options.yaml
  41. 22
      quill_pdf_converter/lib/quill_pdf_converter.dart
  42. 34
      quill_pdf_converter/pubspec.yaml
  43. 5
      quill_pdf_converter/pubspec_overrides.yaml.disabled
  44. 7
      quill_pdf_converter/test/quill_pdf_converter_test.dart

@ -147,9 +147,6 @@ class MyQuillToolbar extends StatelessWidget {
QuillToolbarCameraButton(
controller: controller,
),
QuillToolbarVideoButton(
controller: controller,
),
const VerticalDivider(),
QuillToolbarColorButton(
controller: controller,

@ -7,8 +7,6 @@ import 'package:flutter_quill_extensions/flutter_quill_extensions.dart'
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
import 'package:printing/printing.dart';
import 'package:quill_html_converter/quill_html_converter.dart';
import 'package:quill_pdf_converter/quill_pdf_converter.dart';
import 'package:share_plus/share_plus.dart' show Share;
import '../../extensions/scaffold_messenger.dart';
@ -63,51 +61,6 @@ class _QuillScreenState extends State<QuillScreen> {
appBar: AppBar(
title: const Text('Flutter Quill'),
actions: [
MenuAnchor(
builder: (context, controller, child) {
return IconButton(
onPressed: () {
if (controller.isOpen) {
controller.close();
return;
}
controller.open();
},
icon: const Icon(
Icons.more_vert,
),
);
},
menuChildren: [
MenuItemButton(
onPressed: () {
final html = _controller.document.toDelta().toHtml();
_controller.document =
Document.fromDelta(Document.fromHtml(html));
},
child: const Text('Load with HTML'),
),
MenuItemButton(
onPressed: () async {
final pdfDocument = pw.Document();
final pdfWidgets =
await _controller.document.toDelta().toPdf();
pdfDocument.addPage(
pw.MultiPage(
maxPages: 200,
pageFormat: PdfPageFormat.a4,
build: (context) {
return pdfWidgets;
},
),
);
await Printing.layoutPdf(
onLayout: (format) async => pdfDocument.save());
},
child: const Text('Print as PDF'),
),
],
),
IconButton(
tooltip: 'Share',
onPressed: () {

@ -8,7 +8,6 @@ import Foundation
import desktop_drop
import device_info_plus
import file_selector_macos
import flutter_inappwebview_macos
import gal
import irondash_engine_context
import path_provider_foundation
@ -23,7 +22,6 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
DesktopDropPlugin.register(with: registry.registrar(forPlugin: "DesktopDropPlugin"))
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin"))
GalPlugin.register(with: registry.registrar(forPlugin: "GalPlugin"))
IrondashEngineContextPlugin.register(with: registry.registrar(forPlugin: "IrondashEngineContextPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))

@ -12,6 +12,7 @@ class QuillEditorImageEmbedBuilder extends EmbedBuilder {
QuillEditorImageEmbedBuilder({
required this.configurations,
});
final QuillEditorImageEmbedConfigurations configurations;
@override
@ -37,8 +38,8 @@ class QuillEditorImageEmbedBuilder extends EmbedBuilder {
context,
);
final width = imageSize.width;
final height = imageSize.height;
final width = imageSize.width ?? MediaQuery.of(context).size.width;
final height = imageSize.height ?? 160;
final image = getImageWidgetByImageSource(
context: context,
@ -81,10 +82,12 @@ class QuillEditorImageEmbedBuilder extends EmbedBuilder {
if (margin != null) {
return Padding(
padding: EdgeInsets.all(margin),
child: image,
child: ClipRRect(
borderRadius: BorderRadius.circular(8), child: image),
);
}
return image;
return ClipRRect(
borderRadius: BorderRadius.circular(8), child: image);
},
),
);

@ -43,7 +43,6 @@ typedef OnImageInsertedCallback = Future<void> Function(
enum InsertImageSource {
gallery,
camera,
link,
}
/// Configurations for dealing with images, on insert a image

@ -139,7 +139,6 @@ class QuillToolbarImageButton extends StatelessWidget {
source: ImageSource.gallery,
))
?.path,
InsertImageSource.link => await _typeLink(context),
InsertImageSource.camera => (await imagePickerService.pickImage(
source: ImageSource.camera,
))
@ -155,18 +154,4 @@ class QuillToolbarImageButton extends StatelessWidget {
?.call(imageUrl);
}
}
Future<String?> _typeLink(BuildContext context) async {
final value = await showDialog<String>(
context: context,
builder: (_) => FlutterQuillLocalizationsWidget(
child: TypeLinkDialog(
dialogTheme: options.dialogTheme,
linkRegExp: options.linkRegExp,
linkType: LinkType.image,
),
),
);
return value;
}
}

@ -9,38 +9,32 @@ class SelectImageSourceDialog extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
constraints: const BoxConstraints(minHeight: 200),
width: double.infinity,
child: SingleChildScrollView(
child: Column(
children: [
ListTile(
title: Text(context.loc.gallery),
subtitle: Text(
context.loc.pickAPhotoFromYourGallery,
return SafeArea(
child: Container(
constraints: const BoxConstraints(minHeight: 200),
width: double.infinity,
child: SingleChildScrollView(
child: Column(
children: [
ListTile(
title: Text(context.loc.gallery),
subtitle: Text(
context.loc.pickAPhotoFromYourGallery,
),
leading: const Icon(Icons.photo_sharp),
onTap: () => Navigator.of(context).pop(InsertImageSource.gallery),
),
leading: const Icon(Icons.photo_sharp),
onTap: () => Navigator.of(context).pop(InsertImageSource.gallery),
),
ListTile(
title: Text(context.loc.camera),
subtitle: Text(
context.loc.takeAPhotoUsingYourCamera,
ListTile(
title: Text(context.loc.camera),
subtitle: Text(
context.loc.takeAPhotoUsingYourCamera,
),
leading: const Icon(Icons.camera),
enabled: !isDesktop(supportWeb: false),
onTap: () => Navigator.of(context).pop(InsertImageSource.camera),
),
leading: const Icon(Icons.camera),
enabled: !isDesktop(supportWeb: false),
onTap: () => Navigator.of(context).pop(InsertImageSource.camera),
),
ListTile(
title: Text(context.loc.link),
subtitle: Text(
context.loc.pasteAPhotoUsingALink,
),
leading: const Icon(Icons.link),
onTap: () => Navigator.of(context).pop(InsertImageSource.link),
),
],
],
),
),
),
);

@ -143,19 +143,6 @@ class QuillToolbarCameraButton extends StatelessWidget {
}
switch (cameraAction) {
case CameraAction.video:
final videoFile = await imagePickerService.pickVideo(
source: ImageSource.camera,
);
if (videoFile == null) {
return;
}
await options.cameraConfigurations.onVideoInsertCallback(
videoFile.path,
controller,
);
await options.cameraConfigurations.onVideoInsertedCallback
?.call(videoFile.path);
case CameraAction.image:
final imageFile = await imagePickerService.pickImage(
source: ImageSource.camera,

@ -2,10 +2,8 @@ import 'package:flutter/widgets.dart' show BuildContext;
import 'package:meta/meta.dart' show immutable;
import '../../image/editor/image_embed_types.dart';
import '../../video/video.dart';
enum CameraAction {
video,
image,
}
@ -23,10 +21,8 @@ class QuillToolbarCameraConfigurations {
this.onRequestCameraActionCallback,
OnImageInsertCallback? onImageInsertCallback,
this.onImageInsertedCallback,
this.onVideoInsertedCallback,
OnVideoInsertCallback? onVideoInsertCallback,
}) : _onImageInsertCallback = onImageInsertCallback,
_onVideoInsertCallback = onVideoInsertCallback;
}) : _onImageInsertCallback = onImageInsertCallback;
final OnRequestCameraActionCallback? onRequestCameraActionCallback;
@ -38,11 +34,4 @@ class QuillToolbarCameraConfigurations {
return _onImageInsertCallback ?? defaultOnImageInsertCallback();
}
final OnVideoInsertedCallback? onVideoInsertedCallback;
final OnVideoInsertCallback? _onVideoInsertCallback;
OnVideoInsertCallback get onVideoInsertCallback {
return _onVideoInsertCallback ?? defaultOnVideoInsertCallback();
}
}

@ -24,15 +24,6 @@ class SelectCameraActionDialog extends StatelessWidget {
enabled: !isDesktop(supportWeb: false),
onTap: () => Navigator.of(context).pop(CameraAction.image),
),
ListTile(
title: Text(context.loc.video),
subtitle: Text(
context.loc.recordAVideoUsingYourCamera,
),
leading: const Icon(Icons.camera),
enabled: !isDesktop(supportWeb: false),
onTap: () => Navigator.of(context).pop(CameraAction.video),
),
],
),
),

@ -1,277 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_quill/flutter_quill.dart' show QuillDialogTheme;
import 'package:flutter_quill/translations.dart';
import '../../utils/patterns.dart';
enum LinkType {
video,
image,
}
class TypeLinkDialog extends StatefulWidget {
const TypeLinkDialog({
required this.linkType,
this.dialogTheme,
this.link,
this.linkRegExp,
super.key,
});
final QuillDialogTheme? dialogTheme;
final String? link;
final RegExp? linkRegExp;
final LinkType linkType;
@override
TypeLinkDialogState createState() => TypeLinkDialogState();
}
class TypeLinkDialogState extends State<TypeLinkDialog> {
late String _link;
late TextEditingController _controller;
RegExp? _linkRegExp;
@override
void initState() {
super.initState();
_link = widget.link ?? '';
_controller = TextEditingController(text: _link);
_linkRegExp = widget.linkRegExp;
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AlertDialog(
backgroundColor: widget.dialogTheme?.dialogBackgroundColor,
content: TextField(
keyboardType: TextInputType.url,
textInputAction: TextInputAction.done,
maxLines: null,
style: widget.dialogTheme?.inputTextStyle,
decoration: InputDecoration(
labelText: context.loc.pasteLink,
hintText: widget.linkType == LinkType.image
? context.loc.pleaseEnterAValidImageURL
: context.loc.pleaseEnterAValidVideoURL,
labelStyle: widget.dialogTheme?.labelTextStyle,
floatingLabelStyle: widget.dialogTheme?.labelTextStyle,
),
autofocus: true,
onChanged: _linkChanged,
controller: _controller,
onEditingComplete: () {
if (!_canPress()) {
return;
}
_applyLink();
},
),
actions: [
TextButton(
onPressed: _canPress() ? _applyLink : null,
child: Text(
context.loc.ok,
style: widget.dialogTheme?.labelTextStyle,
),
),
],
);
}
void _linkChanged(String value) {
setState(() {
_link = value;
});
}
void _applyLink() {
Navigator.pop(context, _link.trim());
}
RegExp get linkRegExp {
final customRegExp = _linkRegExp;
if (customRegExp != null) {
return customRegExp;
}
switch (widget.linkType) {
case LinkType.video:
if (youtubeRegExp.hasMatch(_link)) {
return youtubeRegExp;
}
return videoRegExp;
case LinkType.image:
return imageRegExp;
}
}
bool _canPress() {
if (_link.isEmpty) {
return false;
}
if (widget.linkType == LinkType.image) {}
return _link.isNotEmpty && linkRegExp.hasMatch(_link);
}
}
// @immutable
// class ImageVideoUtils {
// const ImageVideoUtils._();
// static Future<MediaPickSetting?> selectMediaPickSetting(
// BuildContext context,
// ) =>
// showDialog<MediaPickSetting>(
// context: context,
// builder: (ctx) => AlertDialog(
// contentPadding: EdgeInsets.zero,
// content: Column(
// mainAxisSize: MainAxisSize.min,
// children: [
// TextButton.icon(
// icon: const Icon(
// Icons.collections,
// color: Colors.orangeAccent,
// ),
// label: Text('Gallery'.i18n),
// onPressed: () => Navigator.pop(ctx,
// MediaPickSetting.gallery),
// ),
// TextButton.icon(
// icon: const Icon(
// Icons.link,
// color: Colors.cyanAccent,
// ),
// label: Text('Link'.i18n),
// onPressed: () => Navigator.pop(ctx, MediaPickSetting.link),
// )
// ],
// ),
// ),
// );
// /// For image picking logic
// static Future<void> handleImageButtonTap(
// BuildContext context,
// QuillController controller,
// ImageSource imageSource,
// OnImagePickCallback onImagePickCallback, {
// FilePickImpl? filePickImpl,
// WebImagePickImpl? webImagePickImpl,
// }) async {
// String? imageUrl;
// if (kIsWeb) {
// if (webImagePickImpl != null) {
// imageUrl = await webImagePickImpl(onImagePickCallback);
// return;
// }
// final file = await ImagePicker()
//.pickImage(source: ImageSource.gallery);
// imageUrl = file?.path;
// if (imageUrl == null) {
// return;
// }
// } else if (isMobile()) {
// imageUrl = await _pickImage(imageSource, onImagePickCallback);
// } else {
// assert(filePickImpl != null, 'Desktop must provide filePickImpl');
// imageUrl =
// await _pickImageDesktop
//(context, filePickImpl!, onImagePickCallback);
// }
// if (imageUrl == null) {
// return;
// }
// controller.insertImageBlock(
// imageUrl: imageUrl,
// );
// }
// static Future<String?> _pickImage(
// ImageSource source,
// OnImagePickCallback onImagePickCallback,
// ) async {
// final pickedFile = await ImagePicker().pickImage(source: source);
// if (pickedFile == null) {
// return null;
// }
// return onImagePickCallback(File(pickedFile.path));
// }
// static Future<String?> _pickImageDesktop(
// BuildContext context,
// FilePickImpl filePickImpl,
// OnImagePickCallback onImagePickCallback,
// ) async {
// final filePath = await filePickImpl(context);
// if (filePath == null || filePath.isEmpty) return null;
// final file = File(filePath);
// return onImagePickCallback(file);
// }
// /// For video picking logic
// static Future<void> handleVideoButtonTap(
// BuildContext context,
// QuillController controller,
// ImageSource videoSource,
// OnVideoPickCallback onVideoPickCallback, {
// FilePickImpl? filePickImpl,
// WebVideoPickImpl? webVideoPickImpl,
// }) async {
// final index = controller.selection.baseOffset;
// final length = controller.selection.extentOffset - index;
// String? videoUrl;
// if (kIsWeb) {
// assert(
// webVideoPickImpl != null,
// 'Please provide webVideoPickImpl for Web '
// 'in the options of this button',
// );
// videoUrl = await webVideoPickImpl!(onVideoPickCallback);
// } else if (isMobile()) {
// 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);
// }
// }
// static Future<String?> _pickVideo(
// ImageSource source, OnVideoPickCallback onVideoPickCallback) async {
// final pickedFile = await ImagePicker().pickVideo(source: source);
// if (pickedFile == null) {
// return null;
// }
// return onVideoPickCallback(File(pickedFile.path));
// }
// static Future<String?> _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);
// }
// }

@ -1,537 +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,
// 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,
// iconButtonFactor: iconButtonFactor,
// 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 * iconButtonFactor,
// fillColor: iconFillColor,
// borderRadius: iconTheme?.borderRadius ?? 2,
// onPressed: () => _sharedOnPressed(context),
// );
// }
// Future<void> _onPressedHandler(BuildContext context) async {
// if (options.onMediaPickedCallback == null) {
// _inputLink(context);
// return;
// }
// final mediaSource = await showDialog<MediaPickSetting>(
// 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<void> _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<MediaFileUrl?> _pickMediaFileUrl() async {
// final mediaFile = await options.mediaFilePicker?.call(options.type);
// return mediaFile != null
// ? options.onMediaPickedCallback?.call(mediaFile)
// : null;
// }
// void _inputLink(BuildContext context) {
// showDialog<String>(
// 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<MediaLinkDialog> createState() => _MediaLinkDialogState();
// }
// class _MediaLinkDialogState extends State<MediaLinkDialog> {
// 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: <Widget>[
// Icon(icon),
// SizedBox(height: gap),
// Flexible(child: Text(label)),
// ],
// ),
// ),
// ),
// );
// }
// }
// /// Default file picker.
// // Future<QuillFile?> _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;
// // }

@ -1,62 +0,0 @@
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:flutter/material.dart';
import 'package:flutter_quill/flutter_quill.dart';
import '../../../models/config/video/editor/video_configurations.dart';
import '../../../utils/element_utils/element_utils.dart';
import '../../../utils/utils.dart';
import '../../widgets/video_app.dart';
import '../../widgets/youtube_video_app.dart';
class QuillEditorVideoEmbedBuilder extends EmbedBuilder {
const QuillEditorVideoEmbedBuilder({
required this.configurations,
});
final QuillEditorVideoEmbedConfigurations configurations;
@override
String get key => BlockEmbed.videoType;
@override
bool get expanded => false;
@override
Widget build(
BuildContext context,
QuillController controller,
Embed node,
bool readOnly,
bool inline,
TextStyle textStyle,
) {
assert(!kIsWeb, 'Please provide video EmbedBuilder for Web');
final videoUrl = node.value.data;
if (isYouTubeUrl(videoUrl)) {
return YoutubeVideoApp(
videoUrl: videoUrl,
readOnly: readOnly,
);
}
final ((elementSize), margin, alignment) = getElementAttributes(
node,
context,
);
final width = elementSize.width;
final height = elementSize.height;
return Container(
width: width,
height: height,
margin: EdgeInsets.all(margin ?? 0.0),
alignment: alignment,
child: VideoApp(
videoUrl: videoUrl,
context: context,
readOnly: readOnly,
onVideoInit: configurations.onVideoInit,
),
);
}
}

@ -1,64 +0,0 @@
import 'package:flutter/widgets.dart';
import 'package:flutter_quill/flutter_quill.dart';
import 'package:universal_html/html.dart' as html;
import 'package:youtube_player_flutter/youtube_player_flutter.dart'
show YoutubePlayer;
import '../../../models/config/video/editor/video_web_configurations.dart';
import '../../../utils/dart_ui/dart_ui_fake.dart'
if (dart.library.html) '../../../utils/dart_ui/dart_ui_real.dart' as ui;
import '../../../utils/element_utils/element_web_utils.dart';
import '../../../utils/utils.dart';
class QuillEditorWebVideoEmbedBuilder extends EmbedBuilder {
const QuillEditorWebVideoEmbedBuilder({
required this.configurations,
});
final QuillEditorWebVideoEmbedConfigurations configurations;
@override
String get key => BlockEmbed.videoType;
@override
bool get expanded => false;
@override
Widget build(
BuildContext context,
QuillController controller,
Embed node,
bool readOnly,
bool inline,
TextStyle textStyle,
) {
var videoUrl = node.value.data;
if (isYouTubeUrl(videoUrl)) {
final youtubeID = YoutubePlayer.convertUrlToId(videoUrl);
if (youtubeID != null) {
videoUrl = 'https://www.youtube.com/embed/$youtubeID';
}
}
final (height, width, margin, alignment) = getWebElementAttributes(node);
ui.PlatformViewRegistry().registerViewFactory(
videoUrl,
(id) => html.IFrameElement()
..style.width = width
..style.height = height
..src = videoUrl
..style.border = 'none'
..style.margin = margin
..style.alignSelf = alignment
..attributes['loading'] = 'lazy',
);
return SizedBox(
height: 500,
child: HtmlElementView(
viewType: videoUrl,
),
);
}
}

@ -1,59 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_quill/extensions.dart' show isDesktop;
import 'package:flutter_quill/translations.dart';
import '../video.dart';
class SelectVideoSourceDialog extends StatelessWidget {
const SelectVideoSourceDialog({super.key});
@override
Widget build(BuildContext context) {
return Container(
constraints: const BoxConstraints(minHeight: 200),
width: double.infinity,
child: SingleChildScrollView(
child: Column(
children: [
ListTile(
title: Text(context.loc.gallery),
subtitle: Text(
context.loc.pickAVideoFromYourGallery,
),
leading: const Icon(Icons.photo_sharp),
onTap: () => Navigator.of(context).pop(InsertVideoSource.gallery),
),
ListTile(
title: Text(context.loc.camera),
subtitle: Text(context.loc.recordAVideoUsingYourCamera),
leading: const Icon(Icons.camera),
enabled: !isDesktop(supportWeb: false),
onTap: () => Navigator.of(context).pop(InsertVideoSource.camera),
),
ListTile(
title: Text(context.loc.link),
subtitle: Text(
context.loc.pasteAVideoUsingALink,
),
leading: const Icon(Icons.link),
onTap: () => Navigator.of(context).pop(InsertVideoSource.link),
),
],
),
),
);
}
}
Future<InsertVideoSource?> showSelectVideoSourceDialog({
required BuildContext context,
}) async {
final imageSource = await showModalBottomSheet<InsertVideoSource>(
showDragHandle: true,
context: context,
constraints: const BoxConstraints(maxWidth: 640),
builder: (context) =>
const FlutterQuillLocalizationsWidget(child: SelectVideoSourceDialog()),
);
return imageSource;
}

@ -1,188 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_quill/flutter_quill.dart';
import 'package:flutter_quill/translations.dart';
import '../../../models/config/shared_configurations.dart';
import '../../../models/config/video/toolbar/video_configurations.dart';
import '../../../services/image_picker/image_options.dart';
import '../../others/image_video_utils.dart';
import '../video.dart';
import 'select_video_source.dart';
class QuillToolbarVideoButton extends StatelessWidget {
const QuillToolbarVideoButton({
required this.controller,
this.options = const QuillToolbarVideoButtonOptions(),
super.key,
});
final QuillController controller;
final QuillToolbarVideoButtonOptions options;
double _iconSize(BuildContext context) {
final baseFontSize = baseButtonExtraOptions(context)?.iconSize;
final iconSize = options.iconSize;
return iconSize ?? baseFontSize ?? kDefaultIconSize;
}
double _iconButtonFactor(BuildContext context) {
final baseIconFactor = baseButtonExtraOptions(context)?.iconButtonFactor;
final iconButtonFactor = options.iconButtonFactor;
return iconButtonFactor ?? baseIconFactor ?? kDefaultIconButtonFactor;
}
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.quillToolbarBaseButtonOptions;
}
IconData _iconData(BuildContext context) {
return options.iconData ??
baseButtonExtraOptions(context)?.iconData ??
Icons.movie_creation;
}
String _tooltip(BuildContext context) {
return options.tooltip ??
baseButtonExtraOptions(context)?.tooltip ??
'Insert video';
// ('Insert video'.i18n);
}
void _sharedOnPressed(BuildContext context) {
_onPressedHandler(context);
_afterButtonPressed(context);
}
@override
Widget build(BuildContext context) {
final tooltip = _tooltip(context);
final iconSize = _iconSize(context);
final iconButtonFactor = _iconButtonFactor(context);
final iconData = _iconData(context);
final childBuilder =
options.childBuilder ?? baseButtonExtraOptions(context)?.childBuilder;
// final iconColor =
// iconTheme?.iconUnselectedFillColor ?? theme.iconTheme.color;
// final iconFillColor = iconTheme?.iconUnselectedFillColor ??
// (options.fillColor ?? theme.canvasColor);
if (childBuilder != null) {
return childBuilder(
QuillToolbarVideoButtonOptions(
afterButtonPressed: _afterButtonPressed(context),
iconData: iconData,
dialogTheme: options.dialogTheme,
iconSize: options.iconSize,
iconButtonFactor: iconButtonFactor,
linkRegExp: options.linkRegExp,
tooltip: options.tooltip,
iconTheme: options.iconTheme,
videoConfigurations: options.videoConfigurations,
),
QuillToolbarVideoButtonExtraOptions(
context: context,
controller: controller,
onPressed: () => _sharedOnPressed(context),
),
);
}
return QuillToolbarIconButton(
icon: Icon(
iconData,
size: iconSize * iconButtonFactor,
),
tooltip: tooltip,
isSelected: false,
onPressed: () => _sharedOnPressed(context),
iconTheme: _iconTheme(context),
);
}
Future<void> _onPressedHandler(BuildContext context) async {
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);
}
return;
}
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 =>
context.mounted ? await _typeLink(context) : null,
};
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<String?> _typeLink(BuildContext context) async {
final value = await showDialog<String>(
context: context,
builder: (_) => FlutterQuillLocalizationsWidget(
child: TypeLinkDialog(
dialogTheme: options.dialogTheme,
linkType: LinkType.video,
),
),
);
return value;
}
// void _linkSubmitted(String? value) {
// if (value != null && value.isNotEmpty) {
// final index = controller.selection.baseOffset;
// final length = controller.selection.extentOffset - index;
// controller.replaceText(index, length, BlockEmbed.video(value), null);
// }
// }
}

@ -1,66 +0,0 @@
import 'package:flutter/widgets.dart' show BuildContext;
import 'package:flutter_quill/flutter_quill.dart';
import 'package:meta/meta.dart' show immutable;
import '../../extensions/controller_ext.dart';
import '../../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<String?> 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<void> 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<void> 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();
}
}

@ -1,78 +0,0 @@
import 'package:flutter/gestures.dart' show TapGestureRecognizer;
import 'package:flutter/widgets.dart';
import 'package:flutter_quill/flutter_quill.dart' show DefaultStyles;
import 'package:url_launcher/url_launcher.dart' show launchUrl;
import 'package:youtube_player_flutter/youtube_player_flutter.dart';
class YoutubeVideoApp extends StatefulWidget {
const YoutubeVideoApp({
required this.videoUrl,
required this.readOnly,
super.key,
});
final String videoUrl;
final bool readOnly;
@override
YoutubeVideoAppState createState() => YoutubeVideoAppState();
}
class YoutubeVideoAppState extends State<YoutubeVideoApp> {
YoutubePlayerController? _youtubeController;
@override
void initState() {
super.initState();
final videoId = YoutubePlayer.convertUrlToId(widget.videoUrl);
if (videoId != null) {
_youtubeController = YoutubePlayerController(
initialVideoId: videoId,
flags: const YoutubePlayerFlags(
autoPlay: false,
),
);
}
}
@override
Widget build(BuildContext context) {
final defaultStyles = DefaultStyles.getInstance(context);
final youtubeController = _youtubeController;
if (youtubeController == null) {
if (widget.readOnly) {
return RichText(
text: TextSpan(
text: widget.videoUrl,
style: defaultStyles.link,
recognizer: TapGestureRecognizer()
..onTap = () => launchUrl(
Uri.parse(widget.videoUrl),
),
),
);
}
return RichText(
text: TextSpan(text: widget.videoUrl, style: defaultStyles.link),
);
}
return YoutubePlayerBuilder(
player: YoutubePlayer(
controller: youtubeController,
showVideoProgressIndicator: true,
),
builder: (context, player) {
return player;
},
);
}
@override
void dispose() {
_youtubeController?.dispose();
super.dispose();
}
}

@ -6,16 +6,11 @@ import 'embeds/image/editor/image_embed.dart';
import 'embeds/image/editor/image_web_embed.dart';
import 'embeds/image/toolbar/image_button.dart';
import 'embeds/others/camera_button/camera_button.dart';
import 'embeds/video/editor/video_embed.dart';
import 'embeds/video/editor/video_web_embed.dart';
import 'embeds/video/toolbar/video_button.dart';
import 'models/config/camera/camera_configurations.dart';
import 'models/config/image/editor/image_configurations.dart';
import 'models/config/image/toolbar/image_configurations.dart';
import 'models/config/media/media_button_configurations.dart';
import 'models/config/video/editor/video_configurations.dart';
import 'models/config/video/editor/video_web_configurations.dart';
import 'models/config/video/toolbar/video_configurations.dart';
@immutable
class FlutterQuillEmbeds {
@ -43,8 +38,7 @@ class FlutterQuillEmbeds {
static List<fq.EmbedBuilder> editorBuilders({
QuillEditorImageEmbedConfigurations? imageEmbedConfigurations =
const QuillEditorImageEmbedConfigurations(),
QuillEditorVideoEmbedConfigurations? videoEmbedConfigurations =
const QuillEditorVideoEmbedConfigurations(),
}) {
if (kIsWeb) {
throw UnsupportedError(
@ -57,10 +51,6 @@ class FlutterQuillEmbeds {
QuillEditorImageEmbedBuilder(
configurations: imageEmbedConfigurations,
),
if (videoEmbedConfigurations != null)
QuillEditorVideoEmbedBuilder(
configurations: videoEmbedConfigurations,
),
];
}
@ -75,8 +65,6 @@ class FlutterQuillEmbeds {
static List<fq.EmbedBuilder> editorWebBuilders({
QuillEditorImageEmbedConfigurations? imageEmbedConfigurations =
const QuillEditorImageEmbedConfigurations(),
QuillEditorWebVideoEmbedConfigurations? videoEmbedConfigurations =
const QuillEditorWebVideoEmbedConfigurations(),
}) {
if (!kIsWeb) {
throw UnsupportedError(
@ -89,10 +77,6 @@ class FlutterQuillEmbeds {
QuillEditorImageEmbedBuilder(
configurations: imageEmbedConfigurations,
),
if (videoEmbedConfigurations != null)
QuillEditorWebVideoEmbedBuilder(
configurations: videoEmbedConfigurations,
),
];
}
@ -114,8 +98,6 @@ class FlutterQuillEmbeds {
static List<fq.EmbedButtonBuilder> toolbarButtons({
QuillToolbarImageButtonOptions? imageButtonOptions =
const QuillToolbarImageButtonOptions(),
QuillToolbarVideoButtonOptions? videoButtonOptions =
const QuillToolbarVideoButtonOptions(),
QuillToolbarCameraButtonOptions? cameraButtonOptions,
QuillToolbarMediaButtonOptions? mediaButtonOptions,
}) =>
@ -126,12 +108,6 @@ class FlutterQuillEmbeds {
controller: controller,
options: imageButtonOptions,
),
if (videoButtonOptions != null)
(controller, toolbarIconSize, iconTheme, dialogTheme) =>
QuillToolbarVideoButton(
controller: controller,
options: videoButtonOptions,
),
if (cameraButtonOptions != null)
(controller, toolbarIconSize, iconTheme, dialogTheme) =>
QuillToolbarCameraButton(

@ -9,10 +9,6 @@ export 'embeds/image/toolbar/image_button.dart';
export 'embeds/others/camera_button/camera_button.dart';
export 'embeds/others/media_button/media_button.dart';
export 'embeds/unknown/editor/unknown_embed.dart';
export 'embeds/video/editor/video_embed.dart';
export 'embeds/video/editor/video_web_embed.dart';
export 'embeds/video/toolbar/video_button.dart';
export 'embeds/video/video.dart';
export 'extensions/controller_ext.dart';
export 'flutter_quill_embeds.dart';
export 'models/config/camera/camera_configurations.dart';
@ -22,7 +18,4 @@ export 'models/config/image/editor/image_web_configurations.dart';
export 'models/config/image/toolbar/image_configurations.dart';
export 'models/config/media/media_button_configurations.dart';
export 'models/config/shared_configurations.dart';
export 'models/config/video/editor/video_configurations.dart';
export 'models/config/video/editor/video_web_configurations.dart';
export 'models/config/video/toolbar/video_configurations.dart';
export 'utils/utils.dart';

@ -1,24 +0,0 @@
import 'package:flutter/widgets.dart' show GlobalKey;
import 'package:meta/meta.dart' show immutable;
@immutable
class QuillEditorVideoEmbedConfigurations {
const QuillEditorVideoEmbedConfigurations({
this.onVideoInit,
});
/// [onVideoInit] is a callback function that gets triggered when
/// a video is initialized.
/// You can use this to perform actions or setup configurations related
/// to video embedding.
///
///
/// Example usage:
/// ```dart
/// onVideoInit: (videoContainerKey) {
/// // Custom video initialization logic
/// },
/// // Customize other callback functions as needed
/// ```
final void Function(GlobalKey videoContainerKey)? onVideoInit;
}

@ -1,11 +0,0 @@
import 'package:flutter/widgets.dart' show BoxConstraints;
import 'package:meta/meta.dart' show immutable;
@immutable
class QuillEditorWebVideoEmbedConfigurations {
const QuillEditorWebVideoEmbedConfigurations({
this.constraints,
});
final BoxConstraints? constraints;
}

@ -1,36 +0,0 @@
import 'package:flutter/widgets.dart' show Color;
import 'package:flutter_quill/flutter_quill.dart';
import '../../../../embeds/video/video.dart';
class QuillToolbarVideoButtonExtraOptions
extends QuillToolbarBaseButtonExtraOptions {
const QuillToolbarVideoButtonExtraOptions({
required super.controller,
required super.context,
required super.onPressed,
});
}
class QuillToolbarVideoButtonOptions extends QuillToolbarBaseButtonOptions<
QuillToolbarVideoButtonOptions, QuillToolbarVideoButtonExtraOptions> {
const QuillToolbarVideoButtonOptions({
this.linkRegExp,
this.dialogTheme,
this.fillColor,
super.iconSize,
super.iconButtonFactor,
super.iconData,
super.afterButtonPressed,
super.tooltip,
super.iconTheme,
super.childBuilder,
this.videoConfigurations = const QuillToolbarVideoConfigurations(),
});
final RegExp? linkRegExp;
final QuillDialogTheme? dialogTheme;
final QuillToolbarVideoConfigurations videoConfigurations;
final Color? fillColor;
}

@ -40,7 +40,6 @@ dependencies:
# Plugins
video_player: ^2.8.1
youtube_player_flutter: ^8.1.2
url_launcher: ^6.2.1
super_clipboard: ^0.8.4
gal: ^2.2.0

@ -1,30 +0,0 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
/pubspec.lock
**/doc/api/
.dart_tool/
.packages
build/

@ -1,10 +0,0 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: "d211f42860350d914a5ad8102f9ec32764dc6d06"
channel: "stable"
project_type: package

File diff suppressed because it is too large Load Diff

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2023 Flutter Quill Team
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

@ -1,41 +0,0 @@
# Flutter Quill HTML
A extension for [flutter_quill](https://pub.dev/packages/flutter_quill) package to add support for dealing with conversion to/from html
It uses [vsc_quill_delta_to_html](https://pub.dev/packages/vsc_quill_delta_to_html) package to convert the the delta to HTML
This library is **experimental** and the support might be dropped at anytime.
## Features
```markdown
- Easy to use
- Support Flutter Quill package
```
## Getting started
```yaml
dependencies:
quill_html_converter: ^<latest-version-here>
```
## Usage
First, you need to [setup](../README.md#usage) the `flutter_quill` first
Then you can simply convert to/from HTML
```dart
import 'package:quill_html_converter/quill_html_converter.dart';
// Convert Delta to HTML
final html = _controller.document.toDelta().toHtml();
// Load Delta document using HTML
_controller.document =
Document.fromDelta(Document.fromHtml(html));
```
## Additional information
This will be updated soon.

@ -1,36 +0,0 @@
include: package:flutter_lints/flutter.yaml
analyzer:
errors:
undefined_prefixed_name: ignore
unsafe_html: ignore
linter:
rules:
always_declare_return_types: true
always_put_required_named_parameters_first: true
annotate_overrides: true
avoid_empty_else: true
avoid_escaping_inner_quotes: true
avoid_print: true
avoid_redundant_argument_values: true
avoid_types_on_closure_parameters: true
avoid_void_async: true
cascade_invocations: true
directives_ordering: true
omit_local_variable_types: true
prefer_const_constructors: true
prefer_const_constructors_in_immutables: true
prefer_const_declarations: true
prefer_final_fields: true
prefer_final_in_for_each: true
prefer_final_locals: true
prefer_initializing_formals: true
prefer_int_literals: true
prefer_interpolation_to_compose_strings: true
prefer_relative_imports: true
prefer_single_quotes: true
sort_constructors_first: true
sort_unnamed_constructors_first: true
unnecessary_lambdas: true
unnecessary_parenthesis: true
unnecessary_string_interpolations: true

@ -1,28 +0,0 @@
library quill_html_converter;
import 'package:dart_quill_delta/dart_quill_delta.dart';
import 'package:vsc_quill_delta_to_html/vsc_quill_delta_to_html.dart'
as conventer show ConverterOptions, QuillDeltaToHtmlConverter;
typedef ConverterOptions = conventer.ConverterOptions;
/// A extension for [Delta] which comes from `flutter_quill` to extends
/// the functionality of it to support converting the [Delta] to/from HTML
extension DeltaHtmlExt on Delta {
/// Convert the [Delta] instance to HTML Raw string
///
/// It will run using the following steps:
///
/// 1. Convert the [Delta] to json using [toJson]
/// 2. Cast the json map as `List<Map<String, dynamic>>`
/// 3. Pass it to the conventer `vsc_quill_delta_to_html` which is a package
/// that designed specifically for converting the quill delta to html
String toHtml({ConverterOptions? options}) {
final json = toJson();
final html = conventer.QuillDeltaToHtmlConverter(
List.castFrom(json),
options,
).convert();
return html;
}
}

@ -1,38 +0,0 @@
name: quill_html_converter
description: A extension for flutter_quill package to add support for dealing with conversion to/from html
version: 9.2.6
homepage: https://github.com/singerdmx/flutter-quill/tree/master/quill_html_converter/
repository: https://github.com/singerdmx/flutter-quill/tree/master/quill_html_converter/
issue_tracker: https://github.com/singerdmx/flutter-quill/issues/
documentation: https://github.com/singerdmx/flutter-quill/tree/master/quill_html_converter/
topics:
- ui
- widgets
- widget
- rich-text-editor
- quill
environment:
sdk: '>=3.1.5 <4.0.0'
flutter: ">=1.17.0"
dependencies:
flutter:
sdk: flutter
vsc_quill_delta_to_html: ^1.0.3
html2md: ^1.3.1
markdown: ^7.1.1
charcode: ^1.3.1
collection: ^1.18.0
dart_quill_delta: ^0.0.1
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^3.0.1
flutter:
uses-material-design: true

@ -1,3 +0,0 @@
dependency_overrides:
dart_quill_delta:
path: ../dart_quill_delta

@ -1,7 +0,0 @@
import 'package:flutter_test/flutter_test.dart';
void main() {
test('No tests for now', () {
expect(true, true);
});
}

@ -1,29 +0,0 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
/pubspec.lock
**/doc/api/
.dart_tool/
build/

@ -1,10 +0,0 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: "78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9"
channel: "stable"
project_type: package

File diff suppressed because it is too large Load Diff

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2023 Flutter Quill Team
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

@ -1,34 +0,0 @@
# Flutter Quill Pdf
A extension for [flutter_quill](https://pub.dev/packages/flutter_quill) package to add support for dealing with conversion to Pdf
It uses [quill_html_converter](https://pub.dev/packages/quill_html_converter) package to convert the the delta to Html and [htmltopdfwidgets](https://pub.dev/packages/htmltopdfwidgets) to convert the Html to Pdf
This library is **experimental** and the support might be dropped at anytime.
## Features
```markdown
- Easy to use
- Support Flutter Quill package
```
## Getting started
```yaml
dependencies:
quill_pdf_converter: ^<latest-version-here>
```
## Usage
First, you need to [setup](../README.md#usage) the `flutter_quill` first
Then you can simply convert to PDF
```dart
import 'package:quill_pdf_converter/quill_pdf_converter.dart';
// Convert Delta to Pdf
final pdfWidgets = _controller.document.toDelta().toPdf();
```

@ -1,36 +0,0 @@
include: package:flutter_lints/flutter.yaml
analyzer:
errors:
undefined_prefixed_name: ignore
unsafe_html: ignore
linter:
rules:
always_declare_return_types: true
always_put_required_named_parameters_first: true
annotate_overrides: true
avoid_empty_else: true
avoid_escaping_inner_quotes: true
avoid_print: true
avoid_redundant_argument_values: true
avoid_types_on_closure_parameters: true
avoid_void_async: true
cascade_invocations: true
directives_ordering: true
omit_local_variable_types: true
prefer_const_constructors: true
prefer_const_constructors_in_immutables: true
prefer_const_declarations: true
prefer_final_fields: true
prefer_final_in_for_each: true
prefer_final_locals: true
prefer_initializing_formals: true
prefer_int_literals: true
prefer_interpolation_to_compose_strings: true
prefer_relative_imports: true
prefer_single_quotes: true
sort_constructors_first: true
sort_unnamed_constructors_first: true
unnecessary_lambdas: true
unnecessary_parenthesis: true
unnecessary_string_interpolations: true

@ -1,22 +0,0 @@
library quill_pdf_converter;
import 'package:dart_quill_delta/dart_quill_delta.dart';
import 'package:htmltopdfwidgets/htmltopdfwidgets.dart';
import 'package:meta/meta.dart';
import 'package:pdf/widgets.dart' as pw;
import 'package:quill_html_converter/quill_html_converter.dart';
/// Extension on [Delta] to add extra functions for converting to Pdf
extension DeltaPdfExt on Delta {
/// First convert to Html then to Pdf
@experimental
Future<List<pw.Widget>> toPdf() async {
final html = toHtml();
return HTMLToPdf().convert(
html,
fontFallback: [
pw.Font.symbol(),
],
);
}
}

@ -1,34 +0,0 @@
name: quill_pdf_converter
description: A extension for flutter_quill package to add support for dealing with conversion to pdf
version: 9.2.6
homepage: https://github.com/singerdmx/flutter-quill/tree/master/quill_pdf_converter/
repository: https://github.com/singerdmx/flutter-quill/tree/master/quill_pdf_converter/
issue_tracker: https://github.com/singerdmx/flutter-quill/issues/
documentation: https://github.com/singerdmx/flutter-quill/tree/master/quill_pdf_converter/
topics:
- ui
- widgets
- widget
- rich-text-editor
- quill
environment:
sdk: '>=3.2.3 <4.0.0'
flutter: ">=1.17.0"
dependencies:
flutter:
sdk: flutter
dart_quill_delta: ^0.0.1
quill_html_converter: ^9.1.0
pdf: ^3.10.7
htmltopdfwidgets: ^0.0.9+2
meta: ^1.10.0
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^3.0.1
flutter:

@ -1,5 +0,0 @@
dependency_overrides:
dart_quill_delta:
path: ../dart_quill_delta
quill_html_converter:
path: ../quill_html_converter

@ -1,7 +0,0 @@
import 'package:flutter_test/flutter_test.dart';
void main() {
test('No tests for now', () {
expect(true, true);
});
}
Loading…
Cancel
Save