Skip to content

Commit 66de353

Browse files
committed
chore: Add web embeds in readme section. #1
1 parent e6c3ba0 commit 66de353

File tree

1 file changed

+247
-0
lines changed

1 file changed

+247
-0
lines changed

Diff for: README.md

+247
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
- [4.2 Defining triple click selection behaviour](#42-defining-triple-click-selection-behaviour)
3030
- [4.3 Reassigning `quillEditor` on web platforms](#43-reassigning-quilleditor-on-web-platforms)
3131
- [4.3.1 Creating web embeds](#431-creating-web-embeds)
32+
- [4.4 Creating the toolbar](#44-creating-the-toolbar)
3233

3334

3435
# Why? 🤷‍
@@ -654,7 +655,253 @@ As noted in `flutter-quill`'s documentation
654655
(https://github.com/singerdmx/flutter-quill/tree/master#web),
655656
we need to define web embeds so Quill Editor works properly.
656657

658+
If we want to embed an image on a web-based platform
659+
and show it to the person,
660+
we need to define our own embed.
661+
For this, create a folder called `web_embeds` inside `lib`.
662+
Create a file called `web_embeds.dart`
663+
and paste the following code.
664+
665+
```dart
666+
667+
import 'package:app/main.dart';
668+
import 'package:flutter/cupertino.dart';
669+
import 'package:flutter_quill/flutter_quill.dart';
670+
import 'package:flutter_quill_extensions/flutter_quill_extensions.dart';
671+
import 'package:responsive_framework/responsive_framework.dart';
672+
import 'package:universal_html/html.dart' as html;
673+
674+
// Conditionally importing the PlatformViewRegistry class according to the platform
675+
import 'mobile_platform_registry.dart' if (dart.library.html) 'web_platform_registry.dart' as ui_instance;
676+
677+
/// Custom embed for images to work on the web.
678+
class ImageEmbedBuilderWeb extends EmbedBuilder {
679+
@override
680+
String get key => BlockEmbed.imageType;
681+
682+
@override
683+
Widget build(
684+
BuildContext context,
685+
QuillController controller,
686+
Embed node,
687+
bool readOnly,
688+
bool inline,
689+
TextStyle textStyle,
690+
) {
691+
final imageUrl = node.value.data;
692+
if (isImageBase64(imageUrl)) {
693+
// TODO: handle imageUrl of base64
694+
return const SizedBox();
695+
}
696+
final size = MediaQuery.of(context).size;
697+
698+
// This is needed for images to be correctly embedded on the web.
699+
ImageUniversalUI().platformViewRegistry.registerViewFactory(PlatformService(), imageUrl, (viewId) {
700+
return html.ImageElement()
701+
..src = imageUrl
702+
..style.height = 'auto'
703+
..style.width = 'auto';
704+
});
705+
706+
// Rendering responsive image
707+
return Padding(
708+
padding: EdgeInsets.only(
709+
right: ResponsiveBreakpoints.of(context).smallerThan(TABLET)
710+
? size.width * 0.5
711+
: (ResponsiveBreakpoints.of(context).equals('4K'))
712+
? size.width * 0.75
713+
: size.width * 0.2,
714+
),
715+
child: SizedBox(
716+
height: MediaQuery.of(context).size.height * 0.45,
717+
child: HtmlElementView(
718+
viewType: imageUrl,
719+
),
720+
),
721+
);
722+
}
723+
}
724+
725+
/// List of default web embed builders.
726+
List<EmbedBuilder> get defaultEmbedBuildersWeb => [
727+
ImageEmbedBuilderWeb(),
728+
];
729+
730+
```
731+
732+
This is where we define `defaultEmbedBuildersWeb`
733+
we're using in `_buildEditor()`.
734+
This array variable uses the `ImageEmbedBuilderWeb`,
735+
our custom web embed so images are shown in web platforms.
736+
We technically can add more embeds
737+
(for example, to show videos).
738+
But for now, let's keep it simple and only allow the person to add images.
739+
740+
The `ImageEmbedBuilderWeb` class pertains
741+
to the web image embed,
742+
extending the [`EmbedBuilder`](https://github.com/singerdmx/flutter-quill/blob/36d72c1987f0cb8d6c689c12542600364c07e20f/lib/src/widgets/embeds.dart#L9)
743+
class from `flutter-quill`.
744+
745+
Let's break it down.
746+
We define the `key` of the class
747+
of *what type of object the embed pertains to*.
748+
In our case, it's an image.
749+
750+
```dart
751+
@override
752+
String get key => BlockEmbed.imageType;
753+
```
754+
755+
In the `build` function,
756+
we render the widget we want in the screen.
757+
In this case, we render an `ImageElement`
758+
with the `imageURL` that is passed to the class.
759+
We use `ResponsiveBreakpoints` from `responsive_framework`
760+
to show properly show the image across different device sizes.
761+
762+
Inside the `build()` function, you may notice the following lines:
763+
764+
```dart
765+
ImageUniversalUI().platformViewRegistry.registerViewFactory(PlatformService(), imageUrl, (viewId) {
766+
return html.ImageElement()
767+
..src = imageUrl
768+
..style.height = 'auto'
769+
..style.width = 'auto';
770+
});
771+
```
772+
773+
We need to call `registerViewFactory` from `dart:ui_web`
774+
so the image is properly shown in web devices.
775+
If we do not do this,
776+
**build compilation fails**.
777+
This is because the package it's called from doesn't compile
778+
when we create a build for mobile devices.
779+
This is why we create a `ImageUniversalUI` class
780+
that conditionally imports the package
781+
so it compiles on both type of devices.
782+
For more information,
783+
check https://github.com/flutter/flutter/issues/41563#issuecomment-547923478.
784+
785+
For this to work,
786+
in the same file,
787+
add this piece of code:
788+
789+
```dart
790+
/// Class used to conditionally register the view factory.
791+
/// For more information, check https://github.com/flutter/flutter/issues/41563#issuecomment-547923478.
792+
class PlatformViewRegistryFix {
793+
void registerViewFactory(PlatformService platformService, imageURL, dynamic cbFnc) {
794+
if (platformService.isWebPlatform()) {
795+
ui_instance.PlatformViewRegistry.registerViewFactory(
796+
imageURL,
797+
cbFnc,
798+
);
799+
}
800+
}
801+
}
802+
803+
/// Class that conditionally registers the `platformViewRegistry`.
804+
class ImageUniversalUI {
805+
PlatformViewRegistryFix platformViewRegistry = PlatformViewRegistryFix();
806+
}
807+
```
808+
809+
`PlatformViewRegistryFix` calls the `registerViewFactory`
810+
only on web platforms.
811+
It uses the `ui_instance` object,
812+
which is conditionally imported on top of the file.
813+
This `ui_instance` variable uses the appropriate package
814+
to call `registerViewFactory`.
815+
816+
> [!NOTE]
817+
> Check https://github.com/flutter/flutter/issues/41563#issuecomment-547923478
818+
> for more information about `dart:ui` and `dart:web_ui`
819+
> to better understand why we need to conditionally import them separately
820+
> so the application compiles to both target devices.
821+
822+
This is why we import the correct `ui_instance`
823+
so we can compile to both targets `web` and `mobile` devices.
824+
825+
```dart
826+
import 'mobile_platform_registry.dart' if (dart.library.html) 'web_platform_registry.dart' as ui_instance;
827+
```
828+
829+
Neither of these files are created.
830+
So let's do that!
831+
In the same folder `lib/web_embeds`,
832+
create `mobile_platform_registry.dart`
833+
and add:
834+
835+
```dart
836+
/// Class used to `registerViewFactory` for mobile platforms.
837+
///
838+
/// Please check https://github.com/flutter/flutter/issues/41563#issuecomment-547923478 for more information.
839+
class PlatformViewRegistry {
840+
static void registerViewFactory(String viewId, dynamic cb) {}
841+
}
842+
```
843+
844+
This is just a simple class with `registerViewFactory` function
845+
that effectively does nothing.
846+
We don't *need for it to do anything*
847+
because we are implementing the embed **only for the web**.
848+
So we just only need this to compile.
849+
850+
Now, in the same folder, create the file `web_platform_registry.dart`
851+
and add:
852+
853+
```dart
854+
import 'dart:ui_web' as web_ui;
855+
856+
/// Class used to `registerViewFactory` for web platforms.
857+
///
858+
/// Please check https://github.com/flutter/flutter/issues/41563#issuecomment-547923478 for more information.
859+
class PlatformViewRegistry {
860+
static void registerViewFactory(String viewId, dynamic cb) {
861+
web_ui.platformViewRegistry.registerViewFactory(viewId, cb);
862+
}
863+
}
864+
```
865+
866+
In here, we are importing `dart:ui_web`
867+
(which only compiles on web devices)
868+
and performing the `registerViewFactory`.
869+
870+
And that's it!
871+
We've successfully created a web embed
872+
that embeds image on the web!
873+
874+
To recap:
875+
- we export the `defaultEmbedBuildersWeb`
876+
array variable with our custom web embed
877+
`ImageEmbedBuilderWeb()`.
878+
- `ImageEmbedBuilderWeb` class returns a widget
879+
that shows an HTML image in the editor.
880+
For this to work, we need to call `registerViewFactory`
881+
from the `dart:web_ui` package.
882+
- because `dart:web_ui` only compiles for the web,
883+
we need to import `dart:ui` and `dart:web_ui`
884+
separately from different files
885+
that perform this register.
886+
This will ensure the packages
887+
are conditionally imported
888+
and compilation always work,
889+
regardless of the target platform!
890+
891+
892+
### 4.4 Creating the toolbar
893+
894+
Now that we've defined the editor
895+
and the appropriate web embeds so images work on web devices,
896+
it's time to create **our toolbar**.
897+
898+
This toolbar will have some options for the person
899+
to stylize the text (e.g make it bold or italic)
900+
and add images
901+
(which make use of the embed we've just created for web devices).
902+
657903

658904

659905
- toolbar next
906+
- show how it passes the imageURL to the web embed
660907

0 commit comments

Comments
 (0)