2022-11-09 11:37:26 +08:00
|
|
|
import 'dart:convert';
|
|
|
|
|
|
2024-09-11 17:51:53 +08:00
|
|
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
2023-09-20 11:17:39 +08:00
|
|
|
import 'package:example/pages/desktop_editor.dart';
|
2023-09-18 22:05:17 +08:00
|
|
|
import 'package:example/pages/mobile_editor.dart';
|
2024-09-11 17:51:53 +08:00
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
|
import 'package:universal_platform/universal_platform.dart';
|
2022-11-09 11:37:26 +08:00
|
|
|
|
2023-12-08 14:39:18 +07:00
|
|
|
class Editor extends StatefulWidget {
|
2023-07-07 16:07:47 +08:00
|
|
|
const Editor({
|
2022-11-09 11:37:26 +08:00
|
|
|
super.key,
|
|
|
|
|
required this.jsonString,
|
|
|
|
|
required this.onEditorStateChange,
|
2023-06-23 18:36:33 +08:00
|
|
|
this.editorStyle,
|
2023-09-20 11:17:39 +08:00
|
|
|
this.textDirection = TextDirection.ltr,
|
2022-11-09 11:37:26 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
final Future<String> jsonString;
|
2023-06-23 18:36:33 +08:00
|
|
|
final EditorStyle? editorStyle;
|
2022-11-09 11:37:26 +08:00
|
|
|
final void Function(EditorState editorState) onEditorStateChange;
|
|
|
|
|
|
2023-09-20 11:17:39 +08:00
|
|
|
final TextDirection textDirection;
|
|
|
|
|
|
2023-12-08 14:39:18 +07:00
|
|
|
@override
|
|
|
|
|
State<Editor> createState() => _EditorState();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class _EditorState extends State<Editor> {
|
2024-02-07 16:30:27 +01:00
|
|
|
bool isInitialized = false;
|
|
|
|
|
|
2023-12-08 14:39:18 +07:00
|
|
|
EditorState? editorState;
|
2024-02-07 16:30:27 +01:00
|
|
|
WordCountService? wordCountService;
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
void didUpdateWidget(covariant Editor oldWidget) {
|
|
|
|
|
if (oldWidget.jsonString != widget.jsonString) {
|
|
|
|
|
editorState = null;
|
|
|
|
|
isInitialized = false;
|
|
|
|
|
}
|
|
|
|
|
super.didUpdateWidget(oldWidget);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int wordCount = 0;
|
|
|
|
|
int charCount = 0;
|
|
|
|
|
|
2024-02-08 10:46:45 +01:00
|
|
|
int selectedWordCount = 0;
|
|
|
|
|
int selectedCharCount = 0;
|
|
|
|
|
|
2024-02-07 16:30:27 +01:00
|
|
|
void registerWordCounter() {
|
|
|
|
|
wordCountService?.removeListener(onWordCountUpdate);
|
|
|
|
|
wordCountService?.dispose();
|
|
|
|
|
|
|
|
|
|
wordCountService = WordCountService(editorState: editorState!)..register();
|
|
|
|
|
wordCountService!.addListener(onWordCountUpdate);
|
|
|
|
|
|
|
|
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
|
|
|
onWordCountUpdate();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void onWordCountUpdate() {
|
|
|
|
|
setState(() {
|
2024-02-08 10:46:45 +01:00
|
|
|
wordCount = wordCountService!.documentCounters.wordCount;
|
|
|
|
|
charCount = wordCountService!.documentCounters.charCount;
|
|
|
|
|
selectedWordCount = wordCountService!.selectionCounters.wordCount;
|
|
|
|
|
selectedCharCount = wordCountService!.selectionCounters.charCount;
|
2024-02-07 16:30:27 +01:00
|
|
|
});
|
|
|
|
|
}
|
2023-12-08 14:39:18 +07:00
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
void dispose() {
|
|
|
|
|
editorState?.dispose();
|
|
|
|
|
super.dispose();
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-09 11:37:26 +08:00
|
|
|
@override
|
|
|
|
|
Widget build(BuildContext context) {
|
2024-02-07 16:30:27 +01:00
|
|
|
return Stack(
|
|
|
|
|
children: [
|
|
|
|
|
ColoredBox(
|
|
|
|
|
color: Colors.white,
|
|
|
|
|
child: FutureBuilder<String>(
|
|
|
|
|
future: widget.jsonString,
|
|
|
|
|
builder: (context, snapshot) {
|
|
|
|
|
if (snapshot.hasData &&
|
|
|
|
|
snapshot.connectionState == ConnectionState.done) {
|
|
|
|
|
if (!isInitialized || editorState == null) {
|
|
|
|
|
isInitialized = true;
|
|
|
|
|
EditorState editorState = EditorState(
|
|
|
|
|
document: Document.fromJson(
|
|
|
|
|
Map<String, Object>.from(
|
|
|
|
|
json.decode(snapshot.data!),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
editorState.logConfiguration
|
|
|
|
|
..handler = debugPrint
|
2025-10-27 14:49:14 +08:00
|
|
|
..level = AppFlowyEditorLogLevel.off;
|
2024-02-07 16:30:27 +01:00
|
|
|
|
|
|
|
|
editorState.transactionStream.listen((event) {
|
|
|
|
|
if (event.$1 == TransactionTime.after) {
|
|
|
|
|
widget.onEditorStateChange(editorState);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2024-03-19 16:38:14 +07:00
|
|
|
widget.onEditorStateChange(editorState);
|
|
|
|
|
|
2024-02-07 16:30:27 +01:00
|
|
|
this.editorState = editorState;
|
|
|
|
|
registerWordCounter();
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-11 17:51:53 +08:00
|
|
|
if (UniversalPlatform.isDesktopOrWeb) {
|
2024-02-07 16:30:27 +01:00
|
|
|
return DesktopEditor(
|
|
|
|
|
editorState: editorState!,
|
|
|
|
|
textDirection: widget.textDirection,
|
|
|
|
|
);
|
2024-09-11 17:51:53 +08:00
|
|
|
} else if (UniversalPlatform.isMobile) {
|
2024-02-07 16:30:27 +01:00
|
|
|
return MobileEditor(editorState: editorState!);
|
|
|
|
|
}
|
2023-09-20 11:17:39 +08:00
|
|
|
}
|
2024-02-07 16:30:27 +01:00
|
|
|
|
|
|
|
|
return const SizedBox.shrink();
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
Positioned(
|
|
|
|
|
bottom: 0,
|
|
|
|
|
right: 0,
|
|
|
|
|
child: Container(
|
|
|
|
|
padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 12),
|
|
|
|
|
decoration: BoxDecoration(
|
2025-01-17 10:46:47 +08:00
|
|
|
color: Colors.black.withValues(alpha: 0.1),
|
2024-02-07 16:30:27 +01:00
|
|
|
borderRadius: BorderRadius.only(
|
|
|
|
|
topLeft: const Radius.circular(8),
|
2024-09-11 17:51:53 +08:00
|
|
|
bottomLeft: UniversalPlatform.isMobile
|
2024-02-07 16:30:27 +01:00
|
|
|
? const Radius.circular(8)
|
|
|
|
|
: Radius.zero,
|
|
|
|
|
),
|
|
|
|
|
),
|
2024-02-08 10:46:45 +01:00
|
|
|
child: Column(
|
|
|
|
|
children: [
|
|
|
|
|
Text(
|
|
|
|
|
'Word Count: $wordCount | Character Count: $charCount',
|
|
|
|
|
style: const TextStyle(fontSize: 11),
|
|
|
|
|
),
|
|
|
|
|
if (!(editorState?.selection?.isCollapsed ?? true))
|
|
|
|
|
Text(
|
|
|
|
|
'(In-selection) Word Count: $selectedWordCount | Character Count: $selectedCharCount',
|
|
|
|
|
style: const TextStyle(fontSize: 11),
|
|
|
|
|
),
|
|
|
|
|
],
|
2024-02-07 16:30:27 +01:00
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
2023-04-20 13:45:46 +08:00
|
|
|
);
|
|
|
|
|
}
|
2022-11-09 11:37:26 +08:00
|
|
|
}
|