[editor] Impl image and flutter logo test embed builders

This commit is contained in:
Jaylen Bian 2021-07-09 23:43:18 +08:00
parent f1c23ca91a
commit a728e16469
7 changed files with 142 additions and 103 deletions

View File

@ -13,7 +13,18 @@
"flutter_logo": ""
},
"attributes":{
"size": 50.0
"size": 200.0
}
},
{
"insert":"\n"
},
{
"insert": {
"test_block_type": "test_data"
},
"attributes":{
}
},
{

View File

@ -1,12 +1,6 @@
import 'dart:convert';
import 'dart:io' as io;
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:string_validator/string_validator.dart';
import '../model/document/node/leaf.dart';
import '../widget/raw_editor.dart';
import '../widget/selection.dart';
import '../rendering/editor.dart';

View File

@ -1,22 +1,18 @@
import 'dart:io' as io;
import 'dart:convert';
import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:string_validator/string_validator.dart';
import '../widget/raw_editor.dart';
import '../widget/builder.dart';
import '../widget/embed.dart';
import '../widget/proxy.dart';
import '../widget/image_viewer_screen.dart';
import '../model/document/attribute.dart';
import '../model/document/document.dart';
import '../model/document/node/embed.dart';
import '../model/document/node/line.dart';
import '../model/document/node/container.dart' as container_node;
import '../model/document/node/leaf.dart' as leaf;
import '../model/document/node/leaf.dart' show Leaf;
import '../service/controller.dart';
import '../service/cursor.dart';
import '../service/style.dart';
@ -61,13 +57,13 @@ class FlowyEditor extends StatefulWidget {
this.textCapitalization = TextCapitalization.sentences,
this.keyboardAppearance = Brightness.light,
this.scrollPhysics,
this.embedBuilder = EmbedBuilder.defaultBuilder,
this.onLaunchUrl,
this.onTapDown,
this.onTapUp,
this.onLongPressStart,
this.onLongPressMoveUpdate,
this.onLongPressEnd,
this.embedProvider = EmbedBaseProvider.buildEmbedWidget,
});
factory FlowyEditor.basic({
@ -105,7 +101,7 @@ class FlowyEditor extends StatefulWidget {
final TextCapitalization textCapitalization;
final Brightness keyboardAppearance;
final ScrollPhysics? scrollPhysics;
final EmbedBuilderFuncion embedBuilder;
final EmbedBuilderFuncion embedProvider;
// Callback
@ -222,7 +218,7 @@ class _FlowyEditorState extends State<FlowyEditor> implements EditorTextSelectio
widget.keyboardAppearance,
widget.enableInteractiveSelection,
widget.scrollPhysics,
widget.embedBuilder,
widget.embedProvider,
),
);
}
@ -429,7 +425,7 @@ class _FlowyEditorSelectionGestureDetectorBuilder extends EditorTextSelectionGes
}
// Link
final segment = segmentResult.node as leaf.Leaf;
final segment = segmentResult.node as Leaf;
if (segment.style.containsKey(Attribute.link.key)) {
var launchUrl = getEditor()!.widget.onLaunchUrl;
launchUrl ??= _launchUrl;
@ -444,27 +440,6 @@ class _FlowyEditorSelectionGestureDetectorBuilder extends EditorTextSelectionGes
return false;
}
// Image
if (getEditor()!.widget.readOnly && segment.value is BlockEmbed) {
final blockEmbed = segment.value as BlockEmbed;
if (blockEmbed.type == 'image') {
final imageUrl = EmbedBuilder.standardizeImageUrl(blockEmbed.data);
Navigator.push(
getEditor()!.context,
MaterialPageRoute(builder: (context) {
return ImageTapWrapper(
imageProvider: imageUrl.startsWith('http')
? NetworkImage(imageUrl)
: isBase64(imageUrl)
? Image.memory(base64.decode(imageUrl)) as ImageProvider<Object>?
: FileImage(io.File(imageUrl)),
);
}),
);
}
return false;
}
// Fallback
if (_flipListCheckbox(position, line, segmentResult)) {
return true;

View File

@ -1,78 +1,38 @@
import 'dart:convert';
import 'dart:io' as io;
import 'package:flowy_editor/src/widget/embed_builder/logo_builder.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:string_validator/string_validator.dart';
import '../widget/embed_builder/image_builder.dart';
import '../model/document/node/leaf.dart' show Embed;
abstract class EmbedWidgetBuilder {
const EmbedWidgetBuilder();
bool canHandle(String type);
Widget? buildeWidget(BuildContext context, Embed node);
}
/* ---------------------------------- Embed --------------------------------- */
class EmbedBuilder {
static const kImageTypeKey = 'image';
class EmbedBaseProvider {
static const kFlutterLogoTypeKey = 'flutter_logo';
static const builtInTypes = [kImageTypeKey, kFlutterLogoTypeKey];
static const builtInProviders = <EmbedWidgetBuilder>[
ImageEmbedBuilder(),
LogoEmbedBuilder(),
];
static Widget defaultBuilder(BuildContext context, Embed node) {
assert(!kIsWeb, 'Please provide EmbedBuilder for Web');
switch (node.value.type) {
case kImageTypeKey:
return _generateImageEmbed(context, node);
case kFlutterLogoTypeKey:
return _generateFlutterLogoEmbed(context, node);
default:
return Align(
alignment: Alignment.center,
child: _UnsupportedHintBlock(node),
);
static Widget buildEmbedWidget(BuildContext context, Embed node) {
Widget? result;
for (final builder in builtInProviders) {
if (builder.canHandle(node.value.type)) {
result = builder.buildeWidget(context, node);
if (result != null) {
break;
}
}
}
}
// Generator
static Widget _generateImageEmbed(BuildContext context, Embed node) {
final imageUrl = standardizeImageUrl(node.value.data);
return imageUrl.startsWith('http')
? Image.network(imageUrl)
: isBase64(imageUrl)
? Image.memory(base64.decode(imageUrl))
: Image.file(io.File(imageUrl));
}
static Widget _generateFlutterLogoEmbed(BuildContext context, Embed node) {
final size = node.style.attributes['size'];
var logoSize = size != null ? size.value as double? ?? 100.0 : 100.0;
return Align(
alignment: Alignment.center,
child: Container(
width: logoSize,
height: logoSize,
color: Colors.red,
child: GestureDetector(
onTap: () {
print('Flutter logo tapped');
},
child: FlutterLogo(size: logoSize),
),
),
);
}
// Helper
static String standardizeImageUrl(String url) {
if (url.contains('base64')) {
return url.split(',')[1];
}
return url;
return result ?? Align(alignment: Alignment.center, child: _UnsupportedHintBlock(node));
}
}

View File

@ -0,0 +1,52 @@
import 'dart:convert';
import 'dart:io' as io;
import 'package:flutter/src/widgets/framework.dart';
import 'package:flutter/material.dart';
import 'package:string_validator/string_validator.dart';
import '../../model/document/node/leaf.dart' show Embed;
import '../embed.dart';
/* --------------------------------- Sample --------------------------------- */
///
/// {
/// "insert": {
/// "image": "https://test.com/sample.png"
/// },
/// "attributes" : {
/// "width": "100.0"
/// }
/// }
///
/* --------------------------------- Builder -------------------------------- */
class ImageEmbedBuilder extends EmbedWidgetBuilder {
const ImageEmbedBuilder() : super();
static const kImageTypeKey = 'image';
@override
bool canHandle(String type) {
return type == kImageTypeKey;
}
@override
Widget? buildeWidget(BuildContext context, Embed node) {
final imageUrl = _standardizeImageUrl(node.value.data);
return imageUrl.startsWith('http')
? Image.network(imageUrl)
: isBase64(imageUrl)
? Image.memory(base64.decode(imageUrl))
: Image.file(io.File(imageUrl));
}
String _standardizeImageUrl(String url) {
if (url.contains('base64')) {
return url.split(',')[1];
}
return url;
}
}

View File

@ -0,0 +1,50 @@
import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/framework.dart';
import '../../model/document/node/leaf.dart';
import '../../widget/embed.dart';
/* --------------------------------- Sample --------------------------------- */
///
/// {
/// "insert": {
/// "flutter_logo": ""
/// },
/// "attributes" : {
/// "size": 100.0
/// }
/// }
///
/* --------------------------------- Builder -------------------------------- */
class LogoEmbedBuilder extends EmbedWidgetBuilder {
const LogoEmbedBuilder() : super();
static const kImageTypeKey = 'flutter_logo';
@override
bool canHandle(String type) {
return type == kImageTypeKey;
}
@override
Widget? buildeWidget(BuildContext context, Embed node) {
final size = node.style.attributes['size'];
var logoSize = size != null ? size.value as double? ?? 100.0 : 100.0;
return Align(
alignment: Alignment.center,
child: Container(
width: logoSize,
height: logoSize,
child: GestureDetector(
onTap: () {
print('Flutter logo tapped');
},
child: FlutterLogo(size: logoSize),
),
),
);
}
}

View File

@ -22,8 +22,7 @@ class BaselineProxy extends SingleChildRenderObjectWidget {
}
@override
void updateRenderObject(
BuildContext context, covariant RenderBaselineProxy renderObject) {
void updateRenderObject(BuildContext context, covariant RenderBaselineProxy renderObject) {
renderObject
..textStyle = textStyle!
..padding = padding!;
@ -38,8 +37,7 @@ class EmbedProxy extends SingleChildRenderObjectWidget {
const EmbedProxy(Widget child) : super(child: child);
@override
RenderEmbedProxy createRenderObject(BuildContext context) =>
RenderEmbedProxy(null);
RenderEmbedProxy createRenderObject(BuildContext context) => RenderEmbedProxy(null);
}
/* ---------------------------------- Text ---------------------------------- */
@ -82,8 +80,7 @@ class RichTextProxy extends SingleChildRenderObjectWidget {
}
@override
void updateRenderObject(
BuildContext context, covariant RenderParagraphProxy renderObject) {
void updateRenderObject(BuildContext context, covariant RenderParagraphProxy renderObject) {
renderObject
..textStyle = textStyle
..textAlign = textAlign