mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
[editor] Impl image and flutter logo test embed builders
This commit is contained in:
parent
f1c23ca91a
commit
a728e16469
@ -13,7 +13,18 @@
|
||||
"flutter_logo": ""
|
||||
},
|
||||
"attributes":{
|
||||
"size": 50.0
|
||||
"size": 200.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"insert":"\n"
|
||||
},
|
||||
{
|
||||
"insert": {
|
||||
"test_block_type": "test_data"
|
||||
},
|
||||
"attributes":{
|
||||
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -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';
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user