diff --git a/frontend/app_flowy/packages/flowy_editor/example/assets/document.json b/frontend/app_flowy/packages/flowy_editor/example/assets/document.json index 2ddedf70b3..bec451fba4 100644 --- a/frontend/app_flowy/packages/flowy_editor/example/assets/document.json +++ b/frontend/app_flowy/packages/flowy_editor/example/assets/document.json @@ -1,9 +1,7 @@ { "document": { - "type": "text", - "attributes": { - "content": "TITLE" - }, + "type": "editor", + "attributes": {}, "children": [ { "type": "text", diff --git a/frontend/app_flowy/packages/flowy_editor/example/lib/main.dart b/frontend/app_flowy/packages/flowy_editor/example/lib/main.dart index 4c1cd079b1..cc9acce929 100644 --- a/frontend/app_flowy/packages/flowy_editor/example/lib/main.dart +++ b/frontend/app_flowy/packages/flowy_editor/example/lib/main.dart @@ -1,5 +1,6 @@ import 'dart:convert'; +import 'package:example/plugin/document_node_widget.dart'; import 'package:example/plugin/image_node_widget.dart'; import 'package:example/plugin/text_node_widget.dart'; import 'package:example/plugin/text_with_check_box_node_widget.dart'; @@ -56,23 +57,16 @@ class MyHomePage extends StatefulWidget { class _MyHomePageState extends State { final RenderPlugins renderPlugins = RenderPlugins(); + late EditorState _editorState; @override void initState() { super.initState(); renderPlugins - ..register( - 'text', - TextNodeBuilder.create, - ) - ..register( - 'image', - ImageNodeBuilder.create, - ) - ..register( - 'text/with-checkbox', - TextWithCheckBoxNodeBuilder.create, - ); + ..register('editor', EditorNodeWidgetBuilder.create) + ..register('text', TextNodeBuilder.create) + ..register('image', ImageNodeBuilder.create) + ..register('text/with-checkbox', TextWithCheckBoxNodeBuilder.create); } @override @@ -83,37 +77,23 @@ class _MyHomePageState extends State { // the App.build method, and use it to set our appbar title. title: Text(widget.title), ), - body: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - FutureBuilder( - future: rootBundle.loadString('assets/document.json'), - builder: (context, snapshot) { - if (!snapshot.hasData) { - return const Center( - child: CircularProgressIndicator(), - ); - } else { - final data = - Map.from(json.decode(snapshot.data!)); - final document = StateTree.fromJson(data); - print(document.root.toString()); - final editorState = EditorState( - document: document, - renderPlugins: renderPlugins, - ); - return editorState.build(context); - } - }, - ), - SizedBox( - height: 50, - width: MediaQuery.of(context).size.width, - child: Container( - color: Colors.red, - ), - ) - ], + body: FutureBuilder( + future: rootBundle.loadString('assets/document.json'), + builder: (context, snapshot) { + if (!snapshot.hasData) { + return const Center( + child: CircularProgressIndicator(), + ); + } else { + final data = Map.from(json.decode(snapshot.data!)); + final document = StateTree.fromJson(data); + _editorState = EditorState( + document: document, + renderPlugins: renderPlugins, + ); + return _editorState.build(context); + } + }, ), ); } diff --git a/frontend/app_flowy/packages/flowy_editor/example/lib/plugin/document_node_widget.dart b/frontend/app_flowy/packages/flowy_editor/example/lib/plugin/document_node_widget.dart new file mode 100644 index 0000000000..2de62948d5 --- /dev/null +++ b/frontend/app_flowy/packages/flowy_editor/example/lib/plugin/document_node_widget.dart @@ -0,0 +1,50 @@ +import 'package:flowy_editor/flowy_editor.dart'; +import 'package:flutter/material.dart'; + +class EditorNodeWidgetBuilder extends NodeWidgetBuilder { + EditorNodeWidgetBuilder.create({ + required super.editorState, + required super.node, + }) : super.create(); + + @override + Widget build(BuildContext buildContext) { + return SingleChildScrollView( + child: _EditorNodeWidget( + node: node, + editorState: editorState, + ), + ); + } +} + +class _EditorNodeWidget extends StatelessWidget { + final Node node; + final EditorState editorState; + + const _EditorNodeWidget({ + Key? key, + required this.node, + required this.editorState, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: node.children + .map( + (e) => editorState.renderPlugins.buildWidget( + context: NodeWidgetContext( + buildContext: context, + node: e, + editorState: editorState, + ), + ), + ) + .toList(), + ), + ); + } +} diff --git a/frontend/app_flowy/packages/flowy_editor/example/lib/plugin/image_node_widget.dart b/frontend/app_flowy/packages/flowy_editor/example/lib/plugin/image_node_widget.dart index 5084b6333c..c48941011f 100644 --- a/frontend/app_flowy/packages/flowy_editor/example/lib/plugin/image_node_widget.dart +++ b/frontend/app_flowy/packages/flowy_editor/example/lib/plugin/image_node_widget.dart @@ -1,7 +1,5 @@ import 'package:flowy_editor/flowy_editor.dart'; import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; -import 'package:flowy_editor/document/attributes.dart'; class ImageNodeBuilder extends NodeWidgetBuilder { ImageNodeBuilder.create({ @@ -33,12 +31,7 @@ class _ImageNodeWidget extends StatelessWidget { @override Widget build(BuildContext context) { return GestureDetector( - child: ChangeNotifierProvider.value( - value: node, - builder: (_, __) => Consumer( - builder: ((context, value, child) => _build(context)), - ), - ), + child: _build(context), onTap: () { editorState.update(node, { 'image_src': diff --git a/frontend/app_flowy/packages/flowy_editor/example/lib/plugin/text_node_widget.dart b/frontend/app_flowy/packages/flowy_editor/example/lib/plugin/text_node_widget.dart index 59e466c92e..a56327ead5 100644 --- a/frontend/app_flowy/packages/flowy_editor/example/lib/plugin/text_node_widget.dart +++ b/frontend/app_flowy/packages/flowy_editor/example/lib/plugin/text_node_widget.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flowy_editor/flowy_editor.dart'; import 'package:flutter/services.dart'; -import 'package:provider/provider.dart'; import 'package:flowy_editor/document/attributes.dart'; class TextNodeBuilder extends NodeWidgetBuilder { @@ -56,47 +55,40 @@ class __TextNodeWidgetState extends State<_TextNodeWidget> @override Widget build(BuildContext context) { - return ChangeNotifierProvider.value( - value: node, - builder: (_, __) => Consumer( - builder: ((context, value, child) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SelectableText.rich( - TextSpan( - text: content, - style: node.attributes.toTextStyle(), - ), - onTap: () { - _textInputConnection?.close(); - _textInputConnection = TextInput.attach( - this, - const TextInputConfiguration( - enableDeltaModel: false, - inputType: TextInputType.multiline, - textCapitalization: TextCapitalization.sentences, - ), - ); - _textInputConnection - ?..show() - ..setEditingState(textEditingValue); - }, + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SelectableText.rich( + TextSpan( + text: content, + style: node.attributes.toTextStyle(), + ), + onTap: () { + _textInputConnection?.close(); + _textInputConnection = TextInput.attach( + this, + const TextInputConfiguration( + enableDeltaModel: false, + inputType: TextInputType.multiline, + textCapitalization: TextCapitalization.sentences, ), - if (node.children.isNotEmpty) - ...node.children.map( - (e) => editorState.renderPlugins.buildWidget( - context: NodeWidgetContext( - buildContext: context, - node: e, - editorState: editorState, - ), - ), - ) - ], - ); - }), - ), + ); + _textInputConnection + ?..show() + ..setEditingState(textEditingValue); + }, + ), + if (node.children.isNotEmpty) + ...node.children.map( + (e) => editorState.renderPlugins.buildWidget( + context: NodeWidgetContext( + buildContext: context, + node: e, + editorState: editorState, + ), + ), + ) + ], ); } diff --git a/frontend/app_flowy/packages/flowy_editor/lib/render/node_widget_builder.dart b/frontend/app_flowy/packages/flowy_editor/lib/render/node_widget_builder.dart index 4fb99419ce..badce60694 100644 --- a/frontend/app_flowy/packages/flowy_editor/lib/render/node_widget_builder.dart +++ b/frontend/app_flowy/packages/flowy_editor/lib/render/node_widget_builder.dart @@ -2,12 +2,15 @@ import 'package:flowy_editor/editor_state.dart'; import 'package:flowy_editor/document/node.dart'; import 'package:flowy_editor/render/render_plugins.dart'; import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; typedef NodeValidator = bool Function(T node); class NodeWidgetBuilder { final EditorState editorState; final T node; + + bool rebuildOnNodeChanged; NodeValidator? nodeValidator; RenderPlugins get renderPlugins => editorState.renderPlugins; @@ -15,6 +18,7 @@ class NodeWidgetBuilder { NodeWidgetBuilder.create({ required this.editorState, required this.node, + this.rebuildOnNodeChanged = true, }); /// Render the current [Node] @@ -29,6 +33,23 @@ class NodeWidgetBuilder { throw Exception( 'Node validate failure, node = { type: ${node.type}, attributes: ${node.attributes} }'); } - return build(buildContext); + + if (rebuildOnNodeChanged) { + return _buildNodeChangeNotifier(buildContext); + } else { + return build(buildContext); + } + } + + Widget _buildNodeChangeNotifier(BuildContext buildContext) { + return ChangeNotifierProvider.value( + value: node, + builder: (_, __) => Consumer( + builder: ((context, value, child) { + debugPrint('Node changed, and rebuilding...'); + return build(context); + }), + ), + ); } } diff --git a/frontend/app_flowy/packages/flowy_editor/pubspec.yaml b/frontend/app_flowy/packages/flowy_editor/pubspec.yaml index 6a6d32d580..74ca437e27 100644 --- a/frontend/app_flowy/packages/flowy_editor/pubspec.yaml +++ b/frontend/app_flowy/packages/flowy_editor/pubspec.yaml @@ -11,6 +11,8 @@ dependencies: flutter: sdk: flutter + provider: ^6.0.3 + dev_dependencies: flutter_test: sdk: flutter