Improving flutter quill extensions (#1564)

* organizations for flutter quill extensions
pull/1577/head
Ellet 1 year ago committed by GitHub
parent 7ccd61e63e
commit 039e3193d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      .gitignore
  2. 4
      example/lib/presentation/quill/quill_editor.dart
  3. 18
      example/lib/presentation/quill/samples/quill_images_sample.dart
  4. 8
      flutter_quill_extensions/CHANGELOG.md
  5. 25
      flutter_quill_extensions/README.md
  6. 0
      flutter_quill_extensions/lib/embeds/embed_types.dart
  7. 5
      flutter_quill_extensions/lib/embeds/formula/editor/formula_embed.dart
  8. 2
      flutter_quill_extensions/lib/embeds/formula/toolbar/formula_button.dart
  9. 86
      flutter_quill_extensions/lib/embeds/image/editor/image_embed.dart
  10. 4
      flutter_quill_extensions/lib/embeds/image/editor/image_embed_types.dart
  11. 12
      flutter_quill_extensions/lib/embeds/image/editor/image_menu.dart
  12. 8
      flutter_quill_extensions/lib/embeds/image/editor/image_web_embed.dart
  13. 8
      flutter_quill_extensions/lib/embeds/image/toolbar/image_button.dart
  14. 30
      flutter_quill_extensions/lib/embeds/image/toolbar/select_image_source.dart
  15. 6
      flutter_quill_extensions/lib/embeds/others/camera_button/camera_button.dart
  16. 4
      flutter_quill_extensions/lib/embeds/others/camera_button/camera_types.dart
  17. 2
      flutter_quill_extensions/lib/embeds/others/camera_button/select_camera_action.dart
  18. 0
      flutter_quill_extensions/lib/embeds/others/image_video_utils.dart
  19. 0
      flutter_quill_extensions/lib/embeds/others/media_button/media_button.dart
  20. 0
      flutter_quill_extensions/lib/embeds/unknown/editor/unknown_embed.dart
  21. 4
      flutter_quill_extensions/lib/embeds/video/editor/video_embed.dart
  22. 6
      flutter_quill_extensions/lib/embeds/video/editor/video_web_embed.dart
  23. 22
      flutter_quill_extensions/lib/embeds/video/toolbar/select_video_source.dart
  24. 9
      flutter_quill_extensions/lib/embeds/video/toolbar/video_button.dart
  25. 4
      flutter_quill_extensions/lib/embeds/video/video.dart
  26. 2
      flutter_quill_extensions/lib/embeds/widgets/image.dart
  27. 0
      flutter_quill_extensions/lib/embeds/widgets/image_resizer.dart
  28. 2
      flutter_quill_extensions/lib/embeds/widgets/video_app.dart
  29. 0
      flutter_quill_extensions/lib/embeds/widgets/youtube_video_app.dart
  30. 32
      flutter_quill_extensions/lib/extensions/attribute.dart
  31. 23
      flutter_quill_extensions/lib/extensions/controller.dart
  32. 87
      flutter_quill_extensions/lib/flutter_quill_extensions.dart
  33. 30
      flutter_quill_extensions/lib/logic/extensions/attribute.dart
  34. 2
      flutter_quill_extensions/lib/models/config/editor/image/image.dart
  35. 0
      flutter_quill_extensions/lib/models/config/editor/image/image_web.dart
  36. 0
      flutter_quill_extensions/lib/models/config/editor/video/video.dart
  37. 0
      flutter_quill_extensions/lib/models/config/editor/video/video_web.dart
  38. 0
      flutter_quill_extensions/lib/models/config/editor/webview.dart
  39. 0
      flutter_quill_extensions/lib/models/config/shared_configurations.dart
  40. 2
      flutter_quill_extensions/lib/models/config/toolbar/buttons/camera.dart
  41. 0
      flutter_quill_extensions/lib/models/config/toolbar/buttons/formula.dart
  42. 2
      flutter_quill_extensions/lib/models/config/toolbar/buttons/image.dart
  43. 0
      flutter_quill_extensions/lib/models/config/toolbar/buttons/media_button.dart
  44. 2
      flutter_quill_extensions/lib/models/config/toolbar/buttons/video.dart
  45. 150
      flutter_quill_extensions/lib/presentation/embeds/editor/image/image.dart
  46. 58
      flutter_quill_extensions/lib/presentation/embeds/editor/webview.dart
  47. 201
      flutter_quill_extensions/lib/presentation/utils/utils.dart
  48. 0
      flutter_quill_extensions/lib/services/image_picker/image_options.dart
  49. 0
      flutter_quill_extensions/lib/services/image_picker/image_picker.dart
  50. 0
      flutter_quill_extensions/lib/services/image_picker/packages/image_picker.dart
  51. 0
      flutter_quill_extensions/lib/services/image_picker/s_image_picker.dart
  52. 0
      flutter_quill_extensions/lib/services/image_saver/exceptions.dart
  53. 0
      flutter_quill_extensions/lib/services/image_saver/image_saver.dart
  54. 0
      flutter_quill_extensions/lib/services/image_saver/packages/gal.dart
  55. 0
      flutter_quill_extensions/lib/services/image_saver/s_image_saver.dart
  56. 0
      flutter_quill_extensions/lib/utils/dart_ui/dart_ui_fake.dart
  57. 0
      flutter_quill_extensions/lib/utils/dart_ui/dart_ui_real.dart
  58. 29
      flutter_quill_extensions/lib/utils/element_utils/element_shared_utils.dart
  59. 98
      flutter_quill_extensions/lib/utils/element_utils/element_utils.dart
  60. 24
      flutter_quill_extensions/lib/utils/element_utils/element_web_utils.dart
  61. 2
      flutter_quill_extensions/lib/utils/quill_image_utils.dart
  62. 12
      flutter_quill_extensions/lib/utils/string.dart
  63. 89
      flutter_quill_extensions/lib/utils/utils.dart
  64. 2
      flutter_quill_extensions/pubspec.yaml
  65. 1
      lib/flutter_quill.dart
  66. 36
      lib/src/l10n/generated/quill_localizations.dart
  67. 20
      lib/src/l10n/generated/quill_localizations_ar.dart
  68. 20
      lib/src/l10n/generated/quill_localizations_bg.dart
  69. 20
      lib/src/l10n/generated/quill_localizations_bn.dart
  70. 20
      lib/src/l10n/generated/quill_localizations_cs.dart
  71. 20
      lib/src/l10n/generated/quill_localizations_da.dart
  72. 20
      lib/src/l10n/generated/quill_localizations_de.dart
  73. 20
      lib/src/l10n/generated/quill_localizations_en.dart
  74. 20
      lib/src/l10n/generated/quill_localizations_es.dart
  75. 20
      lib/src/l10n/generated/quill_localizations_fa.dart
  76. 20
      lib/src/l10n/generated/quill_localizations_fr.dart
  77. 20
      lib/src/l10n/generated/quill_localizations_he.dart
  78. 20
      lib/src/l10n/generated/quill_localizations_hi.dart
  79. 20
      lib/src/l10n/generated/quill_localizations_id.dart
  80. 20
      lib/src/l10n/generated/quill_localizations_it.dart
  81. 20
      lib/src/l10n/generated/quill_localizations_ja.dart
  82. 20
      lib/src/l10n/generated/quill_localizations_ko.dart
  83. 20
      lib/src/l10n/generated/quill_localizations_ms.dart
  84. 20
      lib/src/l10n/generated/quill_localizations_nl.dart
  85. 20
      lib/src/l10n/generated/quill_localizations_no.dart
  86. 20
      lib/src/l10n/generated/quill_localizations_pl.dart
  87. 20
      lib/src/l10n/generated/quill_localizations_pt.dart
  88. 20
      lib/src/l10n/generated/quill_localizations_ru.dart
  89. 20
      lib/src/l10n/generated/quill_localizations_sr.dart
  90. 20
      lib/src/l10n/generated/quill_localizations_sw.dart
  91. 20
      lib/src/l10n/generated/quill_localizations_tk.dart
  92. 20
      lib/src/l10n/generated/quill_localizations_tr.dart
  93. 20
      lib/src/l10n/generated/quill_localizations_uk.dart
  94. 20
      lib/src/l10n/generated/quill_localizations_ur.dart
  95. 20
      lib/src/l10n/generated/quill_localizations_vi.dart
  96. 20
      lib/src/l10n/generated/quill_localizations_zh.dart
  97. 8
      lib/src/l10n/quill_en.arb
  98. 299
      lib/src/l10n/untranslated.json
  99. 1
      lib/translations.dart

5
.gitignore vendored

@ -80,4 +80,7 @@ pubspec.lock
# For local development
pubspec_overrides.yaml
old_example
old_example
# A directory where you put all of your local things that you don't want to push
.flutter-quill

@ -7,9 +7,9 @@ import 'package:desktop_drop/desktop_drop.dart' show DropTarget;
import 'package:flutter/material.dart';
import 'package:flutter_quill/extensions.dart' show isAndroid, isIOS, isWeb;
import 'package:flutter_quill/flutter_quill.dart';
import 'package:flutter_quill_extensions/flutter_quill_extensions.dart';
import 'package:flutter_quill_extensions/presentation/embeds/widgets/image.dart'
import 'package:flutter_quill_extensions/embeds/widgets/image.dart'
show getImageProviderByImageSource, imageFileExtensions;
import 'package:flutter_quill_extensions/flutter_quill_extensions.dart';
import 'package:path/path.dart' as path;
import '../extensions/scaffold_messenger.dart';

File diff suppressed because one or more lines are too long

@ -2,6 +2,14 @@
All notable changes to this project will be documented in this file.
## 0.8.0-dev
* **Breaking Change**: Completly change the way how the source code structured to more basic and simple way, organize folders and file names, if you use the library
from `flutter_quill_extensions.dart` then there is nothing you need to do, but if you are using any other import then you need to re-imports
embed, this won't affect how quill js work
* Improvemenets to the image embed
* Add support for `margin` for web
* Add untranslated strings to the `quill_en.arb`
## 0.7.2
* Fix a bug when opening the link dialog for both video and image buttons
* Update `README.md`

@ -14,8 +14,8 @@ to support embedding widgets like images, formulas, videos, and more.
- [Platform Specific Configurations](#platform-specific-configurations)
- [Usage](#usage)
- [Embed Blocks](#embed-blocks)
- [Custom Size Image for Mobile](#custom-size-image-for-mobile)
- [Custom Size Image for other platforms](#custom-size-image-for-other-platforms)
- [Element properties](#element-properties)
- [Custom Element properties](#custom-element-properties)
- [Drag and drop feature](#drag-and-drop-feature)
- [Features](#features)
- [Contributing](#contributing)
@ -130,37 +130,40 @@ As of version [flutter_quill](https://pub.dev/packages/flutter_quill) 6.0, embed
The instructions for using the embed blocks are in the [Usage](#usage) section
### Custom Size Image for Mobile
### Element properties
Define `mobileWidth`, `mobileHeight`, `mobileMargin`, `mobileAlignment` as follows:
Currently the library has limitied support for the image and video properties
and it supports only `width`, `height`, `margin`
```json
{
"insert": {
"image": "https://user-images.githubusercontent.com/122956/72955931-ccc07900-3d52-11ea-89b1-d468a6e2aa2b.png"
},
"attributes":{
"style":"mobileWidth: 50; mobileHeight: 50; mobileMargin: 10; mobileAlignment: topLeft"
"attributes": {
"style":"width: 50px; height: 50px; margin: 10px;"
}
}
```
### Custom Size Image for other platforms
### Custom Element properties
Doesn't apply to official Quill JS
Define `width`, `height`, `margin`, `alignment` as follows:
Define flutterAlignment` as follows:
```json
{
"insert": {
"image": "https://user-images.githubusercontent.com/122956/72955931-ccc07900-3d52-11ea-89b1-d468a6e2aa2b.png"
},
"attributes": {
"style":"width: 50; height: 50; margin: 10; alignment: topLeft"
"attributes":{
"style":"flutterAlignment: topLeft"
}
}
```
This works for all platforms except Web
### Drag and drop feature
Currently, the drag-and-drop feature is not officially supported, but you can achieve this very easily in the following steps:

@ -1,7 +1,6 @@
import 'package:flutter/widgets.dart';
import 'package:flutter_quill/extensions.dart' as base;
import 'package:flutter_quill/flutter_quill.dart'
show BlockEmbed, EmbedBuilder, QuillController;
show BlockEmbed, Embed, EmbedBuilder, QuillController;
class QuillEditorFormulaEmbedBuilder extends EmbedBuilder {
const QuillEditorFormulaEmbedBuilder();
@ -15,7 +14,7 @@ class QuillEditorFormulaEmbedBuilder extends EmbedBuilder {
Widget build(
BuildContext context,
QuillController controller,
base.Embed node,
Embed node,
bool readOnly,
bool inline,
TextStyle textStyle,

@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_quill/flutter_quill.dart';
import '../../models/config/toolbar/buttons/formula.dart';
import '../../../models/config/toolbar/buttons/formula.dart';
class QuillToolbarFormulaButton extends StatelessWidget {
const QuillToolbarFormulaButton({

@ -0,0 +1,86 @@
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:flutter/material.dart';
import 'package:flutter_quill/flutter_quill.dart' hide OptionalSize;
import 'package:flutter_quill/translations.dart';
import '../../../models/config/editor/image/image.dart';
import '../../../models/config/shared_configurations.dart';
import '../../../utils/element_utils/element_utils.dart';
import '../../widgets/image.dart';
import 'image_menu.dart';
class QuillEditorImageEmbedBuilder extends EmbedBuilder {
QuillEditorImageEmbedBuilder({
required this.configurations,
});
final QuillEditorImageEmbedConfigurations configurations;
@override
String get key => BlockEmbed.imageType;
@override
bool get expanded => false;
@override
Widget build(
BuildContext context,
QuillController controller,
Embed node,
bool readOnly,
bool inline,
TextStyle textStyle,
) {
assert(!kIsWeb, 'Please provide image EmbedBuilder for Web');
final imageSource = standardizeImageUrl(node.value.data);
final ((imageSize), margin, alignment) = getElementAttributes(node);
final width = imageSize.width;
final height = imageSize.height;
final image = getImageWidgetByImageSource(
imageSource,
imageProviderBuilder: configurations.imageProviderBuilder,
imageErrorWidgetBuilder: configurations.imageErrorWidgetBuilder,
alignment: alignment,
height: height,
width: width,
assetsPrefix: QuillSharedExtensionsConfigurations.get(context: context)
.assetsPrefix,
);
final imageSaverService =
QuillSharedExtensionsConfigurations.get(context: context)
.imageSaverService;
return GestureDetector(
onTap: configurations.onImageClicked ??
() => showDialog(
context: context,
builder: (_) => QuillProvider.value(
value: context.requireQuillProvider,
child: FlutterQuillLocalizationsWidget(
child: ImageOptionsMenu(
controller: controller,
configurations: configurations,
imageSource: imageSource,
imageSize: imageSize,
isReadOnly: readOnly,
imageSaverService: imageSaverService,
),
),
),
),
child: Builder(
builder: (context) {
if (margin != null) {
return Padding(
padding: EdgeInsets.all(margin),
child: image,
);
}
return image;
},
),
);
}
}

@ -4,8 +4,8 @@ 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';
import '../../../extensions/controller.dart';
import '../../../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

@ -1,9 +1,7 @@
import 'package:flutter/cupertino.dart' show showCupertinoModalPopup;
import 'package:flutter/material.dart';
import 'package:flutter_quill/extensions.dart' show isMobile;
import 'package:flutter_quill/flutter_quill.dart'
show
FlutterQuillLocalizationsWidget,
ImageUrl,
QuillController,
QuillProvider,
@ -12,10 +10,11 @@ import 'package:flutter_quill/flutter_quill.dart'
getEmbedNode;
import 'package:flutter_quill/translations.dart';
import '../../../../logic/models/config/shared_configurations.dart';
import '../../../../logic/services/image_saver/s_image_saver.dart';
import '../../../../logic/utils/string.dart';
import '../../../models/config/editor/image/image.dart';
import '../../../models/config/shared_configurations.dart';
import '../../../services/image_saver/s_image_saver.dart';
import '../../../utils/element_utils/element_utils.dart';
import '../../../utils/string.dart';
import '../../../utils/utils.dart';
import '../../widgets/image.dart' show ImageTapWrapper, getImageStyleString;
import '../../widgets/image_resizer.dart' show ImageResizer;
@ -34,7 +33,7 @@ class ImageOptionsMenu extends StatelessWidget {
final QuillController controller;
final QuillEditorImageEmbedConfigurations configurations;
final String imageSource;
final OptionalSize imageSize;
final ElementSize imageSize;
final bool isReadOnly;
final ImageSaverService imageSaverService;
@ -70,7 +69,6 @@ class ImageOptionsMenu extends StatelessWidget {
getImageStyleString(controller),
width: width,
height: height,
isMobile: isMobile(supportWeb: false),
);
controller
..skipRequestKeyboard = true

@ -4,10 +4,10 @@ import 'package:flutter_quill/flutter_quill.dart';
import 'package:universal_html/html.dart' as html;
import '../../../models/config/editor/image/image_web.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';
import '../../../utils/web_utils.dart';
import '../shims/dart_ui_fake.dart'
if (dart.library.html) '../shims/dart_ui_real.dart' as ui;
class QuillEditorWebImageEmbedBuilder extends EmbedBuilder {
const QuillEditorWebImageEmbedBuilder({
@ -43,7 +43,7 @@ class QuillEditorWebImageEmbedBuilder extends EmbedBuilder {
// if not then it will add the data:image/png;base64, at the first
if (isImageBase64(imageSource)) {
// Sometimes the image base 64 for some reasons
// doesn't displayed with the
// doesn't displayed with the 'data:image/png;base64'
if (!(imageSource.startsWith('data:image/') &&
imageSource.contains('base64'))) {
imageSource = 'data:image/png;base64, $imageSource';

@ -4,11 +4,11 @@ import 'package:flutter/material.dart';
import 'package:flutter_quill/flutter_quill.dart';
import 'package:flutter_quill/translations.dart';
import '../../../../logic/models/config/shared_configurations.dart';
import '../../../../logic/services/image_picker/image_picker.dart';
import '../../../models/config/shared_configurations.dart';
import '../../../models/config/toolbar/buttons/image.dart';
import '../../embed_types/image.dart';
import '../utils/image_video_utils.dart';
import '../../../services/image_picker/image_picker.dart';
import '../../others/image_video_utils.dart';
import '../editor/image_embed_types.dart';
import 'select_image_source.dart';
class QuillToolbarImageButton extends StatelessWidget {

@ -1,7 +1,9 @@
import 'package:flutter/material.dart';
import 'package:flutter_quill/extensions.dart' show isDesktop;
import 'package:flutter_quill/flutter_quill.dart';
import 'package:flutter_quill/translations.dart';
import '../../embed_types/image.dart';
import '../editor/image_embed_types.dart';
class SelectImageSourceDialog extends StatelessWidget {
const SelectImageSourceDialog({super.key});
@ -14,28 +16,27 @@ class SelectImageSourceDialog extends StatelessWidget {
child: SingleChildScrollView(
child: Column(
children: [
// TODO: Needs to be translated
ListTile(
title: const Text('Gallery'),
subtitle: const Text(
'Pick a photo from your gallery',
title: Text(context.loc.gallery),
subtitle: Text(
context.loc.pickAPhotoFromYourGallery,
),
leading: const Icon(Icons.photo_sharp),
onTap: () => Navigator.of(context).pop(InsertImageSource.gallery),
),
ListTile(
title: const Text('Camera'),
subtitle: const Text(
'Take a photo using your phone camera',
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),
),
ListTile(
title: const Text('Link'),
subtitle: const Text(
'Paste a photo using a link',
title: Text(context.loc.link),
subtitle: Text(
context.loc.pasteAPhotoUsingALink,
),
leading: const Icon(Icons.link),
onTap: () => Navigator.of(context).pop(InsertImageSource.link),
@ -54,7 +55,12 @@ Future<InsertImageSource?> showSelectImageSourceDialog({
showDragHandle: true,
context: context,
constraints: const BoxConstraints(maxWidth: 640),
builder: (_) => const SelectImageSourceDialog(),
builder: (_) => QuillProvider.value(
value: context.requireQuillProvider,
child: const FlutterQuillLocalizationsWidget(
child: SelectImageSourceDialog(),
),
),
);
return imageSource;
}

@ -8,10 +8,10 @@ import 'package:flutter_quill/flutter_quill.dart'
QuillToolbarIconButton;
import 'package:flutter_quill/translations.dart';
import '../../../../logic/models/config/shared_configurations.dart';
import '../../../../logic/services/image_picker/image_options.dart';
import '../../../models/config/shared_configurations.dart';
import '../../../models/config/toolbar/buttons/camera.dart';
import '../../embed_types/camera.dart';
import '../../../services/image_picker/image_options.dart';
import 'camera_types.dart';
import 'select_camera_action.dart';
class QuillToolbarCameraButton extends StatelessWidget {

@ -1,8 +1,8 @@
import 'package:flutter/widgets.dart' show BuildContext;
import 'package:meta/meta.dart' show immutable;
import 'image.dart';
import 'video.dart';
import '../../image/editor/image_embed_types.dart';
import '../../video/video.dart';
enum CameraAction {
video,

@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_quill/translations.dart';
import '../../embed_types/camera.dart';
import 'camera_types.dart';
class SelectCameraActionDialog extends StatelessWidget {
const SelectCameraActionDialog({super.key});

@ -1,9 +1,9 @@
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:flutter/material.dart';
import 'package:flutter_quill/extensions.dart' as base;
import 'package:flutter_quill/flutter_quill.dart';
import '../../../models/config/editor/video/video.dart';
import '../../../utils/element_utils/element_utils.dart';
import '../../../utils/utils.dart';
import '../../widgets/video_app.dart';
import '../../widgets/youtube_video_app.dart';
@ -22,7 +22,7 @@ class QuillEditorVideoEmbedBuilder extends EmbedBuilder {
Widget build(
BuildContext context,
QuillController controller,
base.Embed node,
Embed node,
bool readOnly,
bool inline,
TextStyle textStyle,

@ -5,10 +5,10 @@ import 'package:youtube_player_flutter/youtube_player_flutter.dart'
show YoutubePlayer;
import '../../../models/config/editor/video/video_web.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';
import '../../../utils/web_utils.dart';
import '../shims/dart_ui_fake.dart'
if (dart.library.html) '../shims/dart_ui_real.dart' as ui;
class QuillEditorWebVideoEmbedBuilder extends EmbedBuilder {
const QuillEditorWebVideoEmbedBuilder({

@ -1,7 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter_quill/extensions.dart' show isDesktop;
import 'package:flutter_quill/translations.dart';
import '../../embed_types/video.dart';
import '../video.dart';
class SelectVideoSourceDialog extends StatelessWidget {
const SelectVideoSourceDialog({super.key});
@ -14,28 +15,25 @@ class SelectVideoSourceDialog extends StatelessWidget {
child: SingleChildScrollView(
child: Column(
children: [
// TODO: Needs to be translated
ListTile(
title: const Text('Gallery'),
subtitle: const Text(
'Pick a video from your gallery',
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: const Text('Camera'),
subtitle: const Text(
'Record a video using your phone camera',
),
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: const Text('Link'),
subtitle: const Text(
'Paste a video using a link',
title: Text(context.loc.link),
subtitle: Text(
context.loc.pasteAVideoUsingALink,
),
leading: const Icon(Icons.link),
onTap: () => Navigator.of(context).pop(InsertVideoSource.link),

@ -2,12 +2,13 @@
import 'package:flutter/material.dart';
import 'package:flutter_quill/flutter_quill.dart';
import 'package:flutter_quill/translations.dart';
import '../../../../logic/models/config/shared_configurations.dart';
import '../../../../logic/services/image_picker/image_options.dart';
import '../../../models/config/shared_configurations.dart';
import '../../../models/config/toolbar/buttons/video.dart';
import '../../embed_types/video.dart';
import '../utils/image_video_utils.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 {

@ -2,8 +2,8 @@ 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';
import '../../extensions/controller.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

@ -7,7 +7,7 @@ import 'package:photo_view/photo_view.dart';
import '../../models/config/editor/image/image.dart';
import '../../utils/utils.dart';
import '../embed_types/image.dart';
import '../image/editor/image_embed_types.dart';
const List<String> imageFileExtensions = [
'.jpeg',

@ -6,7 +6,7 @@ import 'package:flutter_quill/flutter_quill.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:video_player/video_player.dart';
import '../../../flutter_quill_extensions.dart';
import '../../flutter_quill_extensions.dart';
/// Widget for playing back video
/// Refer to https://github.com/flutter/plugins/tree/master/packages/video_player/video_player

@ -0,0 +1,32 @@
import 'package:flutter_quill/flutter_quill.dart'
show Attribute, AttributeScope;
// class FlutterWidthAttribute extends Attribute<String?> {
// const FlutterWidthAttribute(String? val)
// : super('flutterWidth', AttributeScope.ignore, val);
// }
// class FlutterHeightAttribute extends Attribute<String?> {
// const FlutterHeightAttribute(String? val)
// : super('flutterHeight', AttributeScope.ignore, val);
// }
// class FlutterMarginAttribute extends Attribute<String?> {
// const FlutterMarginAttribute(String? val)
// : super('flutterMargin', AttributeScope.ignore, val);
// }
class FlutterAlignmentAttribute extends Attribute<String?> {
const FlutterAlignmentAttribute(String? val)
: super('flutterAlignment', AttributeScope.ignore, val);
}
extension AttributeExt on Attribute {
// static const FlutterWidthAttribute flutterWidth = FlutterWidthAttribute(null);
// static const FlutterHeightAttribute flutterHeight =
// FlutterHeightAttribute(null);
// static const FlutterMarginAttribute flutterMargin =
// FlutterMarginAttribute(null);
static const FlutterAlignmentAttribute flutterAlignment =
FlutterAlignmentAttribute(null);
}

@ -1,7 +1,5 @@
import 'package:flutter_quill/flutter_quill.dart';
// ignore: unused_import
import '../../presentation/embeds/editor/webview.dart';
import '../utils/quill_image_utils.dart';
/// Extension functions on [QuillController]
@ -12,27 +10,6 @@ extension QuillControllerExt on QuillController {
int get index => selection.baseOffset;
int get length => selection.extentOffset - index;
/// Insert webview embed block, it requires [initialUrl] to load
/// the initial page
// void insertWebViewBlock({
// required String initialUrl,
// }) {
// final block = BlockEmbed.custom(
// QuillEditorWebViewBlockEmbed(
// initialUrl,
// ),
// );
// this
// ..skipRequestKeyboard = true
// ..replaceText(
// index,
// length,
// block,
// null,
// );
// }
/// Insert image embed block, it requires the [imageSource]
///
/// it could be local image on the system file

@ -6,52 +6,49 @@ import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:flutter_quill/flutter_quill.dart';
import 'package:meta/meta.dart' show immutable;
import 'presentation/embeds/editor/image/image.dart';
import 'presentation/embeds/editor/image/image_web.dart';
import 'presentation/embeds/editor/video/video.dart';
import 'presentation/embeds/editor/video/video_web.dart';
import 'presentation/embeds/editor/webview.dart';
import 'presentation/embeds/toolbar/camera_button/camera_button.dart';
import 'presentation/embeds/toolbar/image_button/image_button.dart';
import 'presentation/embeds/toolbar/video_button/video_button.dart';
import 'presentation/models/config/editor/image/image.dart';
import 'presentation/models/config/editor/image/image_web.dart';
import 'presentation/models/config/editor/video/video.dart';
import 'presentation/models/config/editor/video/video_web.dart';
import 'presentation/models/config/editor/webview.dart';
import 'presentation/models/config/toolbar/buttons/camera.dart';
import 'presentation/models/config/toolbar/buttons/image.dart';
import 'presentation/models/config/toolbar/buttons/media_button.dart';
import 'presentation/models/config/toolbar/buttons/video.dart';
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/editor/image/image.dart';
import 'models/config/editor/image/image_web.dart';
import 'models/config/editor/video/video.dart';
import 'models/config/editor/video/video_web.dart';
import 'models/config/editor/webview.dart';
import 'models/config/toolbar/buttons/camera.dart';
import 'models/config/toolbar/buttons/image.dart';
import 'models/config/toolbar/buttons/media_button.dart';
import 'models/config/toolbar/buttons/video.dart';
export '/logic/extensions/controller.dart';
export '/presentation/models/config/editor/webview.dart';
export 'logic/models/config/shared_configurations.dart';
export 'presentation/embeds/editor/image/image.dart';
export 'presentation/embeds/editor/image/image_web.dart';
export 'presentation/embeds/editor/unknown.dart';
export 'presentation/embeds/editor/video/video.dart';
export 'presentation/embeds/editor/video/video_web.dart';
export 'presentation/embeds/editor/webview.dart';
export 'presentation/embeds/embed_types.dart';
export 'presentation/embeds/embed_types/image.dart';
export 'presentation/embeds/embed_types/video.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/media_button.dart';
export 'presentation/embeds/toolbar/utils/image_video_utils.dart';
export 'presentation/embeds/toolbar/video_button/video_button.dart';
export 'presentation/models/config/editor/image/image.dart';
export 'presentation/models/config/editor/image/image_web.dart';
export 'presentation/models/config/editor/video/video.dart';
export 'presentation/models/config/editor/video/video_web.dart';
export 'presentation/models/config/toolbar/buttons/camera.dart';
export 'presentation/models/config/toolbar/buttons/formula.dart';
export 'presentation/models/config/toolbar/buttons/image.dart';
export 'presentation/models/config/toolbar/buttons/media_button.dart';
export 'presentation/models/config/toolbar/buttons/video.dart';
export 'presentation/utils/utils.dart';
export 'embeds/embed_types.dart';
export 'embeds/formula/toolbar/formula_button.dart';
export 'embeds/image/editor/image_embed.dart';
export 'embeds/image/editor/image_embed_types.dart';
export 'embeds/image/editor/image_web_embed.dart';
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.dart';
export 'models/config/editor/image/image.dart';
export 'models/config/editor/image/image_web.dart';
export 'models/config/editor/video/video.dart';
export 'models/config/editor/video/video_web.dart';
export 'models/config/editor/webview.dart';
export 'models/config/shared_configurations.dart';
export 'models/config/toolbar/buttons/camera.dart';
export 'models/config/toolbar/buttons/formula.dart';
export 'models/config/toolbar/buttons/image.dart';
export 'models/config/toolbar/buttons/media_button.dart';
export 'models/config/toolbar/buttons/video.dart';
export 'utils/utils.dart';
@immutable
class FlutterQuillEmbeds {

@ -1,30 +0,0 @@
import 'package:flutter_quill/flutter_quill.dart'
show Attribute, AttributeScope;
class MobileWidthAttribute extends Attribute<String?> {
const MobileWidthAttribute(String? val)
: super('mobileWidth', AttributeScope.ignore, val);
}
class MobileHeightAttribute extends Attribute<String?> {
const MobileHeightAttribute(String? val)
: super('mobileHeight', AttributeScope.ignore, val);
}
class MobileMarginAttribute extends Attribute<String?> {
const MobileMarginAttribute(String? val)
: super('mobileMargin', AttributeScope.ignore, val);
}
class MobileAlignmentAttribute extends Attribute<String?> {
const MobileAlignmentAttribute(String? val)
: super('mobileAlignment', AttributeScope.ignore, val);
}
extension AttributeExt on Attribute {
static const MobileWidthAttribute mobileWidth = MobileWidthAttribute(null);
static const MobileHeightAttribute mobileHeight = MobileHeightAttribute(null);
static const MobileMarginAttribute mobileMargin = MobileMarginAttribute(null);
static const MobileAlignmentAttribute mobileAlignment =
MobileAlignmentAttribute(null);
}

@ -4,7 +4,7 @@ import 'package:flutter/foundation.dart' show VoidCallback;
import 'package:flutter_quill/extensions.dart';
import 'package:meta/meta.dart' show immutable;
import '../../../../embeds/embed_types/image.dart';
import '../../../../embeds/image/editor/image_embed_types.dart';
/// [QuillEditorImageEmbedConfigurations] for desktop, mobile and
/// other platforms

@ -1,7 +1,7 @@
import 'package:flutter/widgets.dart' show Color;
import 'package:flutter_quill/flutter_quill.dart';
import '../../../../embeds/embed_types/camera.dart';
import '../../../../embeds/others/camera_button/camera_types.dart';
class QuillToolbarCameraButtonExtraOptions
extends QuillToolbarBaseButtonExtraOptions {

@ -2,7 +2,7 @@ import 'package:flutter/widgets.dart' show Color;
import 'package:flutter_quill/flutter_quill.dart';
import 'package:meta/meta.dart' show immutable;
import '../../../../embeds/embed_types/image.dart';
import '../../../../embeds/image/editor/image_embed_types.dart';
class QuillToolbarImageButtonExtraOptions
extends QuillToolbarBaseButtonExtraOptions {

@ -1,7 +1,7 @@
import 'package:flutter/widgets.dart' show Color;
import 'package:flutter_quill/flutter_quill.dart';
import '../../../../embeds/embed_types/video.dart';
import '../../../../embeds/video/video.dart';
class QuillToolbarVideoButtonExtraOptions
extends QuillToolbarBaseButtonExtraOptions {

@ -1,150 +0,0 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_quill/extensions.dart' as base;
import 'package:flutter_quill/flutter_quill.dart' hide OptionalSize;
import '../../../../logic/models/config/shared_configurations.dart';
import '../../../models/config/editor/image/image.dart';
import '../../../utils/utils.dart';
import '../../widgets/image.dart';
import 'image_menu.dart';
class QuillEditorImageEmbedBuilder extends EmbedBuilder {
QuillEditorImageEmbedBuilder({
required this.configurations,
});
final QuillEditorImageEmbedConfigurations configurations;
@override
String get key => BlockEmbed.imageType;
@override
bool get expanded => false;
@override
Widget build(
BuildContext context,
QuillController controller,
base.Embed node,
bool readOnly,
bool inline,
TextStyle textStyle,
) {
assert(!kIsWeb, 'Please provide image EmbedBuilder for Web');
final imageSource = standardizeImageUrl(node.value.data);
final ((imageSize), margin, alignment) = getElementAttributes(node);
final width = imageSize.width;
final height = imageSize.height;
final image = getImageWidgetByImageSource(
imageSource,
imageProviderBuilder: configurations.imageProviderBuilder,
imageErrorWidgetBuilder: configurations.imageErrorWidgetBuilder,
alignment: alignment,
height: height,
width: width,
assetsPrefix: QuillSharedExtensionsConfigurations.get(context: context)
.assetsPrefix,
);
// OptionalSize? imageSize;
// final style = node.style.attributes['style'];
// if (style != null) {
// final attrs = base.isMobile(supportWeb: false)
// ? base.parseKeyValuePairs(style.value.toString(), {
// Attribute.mobileWidth,
// Attribute.mobileHeight,
// Attribute.mobileMargin,
// Attribute.mobileAlignment,
// })
// : base.parseKeyValuePairs(style.value.toString(), {
// Attribute.width.key,
// Attribute.height.key,
// Attribute.margin,
// Attribute.alignment,
// });
// if (attrs.isNotEmpty) {
// final width = double.tryParse(
// (base.isMobile(supportWeb: false)
// ? attrs[Attribute.mobileWidth]
// : attrs[Attribute.width.key]) ??
// '',
// );
// final height = double.tryParse(
// (base.isMobile(supportWeb: false)
// ? attrs[Attribute.mobileHeight]
// : attrs[Attribute.height.key]) ??
// '',
// );
// final alignment = base.getAlignment(base.isMobile(supportWeb: false)
// ? attrs[Attribute.mobileAlignment]
// : attrs[Attribute.alignment]);
// final margin = (base.isMobile(supportWeb: false)
// ? double.tryParse(Attribute.mobileMargin)
// : double.tryParse(Attribute.margin)) ??
// 0.0;
// imageSize = OptionalSize(width, height);
// image = Padding(
// padding: EdgeInsets.all(margin),
// child: getImageWidgetByImageSource(
// imageSource,
// width: width,
// height: height,
// alignment: alignment,
// imageProviderBuilder: configurations.imageProviderBuilder,
// imageErrorWidgetBuilder: configurations.imageErrorWidgetBuilder,
// ),
// );
// }
// }
// if (imageSize == null) {
// image = getImageWidgetByImageSource(
// imageSource,
// imageProviderBuilder: configurations.imageProviderBuilder,
// imageErrorWidgetBuilder: configurations.imageErrorWidgetBuilder,
// );
// imageSize = OptionalSize((image as Image).width, image.height);
// }
final imageSaverService =
QuillSharedExtensionsConfigurations.get(context: context)
.imageSaverService;
return GestureDetector(
onTap: configurations.onImageClicked ??
() => showDialog(
context: context,
builder: (_) {
return QuillProvider.value(
value: context.requireQuillProvider,
child: FlutterQuillLocalizationsWidget(
child: ImageOptionsMenu(
controller: controller,
configurations: configurations,
imageSource: imageSource,
imageSize: imageSize,
isReadOnly: readOnly,
imageSaverService: imageSaverService,
),
),
);
},
),
child: Builder(
builder: (context) {
if (margin != null) {
return Padding(
padding: EdgeInsets.all(margin),
child: image,
);
}
return image;
},
),
);
}
}

@ -1,58 +0,0 @@
// import 'dart:convert' show jsonDecode, jsonEncode;
// import 'package:flutter/widgets.dart';
// import 'package:flutter_inappwebview/flutter_inappwebview.dart';
// import 'package:flutter_quill/flutter_quill.dart';
// import 'package:meta/meta.dart' show experimental;
// import '../../models/config/editor/webview.dart';
// @experimental
// class QuillEditorWebViewBlockEmbed extends CustomBlockEmbed {
// const QuillEditorWebViewBlockEmbed(
// String value,
// ) : super(webViewType, value);
// factory QuillEditorWebViewBlockEmbed.fromDocument(Document document) =>
// QuillEditorWebViewBlockEmbed(jsonEncode(document.toDelta().toJson()));
// static const String webViewType = 'webview';
// Document get document => Document.fromJson(jsonDecode(data));
// }
// @experimental
// class QuillEditorWebViewEmbedBuilder extends EmbedBuilder {
// const QuillEditorWebViewEmbedBuilder({
// required this.configurations,
// });
// @override
// bool get expanded => false;
// final QuillEditorWebViewEmbedConfigurations configurations;
// @override
// Widget build(
// BuildContext context,
// QuillController controller,
// Embed node,
// bool readOnly,
// bool inline,
// TextStyle textStyle,
// ) {
// final url = node.value.data as String;
// return SizedBox(
// width: double.infinity,
// height: 200,
// child: InAppWebView(
// initialUrlRequest: URLRequest(
// url: Uri.parse(url),
// ),
// ),
// );
// }
// @override
// String get key => 'webview';
// }

@ -1,201 +0,0 @@
import 'dart:io' show File;
import 'package:flutter/foundation.dart' show immutable;
import 'package:flutter/widgets.dart' show Alignment;
import 'package:flutter_quill/extensions.dart' as base;
import 'package:flutter_quill/flutter_quill.dart' show Attribute, Node;
import '../../logic/extensions/attribute.dart';
import '../../logic/services/image_saver/s_image_saver.dart';
import '../embeds/widgets/image.dart';
RegExp _base64 = RegExp(
r'^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{4})$',
);
bool isBase64(String str) {
return _base64.hasMatch(str);
}
bool isHttpBasedUrl(String url) {
try {
final uri = Uri.parse(url.trim());
return uri.isScheme('HTTP') || uri.isScheme('HTTPS');
} catch (_) {
return false;
}
}
bool isImageBase64(String imageUrl) {
return !isHttpBasedUrl(imageUrl) && isBase64(imageUrl);
}
bool isYouTubeUrl(String videoUrl) {
try {
final uri = Uri.parse(videoUrl);
return uri.host == 'www.youtube.com' ||
uri.host == 'youtube.com' ||
uri.host == 'youtu.be' ||
uri.host == 'www.youtu.be';
} catch (_) {
return false;
}
}
enum SaveImageResultMethod { network, localStorage }
@immutable
class SaveImageResult {
const SaveImageResult({required this.error, required this.method});
final String? error;
final SaveImageResultMethod method;
}
Future<SaveImageResult> saveImage({
required String imageUrl,
required ImageSaverService imageSaverService,
}) async {
final imageFile = File(imageUrl);
final hasPermission = await imageSaverService.hasAccess();
if (!hasPermission) {
await imageSaverService.requestAccess();
}
final imageExistsLocally = await imageFile.exists();
if (!imageExistsLocally) {
try {
await imageSaverService.saveImageFromNetwork(
Uri.parse(appendFileExtensionToImageUrl(imageUrl)),
);
return const SaveImageResult(
error: null,
method: SaveImageResultMethod.network,
);
} catch (e) {
return SaveImageResult(
error: e.toString(),
method: SaveImageResultMethod.network,
);
}
}
try {
await imageSaverService.saveLocalImage(imageUrl);
return const SaveImageResult(
error: null,
method: SaveImageResultMethod.localStorage,
);
} catch (e) {
return SaveImageResult(
error: e.toString(),
method: SaveImageResultMethod.localStorage,
);
}
}
(
OptionalSize elementSize,
double? margin,
Alignment alignment,
) getElementAttributes(
Node node,
) {
var elementSize = const OptionalSize(null, null);
var elementAlignment = Alignment.center;
double? elementMargin;
// Usually double value
final heightValue = double.tryParse(
node.style.attributes[Attribute.height.key]?.value.toString() ?? '');
final widthValue = double.tryParse(
node.style.attributes[Attribute.width.key]?.value.toString() ?? '');
if (heightValue != null) {
elementSize = elementSize.copyWith(
height: heightValue,
);
}
if (widthValue != null) {
elementSize = elementSize.copyWith(
width: widthValue,
);
}
final cssStyle = node.style.attributes['style'];
if (cssStyle != null) {
final attrs = base.isMobile(supportWeb: false)
? base.parseKeyValuePairs(cssStyle.value.toString(), {
AttributeExt.mobileWidth.key,
AttributeExt.mobileHeight.key,
AttributeExt.mobileMargin.key,
AttributeExt.mobileAlignment.key,
})
: base.parseKeyValuePairs(cssStyle.value.toString(), {
Attribute.width.key,
Attribute.height.key,
'margin',
'alignment',
});
if (attrs.isEmpty) {
return (elementSize, elementMargin, elementAlignment);
}
// It css value as string but we will try to support it anyway
// TODO: This could be improved much better
final cssHeightValue = double.tryParse(((base.isMobile(supportWeb: false)
? attrs[AttributeExt.mobileHeight.key]
: attrs[Attribute.height.key]) ??
'')
.replaceFirst('px', ''));
final cssWidthValue = double.tryParse(((!base.isMobile(supportWeb: false)
? attrs[Attribute.width.key]
: attrs[AttributeExt.mobileWidth.key]) ??
'')
.replaceFirst('px', ''));
if (cssHeightValue != null) {
elementSize = elementSize.copyWith(height: cssHeightValue);
}
if (cssWidthValue != null) {
elementSize = elementSize.copyWith(width: cssWidthValue);
}
elementAlignment = base.getAlignment(base.isMobile(supportWeb: false)
? attrs[AttributeExt.mobileAlignment.key]
: attrs['alignment']);
final margin = (base.isMobile(supportWeb: false)
? double.tryParse(AttributeExt.mobileMargin.key)
: double.tryParse('margin'));
if (margin != null) {
elementMargin = margin;
}
}
return (elementSize, elementMargin, elementAlignment);
}
@immutable
class OptionalSize {
const OptionalSize(
this.width,
this.height,
);
/// If non-null, requires the child to have exactly this width.
/// If null, the child is free to choose its own width.
final double? width;
/// If non-null, requires the child to have exactly this height.
/// If null, the child is free to choose its own height.
final double? height;
OptionalSize copyWith({
double? width,
double? height,
}) {
return OptionalSize(
width ?? this.width,
height ?? this.height,
);
}
}

@ -0,0 +1,29 @@
Map<String, String> parseCssString(String cssString) {
final result = <String, String>{};
final declarations = cssString.split(';');
for (final declaration in declarations) {
final parts = declaration.split(':');
if (parts.length == 2) {
final property = parts[0].trim();
final value = parts[1].trim();
result[property] = value;
}
}
return result;
}
double? parseCssPropertyAsDouble(String value) {
if (value.trim().isEmpty) {
return null;
}
final list = [
'px',
// '%', 'vw', 'vh', 'em', 'rem'
];
for (final element in list) {
value = value.replaceFirst(element, '');
}
return double.tryParse(value);
}

@ -0,0 +1,98 @@
import 'package:flutter/foundation.dart' show immutable;
import 'package:flutter/widgets.dart' show Alignment;
import 'package:flutter_quill/extensions.dart';
import 'package:flutter_quill/flutter_quill.dart' show Attribute, Node;
import 'element_shared_utils.dart';
/// Theses properties are not officialy supported by quill js
/// but they are only used in all platforms other than web
/// and they will be stored in css style property so quill js ignore them
enum ExtraElementProperties {
deletable,
}
(
ElementSize elementSize,
double? margin,
Alignment alignment,
) getElementAttributes(
Node node,
) {
var elementSize = const ElementSize(null, null);
var elementAlignment = Alignment.center;
double? elementMargin;
final heightValue = parseCssPropertyAsDouble(
node.style.attributes[Attribute.height.key]?.value.toString() ?? '');
final widthValue = parseCssPropertyAsDouble(
node.style.attributes[Attribute.width.key]?.value.toString() ?? '');
if (heightValue != null) {
elementSize = elementSize.copyWith(
height: heightValue,
);
}
if (widthValue != null) {
elementSize = elementSize.copyWith(
width: widthValue,
);
}
final cssStyle = node.style.attributes['style'];
if (cssStyle != null) {
// It css value as string but we will try to support it anyway
final cssAttrs = parseCssString(cssStyle.value.toString());
// todo: This could be improved much better
final cssHeightValue =
parseCssPropertyAsDouble((cssAttrs[Attribute.height.key]) ?? '');
final cssWidthValue =
parseCssPropertyAsDouble((cssAttrs[Attribute.width.key]) ?? '');
// cssHeightValue != null && elementSize.height == null
if (cssHeightValue != null) {
elementSize = elementSize.copyWith(height: cssHeightValue);
}
if (cssWidthValue != null) {
elementSize = elementSize.copyWith(width: cssWidthValue);
}
elementAlignment = getAlignment(cssAttrs['alignment']);
final margin = double.tryParse('margin');
if (margin != null) {
elementMargin = margin;
}
}
return (elementSize, elementMargin, elementAlignment);
}
@immutable
class ElementSize {
const ElementSize(
this.width,
this.height,
);
/// If non-null, requires the child to have exactly this width.
/// If null, the child is free to choose its own width.
final double? width;
/// If non-null, requires the child to have exactly this height.
/// If null, the child is free to choose its own height.
final double? height;
ElementSize copyWith({
double? width,
double? height,
}) {
return ElementSize(
width ?? this.width,
height ?? this.height,
);
}
}

@ -1,6 +1,7 @@
import 'package:flutter_quill/extensions.dart' as base;
import 'package:flutter_quill/flutter_quill.dart' show Attribute, Node;
import 'element_shared_utils.dart';
/// Prefer the width, and height from the css style attribute if exits
/// it can be `auto` or `100px` so it's specific to HTML && CSS
/// if not, we will use the one from attributes which is usually just an double
@ -14,26 +15,20 @@ import 'package:flutter_quill/flutter_quill.dart' show Attribute, Node;
) {
var height = 'auto';
var width = 'auto';
// TODO: Add support for margin and alignment
const margin = 'auto';
// TODO(): Add support for margin and alignment
var margin = 'auto';
const alignment = 'center';
// return (height, width, margin, alignment);
final cssStyle = node.style.attributes['style'];
// Usually double value
final heightValue = node.style.attributes[Attribute.height.key]?.value;
final widthValue = node.style.attributes[Attribute.width.key]?.value;
if (cssStyle != null) {
final attrs = base.parseKeyValuePairs(cssStyle.value.toString(), {
Attribute.width.key,
Attribute.height.key,
'margin',
'alignment',
});
final attrs = parseCssString(cssStyle.value.toString());
final cssHeightValue = attrs[Attribute.height.key];
if (cssHeightValue != null) {
height = cssHeightValue;
} else {
@ -46,6 +41,11 @@ import 'package:flutter_quill/flutter_quill.dart' show Attribute, Node;
width = '${widthValue}px';
}
final cssMarginValue = attrs['margin'];
if (cssMarginValue != null) {
margin = cssMarginValue;
}
return (height, width, margin, alignment);
}

@ -4,7 +4,7 @@ import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:flutter_quill/flutter_quill.dart' as quill;
import 'package:path/path.dart' as path;
import '../../presentation/utils/utils.dart';
import 'utils.dart';
typedef OnGenerateNewFileNameCallback = String Function(
String currentFileName,

@ -1,12 +1,9 @@
import 'package:flutter_quill/flutter_quill.dart' show Attribute;
import '../extensions/attribute.dart';
String replaceStyleStringWithSize(
String cssStyle, {
required double width,
required double height,
required bool isMobile,
}) {
final result = <String, String>{};
final pairs = cssStyle.split(';');
@ -19,13 +16,8 @@ String replaceStyleStringWithSize(
result[key] = pair.substring(index + 1).trim();
}
if (isMobile) {
result[AttributeExt.mobileWidth.key] = width.toString();
result[AttributeExt.mobileHeight.key] = height.toString();
} else {
result[Attribute.width.key] = width.toString();
result[Attribute.height.key] = height.toString();
}
result[Attribute.width.key] = width.toString();
result[Attribute.height.key] = height.toString();
final sb = StringBuffer();
for (final pair in result.entries) {
sb

@ -0,0 +1,89 @@
import 'dart:io' show File;
import 'package:flutter/foundation.dart' show immutable;
import '../embeds/widgets/image.dart';
import '../services/image_saver/s_image_saver.dart';
RegExp _base64 = RegExp(
r'^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{4})$',
);
bool isBase64(String str) {
return _base64.hasMatch(str);
}
bool isHttpBasedUrl(String url) {
try {
final uri = Uri.parse(url.trim());
return uri.isScheme('HTTP') || uri.isScheme('HTTPS');
} catch (_) {
return false;
}
}
bool isImageBase64(String imageUrl) {
return !isHttpBasedUrl(imageUrl) && isBase64(imageUrl);
}
bool isYouTubeUrl(String videoUrl) {
try {
final uri = Uri.parse(videoUrl);
return uri.host == 'www.youtube.com' ||
uri.host == 'youtube.com' ||
uri.host == 'youtu.be' ||
uri.host == 'www.youtu.be';
} catch (_) {
return false;
}
}
enum SaveImageResultMethod { network, localStorage }
@immutable
class SaveImageResult {
const SaveImageResult({required this.error, required this.method});
final String? error;
final SaveImageResultMethod method;
}
Future<SaveImageResult> saveImage({
required String imageUrl,
required ImageSaverService imageSaverService,
}) async {
final imageFile = File(imageUrl);
final hasPermission = await imageSaverService.hasAccess();
if (!hasPermission) {
await imageSaverService.requestAccess();
}
final imageExistsLocally = await imageFile.exists();
if (!imageExistsLocally) {
try {
await imageSaverService.saveImageFromNetwork(
Uri.parse(appendFileExtensionToImageUrl(imageUrl)),
);
return const SaveImageResult(
error: null,
method: SaveImageResultMethod.network,
);
} catch (e) {
return SaveImageResult(
error: e.toString(),
method: SaveImageResultMethod.network,
);
}
}
try {
await imageSaverService.saveLocalImage(imageUrl);
return const SaveImageResult(
error: null,
method: SaveImageResultMethod.localStorage,
);
} catch (e) {
return SaveImageResult(
error: e.toString(),
method: SaveImageResultMethod.localStorage,
);
}
}

@ -1,6 +1,6 @@
name: flutter_quill_extensions
description: Embed extensions for flutter_quill including image, video, formula and etc.
version: 0.7.2
version: 0.8.0-dev
homepage: https://github.com/singerdmx/flutter-quill/tree/master/flutter_quill_extensions/
repository: https://github.com/singerdmx/flutter-quill/tree/master/flutter_quill_extensions/
issue_tracker: https://github.com/singerdmx/flutter-quill/issues/

@ -1,7 +1,6 @@
library flutter_quill;
export 'src/extensions/quill_provider.dart';
export 'src/l10n/widgets/localizations.dart';
export 'src/models/config/quill_configurations.dart';
export 'src/models/config/raw_editor/configurations.dart';
export 'src/models/config/toolbar/base_toolbar_configurations.dart';

@ -600,6 +600,42 @@ abstract class FlutterQuillLocalizations {
/// In en, this message translates to:
/// **'Insert image'**
String get insertImage;
/// No description provided for @pickAPhotoFromYourGallery.
///
/// In en, this message translates to:
/// **'Pick a photo from your gallery'**
String get pickAPhotoFromYourGallery;
/// No description provided for @takeAPhotoUsingYourCamera.
///
/// In en, this message translates to:
/// **'Take a photo using your phone camera'**
String get takeAPhotoUsingYourCamera;
/// No description provided for @pasteAPhotoUsingALink.
///
/// In en, this message translates to:
/// **'Paste a photo using a link'**
String get pasteAPhotoUsingALink;
/// No description provided for @pickAVideoFromYourGallery.
///
/// In en, this message translates to:
/// **'Pick a video from your gallery'**
String get pickAVideoFromYourGallery;
/// No description provided for @recordAVideoUsingYourCamera.
///
/// In en, this message translates to:
/// **'Record a video using your phone camera'**
String get recordAVideoUsingYourCamera;
/// No description provided for @pasteAVideoUsingALink.
///
/// In en, this message translates to:
/// **'Paste a video using a link'**
String get pasteAVideoUsingALink;
}
class _FlutterQuillLocalizationsDelegate

@ -226,4 +226,24 @@ class FlutterQuillLocalizationsAr extends FlutterQuillLocalizations {
@override
String get insertImage => 'إدراج صورة';
@override
String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery';
@override
String get takeAPhotoUsingYourCamera =>
'Take a photo using your phone camera';
@override
String get pasteAPhotoUsingALink => 'Paste a photo using a link';
@override
String get pickAVideoFromYourGallery => 'Pick a video from your gallery';
@override
String get recordAVideoUsingYourCamera =>
'Record a video using your phone camera';
@override
String get pasteAVideoUsingALink => 'Paste a video using a link';
}

@ -228,4 +228,24 @@ class FlutterQuillLocalizationsBg extends FlutterQuillLocalizations {
@override
String get insertImage => 'Вмъкване на изображение';
@override
String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery';
@override
String get takeAPhotoUsingYourCamera =>
'Take a photo using your phone camera';
@override
String get pasteAPhotoUsingALink => 'Paste a photo using a link';
@override
String get pickAVideoFromYourGallery => 'Pick a video from your gallery';
@override
String get recordAVideoUsingYourCamera =>
'Record a video using your phone camera';
@override
String get pasteAVideoUsingALink => 'Paste a video using a link';
}

@ -228,4 +228,24 @@ class FlutterQuillLocalizationsBn extends FlutterQuillLocalizations {
@override
String get insertImage => 'ির সনি';
@override
String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery';
@override
String get takeAPhotoUsingYourCamera =>
'Take a photo using your phone camera';
@override
String get pasteAPhotoUsingALink => 'Paste a photo using a link';
@override
String get pickAVideoFromYourGallery => 'Pick a video from your gallery';
@override
String get recordAVideoUsingYourCamera =>
'Record a video using your phone camera';
@override
String get pasteAVideoUsingALink => 'Paste a video using a link';
}

@ -228,4 +228,24 @@ class FlutterQuillLocalizationsCs extends FlutterQuillLocalizations {
@override
String get insertImage => 'Vložit obrázek';
@override
String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery';
@override
String get takeAPhotoUsingYourCamera =>
'Take a photo using your phone camera';
@override
String get pasteAPhotoUsingALink => 'Paste a photo using a link';
@override
String get pickAVideoFromYourGallery => 'Pick a video from your gallery';
@override
String get recordAVideoUsingYourCamera =>
'Record a video using your phone camera';
@override
String get pasteAVideoUsingALink => 'Paste a video using a link';
}

@ -226,4 +226,24 @@ class FlutterQuillLocalizationsDa extends FlutterQuillLocalizations {
@override
String get insertImage => 'Indsæt billede';
@override
String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery';
@override
String get takeAPhotoUsingYourCamera =>
'Take a photo using your phone camera';
@override
String get pasteAPhotoUsingALink => 'Paste a photo using a link';
@override
String get pickAVideoFromYourGallery => 'Pick a video from your gallery';
@override
String get recordAVideoUsingYourCamera =>
'Record a video using your phone camera';
@override
String get pasteAVideoUsingALink => 'Paste a video using a link';
}

@ -227,4 +227,24 @@ class FlutterQuillLocalizationsDe extends FlutterQuillLocalizations {
@override
String get insertImage => 'Bild einfügen';
@override
String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery';
@override
String get takeAPhotoUsingYourCamera =>
'Take a photo using your phone camera';
@override
String get pasteAPhotoUsingALink => 'Paste a photo using a link';
@override
String get pickAVideoFromYourGallery => 'Pick a video from your gallery';
@override
String get recordAVideoUsingYourCamera =>
'Record a video using your phone camera';
@override
String get pasteAVideoUsingALink => 'Paste a video using a link';
}

@ -228,6 +228,26 @@ class FlutterQuillLocalizationsEn extends FlutterQuillLocalizations {
@override
String get insertImage => 'Insert image';
@override
String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery';
@override
String get takeAPhotoUsingYourCamera =>
'Take a photo using your phone camera';
@override
String get pasteAPhotoUsingALink => 'Paste a photo using a link';
@override
String get pickAVideoFromYourGallery => 'Pick a video from your gallery';
@override
String get recordAVideoUsingYourCamera =>
'Record a video using your phone camera';
@override
String get pasteAVideoUsingALink => 'Paste a video using a link';
}
/// The translations for English, as used in the United States (`en_US`).

@ -227,4 +227,24 @@ class FlutterQuillLocalizationsEs extends FlutterQuillLocalizations {
@override
String get insertImage => 'Insertar imagen';
@override
String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery';
@override
String get takeAPhotoUsingYourCamera =>
'Take a photo using your phone camera';
@override
String get pasteAPhotoUsingALink => 'Paste a photo using a link';
@override
String get pickAVideoFromYourGallery => 'Pick a video from your gallery';
@override
String get recordAVideoUsingYourCamera =>
'Record a video using your phone camera';
@override
String get pasteAVideoUsingALink => 'Paste a video using a link';
}

@ -229,4 +229,24 @@ class FlutterQuillLocalizationsFa extends FlutterQuillLocalizations {
@override
String get insertImage => 'وارد کردن تصویر';
@override
String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery';
@override
String get takeAPhotoUsingYourCamera =>
'Take a photo using your phone camera';
@override
String get pasteAPhotoUsingALink => 'Paste a photo using a link';
@override
String get pickAVideoFromYourGallery => 'Pick a video from your gallery';
@override
String get recordAVideoUsingYourCamera =>
'Record a video using your phone camera';
@override
String get pasteAVideoUsingALink => 'Paste a video using a link';
}

@ -230,4 +230,24 @@ class FlutterQuillLocalizationsFr extends FlutterQuillLocalizations {
@override
String get insertImage => 'Insérer une image';
@override
String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery';
@override
String get takeAPhotoUsingYourCamera =>
'Take a photo using your phone camera';
@override
String get pasteAPhotoUsingALink => 'Paste a photo using a link';
@override
String get pickAVideoFromYourGallery => 'Pick a video from your gallery';
@override
String get recordAVideoUsingYourCamera =>
'Record a video using your phone camera';
@override
String get pasteAVideoUsingALink => 'Paste a video using a link';
}

@ -228,4 +228,24 @@ class FlutterQuillLocalizationsHe extends FlutterQuillLocalizations {
@override
String get insertImage => 'הכנס תמונה';
@override
String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery';
@override
String get takeAPhotoUsingYourCamera =>
'Take a photo using your phone camera';
@override
String get pasteAPhotoUsingALink => 'Paste a photo using a link';
@override
String get pickAVideoFromYourGallery => 'Pick a video from your gallery';
@override
String get recordAVideoUsingYourCamera =>
'Record a video using your phone camera';
@override
String get pasteAVideoUsingALink => 'Paste a video using a link';
}

@ -229,4 +229,24 @@ class FlutterQuillLocalizationsHi extends FlutterQuillLocalizations {
@override
String get insertImage => 'छवि';
@override
String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery';
@override
String get takeAPhotoUsingYourCamera =>
'Take a photo using your phone camera';
@override
String get pasteAPhotoUsingALink => 'Paste a photo using a link';
@override
String get pickAVideoFromYourGallery => 'Pick a video from your gallery';
@override
String get recordAVideoUsingYourCamera =>
'Record a video using your phone camera';
@override
String get pasteAVideoUsingALink => 'Paste a video using a link';
}

@ -230,4 +230,24 @@ class FlutterQuillLocalizationsId extends FlutterQuillLocalizations {
@override
String get insertImage => 'Sisipkan Gambar';
@override
String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery';
@override
String get takeAPhotoUsingYourCamera =>
'Take a photo using your phone camera';
@override
String get pasteAPhotoUsingALink => 'Paste a photo using a link';
@override
String get pickAVideoFromYourGallery => 'Pick a video from your gallery';
@override
String get recordAVideoUsingYourCamera =>
'Record a video using your phone camera';
@override
String get pasteAVideoUsingALink => 'Paste a video using a link';
}

@ -230,4 +230,24 @@ class FlutterQuillLocalizationsIt extends FlutterQuillLocalizations {
@override
String get insertImage => 'Inserisci immagine';
@override
String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery';
@override
String get takeAPhotoUsingYourCamera =>
'Take a photo using your phone camera';
@override
String get pasteAPhotoUsingALink => 'Paste a photo using a link';
@override
String get pickAVideoFromYourGallery => 'Pick a video from your gallery';
@override
String get recordAVideoUsingYourCamera =>
'Record a video using your phone camera';
@override
String get pasteAVideoUsingALink => 'Paste a video using a link';
}

@ -225,4 +225,24 @@ class FlutterQuillLocalizationsJa extends FlutterQuillLocalizations {
@override
String get insertImage => '画像を挿入';
@override
String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery';
@override
String get takeAPhotoUsingYourCamera =>
'Take a photo using your phone camera';
@override
String get pasteAPhotoUsingALink => 'Paste a photo using a link';
@override
String get pickAVideoFromYourGallery => 'Pick a video from your gallery';
@override
String get recordAVideoUsingYourCamera =>
'Record a video using your phone camera';
@override
String get pasteAVideoUsingALink => 'Paste a video using a link';
}

@ -225,4 +225,24 @@ class FlutterQuillLocalizationsKo extends FlutterQuillLocalizations {
@override
String get insertImage => '이미지 삽입';
@override
String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery';
@override
String get takeAPhotoUsingYourCamera =>
'Take a photo using your phone camera';
@override
String get pasteAPhotoUsingALink => 'Paste a photo using a link';
@override
String get pickAVideoFromYourGallery => 'Pick a video from your gallery';
@override
String get recordAVideoUsingYourCamera =>
'Record a video using your phone camera';
@override
String get pasteAVideoUsingALink => 'Paste a video using a link';
}

@ -228,4 +228,24 @@ class FlutterQuillLocalizationsMs extends FlutterQuillLocalizations {
@override
String get insertImage => 'Masukkan imej';
@override
String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery';
@override
String get takeAPhotoUsingYourCamera =>
'Take a photo using your phone camera';
@override
String get pasteAPhotoUsingALink => 'Paste a photo using a link';
@override
String get pickAVideoFromYourGallery => 'Pick a video from your gallery';
@override
String get recordAVideoUsingYourCamera =>
'Record a video using your phone camera';
@override
String get pasteAVideoUsingALink => 'Paste a video using a link';
}

@ -230,4 +230,24 @@ class FlutterQuillLocalizationsNl extends FlutterQuillLocalizations {
@override
String get insertImage => 'Afbeelding invoegen';
@override
String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery';
@override
String get takeAPhotoUsingYourCamera =>
'Take a photo using your phone camera';
@override
String get pasteAPhotoUsingALink => 'Paste a photo using a link';
@override
String get pickAVideoFromYourGallery => 'Pick a video from your gallery';
@override
String get recordAVideoUsingYourCamera =>
'Record a video using your phone camera';
@override
String get pasteAVideoUsingALink => 'Paste a video using a link';
}

@ -230,4 +230,24 @@ class FlutterQuillLocalizationsNo extends FlutterQuillLocalizations {
@override
String get insertImage => 'Sett inn bilde';
@override
String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery';
@override
String get takeAPhotoUsingYourCamera =>
'Take a photo using your phone camera';
@override
String get pasteAPhotoUsingALink => 'Paste a photo using a link';
@override
String get pickAVideoFromYourGallery => 'Pick a video from your gallery';
@override
String get recordAVideoUsingYourCamera =>
'Record a video using your phone camera';
@override
String get pasteAVideoUsingALink => 'Paste a video using a link';
}

@ -227,4 +227,24 @@ class FlutterQuillLocalizationsPl extends FlutterQuillLocalizations {
@override
String get insertImage => 'Wstaw obraz';
@override
String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery';
@override
String get takeAPhotoUsingYourCamera =>
'Take a photo using your phone camera';
@override
String get pasteAPhotoUsingALink => 'Paste a photo using a link';
@override
String get pickAVideoFromYourGallery => 'Pick a video from your gallery';
@override
String get recordAVideoUsingYourCamera =>
'Record a video using your phone camera';
@override
String get pasteAVideoUsingALink => 'Paste a video using a link';
}

@ -228,6 +228,26 @@ class FlutterQuillLocalizationsPt extends FlutterQuillLocalizations {
@override
String get insertImage => 'Inserir imagem';
@override
String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery';
@override
String get takeAPhotoUsingYourCamera =>
'Take a photo using your phone camera';
@override
String get pasteAPhotoUsingALink => 'Paste a photo using a link';
@override
String get pickAVideoFromYourGallery => 'Pick a video from your gallery';
@override
String get recordAVideoUsingYourCamera =>
'Record a video using your phone camera';
@override
String get pasteAVideoUsingALink => 'Paste a video using a link';
}
/// The translations for Portuguese, as used in Brazil (`pt_BR`).

@ -227,4 +227,24 @@ class FlutterQuillLocalizationsRu extends FlutterQuillLocalizations {
@override
String get insertImage => 'Вставить изображение';
@override
String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery';
@override
String get takeAPhotoUsingYourCamera =>
'Take a photo using your phone camera';
@override
String get pasteAPhotoUsingALink => 'Paste a photo using a link';
@override
String get pickAVideoFromYourGallery => 'Pick a video from your gallery';
@override
String get recordAVideoUsingYourCamera =>
'Record a video using your phone camera';
@override
String get pasteAVideoUsingALink => 'Paste a video using a link';
}

@ -229,4 +229,24 @@ class FlutterQuillLocalizationsSr extends FlutterQuillLocalizations {
@override
String get insertImage => 'Umetni sliku';
@override
String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery';
@override
String get takeAPhotoUsingYourCamera =>
'Take a photo using your phone camera';
@override
String get pasteAPhotoUsingALink => 'Paste a photo using a link';
@override
String get pickAVideoFromYourGallery => 'Pick a video from your gallery';
@override
String get recordAVideoUsingYourCamera =>
'Record a video using your phone camera';
@override
String get pasteAVideoUsingALink => 'Paste a video using a link';
}

@ -227,4 +227,24 @@ class FlutterQuillLocalizationsSw extends FlutterQuillLocalizations {
@override
String get insertImage => 'Weka Picha';
@override
String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery';
@override
String get takeAPhotoUsingYourCamera =>
'Take a photo using your phone camera';
@override
String get pasteAPhotoUsingALink => 'Paste a photo using a link';
@override
String get pickAVideoFromYourGallery => 'Pick a video from your gallery';
@override
String get recordAVideoUsingYourCamera =>
'Record a video using your phone camera';
@override
String get pasteAVideoUsingALink => 'Paste a video using a link';
}

@ -226,4 +226,24 @@ class FlutterQuillLocalizationsTk extends FlutterQuillLocalizations {
@override
String get insertImage => 'Surat goş';
@override
String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery';
@override
String get takeAPhotoUsingYourCamera =>
'Take a photo using your phone camera';
@override
String get pasteAPhotoUsingALink => 'Paste a photo using a link';
@override
String get pickAVideoFromYourGallery => 'Pick a video from your gallery';
@override
String get recordAVideoUsingYourCamera =>
'Record a video using your phone camera';
@override
String get pasteAVideoUsingALink => 'Paste a video using a link';
}

@ -227,4 +227,24 @@ class FlutterQuillLocalizationsTr extends FlutterQuillLocalizations {
@override
String get insertImage => 'Görüntü ekle';
@override
String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery';
@override
String get takeAPhotoUsingYourCamera =>
'Take a photo using your phone camera';
@override
String get pasteAPhotoUsingALink => 'Paste a photo using a link';
@override
String get pickAVideoFromYourGallery => 'Pick a video from your gallery';
@override
String get recordAVideoUsingYourCamera =>
'Record a video using your phone camera';
@override
String get pasteAVideoUsingALink => 'Paste a video using a link';
}

@ -229,4 +229,24 @@ class FlutterQuillLocalizationsUk extends FlutterQuillLocalizations {
@override
String get insertImage => 'Вставити зображення';
@override
String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery';
@override
String get takeAPhotoUsingYourCamera =>
'Take a photo using your phone camera';
@override
String get pasteAPhotoUsingALink => 'Paste a photo using a link';
@override
String get pickAVideoFromYourGallery => 'Pick a video from your gallery';
@override
String get recordAVideoUsingYourCamera =>
'Record a video using your phone camera';
@override
String get pasteAVideoUsingALink => 'Paste a video using a link';
}

@ -231,4 +231,24 @@ class FlutterQuillLocalizationsUr extends FlutterQuillLocalizations {
@override
String get insertImage => 'تصویر داخل کریں';
@override
String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery';
@override
String get takeAPhotoUsingYourCamera =>
'Take a photo using your phone camera';
@override
String get pasteAPhotoUsingALink => 'Paste a photo using a link';
@override
String get pickAVideoFromYourGallery => 'Pick a video from your gallery';
@override
String get recordAVideoUsingYourCamera =>
'Record a video using your phone camera';
@override
String get pasteAVideoUsingALink => 'Paste a video using a link';
}

@ -228,4 +228,24 @@ class FlutterQuillLocalizationsVi extends FlutterQuillLocalizations {
@override
String get insertImage => 'Chèn hình ảnh';
@override
String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery';
@override
String get takeAPhotoUsingYourCamera =>
'Take a photo using your phone camera';
@override
String get pasteAPhotoUsingALink => 'Paste a photo using a link';
@override
String get pickAVideoFromYourGallery => 'Pick a video from your gallery';
@override
String get recordAVideoUsingYourCamera =>
'Record a video using your phone camera';
@override
String get pasteAVideoUsingALink => 'Paste a video using a link';
}

@ -225,6 +225,26 @@ class FlutterQuillLocalizationsZh extends FlutterQuillLocalizations {
@override
String get insertImage => '插入图像';
@override
String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery';
@override
String get takeAPhotoUsingYourCamera =>
'Take a photo using your phone camera';
@override
String get pasteAPhotoUsingALink => 'Paste a photo using a link';
@override
String get pickAVideoFromYourGallery => 'Pick a video from your gallery';
@override
String get recordAVideoUsingYourCamera =>
'Record a video using your phone camera';
@override
String get pasteAVideoUsingALink => 'Paste a video using a link';
}
/// The translations for Chinese, as used in China (`zh_CN`).

@ -73,5 +73,11 @@
"photo": "Photo",
"image": "Image",
"caseSensitivityAndWholeWordSearch": "Case sensitivity and whole word search",
"insertImage": "Insert image"
"insertImage": "Insert image",
"pickAPhotoFromYourGallery": "Pick a photo from your gallery",
"takeAPhotoUsingYourCamera": "Take a photo using your phone camera",
"pasteAPhotoUsingALink": "Paste a photo using a link",
"pickAVideoFromYourGallery": "Pick a video from your gallery",
"recordAVideoUsingYourCamera": "Record a video using your phone camera",
"pasteAVideoUsingALink": "Paste a video using a link"
}

@ -1 +1,298 @@
{}
{
"ar": [
"pickAPhotoFromYourGallery",
"takeAPhotoUsingYourCamera",
"pasteAPhotoUsingALink",
"pickAVideoFromYourGallery",
"recordAVideoUsingYourCamera",
"pasteAVideoUsingALink"
],
"bg": [
"pickAPhotoFromYourGallery",
"takeAPhotoUsingYourCamera",
"pasteAPhotoUsingALink",
"pickAVideoFromYourGallery",
"recordAVideoUsingYourCamera",
"pasteAVideoUsingALink"
],
"bn": [
"pickAPhotoFromYourGallery",
"takeAPhotoUsingYourCamera",
"pasteAPhotoUsingALink",
"pickAVideoFromYourGallery",
"recordAVideoUsingYourCamera",
"pasteAVideoUsingALink"
],
"cs": [
"pickAPhotoFromYourGallery",
"takeAPhotoUsingYourCamera",
"pasteAPhotoUsingALink",
"pickAVideoFromYourGallery",
"recordAVideoUsingYourCamera",
"pasteAVideoUsingALink"
],
"da": [
"pickAPhotoFromYourGallery",
"takeAPhotoUsingYourCamera",
"pasteAPhotoUsingALink",
"pickAVideoFromYourGallery",
"recordAVideoUsingYourCamera",
"pasteAVideoUsingALink"
],
"de": [
"pickAPhotoFromYourGallery",
"takeAPhotoUsingYourCamera",
"pasteAPhotoUsingALink",
"pickAVideoFromYourGallery",
"recordAVideoUsingYourCamera",
"pasteAVideoUsingALink"
],
"en_US": [
"pickAPhotoFromYourGallery",
"takeAPhotoUsingYourCamera",
"pasteAPhotoUsingALink",
"pickAVideoFromYourGallery",
"recordAVideoUsingYourCamera",
"pasteAVideoUsingALink"
],
"es": [
"pickAPhotoFromYourGallery",
"takeAPhotoUsingYourCamera",
"pasteAPhotoUsingALink",
"pickAVideoFromYourGallery",
"recordAVideoUsingYourCamera",
"pasteAVideoUsingALink"
],
"fa": [
"pickAPhotoFromYourGallery",
"takeAPhotoUsingYourCamera",
"pasteAPhotoUsingALink",
"pickAVideoFromYourGallery",
"recordAVideoUsingYourCamera",
"pasteAVideoUsingALink"
],
"fr": [
"pickAPhotoFromYourGallery",
"takeAPhotoUsingYourCamera",
"pasteAPhotoUsingALink",
"pickAVideoFromYourGallery",
"recordAVideoUsingYourCamera",
"pasteAVideoUsingALink"
],
"he": [
"pickAPhotoFromYourGallery",
"takeAPhotoUsingYourCamera",
"pasteAPhotoUsingALink",
"pickAVideoFromYourGallery",
"recordAVideoUsingYourCamera",
"pasteAVideoUsingALink"
],
"hi": [
"pickAPhotoFromYourGallery",
"takeAPhotoUsingYourCamera",
"pasteAPhotoUsingALink",
"pickAVideoFromYourGallery",
"recordAVideoUsingYourCamera",
"pasteAVideoUsingALink"
],
"id": [
"pickAPhotoFromYourGallery",
"takeAPhotoUsingYourCamera",
"pasteAPhotoUsingALink",
"pickAVideoFromYourGallery",
"recordAVideoUsingYourCamera",
"pasteAVideoUsingALink"
],
"it": [
"pickAPhotoFromYourGallery",
"takeAPhotoUsingYourCamera",
"pasteAPhotoUsingALink",
"pickAVideoFromYourGallery",
"recordAVideoUsingYourCamera",
"pasteAVideoUsingALink"
],
"ja": [
"pickAPhotoFromYourGallery",
"takeAPhotoUsingYourCamera",
"pasteAPhotoUsingALink",
"pickAVideoFromYourGallery",
"recordAVideoUsingYourCamera",
"pasteAVideoUsingALink"
],
"ko": [
"pickAPhotoFromYourGallery",
"takeAPhotoUsingYourCamera",
"pasteAPhotoUsingALink",
"pickAVideoFromYourGallery",
"recordAVideoUsingYourCamera",
"pasteAVideoUsingALink"
],
"ms": [
"pickAPhotoFromYourGallery",
"takeAPhotoUsingYourCamera",
"pasteAPhotoUsingALink",
"pickAVideoFromYourGallery",
"recordAVideoUsingYourCamera",
"pasteAVideoUsingALink"
],
"nl": [
"pickAPhotoFromYourGallery",
"takeAPhotoUsingYourCamera",
"pasteAPhotoUsingALink",
"pickAVideoFromYourGallery",
"recordAVideoUsingYourCamera",
"pasteAVideoUsingALink"
],
"no": [
"pickAPhotoFromYourGallery",
"takeAPhotoUsingYourCamera",
"pasteAPhotoUsingALink",
"pickAVideoFromYourGallery",
"recordAVideoUsingYourCamera",
"pasteAVideoUsingALink"
],
"pl": [
"pickAPhotoFromYourGallery",
"takeAPhotoUsingYourCamera",
"pasteAPhotoUsingALink",
"pickAVideoFromYourGallery",
"recordAVideoUsingYourCamera",
"pasteAVideoUsingALink"
],
"pt": [
"pickAPhotoFromYourGallery",
"takeAPhotoUsingYourCamera",
"pasteAPhotoUsingALink",
"pickAVideoFromYourGallery",
"recordAVideoUsingYourCamera",
"pasteAVideoUsingALink"
],
"pt_BR": [
"pickAPhotoFromYourGallery",
"takeAPhotoUsingYourCamera",
"pasteAPhotoUsingALink",
"pickAVideoFromYourGallery",
"recordAVideoUsingYourCamera",
"pasteAVideoUsingALink"
],
"ru": [
"pickAPhotoFromYourGallery",
"takeAPhotoUsingYourCamera",
"pasteAPhotoUsingALink",
"pickAVideoFromYourGallery",
"recordAVideoUsingYourCamera",
"pasteAVideoUsingALink"
],
"sr": [
"pickAPhotoFromYourGallery",
"takeAPhotoUsingYourCamera",
"pasteAPhotoUsingALink",
"pickAVideoFromYourGallery",
"recordAVideoUsingYourCamera",
"pasteAVideoUsingALink"
],
"sw": [
"pickAPhotoFromYourGallery",
"takeAPhotoUsingYourCamera",
"pasteAPhotoUsingALink",
"pickAVideoFromYourGallery",
"recordAVideoUsingYourCamera",
"pasteAVideoUsingALink"
],
"tk": [
"pickAPhotoFromYourGallery",
"takeAPhotoUsingYourCamera",
"pasteAPhotoUsingALink",
"pickAVideoFromYourGallery",
"recordAVideoUsingYourCamera",
"pasteAVideoUsingALink"
],
"tr": [
"pickAPhotoFromYourGallery",
"takeAPhotoUsingYourCamera",
"pasteAPhotoUsingALink",
"pickAVideoFromYourGallery",
"recordAVideoUsingYourCamera",
"pasteAVideoUsingALink"
],
"uk": [
"pickAPhotoFromYourGallery",
"takeAPhotoUsingYourCamera",
"pasteAPhotoUsingALink",
"pickAVideoFromYourGallery",
"recordAVideoUsingYourCamera",
"pasteAVideoUsingALink"
],
"ur": [
"pickAPhotoFromYourGallery",
"takeAPhotoUsingYourCamera",
"pasteAPhotoUsingALink",
"pickAVideoFromYourGallery",
"recordAVideoUsingYourCamera",
"pasteAVideoUsingALink"
],
"vi": [
"pickAPhotoFromYourGallery",
"takeAPhotoUsingYourCamera",
"pasteAPhotoUsingALink",
"pickAVideoFromYourGallery",
"recordAVideoUsingYourCamera",
"pasteAVideoUsingALink"
],
"zh": [
"pickAPhotoFromYourGallery",
"takeAPhotoUsingYourCamera",
"pasteAPhotoUsingALink",
"pickAVideoFromYourGallery",
"recordAVideoUsingYourCamera",
"pasteAVideoUsingALink"
],
"zh_CN": [
"pickAPhotoFromYourGallery",
"takeAPhotoUsingYourCamera",
"pasteAPhotoUsingALink",
"pickAVideoFromYourGallery",
"recordAVideoUsingYourCamera",
"pasteAVideoUsingALink"
],
"zh_HK": [
"pickAPhotoFromYourGallery",
"takeAPhotoUsingYourCamera",
"pasteAPhotoUsingALink",
"pickAVideoFromYourGallery",
"recordAVideoUsingYourCamera",
"pasteAVideoUsingALink"
]
}

@ -1,3 +1,4 @@
library flutter_quill.translations;
export 'src/l10n/extensions/localizations.dart';
export 'src/l10n/widgets/localizations.dart';

Loading…
Cancel
Save