diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0f4f80e3..094eee38 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,8 @@
+# [7.1.10]
+- Image embedding tweaks
+  - Add MediaButton which is intened to superseed the ImageButton and VideoButton. Only image selection is working.
+  - Implement image insert for web (image as base64)
+
 # [7.1.9]
 
 - Editor tweaks PR from [bambinoua](https://github.com/bambinoua).
@@ -8,14 +13,12 @@
   - Update minimum Dart SDK version to 2.17.0 to use enum extensions.
   - Use merging shortcuts and actions correclty (if the key combination is the same)
 
-
 # [7.1.8]
 - Dropdown tweaks
   - Add itemHeight, itemPadding, defaultItemColor for customization of dropdown items.
   - Remove alignment property as useless.
   - Fix bugs with max width when width property is null.
 
-
 # [7.1.7]
 
 - Toolbar tweaks.
@@ -30,7 +33,6 @@
 
 Now the package is more friendly for web projects.
 
-
 # [7.1.6]
 
 - Add enableUnfocusOnTapOutside field to RawEditor and Editor widgets.
diff --git a/flutter_quill_extensions/lib/embeds/builders.dart b/flutter_quill_extensions/lib/embeds/builders.dart
index 4888da9f..cf04a462 100644
--- a/flutter_quill_extensions/lib/embeds/builders.dart
+++ b/flutter_quill_extensions/lib/embeds/builders.dart
@@ -7,7 +7,10 @@ import 'package:flutter_quill/flutter_quill.dart' hide Text;
 import 'package:flutter_quill/translations.dart';
 import 'package:gallery_saver/gallery_saver.dart';
 import 'package:math_keyboard/math_keyboard.dart';
+import 'package:universal_html/html.dart' as html;
 
+import '../shims/dart_ui_fake.dart'
+    if (dart.library.html) '../shims/dart_ui_real.dart' as ui;
 import 'utils.dart';
 import 'widgets/image.dart';
 import 'widgets/image_resizer.dart';
@@ -145,6 +148,41 @@ class ImageEmbedBuilder extends EmbedBuilder {
   }
 }
 
+class ImageEmbedBuilderWeb extends EmbedBuilder {
+  ImageEmbedBuilderWeb({this.constraints})
+      : assert(kIsWeb, 'ImageEmbedBuilderWeb is only for web platform');
+
+  final BoxConstraints? constraints;
+
+  @override
+  String get key => BlockEmbed.imageType;
+
+  @override
+  Widget build(
+    BuildContext context,
+    QuillController controller,
+    Embed node,
+    bool readOnly,
+    bool inline,
+  ) {
+    final imageUrl = node.value.data;
+
+    ui.platformViewRegistry.registerViewFactory(imageUrl, (viewId) {
+      return html.ImageElement()
+        ..src = imageUrl
+        ..style.height = 'auto'
+        ..style.width = 'auto';
+    });
+
+    return ConstrainedBox(
+      constraints: constraints ?? BoxConstraints.loose(const Size(200, 200)),
+      child: HtmlElementView(
+        viewType: imageUrl,
+      ),
+    );
+  }
+}
+
 class VideoEmbedBuilder extends EmbedBuilder {
   VideoEmbedBuilder({this.onVideoInit});
 
diff --git a/flutter_quill_extensions/lib/embeds/embed_types.dart b/flutter_quill_extensions/lib/embeds/embed_types.dart
index 814b77b6..6a48f066 100644
--- a/flutter_quill_extensions/lib/embeds/embed_types.dart
+++ b/flutter_quill_extensions/lib/embeds/embed_types.dart
@@ -1,4 +1,5 @@
 import 'dart:io';
+import 'dart:typed_data';
 
 import 'package:flutter/material.dart';
 
@@ -18,3 +19,28 @@ enum MediaPickSetting {
   Camera,
   Video,
 }
+
+typedef MediaFileUrl = String;
+typedef MediaFilePicker = Future<QuillFile?> Function(QuillMediaType mediaType);
+typedef MediaPickedCallback = Future<MediaFileUrl> Function(QuillFile file);
+
+enum QuillMediaType { image, video }
+
+extension QuillMediaTypeX on QuillMediaType {
+  bool get isImage => this == QuillMediaType.image;
+  bool get isVideo => this == QuillMediaType.video;
+}
+
+/// Represents a file data which returned by file picker.
+class QuillFile {
+  QuillFile({
+    required this.name,
+    this.path = '',
+    Uint8List? bytes,
+  })  : assert(name.isNotEmpty),
+        bytes = bytes ?? Uint8List(0);
+
+  final String name;
+  final String path;
+  final Uint8List bytes;
+}
diff --git a/flutter_quill_extensions/lib/embeds/toolbar/media_button.dart b/flutter_quill_extensions/lib/embeds/toolbar/media_button.dart
new file mode 100644
index 00000000..837ca825
--- /dev/null
+++ b/flutter_quill_extensions/lib/embeds/toolbar/media_button.dart
@@ -0,0 +1,452 @@
+//import 'dart:io';
+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' hide Text;
+import 'package:flutter_quill/translations.dart';
+import 'package:image_picker/image_picker.dart';
+
+import '../embed_types.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 MediaButton extends StatelessWidget {
+  const MediaButton({
+    required this.controller,
+    required this.icon,
+    this.type = QuillMediaType.image,
+    this.iconSize = kDefaultIconSize,
+    this.fillColor,
+    this.mediaFilePicker = _defaultMediaPicker,
+    this.onMediaPickedCallback,
+    this.iconTheme,
+    this.dialogTheme,
+    this.tooltip,
+    this.childrenSpacing = 16.0,
+    this.labelText,
+    this.hintText,
+    this.submitButtonText,
+    this.submitButtonSize,
+    this.galleryButtonText,
+    this.linkButtonText,
+    this.autovalidateMode = AutovalidateMode.disabled,
+    Key? key,
+    this.validationMessage,
+  })  : assert(type == QuillMediaType.image,
+            'Video selection is not supported yet'),
+        super(key: key);
+
+  final QuillController controller;
+  final IconData icon;
+  final double iconSize;
+  final Color? fillColor;
+  final QuillMediaType type;
+  final QuillIconTheme? iconTheme;
+  final QuillDialogTheme? dialogTheme;
+  final String? tooltip;
+  final MediaFilePicker mediaFilePicker;
+  final MediaPickedCallback? onMediaPickedCallback;
+
+  /// 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? submitButtonText;
+
+  /// The size of dialog buttons.
+  final Size? submitButtonSize;
+
+  /// The text of the gallery button [MediaSourceSelectorDialog].
+  final String? galleryButtonText;
+
+  /// The text of the link button [MediaSourceSelectorDialog].
+  final String? linkButtonText;
+
+  final AutovalidateMode autovalidateMode;
+  final String? validationMessage;
+
+  @override
+  Widget build(BuildContext context) {
+    final theme = Theme.of(context);
+    final iconColor = iconTheme?.iconUnselectedColor ?? theme.iconTheme.color;
+    final iconFillColor =
+        iconTheme?.iconUnselectedFillColor ?? fillColor ?? theme.canvasColor;
+
+    return QuillIconButton(
+      icon: Icon(icon, size: iconSize, color: iconColor),
+      tooltip: tooltip,
+      highlightElevation: 0,
+      hoverElevation: 0,
+      size: iconSize * 1.77,
+      fillColor: iconFillColor,
+      borderRadius: iconTheme?.borderRadius ?? 2,
+      onPressed: () => _onPressedHandler(context),
+    );
+  }
+
+  Future<void> _onPressedHandler(BuildContext context) async {
+    if (onMediaPickedCallback != null) {
+      final mediaSource = await showDialog<MediaPickSetting>(
+        context: context,
+        builder: (_) => MediaSourceSelectorDialog(
+          dialogTheme: dialogTheme,
+          galleryButtonText: galleryButtonText,
+          linkButtonText: linkButtonText,
+        ),
+      );
+      if (mediaSource != null) {
+        if (mediaSource == MediaPickSetting.Gallery) {
+          await _pickImage();
+        } else {
+          _inputLink(context);
+        }
+      }
+    } else {
+      _inputLink(context);
+    }
+  }
+
+  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 mediaFilePicker(type);
+    return mediaFile != null ? onMediaPickedCallback?.call(mediaFile) : null;
+  }
+
+  void _inputLink(BuildContext context) {
+    showDialog<String>(
+      context: context,
+      builder: (_) => MediaLinkDialog(
+        dialogTheme: dialogTheme,
+        labelText: labelText,
+        hintText: hintText,
+        buttonText: submitButtonText,
+        buttonSize: submitButtonSize,
+        childrenSpacing: childrenSpacing,
+        autovalidateMode: autovalidateMode,
+        validationMessage: 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 =
+          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({
+    Key? 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),
+        super(key: key);
+
+  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 mediaQuery = MediaQuery.of(context);
+          final maxWidth =
+              kIsWeb ? mediaQuery.size.width / 4 : mediaQuery.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: 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.linkRegExp.hasMatch(value!)) {
+      return widget.validationMessage ?? 'That is not a valid URL';
+    }
+
+    return null;
+  }
+}
+
+/// Media souce selector.
+class MediaSourceSelectorDialog extends StatelessWidget {
+  const MediaSourceSelectorDialog({
+    Key? key,
+    this.dialogTheme,
+    this.galleryButtonText,
+    this.linkButtonText,
+  }) : super(key: key);
+
+  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 mediaQuery = MediaQuery.of(context);
+          double maxWidth, maxHeight;
+          if (kIsWeb) {
+            maxWidth = mediaQuery.size.width / 7;
+            maxHeight = mediaQuery.size.height / 7;
+          } else {
+            maxWidth = mediaQuery.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,
+    Key? key,
+  }) : super(key: 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;
+}
diff --git a/flutter_quill_extensions/lib/embeds/widgets/image.dart b/flutter_quill_extensions/lib/embeds/widgets/image.dart
index d4df2a4c..658c5050 100644
--- a/flutter_quill_extensions/lib/embeds/widgets/image.dart
+++ b/flutter_quill_extensions/lib/embeds/widgets/image.dart
@@ -21,7 +21,7 @@ String getImageStyleString(QuillController controller) {
   final String? s = controller
       .getAllSelectionStyles()
       .firstWhere((s) => s.attributes.containsKey(Attribute.style.key),
-          orElse: () => Style())
+          orElse: Style.new)
       .attributes[Attribute.style.key]
       ?.value;
   return s ?? '';
diff --git a/flutter_quill_extensions/lib/flutter_quill_extensions.dart b/flutter_quill_extensions/lib/flutter_quill_extensions.dart
index 12b3bd82..fdbc54b2 100644
--- a/flutter_quill_extensions/lib/flutter_quill_extensions.dart
+++ b/flutter_quill_extensions/lib/flutter_quill_extensions.dart
@@ -15,18 +15,24 @@ export 'embeds/toolbar/camera_button.dart';
 export 'embeds/toolbar/formula_button.dart';
 export 'embeds/toolbar/image_button.dart';
 export 'embeds/toolbar/image_video_utils.dart';
+export 'embeds/toolbar/media_button.dart';
 export 'embeds/toolbar/video_button.dart';
 export 'embeds/utils.dart';
 
 class FlutterQuillEmbeds {
-  static List<EmbedBuilder> builders(
-          {void Function(GlobalKey videoContainerKey)? onVideoInit}) =>
+  static List<EmbedBuilder> builders({
+    void Function(GlobalKey videoContainerKey)? onVideoInit,
+  }) =>
       [
         ImageEmbedBuilder(),
         VideoEmbedBuilder(onVideoInit: onVideoInit),
         FormulaEmbedBuilder(),
       ];
 
+  static List<EmbedBuilder> webBuilders() => [
+        ImageEmbedBuilderWeb(),
+      ];
+
   static List<EmbedButtonBuilder> buttons({
     bool showImageButton = true,
     bool showVideoButton = true,
@@ -43,58 +49,58 @@ class FlutterQuillEmbeds {
     FilePickImpl? filePickImpl,
     WebImagePickImpl? webImagePickImpl,
     WebVideoPickImpl? webVideoPickImpl,
-  }) {
-    return [
-      if (showImageButton)
-        (controller, toolbarIconSize, iconTheme, dialogTheme) => ImageButton(
-              icon: Icons.image,
-              iconSize: toolbarIconSize,
-              tooltip: imageButtonTooltip,
-              controller: controller,
-              onImagePickCallback: onImagePickCallback,
-              filePickImpl: filePickImpl,
-              webImagePickImpl: webImagePickImpl,
-              mediaPickSettingSelector: mediaPickSettingSelector,
-              iconTheme: iconTheme,
-              dialogTheme: dialogTheme,
-            ),
-      if (showVideoButton)
-        (controller, toolbarIconSize, iconTheme, dialogTheme) => VideoButton(
-              icon: Icons.movie_creation,
-              iconSize: toolbarIconSize,
-              tooltip: videoButtonTooltip,
-              controller: controller,
-              onVideoPickCallback: onVideoPickCallback,
-              filePickImpl: filePickImpl,
-              webVideoPickImpl: webImagePickImpl,
-              mediaPickSettingSelector: mediaPickSettingSelector,
-              iconTheme: iconTheme,
-              dialogTheme: dialogTheme,
-            ),
-      if ((onImagePickCallback != null || onVideoPickCallback != null) &&
-          showCameraButton)
-        (controller, toolbarIconSize, iconTheme, dialogTheme) => CameraButton(
-              icon: Icons.photo_camera,
-              iconSize: toolbarIconSize,
-              tooltip: cameraButtonTooltip,
-              controller: controller,
-              onImagePickCallback: onImagePickCallback,
-              onVideoPickCallback: onVideoPickCallback,
-              filePickImpl: filePickImpl,
-              webImagePickImpl: webImagePickImpl,
-              webVideoPickImpl: webVideoPickImpl,
-              cameraPickSettingSelector: cameraPickSettingSelector,
-              iconTheme: iconTheme,
-            ),
-      if (showFormulaButton)
-        (controller, toolbarIconSize, iconTheme, dialogTheme) => FormulaButton(
-              icon: Icons.functions,
-              iconSize: toolbarIconSize,
-              tooltip: formulaButtonTooltip,
-              controller: controller,
-              iconTheme: iconTheme,
-              dialogTheme: dialogTheme,
-            )
-    ];
-  }
+  }) =>
+      [
+        if (showImageButton)
+          (controller, toolbarIconSize, iconTheme, dialogTheme) => ImageButton(
+                icon: Icons.image,
+                iconSize: toolbarIconSize,
+                tooltip: imageButtonTooltip,
+                controller: controller,
+                onImagePickCallback: onImagePickCallback,
+                filePickImpl: filePickImpl,
+                webImagePickImpl: webImagePickImpl,
+                mediaPickSettingSelector: mediaPickSettingSelector,
+                iconTheme: iconTheme,
+                dialogTheme: dialogTheme,
+              ),
+        if (showVideoButton)
+          (controller, toolbarIconSize, iconTheme, dialogTheme) => VideoButton(
+                icon: Icons.movie_creation,
+                iconSize: toolbarIconSize,
+                tooltip: videoButtonTooltip,
+                controller: controller,
+                onVideoPickCallback: onVideoPickCallback,
+                filePickImpl: filePickImpl,
+                webVideoPickImpl: webImagePickImpl,
+                mediaPickSettingSelector: mediaPickSettingSelector,
+                iconTheme: iconTheme,
+                dialogTheme: dialogTheme,
+              ),
+        if ((onImagePickCallback != null || onVideoPickCallback != null) &&
+            showCameraButton)
+          (controller, toolbarIconSize, iconTheme, dialogTheme) => CameraButton(
+                icon: Icons.photo_camera,
+                iconSize: toolbarIconSize,
+                tooltip: cameraButtonTooltip,
+                controller: controller,
+                onImagePickCallback: onImagePickCallback,
+                onVideoPickCallback: onVideoPickCallback,
+                filePickImpl: filePickImpl,
+                webImagePickImpl: webImagePickImpl,
+                webVideoPickImpl: webVideoPickImpl,
+                cameraPickSettingSelector: cameraPickSettingSelector,
+                iconTheme: iconTheme,
+              ),
+        if (showFormulaButton)
+          (controller, toolbarIconSize, iconTheme, dialogTheme) =>
+              FormulaButton(
+                icon: Icons.functions,
+                iconSize: toolbarIconSize,
+                tooltip: formulaButtonTooltip,
+                controller: controller,
+                iconTheme: iconTheme,
+                dialogTheme: dialogTheme,
+              )
+      ];
 }
diff --git a/flutter_quill_extensions/lib/shims/dart_ui_fake.dart b/flutter_quill_extensions/lib/shims/dart_ui_fake.dart
new file mode 100644
index 00000000..baaf9ebd
--- /dev/null
+++ b/flutter_quill_extensions/lib/shims/dart_ui_fake.dart
@@ -0,0 +1,23 @@
+// ignore_for_file: avoid_classes_with_only_static_members, camel_case_types, lines_longer_than_80_chars
+
+import 'package:universal_html/html.dart' as html;
+
+// Fake interface for the logic that this package needs from (web-only) dart:ui.
+// This is conditionally exported so the analyzer sees these methods as available.
+
+typedef PlatroformViewFactory = html.Element Function(int viewId);
+
+/// Shim for web_ui engine.PlatformViewRegistry
+/// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/ui.dart#L62
+class platformViewRegistry {
+  /// Shim for registerViewFactory
+  /// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/ui.dart#L72
+  static dynamic registerViewFactory(
+      String viewTypeId, PlatroformViewFactory viewFactory) {}
+}
+
+/// Shim for web_ui engine.AssetManager
+/// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/src/engine/assets.dart#L12
+class webOnlyAssetManager {
+  static dynamic getAssetUrl(String asset) {}
+}
diff --git a/flutter_quill_extensions/lib/shims/dart_ui_real.dart b/flutter_quill_extensions/lib/shims/dart_ui_real.dart
new file mode 100644
index 00000000..69c06ee2
--- /dev/null
+++ b/flutter_quill_extensions/lib/shims/dart_ui_real.dart
@@ -0,0 +1 @@
+export 'dart:ui';
diff --git a/flutter_quill_extensions/pubspec.yaml b/flutter_quill_extensions/pubspec.yaml
index 1d7d7606..48ac50ac 100644
--- a/flutter_quill_extensions/pubspec.yaml
+++ b/flutter_quill_extensions/pubspec.yaml
@@ -1,6 +1,6 @@
 name: flutter_quill_extensions
 description: Embed extensions for flutter_quill including image, video, formula and etc.
-version: 0.3.0
+version: 0.3.1
 homepage: https://bulletjournal.us/home/index.html
 repository: https://github.com/singerdmx/flutter-quill/tree/master/flutter_quill_extensions
 
@@ -12,15 +12,18 @@ dependencies:
   flutter:
     sdk: flutter
 
-  flutter_quill: ^7.1.9
+  flutter_quill: ^7.1.10
 
-  image_picker: ^0.8.5+3
+  file_picker: ^5.2.10
+  image_picker: ^0.8.7+3
   photo_view: ^0.14.0
-  video_player: ^2.4.2
-  youtube_player_flutter: ^8.1.1
+  video_player: ^2.6.1
+  youtube_player_flutter: ^8.1.2
   gallery_saver: ^2.3.2
   math_keyboard: ^0.1.8
   string_validator: ^1.0.0
+  universal_ui: ^0.0.8
+  universal_html: ^2.2.1
   url_launcher: ^6.1.9
 
 dev_dependencies:
diff --git a/lib/extensions.dart b/lib/extensions.dart
index 12e961c2..b7c2b0af 100644
--- a/lib/extensions.dart
+++ b/lib/extensions.dart
@@ -4,3 +4,4 @@ export 'src/models/documents/nodes/leaf.dart' hide Text;
 export 'src/models/rules/insert.dart';
 export 'src/utils/platform.dart';
 export 'src/utils/string.dart';
+export 'src/utils/widgets.dart';
diff --git a/lib/src/models/themes/quill_dialog_theme.dart b/lib/src/models/themes/quill_dialog_theme.dart
index 47d732b9..1552c5f5 100644
--- a/lib/src/models/themes/quill_dialog_theme.dart
+++ b/lib/src/models/themes/quill_dialog_theme.dart
@@ -10,7 +10,9 @@ class QuillDialogTheme with Diagnosticable {
     this.shape,
     this.buttonStyle,
     this.linkDialogConstraints,
-    this.imageDialogConstraints,
+    this.linkDialogPadding = const EdgeInsets.all(16),
+    this.mediaSelectorDialogConstraints,
+    this.mediaSelectorDialogPadding = const EdgeInsets.all(16),
     this.isWrappable = false,
     this.runSpacing = 8.0,
   }) : assert(runSpacing >= 0);
@@ -34,8 +36,14 @@ class QuillDialogTheme with Diagnosticable {
   /// Constrains for [LinkStyleDialog].
   final BoxConstraints? linkDialogConstraints;
 
-  /// Constrains for [EmbedImageDialog].
-  final BoxConstraints? imageDialogConstraints;
+  /// The padding for content of [LinkStyleDialog].
+  final EdgeInsetsGeometry linkDialogPadding;
+
+  /// Constrains for [MediaSourceSelectorDialog].
+  final BoxConstraints? mediaSelectorDialogConstraints;
+
+  /// The padding for content of [MediaSourceSelectorDialog].
+  final EdgeInsetsGeometry mediaSelectorDialogPadding;
 
   /// Customizes this button's appearance.
   final ButtonStyle? buttonStyle;
@@ -57,7 +65,9 @@ class QuillDialogTheme with Diagnosticable {
     ShapeBorder? shape,
     ButtonStyle? buttonStyle,
     BoxConstraints? linkDialogConstraints,
+    EdgeInsetsGeometry? linkDialogPadding,
     BoxConstraints? imageDialogConstraints,
+    EdgeInsetsGeometry? mediaDialogPadding,
     bool? isWrappable,
     double? runSpacing,
   }) {
@@ -70,8 +80,11 @@ class QuillDialogTheme with Diagnosticable {
       buttonStyle: buttonStyle ?? this.buttonStyle,
       linkDialogConstraints:
           linkDialogConstraints ?? this.linkDialogConstraints,
-      imageDialogConstraints:
-          imageDialogConstraints ?? this.imageDialogConstraints,
+      linkDialogPadding: linkDialogPadding ?? this.linkDialogPadding,
+      mediaSelectorDialogConstraints:
+          imageDialogConstraints ?? mediaSelectorDialogConstraints,
+      mediaSelectorDialogPadding:
+          mediaDialogPadding ?? mediaSelectorDialogPadding,
       isWrappable: isWrappable ?? this.isWrappable,
       runSpacing: runSpacing ?? this.runSpacing,
     );
@@ -89,7 +102,10 @@ class QuillDialogTheme with Diagnosticable {
         other.shape == shape &&
         other.buttonStyle == buttonStyle &&
         other.linkDialogConstraints == linkDialogConstraints &&
-        other.imageDialogConstraints == imageDialogConstraints &&
+        other.linkDialogPadding == linkDialogPadding &&
+        other.mediaSelectorDialogConstraints ==
+            mediaSelectorDialogConstraints &&
+        other.mediaSelectorDialogPadding == mediaSelectorDialogPadding &&
         other.isWrappable == isWrappable &&
         other.runSpacing == runSpacing;
   }
@@ -102,7 +118,9 @@ class QuillDialogTheme with Diagnosticable {
         shape,
         buttonStyle,
         linkDialogConstraints,
-        imageDialogConstraints,
+        linkDialogPadding,
+        mediaSelectorDialogConstraints,
+        mediaSelectorDialogPadding,
         isWrappable,
         runSpacing,
       );
diff --git a/lib/src/translations/toolbar.i18n.dart b/lib/src/translations/toolbar.i18n.dart
index 507d6ebd..305e0152 100644
--- a/lib/src/translations/toolbar.i18n.dart
+++ b/lib/src/translations/toolbar.i18n.dart
@@ -63,6 +63,7 @@ extension Localization on String {
           'Insert URL': 'Insert URL',
           'Visit link': 'Visit link',
           'Enter link': 'Enter link',
+          'Enter media': 'Enter media',
           'Edit': 'Edit',
           'Apply': 'Apply',
         },
@@ -126,6 +127,7 @@ extension Localization on String {
           'Insert URL': 'Insert URL',
           'Visit link': 'Visit link',
           'Enter link': 'Enter link',
+          'Enter media': 'Enter media',
           'Edit': 'Edit',
           'Apply': 'Apply',
         },
diff --git a/lib/src/widgets/toolbar/link_style_button2.dart b/lib/src/widgets/toolbar/link_style_button2.dart
index ad73152a..42aaff11 100644
--- a/lib/src/widgets/toolbar/link_style_button2.dart
+++ b/lib/src/widgets/toolbar/link_style_button2.dart
@@ -7,7 +7,6 @@ import '../../../translations.dart';
 import '../../models/documents/attribute.dart';
 import '../../models/themes/quill_dialog_theme.dart';
 import '../../models/themes/quill_icon_theme.dart';
-import '../../utils/widgets.dart';
 import '../controller.dart';
 import '../link.dart';
 import '../toolbar.dart';
diff --git a/pubspec.yaml b/pubspec.yaml
index 569616c4..24e9ed14 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,6 +1,6 @@
 name: flutter_quill
 description: A rich text editor supporting mobile and web (Demo App @ bulletjournal.us)
-version: 7.1.9
+version: 7.1.10
 #author: bulletjournal
 homepage: https://bulletjournal.us/home/index.html
 repository: https://github.com/singerdmx/flutter-quill