main.dart 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. import 'dart:convert';
  2. import 'dart:io';
  3. import 'package:example/plugin/underscore_to_italic_key_event_handler.dart';
  4. import 'package:flutter/material.dart';
  5. import 'package:flutter/services.dart';
  6. import 'package:path_provider/path_provider.dart';
  7. import 'package:appflowy_editor/appflowy_editor.dart';
  8. import 'expandable_floating_action_button.dart';
  9. void main() {
  10. runApp(const MyApp());
  11. }
  12. class MyApp extends StatelessWidget {
  13. const MyApp({Key? key}) : super(key: key);
  14. @override
  15. Widget build(BuildContext context) {
  16. return MaterialApp(
  17. localizationsDelegates: const [
  18. AppFlowyEditorLocalizations.delegate,
  19. ],
  20. supportedLocales: AppFlowyEditorLocalizations.delegate.supportedLocales,
  21. debugShowCheckedModeBanner: false,
  22. theme: ThemeData(
  23. primarySwatch: Colors.blue,
  24. ),
  25. home: const MyHomePage(title: 'AppFlowyEditor Example'),
  26. );
  27. }
  28. }
  29. class MyHomePage extends StatefulWidget {
  30. const MyHomePage({Key? key, required this.title}) : super(key: key);
  31. final String title;
  32. @override
  33. State<MyHomePage> createState() => _MyHomePageState();
  34. }
  35. class _MyHomePageState extends State<MyHomePage> {
  36. int _pageIndex = 0;
  37. EditorState? _editorState;
  38. bool darkMode = false;
  39. EditorStyle _editorStyle = EditorStyle.defaultStyle();
  40. Future<String>? _jsonString;
  41. @override
  42. Widget build(BuildContext context) {
  43. return Scaffold(
  44. extendBodyBehindAppBar: true,
  45. body: _buildEditor(context),
  46. floatingActionButton: _buildExpandableFab(),
  47. );
  48. }
  49. Widget _buildEditor(BuildContext context) {
  50. if (_jsonString != null) {
  51. return _buildEditorWithJsonString(_jsonString!);
  52. }
  53. if (_pageIndex == 0) {
  54. return _buildEditorWithJsonString(
  55. rootBundle.loadString('assets/example.json'),
  56. );
  57. } else if (_pageIndex == 1) {
  58. return _buildEditorWithJsonString(
  59. rootBundle.loadString('assets/big_document.json'),
  60. );
  61. } else if (_pageIndex == 2) {
  62. return _buildEditorWithJsonString(
  63. Future.value(
  64. jsonEncode(EditorState.empty().document.toJson()),
  65. ),
  66. );
  67. }
  68. throw UnimplementedError();
  69. }
  70. Widget _buildEditorWithJsonString(Future<String> jsonString) {
  71. return FutureBuilder<String>(
  72. future: jsonString,
  73. builder: (_, snapshot) {
  74. if (snapshot.hasData &&
  75. snapshot.connectionState == ConnectionState.done) {
  76. _editorState ??= EditorState(
  77. document: StateTree.fromJson(
  78. Map<String, Object>.from(
  79. json.decode(snapshot.data!),
  80. ),
  81. ),
  82. );
  83. _editorState!.logConfiguration
  84. ..level = LogLevel.all
  85. ..handler = (message) {
  86. debugPrint(message);
  87. };
  88. _editorState!.operationStream.listen((event) {
  89. debugPrint('Operation: ${event.toJson()}');
  90. });
  91. return Container(
  92. color: darkMode ? Colors.black : Colors.white,
  93. width: MediaQuery.of(context).size.width,
  94. child: AppFlowyEditor(
  95. editorState: _editorState!,
  96. editorStyle: _editorStyle,
  97. shortcutEvents: [
  98. underscoreToItalicEvent,
  99. ],
  100. ),
  101. );
  102. } else {
  103. return const Center(
  104. child: CircularProgressIndicator(),
  105. );
  106. }
  107. },
  108. );
  109. }
  110. Widget _buildExpandableFab() {
  111. return ExpandableFab(
  112. distance: 112.0,
  113. children: [
  114. ActionButton(
  115. icon: const Icon(Icons.abc),
  116. onPressed: () => _switchToPage(0),
  117. ),
  118. ActionButton(
  119. icon: const Icon(Icons.abc),
  120. onPressed: () => _switchToPage(1),
  121. ),
  122. ActionButton(
  123. icon: const Icon(Icons.abc),
  124. onPressed: () => _switchToPage(2),
  125. ),
  126. ActionButton(
  127. icon: const Icon(Icons.print),
  128. onPressed: () => _exportDocument(_editorState!),
  129. ),
  130. ActionButton(
  131. icon: const Icon(Icons.import_export),
  132. onPressed: () => _importDocument(),
  133. ),
  134. ActionButton(
  135. icon: const Icon(Icons.color_lens),
  136. onPressed: () {
  137. setState(() {
  138. _editorStyle = _editorStyle.copyWith(
  139. textStyle: darkMode
  140. ? BuiltInTextStyle.builtIn()
  141. : BuiltInTextStyle.builtInDarkMode(),
  142. );
  143. darkMode = !darkMode;
  144. });
  145. },
  146. ),
  147. ],
  148. );
  149. }
  150. void _exportDocument(EditorState editorState) async {
  151. final document = editorState.document.toJson();
  152. final json = jsonEncode(document);
  153. final directory = await getTemporaryDirectory();
  154. final path = directory.path;
  155. final file = File('$path/editor.json');
  156. await file.writeAsString(json);
  157. if (mounted) {
  158. ScaffoldMessenger.of(context).showSnackBar(
  159. SnackBar(
  160. content: Text('The document is saved to the ${file.path}'),
  161. ),
  162. );
  163. }
  164. }
  165. void _importDocument() async {
  166. final directory = await getTemporaryDirectory();
  167. final path = directory.path;
  168. final file = File('$path/editor.json');
  169. setState(() {
  170. _jsonString = file.readAsString();
  171. });
  172. }
  173. void _switchToPage(int pageIndex) {
  174. if (pageIndex != _pageIndex) {
  175. setState(() {
  176. _editorState = null;
  177. _pageIndex = pageIndex;
  178. });
  179. }
  180. }
  181. }