Skip to content

Commit

Permalink
feat: add fixed toolbar example (#739)
Browse files Browse the repository at this point in the history
* feat: add fixed toolbar example

* feat: highlight bold button if the selected text is all bold
  • Loading branch information
LucasXu0 authored Mar 7, 2024
1 parent 44861b6 commit 014abd7
Show file tree
Hide file tree
Showing 2 changed files with 247 additions and 0 deletions.
9 changes: 9 additions & 0 deletions example/lib/home_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:example/pages/customize_theme_for_editor.dart';
import 'package:example/pages/editor.dart';
import 'package:example/pages/editor_list.dart';
import 'package:example/pages/fixed_toolbar_editor.dart';
import 'package:example/pages/focus_example_for_editor.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/foundation.dart';
Expand Down Expand Up @@ -192,6 +193,14 @@ class _HomePageState extends State<HomePage> {
),
);
}),
_buildListTile(context, 'Fixed Toolbar', () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const FixedToolbarExample(),
),
);
}),

// Encoder Demo
_buildSeparator(context, 'Export To X Demo'),
Expand Down
238 changes: 238 additions & 0 deletions example/lib/pages/fixed_toolbar_editor.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
import 'dart:convert';

import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

class FixedToolbarExample extends StatefulWidget {
const FixedToolbarExample({super.key});

@override
State<FixedToolbarExample> createState() => _FixedToolbarExampleState();
}

class _FixedToolbarExampleState extends State<FixedToolbarExample> {
late final Future<EditorState> editorState;

@override
void initState() {
super.initState();

final jsonString = rootBundle.loadString('assets/example.json');
editorState = jsonString.then((value) {
return EditorState(
document: Document.fromJson(
Map<String, Object>.from(
json.decode(value),
),
),
);
});
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.black,
title: const Text('Fixed Toolbar Example'),
titleTextStyle: const TextStyle(color: Colors.white),
iconTheme: const IconThemeData(
color: Colors.white,
),
),
body: FutureBuilder(
future: editorState,
builder: (context, snapshot) {
return !snapshot.hasData
? const Center(child: CircularProgressIndicator())
: Column(
children: [
SizedBox(
height: 36,
child: _FixedToolbar(
editorState: snapshot.data!,
),
),
const Divider(),
FutureBuilder(
future: editorState,
builder: (context, snapshot) {
return !snapshot.hasData
? const Center(child: CircularProgressIndicator())
: Expanded(
child: AppFlowyEditor(
editorState: snapshot.data!,
),
);
},
),
],
);
},
),
);
}
}

class _FixedToolbar extends StatelessWidget {
const _FixedToolbar({
required this.editorState,
});

final EditorState editorState;

@override
Widget build(BuildContext context) {
final items = [
Icons.format_bold,
Icons.format_italic,
Icons.format_underlined,
Icons.format_strikethrough,
Icons.text_fields,
Icons.format_list_bulleted,
Icons.format_list_numbered,
Icons.format_align_left,
Icons.format_align_center,
Icons.format_align_right,
Icons.format_align_justify,
Icons.link,
Icons.image,
Icons.format_quote,
Icons.code,
Icons.horizontal_rule,
];

return ValueListenableBuilder(
valueListenable: editorState.selectionNotifier,
builder: (context, selection, _) {
return ListView.separated(
scrollDirection: Axis.horizontal,
itemBuilder: (_, index) {
final isBold = _isTextDecorationActive(
editorState,
selection,
AppFlowyRichTextKeys.bold,
);
return IconButton(
icon: Icon(items[index]),
color: items[index] == Icons.format_bold && isBold
? Colors.blue
: Colors.black,
onPressed: () {
debugPrint(items[index].toString());
switch (items[index]) {
case Icons.format_bold:
editorState.toggleAttribute(AppFlowyRichTextKeys.bold);
break;
case Icons.format_italic:
editorState.toggleAttribute(AppFlowyRichTextKeys.italic);
break;
case Icons.format_underlined:
editorState.toggleAttribute(AppFlowyRichTextKeys.underline);
break;
case Icons.format_strikethrough:
editorState
.toggleAttribute(AppFlowyRichTextKeys.strikethrough);
break;
case Icons.text_fields:
editorState.formatNode(null, (node) {
return node.copyWith(
type: ParagraphBlockKeys.type,
);
});
break;
case Icons.format_list_bulleted:
editorState.formatNode(null, (node) {
return node.copyWith(
type: node.type == BulletedListBlockKeys.type
? ParagraphBlockKeys.type
: BulletedListBlockKeys.type,
);
});
break;
case Icons.format_list_numbered:
editorState.formatNode(null, (node) {
return node.copyWith(
type: node.type == NumberedListBlockKeys.type
? ParagraphBlockKeys.type
: NumberedListBlockKeys.type,
);
});
break;
case Icons.format_align_left:
editorState.formatNode(null, (node) {
return node.copyWith(
attributes: {
...node.attributes,
blockComponentAlign: 'left',
},
);
});
break;
case Icons.format_align_center:
editorState.formatNode(null, (node) {
return node.copyWith(
attributes: {
...node.attributes,
blockComponentAlign: 'center',
},
);
});
break;
case Icons.format_align_right:
editorState.formatNode(null, (node) {
return node.copyWith(
attributes: {
...node.attributes,
blockComponentAlign: 'right',
},
);
});
break;
case Icons.horizontal_rule:
final selection = editorState.selection;
if (selection == null) {
return;
}
final transaction = editorState.transaction;
transaction.insertNode(
selection.start.path.next,
dividerNode(),
);
editorState.apply(transaction);
break;
default:
break;
}
},
);
},
separatorBuilder: (_, __) => const VerticalDivider(),
itemCount: items.length,
);
},
);
}

bool _isTextDecorationActive(
EditorState editorState,
Selection? selection,
String name,
) {
selection = selection ?? editorState.selection;
if (selection == null) {
return false;
}
final nodes = editorState.getNodesInSelection(selection);
if (selection.isCollapsed) {
return editorState.toggledStyle.containsKey(name);
} else {
return nodes.allSatisfyInSelection(selection, (delta) {
return delta.everyAttributes(
(attributes) => attributes[name] == true,
);
});
}
}
}

0 comments on commit 014abd7

Please sign in to comment.