Merge pull request #917 from LucasXu0/feat/export

Update example project
This commit is contained in:
Lucas.Xu 2022-08-26 11:37:53 +08:00 committed by GitHub
commit 192438fc20
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 181 additions and 352 deletions

View File

@ -1,276 +1,102 @@
{
"document": {
"type": "editor",
"attributes": {},
"children": [
{
"type": "image",
"attributes": {
"image_src": "https://images.squarespace-cdn.com/content/v1/617f6f16b877c06711e87373/c3f23723-37f4-44d7-9c5d-6e2a53064ae7/Asset+10.png",
"image_src": "https://s1.ax1x.com/2022/08/26/v2sSbR.jpg",
"align": "center"
}
},
{
"type": "text",
"attributes": { "subtype": "heading", "heading": "h1" },
"delta": [
{ "insert": "👋 " },
{ "insert": "Welcome to ", "attributes": { "bold": true } },
{
"insert": "🌶 Read Me"
}
],
"attributes": {
"subtype": "heading",
"heading": "h1"
}
},
{
"type": "text",
"delta": [
{
"insert": "👋 Welcome to FlowyEditor"
}
],
"attributes": {
"subtype": "heading",
"heading": "h2"
}
},
{
"type": "text",
"delta": [
{
"insert": "To be honest, we are still in the alpha stage. There are still many functions that need to be completed. And we are developing more features. Please give us a star if the "
},
{
"insert": "FlowyEditor",
"insert": "AppFlowy Editor",
"attributes": {
"href": "https://github.com/AppFlowy-IO/AppFlowy"
"href": "appflowy.io",
"italic": true,
"bold": true
}
}
]
},
{ "type": "text", "delta": [] },
{
"type": "text",
"delta": [
{ "insert": "AppFlowy Editor is a " },
{ "insert": "highly customizable", "attributes": { "bold": true } },
{ "insert": " " },
{ "insert": "rich-text editor", "attributes": { "italic": true } },
{ "insert": " for " },
{ "insert": "Flutter", "attributes": { "underline": true } }
]
},
{
"type": "text",
"attributes": { "checkbox": true, "subtype": "checkbox" },
"delta": [{ "insert": "Customizable" }]
},
{
"type": "text",
"attributes": { "checkbox": true, "subtype": "checkbox" },
"delta": [{ "insert": "Test-covered" }]
},
{
"type": "text",
"attributes": { "checkbox": false, "subtype": "checkbox" },
"delta": [{ "insert": "more to come!" }]
},
{ "type": "text", "delta": [] },
{
"type": "text",
"attributes": { "subtype": "quote" },
"delta": [{ "insert": "Here is an exmaple you can give it a try" }]
},
{ "type": "text", "delta": [] },
{
"type": "text",
"delta": [
{ "insert": "You can also use " },
{
"insert": "AppFlowy Editor",
"attributes": {
"italic": true,
"bold": true,
"backgroundColor": "0x6000BCF0"
}
},
{ "insert": " as a component to build your own app." }
]
},
{ "type": "text", "delta": [] },
{
"type": "text",
"attributes": { "subtype": "bulleted-list" },
"delta": [{ "insert": "Use / to insert blocks" }]
},
{
"type": "text",
"attributes": { "subtype": "bulleted-list" },
"delta": [
{
"insert": " helps you. 😊😊😊"
"insert": "Select text to trigger to the toolbar to format your notes."
}
]
},
{ "type": "text", "delta": [] },
{
"type": "text",
"delta": [
{
"insert": "Since the FlowyEditor are a community-driven open source editor, we very welcome and appreciate every pull request submissions from everyone.😄😄😄"
"insert": "If you have questions or feedback, please submit an issue on Github or join the community along with 1000+ builders!"
}
]
},
{
"type": "text",
"delta": [
{
"insert": "Here are the basics:"
}
],
"attributes": {
"subtype": "heading",
"heading": "h3"
}
},
{
"type": "text",
"delta": [
{ "insert": "Click " },
{ "insert": "anywhere", "attributes": { "underline": true } },
{ "insert": " and just typing." }
]
},
{
"type": "text",
"delta": [
{
"insert": "Hit"
},
{
"insert": " / ",
"attributes": { "backgroundColor": "0xFFFFFF00" }
},
{
"insert": "to see all the types of content you can add - headers, bulleted lists, checkboxes, etc."
}
]
},
{
"type": "text",
"delta": [
{
"insert": "Highlight",
"attributes": { "backgroundColor": "0xFF00BCFB" }
},
{
"insert": " any text, and use the menu that pops up to "
},
{ "insert": "style", "attributes": { "bold": true } },
{ "insert": " your ", "attributes": { "italic": true } },
{ "insert": "writing", "attributes": { "strikethrough": true } },
{ "insert": "." }
]
},
{
"type": "text",
"delta": [
{
"insert": "Here are the plugins:"
}
],
"attributes": {
"subtype": "heading",
"heading": "h3"
}
},
{
"type": "image",
"attributes": {
"image_src": "https://s1.ax1x.com/2022/08/24/vgAJED.png",
"align": "left",
"width": 300
}
},
{
"type": "text",
"delta": [
{
"insert": "Hello world"
}
],
"attributes": {
"subtype": "checkbox",
"checkbox": false
}
},
{
"type": "text",
"delta": [
{
"insert": "Hello world"
}
],
"attributes": {
"subtype": "checkbox",
"checkbox": false
}
},
{
"type": "text",
"delta": [
{
"insert": "Hello world"
}
],
"attributes": {
"subtype": "checkbox",
"checkbox": false
}
},
{
"type": "text",
"delta": [
{
"insert": "Hello world"
}
],
"attributes": {
"subtype": "bulleted-list"
}
},
{
"type": "text",
"delta": [
{
"insert": "Hello world"
}
],
"attributes": {
"subtype": "bulleted-list"
}
},
{
"type": "text",
"delta": [
{
"insert": "Hello "
},
{
"insert": "world",
"attributes": { "bold": true }
}
],
"attributes": {
"subtype": "bulleted-list"
}
},
{
"type": "text",
"delta": [
{
"insert": "Hello world"
}
],
"attributes": {
"subtype": "quote"
}
},
{
"type": "text",
"delta": [
{
"insert": "Hello world"
}
],
"attributes": {
"subtype": "quote"
}
},
{
"type": "text",
"delta": [
{
"insert": "Hello world"
}
],
"attributes": {
"subtype": "quote"
}
},
{
"type": "text",
"delta": [
{
"insert": "Hello world"
}
],
"attributes": {
"subtype": "number-list",
"number": 1
}
},
{
"type": "text",
"delta": [
{
"insert": "Hello world"
}
],
"attributes": {
"subtype": "number-list",
"number": 2
}
},
{
"type": "text",
"delta": [
{
"insert": "Hello world"
}
],
"attributes": {
"subtype": "number-list",
"number": 3
}
}
]
}

View File

@ -1,14 +1,15 @@
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'expandable_floating_action_button.dart';
// import 'plugin/image_node_widget.dart';
import 'plugin/youtube_link_node_widget.dart';
import 'package:path_provider/path_provider.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'expandable_floating_action_button.dart';
void main() {
runApp(const MyApp());
}
@ -16,20 +17,11 @@ void main() {
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'AppFlowyEditor Example'),
@ -39,16 +31,6 @@ class MyApp extends StatelessWidget {
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
final String title;
@override
@ -56,54 +38,66 @@ class MyHomePage extends StatefulWidget {
}
class _MyHomePageState extends State<MyHomePage> {
final editorKey = GlobalKey();
int page = 0;
int _pageIndex = 0;
late EditorState _editorState;
Future<String>? _jsonString;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
alignment: Alignment.topCenter,
child: _buildBody(),
child: _buildEditor(context),
),
floatingActionButton: _buildExpandableFab(),
);
}
Widget _buildBody() {
if (page == 0) {
return _buildAppFlowyEditorWithExample();
} else if (page == 1) {
return _buildAppFlowyEditorWithEmptyDocument();
} else if (page == 2) {
return _buildAppFlowyEditorWithBigDocument();
Widget _buildEditor(BuildContext context) {
if (_jsonString != null) {
return _buildEditorWithJsonString(_jsonString!);
}
return Container();
if (_pageIndex == 0) {
return _buildEditorWithJsonString(
rootBundle.loadString('assets/example.json'),
);
} else if (_pageIndex == 1) {
return _buildEditorWithJsonString(
rootBundle.loadString('assets/big_document.json'),
);
} else if (_pageIndex == 2) {
return _buildEditorWithJsonString(
Future.value(
jsonEncode(EditorState.empty().document.toJson()),
),
);
}
throw UnimplementedError();
}
Widget _buildAppFlowyEditorWithEmptyDocument() {
final editorState = EditorState.empty();
final editor = AppFlowyEditor(
editorState: editorState,
keyEventHandlers: const [],
customBuilders: const {},
);
return editor;
}
Widget _buildAppFlowyEditorWithExample() {
Widget _buildEditorWithJsonString(Future<String> jsonString) {
return FutureBuilder<String>(
future: rootBundle.loadString('assets/example.json'),
builder: (context, snapshot) {
future: jsonString,
builder: (_, snapshot) {
if (snapshot.hasData) {
final data = Map<String, Object>.from(json.decode(snapshot.data!));
final editorState = EditorState(document: StateTree.fromJson(data));
editorState.logConfiguration
_editorState = EditorState(
document: StateTree.fromJson(
Map<String, Object>.from(
json.decode(snapshot.data!),
),
),
);
_editorState.logConfiguration
..level = LogLevel.all
..handler = (message) {
debugPrint(message);
};
return _buildAppFlowyEditor(editorState);
return Container(
padding: const EdgeInsets.all(20),
child: AppFlowyEditor(
editorState: _editorState,
),
);
} else {
return const Center(
child: CircularProgressIndicator(),
@ -113,71 +107,64 @@ class _MyHomePageState extends State<MyHomePage> {
);
}
Widget _buildAppFlowyEditorWithBigDocument() {
return FutureBuilder<String>(
future: rootBundle.loadString('assets/big_document.json'),
builder: (context, snapshot) {
if (snapshot.hasData) {
final data = Map<String, Object>.from(json.decode(snapshot.data!));
return _buildAppFlowyEditor(EditorState(
document: StateTree.fromJson(data),
));
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
},
);
}
Widget _buildAppFlowyEditor(EditorState editorState) {
return Container(
padding: const EdgeInsets.only(left: 20, right: 20),
child: AppFlowyEditor(
key: editorKey,
editorState: editorState,
keyEventHandlers: const [],
customBuilders: {
// 'image': ImageNodeBuilder(),
'youtube_link': YouTubeLinkNodeBuilder()
},
),
);
}
Widget _buildExpandableFab() {
return ExpandableFab(
distance: 112.0,
children: [
ActionButton(
onPressed: () {
if (page == 0) return;
setState(() {
page = 0;
});
},
icon: const Icon(Icons.note_add),
icon: const Icon(Icons.abc),
onPressed: () => _switchToPage(0),
),
ActionButton(
icon: const Icon(Icons.document_scanner),
onPressed: () {
if (page == 1) return;
setState(() {
page = 1;
});
},
icon: const Icon(Icons.abc),
onPressed: () => _switchToPage(1),
),
ActionButton(
onPressed: () {
if (page == 2) return;
setState(() {
page = 2;
});
},
icon: const Icon(Icons.text_fields),
icon: const Icon(Icons.abc),
onPressed: () => _switchToPage(2),
),
ActionButton(
icon: const Icon(Icons.print),
onPressed: () => {_exportDocument(_editorState)}),
ActionButton(
icon: const Icon(Icons.import_export),
onPressed: () => _importDocument(),
),
],
);
}
void _exportDocument(EditorState editorState) async {
final document = editorState.document.toJson();
final json = jsonEncode(document);
final directory = await getTemporaryDirectory();
final path = directory.path;
final file = File('$path/editor.json');
await file.writeAsString(json);
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('The document is saved to the ${file.path}'),
),
);
}
}
void _importDocument() async {
final directory = await getTemporaryDirectory();
final path = directory.path;
final file = File('$path/editor.json');
setState(() {
_jsonString = file.readAsString();
});
}
void _switchToPage(int pageIndex) {
if (pageIndex != _pageIndex) {
setState(() {
_pageIndex = pageIndex;
});
}
}
}

View File

@ -5,11 +5,13 @@
import FlutterMacOS
import Foundation
import path_provider_macos
import rich_clipboard_macos
import url_launcher_macos
import wakelock_macos
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
RichClipboardPlugin.register(with: registry.registrar(forPlugin: "RichClipboardPlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
WakelockMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockMacosPlugin"))

View File

@ -1,5 +1,7 @@
PODS:
- FlutterMacOS (1.0.0)
- path_provider_macos (0.0.1):
- FlutterMacOS
- rich_clipboard_macos (0.0.1):
- FlutterMacOS
- url_launcher_macos (0.0.1):
@ -9,6 +11,7 @@ PODS:
DEPENDENCIES:
- FlutterMacOS (from `Flutter/ephemeral`)
- path_provider_macos (from `Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos`)
- rich_clipboard_macos (from `Flutter/ephemeral/.symlinks/plugins/rich_clipboard_macos/macos`)
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
- wakelock_macos (from `Flutter/ephemeral/.symlinks/plugins/wakelock_macos/macos`)
@ -16,6 +19,8 @@ DEPENDENCIES:
EXTERNAL SOURCES:
FlutterMacOS:
:path: Flutter/ephemeral
path_provider_macos:
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos
rich_clipboard_macos:
:path: Flutter/ephemeral/.symlinks/plugins/rich_clipboard_macos/macos
url_launcher_macos:
@ -25,6 +30,7 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
FlutterMacOS: 57701585bf7de1b3fc2bb61f6378d73bbdea8424
path_provider_macos: 3c0c3b4b0d4a76d2bf989a913c2de869c5641a19
rich_clipboard_macos: 43364b66b9dc69d203eb8dd6d758e2d12e02723c
url_launcher_macos: 597e05b8e514239626bcf4a850fcf9ef5c856ec3
wakelock_macos: bc3f2a9bd8d2e6c89fee1e1822e7ddac3bd004a9

View File

@ -40,6 +40,7 @@ dependencies:
video_player: ^2.4.5
pod_player: 0.0.8
flutter_inappwebview: ^5.4.3+7
path_provider: ^2.0.11
dev_dependencies:
flutter_test:

View File

@ -163,7 +163,8 @@ class Node extends ChangeNotifier with LinkedListEntry<Node> {
'type': type,
};
if (children.isNotEmpty) {
map['children'] = children.map((node) => node.toJson());
map['children'] =
(children.map((node) => node.toJson())).toList(growable: false);
}
if (_attributes.isNotEmpty) {
map['attributes'] = _attributes;

View File

@ -33,6 +33,12 @@ class StateTree {
return StateTree(root: root);
}
Map<String, Object> toJson() {
return {
'document': root.toJson(),
};
}
Node? nodeAtPath(Path path) {
return root.childAtPath(path);
}