feat: add more showcases on desktop/mobile platform (#481)

This commit is contained in:
Lucas.Xu 2023-09-20 11:17:39 +08:00 • committed by GitHub
commit 78dd254d1e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
33 changed files with 1187 additions and 434 deletions

View file

@ -0,0 +1,109 @@
{
"document": {
"type": "page",
"children": [
{
"type": "heading",
"data": {
"delta": [{ "insert": "👋 مرحبًا بك في محرر AppFlowy" }],
"level": 2,
"align": "center"
}
},
{ "type": "paragraph", "data": { "delta": [] } },
{
"type": "paragraph",
"data": {
"delta": [
{
"insert": "محرر AppFlowy هو محرر نصوص غني قابل للتخصيص بشكل كبير لـ Flutter"
}
]
}
},
{ "type": "paragraph", "data": { "delta": [] } },
{
"type": "bulleted_list",
"data": {
"delta": [
{
"insert": "قابل للتخصيص",
"attributes": { "bg_color": "0x4d2196f3" }
}
]
}
},
{
"type": "bulleted_list",
"data": {
"delta": [
{
"insert": "مُغطى بالاختبارات",
"attributes": { "bg_color": "0x4d2196f3" }
}
]
}
},
{
"type": "bulleted_list",
"data": {
"delta": [
{ "insert": "المزيد قادم قريبًا!", "attributes": { "code": true } }
]
}
},
{ "type": "paragraph", "data": { "delta": [] } },
{
"type": "heading",
"data": {
"delta": [{ "insert": "إليك مثال يمكنك تجربته" }],
"level": 4,
"align": "center"
}
},
{
"type": "todo_list",
"data": {
"checked": false,
"delta": [
{ "insert": "استخدم محرر AppFlowy كمكون لبناء تطبيقك الخاص." }
]
}
},
{
"type": "todo_list",
"data": {
"delta": [
{ "insert": "استخدم" },
{
"insert": " / ",
"attributes": { "code": true, "bg_color": "0x4d2196f3" }
},
{ "insert": "لإدراج الكتل." }
],
"checked": false
}
},
{
"type": "todo_list",
"data": {
"checked": false,
"delta": [
{ "insert": "حدد النص لتشغيل شريط الأدوات وتنسيق ملاحظاتك." }
]
}
},
{ "type": "paragraph", "data": { "delta": [] } },
{
"type": "paragraph",
"data": {
"delta": [
{
"insert": "إذا كانت لديك أسئلة أو ملاحظات، يرجى تقديم مشكلة على Github أو الانضمام إلى المجتمع مع أكثر من 1000 بنّاء!"
}
]
}
}
]
}
}

View file

@ -1,128 +1,85 @@
{
"document": {
"type": "page",
"children": [{
"children": [
{
"type": "heading",
"data": {
"level": 2,
"delta": [{
"insert": "👋 "
},
{
"insert": "Welcome to",
"attributes": {
"bold": true
}
},
{
"insert": " "
},
"delta": [
{ "insert": "👋 " },
{ "insert": "Welcome to", "attributes": { "bold": true } },
{ "insert": " " },
{
"insert": "AppFlowy Editor",
"attributes": {
"href": "appflowy.io",
"italic": true,
"bold": true
}
"attributes": { "italic": false, "bold": true }
}
]
],
"align": "center"
}
},
{
"type": "paragraph",
"data": {
"delta": []
}
},
{
"type": "paragraph",
"data": {
"delta": [{
"insert": "AppFlowy Editor is a"
},
{
"insert": " "
},
"delta": [
{ "insert": "AppFlowy Editor is a" },
{ "insert": " " },
{
"insert": "highly customizable",
"attributes": {
"bold": true
}
"attributes": { "bold": true, "bg_color": "0x7fffeb3b" }
},
{ "insert": " " },
{ "insert": "rich-text editor", "attributes": { "italic": true } },
{ "insert": " for " },
{ "insert": "Flutter", "attributes": { "underline": true } }
]
}
},
{
"type": "bulleted_list",
"data": {
"delta": [
{
"insert": " "
},
{
"insert": "rich-text editor",
"attributes": {
"italic": true
}
},
{
"insert": " for "
},
{
"insert": "Flutter",
"attributes": {
"underline": true
}
"insert": "Customizable",
"attributes": { "bg_color": "0x4d00BCF0" }
}
]
}
},
{
"type": "todo_list",
"type": "bulleted_list",
"data": {
"checked": true,
"delta": [{
"insert": "Customizable"
}]
"delta": [
{
"insert": "Test-covered",
"attributes": { "bg_color": "0x4d00BCF0" }
}
]
}
},
{
"type": "todo_list",
"type": "bulleted_list",
"data": {
"checked": true,
"delta": [{
"insert": "Test-covered"
}]
"delta": [
{ "insert": "More to come!", "attributes": { "code": true } }
]
}
},
{ "type": "paragraph", "data": { "delta": [] } },
{
"type": "heading",
"data": {
"delta": [{ "insert": "Here is an example you can give it a try" }],
"level": 4,
"align": "center"
}
},
{
"type": "todo_list",
"data": {
"checked": false,
"delta": [{
"insert": "more to come!"
}]
}
},
{
"type": "paragraph",
"data": {
"delta": []
}
},
{
"type": "quote",
"data": {
"delta": [{
"insert": "Here is an example you can give a try"
}]
}
},
{
"type": "paragraph",
"data": {
"delta": []
}
},
{
"type": "paragraph",
"data": {
"delta": [{
"insert": "You can also use "
},
"delta": [
{ "insert": "Use " },
{
"insert": "AppFlowy Editor",
"attributes": {
@ -132,49 +89,158 @@
"bg_color": "0x6000BCF0"
}
},
{
"insert": " as a component to build your own app."
}
{ "insert": " as a component to build your own app./" }
]
}
},
{
"type": "paragraph",
"type": "todo_list",
"data": {
"delta": []
"delta": [
{ "insert": "Use the " },
{
"insert": " / ",
"attributes": { "bg_color": "0x4d00BCF0", "code": true }
},
{ "insert": " to insert blocks." }
],
"checked": false
}
},
{
"type": "bulleted_list",
"type": "todo_list",
"data": {
"delta": [{
"insert": "Use / to insert blocks"
}]
"checked": false,
"delta": [
{ "insert": "Select text to " },
{
"insert": "trigger the toolbar",
"attributes": { "bg_color": "0x4de91e63" }
},
{ "insert": " to " },
{
"insert": "format",
"attributes": {
"code": true,
"bold": true,
"underline": true,
"italic": true,
"font_color": "0xfff44336",
"bg_color": "0x4d4caf50"
}
},
{ "insert": " your notes." }
]
}
},
{ "type": "paragraph", "data": { "delta": [] } },
{
"type": "bulleted_list",
"type": "table",
"children": [
{
"type": "table/cell",
"children": [
{
"type": "paragraph",
"data": {
"delta": [
{ "insert": "Insert a " },
{ "insert": "table", "attributes": { "bold": true } },
{ "insert": " via" },
{
"insert": " / ",
"attributes": { "code": true, "bg_color": "0x4d9c27b0" }
},
{ "insert": "command" }
],
"align": "center"
}
}
],
"data": {
"colPosition": 0,
"rowPosition": 0,
"height": 47.0,
"width": 553.09375
}
},
{
"type": "table/cell",
"children": [{ "type": "paragraph", "data": { "delta": [] } }],
"data": {
"colPosition": 0,
"rowPosition": 1,
"height": 46.0,
"width": 553.09375
}
},
{
"type": "table/cell",
"children": [
{
"type": "paragraph",
"data": {
"delta": [
{ "insert": "the " },
{
"insert": "contents",
"attributes": { "underline": true }
},
{ "insert": " in the table also support " },
{
"insert": "rich-text format",
"attributes": { "bold": true, "italic": true }
}
],
"align": "center"
}
}
],
"data": {
"colPosition": 1,
"rowPosition": 0,
"height": 47.0,
"width": 490.3984375,
"colBackgroundColor": "0x4dffeb3b"
}
},
{
"type": "table/cell",
"children": [{ "type": "paragraph", "data": { "delta": [] } }],
"data": {
"colPosition": 1,
"rowPosition": 1,
"height": 46.0,
"width": 490.3984375,
"colBackgroundColor": "0x4dffeb3b"
}
}
],
"data": {
"delta": [{
"insert": "Select text to trigger to the toolbar to format your notes."
}]
"colsLen": 2,
"rowsLen": 2,
"colDefaultWidth": 80.0,
"rowDefaultHeight": 40.0,
"colMinimumWidth": 40.0,
"colsHeight": 99.0
}
},
{ "type": "paragraph", "data": { "delta": [] } },
{
"type": "paragraph",
"data": {
"delta": []
}
},
{
"type": "paragraph",
"data": {
"delta": [{
"insert": "If you have questions or feedback, please submit an issue on Github or join the community along with 1000+ builders!"
}]
"delta": [
{ "insert": "If you have questions or feedback, please " },
{
"insert": "submit an issue",
"attributes": { "bg_color": "0x4de91e63" }
},
{
"insert": " on Github or join the community along with 1000+ builders!"
}
]
}
}
]
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 KiB

View file

@ -0,0 +1,140 @@
{
"document": {
"type": "page",
"children": [
{
"type": "heading",
"data": {
"level": 2,
"delta": [
{ "insert": "👋 " },
{ "insert": "Welcome to", "attributes": { "bold": true } },
{ "insert": " " },
{
"insert": "AppFlowy Editor",
"attributes": { "italic": false, "bold": true }
}
],
"align": "center"
}
},
{
"type": "paragraph",
"data": {
"delta": [
{ "insert": "AppFlowy Editor is a" },
{ "insert": " " },
{
"insert": "highly customizable",
"attributes": { "bold": true, "bg_color": "0x7fffeb3b" }
},
{ "insert": " " },
{ "insert": "rich-text editor", "attributes": { "italic": true } },
{ "insert": " for " },
{ "insert": "Flutter", "attributes": { "underline": true } }
]
}
},
{
"type": "bulleted_list",
"data": {
"delta": [
{
"insert": "Customizable",
"attributes": { "bg_color": "0x4d00BCF0" }
}
]
}
},
{
"type": "bulleted_list",
"data": {
"delta": [
{
"insert": "Test-covered",
"attributes": { "bg_color": "0x4d00BCF0" }
}
]
}
},
{
"type": "bulleted_list",
"data": {
"delta": [
{ "insert": "More to come!", "attributes": { "code": true } }
]
}
},
{ "type": "paragraph", "data": { "delta": [] } },
{
"type": "heading",
"data": {
"delta": [{ "insert": "Here is an example you can give it a try" }],
"level": 4,
"align": "center"
}
},
{
"type": "todo_list",
"data": {
"checked": false,
"delta": [
{ "insert": "Use " },
{
"insert": "AppFlowy Editor",
"attributes": {
"italic": true,
"bold": true,
"font_color": "0xffD70040",
"bg_color": "0x6000BCF0"
}
},
{ "insert": " as a component to build your own app." }
]
}
},
{
"type": "todo_list",
"data": {
"checked": false,
"delta": [
{ "insert": "Select text to " },
{
"insert": "trigger the toolbar",
"attributes": { "bg_color": "0x4de91e63" }
},
{ "insert": " to " },
{
"insert": "format",
"attributes": {
"code": true,
"bold": true,
"underline": true,
"italic": true,
"font_color": "0xfff44336",
"bg_color": "0x4d4caf50"
}
},
{ "insert": " your notes." }
]
}
},
{ "type": "paragraph", "data": { "delta": [] } },
{
"type": "paragraph",
"data": {
"delta": [
{ "insert": "If you have questions or feedback, please " },
{
"insert": "submit an issue",
"attributes": { "bg_color": "0x4de91e63" }
},
{
"insert": " on Github or join the community along with 1000+ builders!"
}
]
}
}
]
}
}

View file

@ -51,7 +51,9 @@ class _HomePageState extends State<HomePage> {
void initState() {
super.initState();
_jsonString = rootBundle.loadString('assets/example.json');
_jsonString = PlatformExtension.isDesktopOrWeb
? rootBundle.loadString('assets/example.json')
: rootBundle.loadString('assets/mobile_example.json');
_widgetBuilder = (context) => Editor(
jsonString: _jsonString,
@ -109,7 +111,9 @@ class _HomePageState extends State<HomePage> {
// AppFlowy Editor Demo
_buildSeparator(context, 'AppFlowy Editor Demo'),
_buildListTile(context, 'With Example.json', () {
final jsonString = rootBundle.loadString('assets/example.json');
final jsonString = PlatformExtension.isDesktopOrWeb
? rootBundle.loadString('assets/example.json')
: rootBundle.loadString('assets/mobile_example.json');
_loadEditor(context, jsonString);
}),
_buildListTile(context, 'With Large Document (10000+ lines)', () {
@ -159,6 +163,16 @@ class _HomePageState extends State<HomePage> {
),
);
}),
_buildListTile(context, 'RTL', () {
final jsonString = rootBundle.loadString(
'assets/arabic_example.json',
);
_loadEditor(
context,
jsonString,
textDirection: TextDirection.rtl,
);
}),
// Encoder Demo
_buildSeparator(context, 'Export To X Demo'),
@ -227,8 +241,9 @@ class _HomePageState extends State<HomePage> {
Future<void> _loadEditor(
BuildContext context,
Future<String> jsonString,
) async {
Future<String> jsonString, {
TextDirection textDirection = TextDirection.ltr,
}) async {
final completer = Completer<void>();
_jsonString = jsonString;
setState(
@ -238,6 +253,7 @@ class _HomePageState extends State<HomePage> {
onEditorStateChange: (editorState) {
_editorState = editorState;
},
textDirection: textDirection,
);
},
);

View file

@ -20,7 +20,10 @@ class _CustomizeThemeForEditorState extends State<CustomizeThemeForEditor> {
void initState() {
super.initState();
editorState = rootBundle.loadString('assets/example.json').then((value) {
final jsonString = PlatformExtension.isDesktopOrWeb
? rootBundle.loadString('assets/example.json')
: rootBundle.loadString('assets/mobile_example.json');
editorState = jsonString.then((value) {
return EditorState(
document: Document.fromJson(
Map<String, Object>.from(
@ -104,10 +107,15 @@ class _CustomizeThemeForEditorState extends State<CustomizeThemeForEditor> {
BulletedListBlockKeys.type: BulletedListBlockComponentBuilder(
configuration: configuration,
iconBuilder: (context, node) {
return const Icon(
Icons.circle,
size: 20,
color: Colors.green,
return Container(
width: 20,
height: 20,
alignment: Alignment.center,
child: const Icon(
Icons.circle,
size: 10,
color: Colors.red,
),
);
},
),

View file

@ -0,0 +1,180 @@
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
class DesktopEditor extends StatefulWidget {
const DesktopEditor({
super.key,
required this.editorState,
this.textDirection = TextDirection.ltr,
});
final EditorState editorState;
final TextDirection textDirection;
@override
State<DesktopEditor> createState() => _DesktopEditorState();
}
class _DesktopEditorState extends State<DesktopEditor> {
EditorState get editorState => widget.editorState;
late final EditorScrollController editorScrollController;
late EditorStyle editorStyle;
late Map<String, BlockComponentBuilder> blockComponentBuilders;
late List<CommandShortcutEvent> commandShortcuts;
@override
void initState() {
super.initState();
editorScrollController = EditorScrollController(
editorState: editorState,
shrinkWrap: false,
);
editorStyle = _buildDesktopEditorStyle();
blockComponentBuilders = _buildBlockComponentBuilders();
commandShortcuts = _buildCommandShortcuts();
}
@override
void reassemble() {
super.reassemble();
editorStyle = _buildDesktopEditorStyle();
blockComponentBuilders = _buildBlockComponentBuilders();
commandShortcuts = _buildCommandShortcuts();
}
@override
Widget build(BuildContext context) {
assert(PlatformExtension.isDesktopOrWeb);
return FloatingToolbar(
items: [
paragraphItem,
...headingItems,
...markdownFormatItems,
quoteItem,
bulletedListItem,
numberedListItem,
linkItem,
buildTextColorItem(),
buildHighlightColorItem(),
...textDirectionItems,
...alignmentItems,
],
editorState: editorState,
editorScrollController: editorScrollController,
child: Directionality(
textDirection: widget.textDirection,
child: AppFlowyEditor(
editorState: editorState,
editorScrollController: editorScrollController,
blockComponentBuilders: blockComponentBuilders,
commandShortcutEvents: commandShortcuts,
editorStyle: editorStyle,
header: Padding(
padding: const EdgeInsets.only(bottom: 10.0),
child: Image.asset(
'assets/images/header.png',
fit: BoxFit.fitWidth,
height: 150,
),
),
footer: const SizedBox(
height: 100,
),
),
),
);
}
// showcase 1: customize the editor style.
EditorStyle _buildDesktopEditorStyle() {
return EditorStyle.desktop(
cursorColor: Colors.blue,
selectionColor: Colors.grey.shade300,
textStyleConfiguration: TextStyleConfiguration(
text: GoogleFonts.poppins(
fontSize: 16,
color: Colors.black,
),
code: GoogleFonts.architectsDaughter(),
bold: GoogleFonts.poppins(
fontWeight: FontWeight.w500,
),
),
padding: const EdgeInsets.symmetric(horizontal: 200.0),
);
}
// showcase 2: customize the block style
Map<String, BlockComponentBuilder> _buildBlockComponentBuilders() {
final map = {
...standardBlockComponentBuilderMap,
};
// customize the image block component to show a menu
map[ImageBlockKeys.type] = ImageBlockComponentBuilder(
showMenu: true,
menuBuilder: (node, _) {
return const Positioned(
right: 10,
child: Text('⭐️ Here is a menu!'),
);
},
);
// customize the heading block component
final levelToFontSize = [
30.0,
26.0,
22.0,
18.0,
16.0,
14.0,
];
map[HeadingBlockKeys.type] = HeadingBlockComponentBuilder(
textStyleBuilder: (level) => GoogleFonts.poppins(
fontSize: levelToFontSize.elementAtOrNull(level - 1) ?? 14.0,
fontWeight: FontWeight.w600,
),
);
// customize the padding
map.forEach((key, value) {
value.configuration = BlockComponentConfiguration(
padding: (_) => const EdgeInsets.symmetric(vertical: 8.0),
);
});
return map;
}
// showcase 3: customize the command shortcuts
List<CommandShortcutEvent> _buildCommandShortcuts() {
return [
// customize the highlight color
customToggleHighlightCommand(
style: ToggleColorsStyle(
highlightColor: Colors.orange.shade700,
),
),
...[
...standardCommandShortcutEvents
..removeWhere(
(el) => el == toggleHighlightCommand,
),
],
...findAndReplaceCommands(
context: context,
localizations: FindReplaceLocalizations(
find: 'Find',
previousMatch: 'Previous match',
nextMatch: 'Next match',
close: 'Close',
replace: 'Replace',
replaceAll: 'Replace all',
),
),
];
}
}

View file

@ -1,6 +1,7 @@
import 'dart:convert';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:example/pages/desktop_editor.dart';
import 'package:example/pages/mobile_editor.dart';
import 'package:flutter/material.dart';
@ -10,122 +11,57 @@ class Editor extends StatelessWidget {
required this.jsonString,
required this.onEditorStateChange,
this.editorStyle,
this.textDirection = TextDirection.ltr,
});
final Future<String> jsonString;
final EditorStyle? editorStyle;
final void Function(EditorState editorState) onEditorStateChange;
final TextDirection textDirection;
@override
Widget build(BuildContext context) {
return FutureBuilder<String>(
future: jsonString,
builder: (context, snapshot) {
if (snapshot.hasData &&
snapshot.connectionState == ConnectionState.done) {
final editorState = EditorState(
document: Document.fromJson(
Map<String, Object>.from(
json.decode(snapshot.data!),
),
),
);
editorState.logConfiguration
..handler = debugPrint
..level = LogLevel.off;
editorState.transactionStream.listen((event) {
if (event.$1 == TransactionTime.after) {
onEditorStateChange(editorState);
}
});
final editorScrollController = EditorScrollController(
editorState: editorState,
shrinkWrap: false,
);
if (PlatformExtension.isDesktopOrWeb) {
return FloatingToolbar(
items: [
paragraphItem,
...headingItems,
...markdownFormatItems,
quoteItem,
bulletedListItem,
numberedListItem,
linkItem,
buildTextColorItem(),
buildHighlightColorItem(),
...textDirectionItems,
...alignmentItems,
],
editorState: editorState,
editorScrollController: editorScrollController,
child: _buildDesktopEditor(
context,
editorState,
editorScrollController,
return Container(
color: Colors.white,
child: FutureBuilder<String>(
future: jsonString,
builder: (context, snapshot) {
if (snapshot.hasData &&
snapshot.connectionState == ConnectionState.done) {
final editorState = EditorState(
document: Document.fromJson(
Map<String, Object>.from(
json.decode(snapshot.data!),
),
),
);
} else if (PlatformExtension.isMobile) {
return MobileEditor(
editorState: editorState,
onEditorStateChange: onEditorStateChange,
);
}
}
return const Center(
child: CircularProgressIndicator(),
);
},
);
}
Widget _buildDesktopEditor(
BuildContext context,
EditorState editorState,
EditorScrollController? editorScrollController,
) {
final customBlockComponentBuilders = {
...standardBlockComponentBuilderMap,
ImageBlockKeys.type: ImageBlockComponentBuilder(
showMenu: true,
menuBuilder: (node, _) {
return const Positioned(
right: 10,
child: Text('Sample Menu'),
);
editorState.logConfiguration
..handler = debugPrint
..level = LogLevel.off;
editorState.transactionStream.listen((event) {
if (event.$1 == TransactionTime.after) {
onEditorStateChange(editorState);
}
});
if (PlatformExtension.isDesktopOrWeb) {
return DesktopEditor(
editorState: editorState,
textDirection: textDirection,
);
} else if (PlatformExtension.isMobile) {
return MobileEditor(
editorState: editorState,
);
}
}
return const SizedBox.shrink();
},
),
};
return AppFlowyEditor(
editorState: editorState,
shrinkWrap: true,
editorScrollController: editorScrollController,
blockComponentBuilders: customBlockComponentBuilders,
commandShortcutEvents: [
customToggleHighlightCommand(
style: ToggleColorsStyle(
highlightColor: Theme.of(context).highlightColor,
),
),
...[
...standardCommandShortcutEvents
..removeWhere(
(el) => el == toggleHighlightCommand,
),
],
...findAndReplaceCommands(
context: context,
localizations: FindReplaceLocalizations(
find: 'Find',
previousMatch: 'Previous match',
nextMatch: 'Next match',
close: 'Close',
replace: 'Replace',
replaceAll: 'Replace all',
),
),
],
characterShortcutEvents: standardCharacterShortcutEvents,
);
}
}

View file

@ -6,13 +6,11 @@ class MobileEditor extends StatefulWidget {
const MobileEditor({
super.key,
required this.editorState,
required this.onEditorStateChange,
this.editorStyle,
});
final EditorState editorState;
final EditorStyle? editorStyle;
final void Function(EditorState editorState) onEditorStateChange;
@override
State<MobileEditor> createState() => _MobileEditorState();
@ -30,18 +28,6 @@ class _MobileEditorState extends State<MobileEditor> {
void initState() {
super.initState();
// customize the log configuration
editorState.logConfiguration
..handler = debugPrint
..level = LogLevel.off;
// listen to editor state change
editorState.transactionStream.listen((event) {
if (event.$1 == TransactionTime.after) {
widget.onEditorStateChange(editorState);
}
});
editorScrollController = EditorScrollController(
editorState: editorState,
shrinkWrap: false,
@ -61,23 +47,29 @@ class _MobileEditorState extends State<MobileEditor> {
@override
Widget build(BuildContext context) {
assert(PlatformExtension.isMobile);
return Column(
children: [
// build appflowy editor
Expanded(
child: AppFlowyEditor(
editorStyle: editorStyle,
child: MobileFloatingToolbar(
editorState: editorState,
editorScrollController: editorScrollController,
blockComponentBuilders: blockComponentBuilders,
// showcase 3: customize the header and footer.
header: Image.asset(
'assets/images/icon.jpeg',
fit: BoxFit.fitWidth,
height: 100,
),
footer: const SizedBox(
height: 100,
child: AppFlowyEditor(
editorStyle: editorStyle,
editorState: editorState,
editorScrollController: editorScrollController,
blockComponentBuilders: blockComponentBuilders,
// showcase 3: customize the header and footer.
header: Padding(
padding: const EdgeInsets.only(bottom: 10.0),
child: Image.asset(
'assets/images/header.png',
),
),
footer: const SizedBox(
height: 100,
),
),
),
),
@ -112,7 +104,7 @@ class _MobileEditorState extends State<MobileEditor> {
),
code: GoogleFonts.badScript(),
),
padding: const EdgeInsets.symmetric(horizontal: 14.0),
padding: const EdgeInsets.symmetric(horizontal: 24.0),
);
}

View file

@ -67,9 +67,9 @@ flutter:
# To add assets to your application, add an assets section, like this:
assets:
- assets/example.json
- assets/example.html
- assets/images/icon.jpeg
- assets/
- assets/
- assets/images/
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware

View file

@ -44,6 +44,11 @@ class BlockSelectionContainer extends StatelessWidget {
Widget build(BuildContext context) {
return Stack(
clipBehavior: Clip.none,
// In RTL mode, if the alignment is topStart,
// the selection will be on the opposite side of the block component.
alignment: Directionality.of(context) == TextDirection.ltr
? AlignmentDirectional.topStart
: AlignmentDirectional.topEnd,
children: [
BlockSelectionArea(
node: node,

View file

@ -117,8 +117,15 @@ class SelectionAreaPainter extends CustomPainter {
..color = selectionColor
..style = PaintingStyle.fill;
for (final rect in rects) {
canvas.drawRect(rect, paint);
for (var rect in rects) {
// if rect.width is 0, we draw a small rect to indicate the selection area
if (rect.width <= 0) {
rect = Rect.fromLTWH(rect.left, rect.top, 8.0, rect.height);
}
canvas.drawRect(
rect,
paint,
);
}
}

View file

@ -36,13 +36,10 @@ Node bulletedListNode({
class BulletedListBlockComponentBuilder extends BlockComponentBuilder {
BulletedListBlockComponentBuilder({
this.configuration = const BlockComponentConfiguration(),
super.configuration,
this.iconBuilder,
});
@override
final BlockComponentConfiguration configuration;
final BlockIconBuilder? iconBuilder;
@override

View file

@ -17,14 +17,11 @@ Node dividerNode() {
class DividerBlockComponentBuilder extends BlockComponentBuilder {
DividerBlockComponentBuilder({
this.configuration = const BlockComponentConfiguration(),
super.configuration,
this.lineColor = Colors.grey,
this.height = 10,
});
@override
final BlockComponentConfiguration configuration;
final Color lineColor;
final double height;

View file

@ -40,13 +40,10 @@ Node headingNode({
class HeadingBlockComponentBuilder extends BlockComponentBuilder {
HeadingBlockComponentBuilder({
this.configuration = const BlockComponentConfiguration(),
super.configuration,
this.textStyleBuilder,
});
@override
final BlockComponentConfiguration configuration;
/// The text style of the heading block.
final TextStyle Function(int level)? textStyleBuilder;

View file

@ -56,14 +56,11 @@ typedef ImageBlockComponentMenuBuilder = Widget Function(
class ImageBlockComponentBuilder extends BlockComponentBuilder {
ImageBlockComponentBuilder({
this.configuration = const BlockComponentConfiguration(),
super.configuration,
this.showMenu = false,
this.menuBuilder,
});
@override
final BlockComponentConfiguration configuration;
/// Whether to show the menu of this block component.
final bool showMenu;

View file

@ -41,13 +41,10 @@ Node numberedListNode({
class NumberedListBlockComponentBuilder extends BlockComponentBuilder {
NumberedListBlockComponentBuilder({
this.configuration = const BlockComponentConfiguration(),
super.configuration,
this.iconBuilder,
});
@override
final BlockComponentConfiguration configuration;
final BlockIconBuilder? iconBuilder;
@override

View file

@ -34,13 +34,10 @@ Node quoteNode({
class QuoteBlockComponentBuilder extends BlockComponentBuilder {
QuoteBlockComponentBuilder({
this.configuration = const BlockComponentConfiguration(),
super.configuration,
this.iconBuilder,
});
@override
final BlockComponentConfiguration configuration;
final BlockIconBuilder? iconBuilder;
@override

View file

@ -58,16 +58,13 @@ typedef TableBlockComponentMenuBuilder = Widget Function(
class TableBlockComponentBuilder extends BlockComponentBuilder {
TableBlockComponentBuilder({
this.configuration = const BlockComponentConfiguration(),
super.configuration,
this.addIcon = TableDefaults.addIcon,
this.borderColor = TableDefaults.borderColor,
this.borderHoverColor = TableDefaults.borderHoverColor,
this.menuBuilder,
});
@override
final BlockComponentConfiguration configuration;
final Widget addIcon;
final TableBlockComponentMenuBuilder? menuBuilder;

View file

@ -24,13 +24,10 @@ class TableCellBlockKeys {
class TableCellBlockComponentBuilder extends BlockComponentBuilder {
TableCellBlockComponentBuilder({
this.configuration = const BlockComponentConfiguration(),
super.configuration,
this.menuBuilder,
});
@override
final BlockComponentConfiguration configuration;
final TableBlockComponentMenuBuilder? menuBuilder;
@override

Some files were not shown because too many files have changed in this diff Show more