|
1 | | -import 'dart:convert'; |
2 | | -import 'dart:io'; |
3 | | - |
4 | 1 | import 'package:example/home_page.dart'; |
5 | | -import 'package:example/plugin/editor_theme.dart'; |
6 | | -import 'package:flutter/foundation.dart'; |
7 | 2 | import 'package:flutter/material.dart'; |
8 | | -import 'package:flutter/services.dart'; |
9 | 3 |
|
10 | | -import 'package:example/plugin/code_block_node_widget.dart'; |
11 | | -import 'package:example/plugin/horizontal_rule_node_widget.dart'; |
12 | | -import 'package:example/plugin/tex_block_node_widget.dart'; |
13 | | -import 'package:file_picker/file_picker.dart'; |
14 | 4 | import 'package:flutter_localizations/flutter_localizations.dart'; |
15 | | -import 'package:path_provider/path_provider.dart'; |
16 | | -import 'package:universal_html/html.dart' as html; |
17 | 5 |
|
18 | 6 | import 'package:appflowy_editor/appflowy_editor.dart'; |
19 | 7 |
|
20 | | -import 'expandable_floating_action_button.dart'; |
21 | | - |
22 | 8 | void main() { |
23 | 9 | runApp(const MyApp()); |
24 | 10 | } |
@@ -51,200 +37,8 @@ class MyHomePage extends StatefulWidget { |
51 | 37 | } |
52 | 38 |
|
53 | 39 | class _MyHomePageState extends State<MyHomePage> { |
54 | | - int _pageIndex = 0; |
55 | | - EditorState? _editorState; |
56 | | - bool darkMode = false; |
57 | | - Future<String>? _jsonString; |
58 | | - |
59 | | - ThemeData? _editorThemeData; |
60 | | - |
61 | 40 | @override |
62 | 41 | Widget build(BuildContext context) { |
63 | 42 | return const HomePage(); |
64 | 43 | } |
65 | | - |
66 | | - Widget _buildEditor(BuildContext context) { |
67 | | - if (_jsonString != null) { |
68 | | - return _buildEditorWithJsonString(_jsonString!); |
69 | | - } |
70 | | - if (_pageIndex == 0) { |
71 | | - return _buildEditorWithJsonString( |
72 | | - rootBundle.loadString('assets/example.json'), |
73 | | - ); |
74 | | - } else if (_pageIndex == 1) { |
75 | | - return _buildEditorWithJsonString( |
76 | | - Future.value( |
77 | | - jsonEncode(EditorState.empty().document.toJson()), |
78 | | - ), |
79 | | - ); |
80 | | - } |
81 | | - throw UnimplementedError(); |
82 | | - } |
83 | | - |
84 | | - Widget _buildEditorWithJsonString(Future<String> jsonString) { |
85 | | - return FutureBuilder<String>( |
86 | | - future: jsonString, |
87 | | - builder: (_, snapshot) { |
88 | | - if (snapshot.hasData && |
89 | | - snapshot.connectionState == ConnectionState.done) { |
90 | | - _editorState ??= EditorState( |
91 | | - document: Document.fromJson( |
92 | | - Map<String, Object>.from( |
93 | | - json.decode(snapshot.data!), |
94 | | - ), |
95 | | - ), |
96 | | - ); |
97 | | - _editorState!.logConfiguration |
98 | | - ..level = LogLevel.all |
99 | | - ..handler = (message) { |
100 | | - debugPrint(message); |
101 | | - }; |
102 | | - _editorState!.transactionStream.listen((event) { |
103 | | - debugPrint('Transaction: ${event.toJson()}'); |
104 | | - }); |
105 | | - _editorThemeData ??= Theme.of(context).copyWith(extensions: [ |
106 | | - if (darkMode) ...darkEditorStyleExtension, |
107 | | - if (darkMode) ...darkPlguinStyleExtension, |
108 | | - if (!darkMode) ...lightEditorStyleExtension, |
109 | | - if (!darkMode) ...lightPlguinStyleExtension, |
110 | | - ]); |
111 | | - return Container( |
112 | | - color: darkMode ? Colors.black : Colors.white, |
113 | | - width: MediaQuery.of(context).size.width, |
114 | | - child: AppFlowyEditor( |
115 | | - editorState: _editorState!, |
116 | | - editable: true, |
117 | | - autoFocus: _editorState!.document.isEmpty, |
118 | | - themeData: _editorThemeData, |
119 | | - customBuilders: { |
120 | | - 'text/code_block': CodeBlockNodeWidgetBuilder(), |
121 | | - 'tex': TeXBlockNodeWidgetBuidler(), |
122 | | - 'horizontal_rule': HorizontalRuleWidgetBuilder(), |
123 | | - }, |
124 | | - shortcutEvents: [ |
125 | | - enterInCodeBlock, |
126 | | - ignoreKeysInCodeBlock, |
127 | | - insertHorizontalRule, |
128 | | - ], |
129 | | - selectionMenuItems: [ |
130 | | - codeBlockMenuItem, |
131 | | - teXBlockMenuItem, |
132 | | - horizontalRuleMenuItem, |
133 | | - ], |
134 | | - ), |
135 | | - ); |
136 | | - } else { |
137 | | - return const Center( |
138 | | - child: CircularProgressIndicator(), |
139 | | - ); |
140 | | - } |
141 | | - }, |
142 | | - ); |
143 | | - } |
144 | | - |
145 | | - Widget _buildExpandableFab(BuildContext context) { |
146 | | - return FloatingActionButton(onPressed: () { |
147 | | - Scaffold.of(context).openDrawer(); |
148 | | - }); |
149 | | - return ExpandableFab( |
150 | | - distance: 112.0, |
151 | | - children: [ |
152 | | - ActionButton( |
153 | | - icon: const Icon(Icons.abc), |
154 | | - onPressed: () => _switchToPage(0), |
155 | | - ), |
156 | | - ActionButton( |
157 | | - icon: const Icon(Icons.abc), |
158 | | - onPressed: () => _switchToPage(1), |
159 | | - ), |
160 | | - ActionButton( |
161 | | - icon: const Icon(Icons.print), |
162 | | - onPressed: () => _exportDocument(_editorState!), |
163 | | - ), |
164 | | - ActionButton( |
165 | | - icon: const Icon(Icons.import_export), |
166 | | - onPressed: () async => await _importDocument(), |
167 | | - ), |
168 | | - ActionButton( |
169 | | - icon: const Icon(Icons.dark_mode), |
170 | | - onPressed: () { |
171 | | - setState(() { |
172 | | - darkMode = !darkMode; |
173 | | - }); |
174 | | - }, |
175 | | - ), |
176 | | - ActionButton( |
177 | | - icon: const Icon(Icons.color_lens), |
178 | | - onPressed: () { |
179 | | - setState(() { |
180 | | - _editorThemeData = customizeEditorTheme(context); |
181 | | - darkMode = true; |
182 | | - }); |
183 | | - }, |
184 | | - ), |
185 | | - ], |
186 | | - ); |
187 | | - } |
188 | | - |
189 | | - void _exportDocument(EditorState editorState) async { |
190 | | - final document = editorState.document.toJson(); |
191 | | - final json = jsonEncode(document); |
192 | | - if (kIsWeb) { |
193 | | - final blob = html.Blob([json], 'text/plain', 'native'); |
194 | | - html.AnchorElement( |
195 | | - href: html.Url.createObjectUrlFromBlob(blob).toString(), |
196 | | - ) |
197 | | - ..setAttribute('download', 'editor.json') |
198 | | - ..click(); |
199 | | - } else { |
200 | | - final directory = await getTemporaryDirectory(); |
201 | | - final path = directory.path; |
202 | | - final file = File('$path/editor.json'); |
203 | | - await file.writeAsString(json); |
204 | | - |
205 | | - if (mounted) { |
206 | | - ScaffoldMessenger.of(context).showSnackBar( |
207 | | - SnackBar( |
208 | | - content: Text('The document is saved to the ${file.path}'), |
209 | | - ), |
210 | | - ); |
211 | | - } |
212 | | - } |
213 | | - } |
214 | | - |
215 | | - Future<void> _importDocument() async { |
216 | | - if (kIsWeb) { |
217 | | - final result = await FilePicker.platform.pickFiles( |
218 | | - allowMultiple: false, |
219 | | - allowedExtensions: ['json'], |
220 | | - type: FileType.custom, |
221 | | - ); |
222 | | - final bytes = result?.files.first.bytes; |
223 | | - if (bytes != null) { |
224 | | - final jsonString = const Utf8Decoder().convert(bytes); |
225 | | - setState(() { |
226 | | - _editorState = null; |
227 | | - _jsonString = Future.value(jsonString); |
228 | | - }); |
229 | | - } |
230 | | - } else { |
231 | | - final directory = await getTemporaryDirectory(); |
232 | | - final path = '${directory.path}/editor.json'; |
233 | | - final file = File(path); |
234 | | - setState(() { |
235 | | - _editorState = null; |
236 | | - _jsonString = file.readAsString(); |
237 | | - }); |
238 | | - } |
239 | | - } |
240 | | - |
241 | | - void _switchToPage(int pageIndex) { |
242 | | - if (pageIndex != _pageIndex) { |
243 | | - setState(() { |
244 | | - _editorThemeData = null; |
245 | | - _editorState = null; |
246 | | - _pageIndex = pageIndex; |
247 | | - }); |
248 | | - } |
249 | | - } |
250 | 44 | } |
0 commit comments