@@ -130,7 +130,9 @@ class _HomePageState extends State<HomePage> {
130130 ),
131131 onLaunchUrl: _launchUrl,
132132 maxContentWidth: 800 ,
133- embedBuilder: _embedBuilder,
133+ embedRegistry: EmbedRegistry .fallbackWithConfigurations (
134+ [IconEmbed (), ImageBlockEmbed (), ImageSpanEmbed ()],
135+ ),
134136 spellCheckConfiguration: SpellCheckConfiguration (
135137 spellCheckService: DefaultSpellCheckService (),
136138 misspelledSelectionColor: Colors .red,
@@ -143,56 +145,6 @@ class _HomePageState extends State<HomePage> {
143145 );
144146 }
145147
146- Widget _embedBuilder (BuildContext context, EmbedNode node) {
147- if (node.value.type == 'icon' ) {
148- final data = node.value.data;
149- // Icons.rocket_launch_outlined
150- return Icon (
151- IconData (int .parse (data['codePoint' ]), fontFamily: data['fontFamily' ]),
152- color: Color (int .parse (data['color' ])),
153- size: 18 ,
154- );
155- }
156-
157- if (node.value.type == 'image' ) {
158- final sourceType = node.value.data['source_type' ];
159- ImageProvider ? image;
160- if (sourceType == 'assets' ) {
161- image = AssetImage (node.value.data['source' ]);
162- } else if (sourceType == 'file' ) {
163- image = FileImage (File (node.value.data['source' ]));
164- } else if (sourceType == 'url' ) {
165- image = NetworkImage (node.value.data['source' ]);
166- } else if (sourceType == 'data' ) {
167- // source: 'data:image/jpeg;base64, LzlqLzRBQ... <!-- Base64 data -->'
168- RegExp regex = RegExp (
169- r'^data:image\/(png|jpe?g|gif|bmp|webp);base64,' ,
170- caseSensitive: false ,
171- );
172- if (regex.hasMatch (node.value.data['source' ])) {
173- String base64Image =
174- node.value.data['source' ].replaceFirst (regex, '' );
175- image = MemoryImage (base64Decode (base64Image));
176- }
177- }
178- if (image != null ) {
179- return Padding (
180- // Caret takes 2 pixels, hence not symmetric padding values.
181- padding: const EdgeInsets .only (left: 4 , right: 2 , top: 2 , bottom: 2 ),
182- child: Container (
183- width: (node.value.data['width' ] as num ? )? .toDouble () ?? 300 ,
184- height: (node.value.data['height' ] as num ? )? .toDouble () ?? 300 ,
185- decoration: BoxDecoration (
186- image: DecorationImage (image: image, fit: BoxFit .cover),
187- ),
188- ),
189- );
190- }
191- }
192-
193- return defaultFleatherEmbedBuilder (context, node);
194- }
195-
196148 void _launchUrl (String ? url) async {
197149 if (url == null ) return ;
198150 final uri = Uri .parse (url);
@@ -203,6 +155,73 @@ class _HomePageState extends State<HomePage> {
203155 }
204156}
205157
158+ class IconEmbed extends SpanEmbedConfiguration {
159+ const IconEmbed ()
160+ : super (type: 'icon' , alignment: PlaceholderAlignment .middle);
161+
162+ @override
163+ Widget build (BuildContext context, Map <String , dynamic > data) {
164+ // Icons.rocket_launch_outlined
165+ return Icon (
166+ IconData (int .parse (data['codePoint' ]), fontFamily: data['fontFamily' ]),
167+ color: Color (int .parse (data['color' ])),
168+ size: 18 ,
169+ );
170+ }
171+ }
172+
173+ Widget _imageBuilder (BuildContext context, Map <String , dynamic > data) {
174+ final sourceType = data['source_type' ];
175+ ImageProvider ? image;
176+ if (sourceType == 'assets' ) {
177+ image = AssetImage (data['source' ]);
178+ } else if (sourceType == 'file' ) {
179+ image = FileImage (File (data['source' ]));
180+ } else if (sourceType == 'url' ) {
181+ image = NetworkImage (data['source' ]);
182+ } else if (sourceType == 'data' ) {
183+ // source: 'data:image/jpeg;base64, LzlqLzRBQ... <!-- Base64 data -->'
184+ RegExp regex = RegExp (
185+ r'^data:image\/(png|jpe?g|gif|bmp|webp);base64,' ,
186+ caseSensitive: false ,
187+ );
188+ if (regex.hasMatch (data['source' ])) {
189+ String base64Image = data['source' ].replaceFirst (regex, '' );
190+ image = MemoryImage (base64Decode (base64Image));
191+ }
192+ }
193+ if (image == null ) {
194+ throw ArgumentError ('Could create an ImageProvider from the provided data' );
195+ }
196+ return Padding (
197+ // Caret takes 2 pixels, hence not symmetric padding values.
198+ padding: const EdgeInsets .only (left: 4 , right: 2 , top: 2 , bottom: 2 ),
199+ child: Container (
200+ width: (data['width' ] as num ? )? .toDouble () ?? 300 ,
201+ height: (data['height' ] as num ? )? .toDouble () ?? 300 ,
202+ decoration: BoxDecoration (
203+ image: DecorationImage (image: image, fit: BoxFit .cover),
204+ ),
205+ ),
206+ );
207+ }
208+
209+ class ImageSpanEmbed extends SpanEmbedConfiguration {
210+ ImageSpanEmbed () : super (type: 'span_image' );
211+
212+ @override
213+ Widget build (BuildContext context, Map <String , dynamic > data) =>
214+ _imageBuilder (context, data);
215+ }
216+
217+ class ImageBlockEmbed extends BlockEmbedConfiguration {
218+ ImageBlockEmbed () : super (type: 'block_image' );
219+
220+ @override
221+ Widget build (BuildContext context, Map <String , dynamic > data) =>
222+ _imageBuilder (context, data);
223+ }
224+
206225/// This is an example insert rule that will insert a new line before and
207226/// after inline image embed.
208227class ForceNewlineForInsertsAroundInlineImageRule extends InsertRule {
0 commit comments