Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## Fleather 1.25.1 > 2.0.0

* `embedBuilder` is not supported anymore. Use `EmbedRegistry` to register embed configuration.


## Fleather 1.4.4 > 1.14.5+1

* Change `SelectorScope.of(context).pushSelector(selector, completer)` to `SelectorScope.showSelector(context, selector, completer)` or `SelectorScope.of(context).showSelector(context, selector, completer)`
Expand Down
6 changes: 3 additions & 3 deletions packages/fleather/example/assets/welcome.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
},
{
"insert": {
"_type": "image",
"_type": "block_image",
"_inline": false,
"source": "images/breeze.jpg",
"source_type": "assets"
Expand Down Expand Up @@ -166,7 +166,7 @@
},
{
"insert": {
"_type": "image",
"_type": "span_image",
"_inline": true,
"source": "images/breeze.jpg",
"orientation": "landscape",
Expand All @@ -177,7 +177,7 @@
},
{
"insert": {
"_type": "image",
"_type": "span_image",
"_inline": true,
"source": "images/cody-manning-82xt4MtbJqg-unsplash.jpg",
"source_type": "assets",
Expand Down
6 changes: 3 additions & 3 deletions packages/fleather/example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ EXTERNAL SOURCES:

SPEC CHECKSUMS:
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a
integration_test: 4a889634ef21a45d28d50d622cf412dc6d9f586e
url_launcher_ios: 694010445543906933d732453a59da0a173ae33d
image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1
integration_test: 252f60fa39af5e17c3aa9899d35d908a0721b573
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe

PODFILE CHECKSUM: 0dbd5a87e0ace00c9610d2037ac22083a01f861d

Expand Down
121 changes: 70 additions & 51 deletions packages/fleather/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,9 @@ class _HomePageState extends State<HomePage> {
),
onLaunchUrl: _launchUrl,
maxContentWidth: 800,
embedBuilder: _embedBuilder,
embedRegistry: EmbedRegistry.fallbackWithConfigurations(
[IconEmbed(), ImageBlockEmbed(), ImageSpanEmbed()],
),
spellCheckConfiguration: SpellCheckConfiguration(
spellCheckService: DefaultSpellCheckService(),
misspelledSelectionColor: Colors.red,
Expand All @@ -143,56 +145,6 @@ class _HomePageState extends State<HomePage> {
);
}

Widget _embedBuilder(BuildContext context, EmbedNode node) {
if (node.value.type == 'icon') {
final data = node.value.data;
// Icons.rocket_launch_outlined
return Icon(
IconData(int.parse(data['codePoint']), fontFamily: data['fontFamily']),
color: Color(int.parse(data['color'])),
size: 18,
);
}

if (node.value.type == 'image') {
final sourceType = node.value.data['source_type'];
ImageProvider? image;
if (sourceType == 'assets') {
image = AssetImage(node.value.data['source']);
} else if (sourceType == 'file') {
image = FileImage(File(node.value.data['source']));
} else if (sourceType == 'url') {
image = NetworkImage(node.value.data['source']);
} else if (sourceType == 'data') {
// source: 'data:image/jpeg;base64, LzlqLzRBQ... <!-- Base64 data -->'
RegExp regex = RegExp(
r'^data:image\/(png|jpe?g|gif|bmp|webp);base64,',
caseSensitive: false,
);
if (regex.hasMatch(node.value.data['source'])) {
String base64Image =
node.value.data['source'].replaceFirst(regex, '');
image = MemoryImage(base64Decode(base64Image));
}
}
if (image != null) {
return Padding(
// Caret takes 2 pixels, hence not symmetric padding values.
padding: const EdgeInsets.only(left: 4, right: 2, top: 2, bottom: 2),
child: Container(
width: (node.value.data['width'] as num?)?.toDouble() ?? 300,
height: (node.value.data['height'] as num?)?.toDouble() ?? 300,
decoration: BoxDecoration(
image: DecorationImage(image: image, fit: BoxFit.cover),
),
),
);
}
}

return defaultFleatherEmbedBuilder(context, node);
}

void _launchUrl(String? url) async {
if (url == null) return;
final uri = Uri.parse(url);
Expand All @@ -203,6 +155,73 @@ class _HomePageState extends State<HomePage> {
}
}

class IconEmbed extends SpanEmbedConfiguration {
const IconEmbed()
: super(key: 'icon', alignment: PlaceholderAlignment.middle);

@override
Widget build(BuildContext context, Map<String, dynamic> data) {
// Icons.rocket_launch_outlined
return Icon(
IconData(int.parse(data['codePoint']), fontFamily: data['fontFamily']),
color: Color(int.parse(data['color'])),
size: 18,
);
}
}

Widget _imageBuilder(BuildContext context, Map<String, dynamic> data) {
final sourceType = data['source_type'];
ImageProvider? image;
if (sourceType == 'assets') {
image = AssetImage(data['source']);
} else if (sourceType == 'file') {
image = FileImage(File(data['source']));
} else if (sourceType == 'url') {
image = NetworkImage(data['source']);
} else if (sourceType == 'data') {
// source: 'data:image/jpeg;base64, LzlqLzRBQ... <!-- Base64 data -->'
RegExp regex = RegExp(
r'^data:image\/(png|jpe?g|gif|bmp|webp);base64,',
caseSensitive: false,
);
if (regex.hasMatch(data['source'])) {
String base64Image = data['source'].replaceFirst(regex, '');
image = MemoryImage(base64Decode(base64Image));
}
}
if (image == null) {
throw ArgumentError('Could create an ImageProvider from the provided data');
}
return Padding(
// Caret takes 2 pixels, hence not symmetric padding values.
padding: const EdgeInsets.only(left: 4, right: 2, top: 2, bottom: 2),
child: Container(
width: (data['width'] as num?)?.toDouble() ?? 300,
height: (data['height'] as num?)?.toDouble() ?? 300,
decoration: BoxDecoration(
image: DecorationImage(image: image, fit: BoxFit.cover),
),
),
);
}

class ImageSpanEmbed extends SpanEmbedConfiguration {
ImageSpanEmbed() : super(key: 'span_image');

@override
Widget build(BuildContext context, Map<String, dynamic> data) =>
_imageBuilder(context, data);
}

class ImageBlockEmbed extends BlockEmbedConfiguration {
ImageBlockEmbed() : super(key: 'block_image');

@override
Widget build(BuildContext context, Map<String, dynamic> data) =>
_imageBuilder(context, data);
}

/// This is an example insert rule that will insert a new line before and
/// after inline image embed.
class ForceNewlineForInsertsAroundInlineImageRule extends InsertRule {
Expand Down
1 change: 1 addition & 0 deletions packages/fleather/lib/fleather.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export 'src/widgets/controller.dart';
export 'src/widgets/cursor.dart';
export 'src/widgets/editor.dart';
export 'src/widgets/editor_toolbar.dart';
export 'src/widgets/embed_registry.dart';
export 'src/widgets/field.dart';
export 'src/widgets/link.dart' show LinkActionPickerDelegate, LinkMenuAction;
export 'src/widgets/text_line.dart';
Expand Down
8 changes: 4 additions & 4 deletions packages/fleather/lib/src/widgets/editable_text_block.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:collection/collection.dart';
import 'package:fleather/src/widgets/embed_registry.dart';
import 'package:flutter/material.dart';
import 'package:parchment/parchment.dart';

Expand All @@ -8,7 +9,6 @@ import 'checkbox.dart';
import 'controller.dart';
import 'cursor.dart';
import 'editable_text_line.dart';
import 'editor.dart';
import 'link.dart';
import 'text_line.dart';
import 'theme.dart';
Expand All @@ -24,7 +24,7 @@ class EditableTextBlock extends StatelessWidget {
final Color selectionColor;
final bool enableInteractiveSelection;
final bool hasFocus;
final FleatherEmbedBuilder embedBuilder;
final EmbedRegistry embedRegistry;
final LinkActionPicker linkActionPicker;
final ValueChanged<String?>? onLaunchUrl;
final EdgeInsets? contentPadding;
Expand All @@ -41,7 +41,7 @@ class EditableTextBlock extends StatelessWidget {
required this.selectionColor,
required this.enableInteractiveSelection,
required this.hasFocus,
required this.embedBuilder,
required this.embedRegistry,
required this.linkActionPicker,
this.onLaunchUrl,
this.contentPadding,
Expand Down Expand Up @@ -83,7 +83,7 @@ class EditableTextBlock extends StatelessWidget {
node: line,
readOnly: readOnly,
controller: controller,
embedBuilder: embedBuilder,
embedRegistry: embedRegistry,
linkActionPicker: linkActionPicker,
onLaunchUrl: onLaunchUrl,
textWidthBasis: textWidthBasis,
Expand Down
Loading