Flutter Quill 富文本编辑器:从入门到精通
Flutter Quill 是一个专为 Flutter 设计的富文本编辑器组件,适用于现代的 Android、iOS、Web 以及桌面平台。该编辑器提供了一个直观的 WYSIWYG(所见即所得)界面,让用户能够轻松创作含有编号列表、项目符号列表、超链接等丰富格式的长篇文章。
项目安装与配置
环境要求
确保你的开发环境已经配置好 Flutter,然后创建一个新的 Flutter 项目:
flutter create my_text_editor_app
cd my_text_editor_app
添加依赖
将 Flutter Quill 添加到你的 pubspec.yaml 文件中:
dependencies:
flutter:
sdk: flutter
flutter_quill: ^latest_version
替换 latest_version 为你实际查找得到的最新版本号。之后,运行 flutter pub get 来安装依赖。
平台特定配置
对于 Android 平台,需要配置以下内容以支持图像复制到剪贴板:
1. 更新 AndroidManifest.xml
在 android/app/src/main/AndroidManifest.xml 的 <application> 标签内添加:
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
2. 创建 file_paths.xml
创建文件 android/app/src/main/res/xml/file_paths.xml 并添加以下内容:
<paths>
<cache-path name="cache" path="." />
</paths>
基础使用教程
初始化应用
在 lib/main.dart 中,初始化并使用 Flutter Quill:
import 'package:flutter/material.dart';
import 'package:flutter_quill/flutter_quill.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
void main() => runApp(const MainApp());
class MainApp extends StatelessWidget {
const MainApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData.light(useMaterial3: true),
darkTheme: ThemeData.dark(useMaterial3: true),
themeMode: ThemeMode.system,
home: HomePage(),
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
FlutterQuillLocalizations.delegate,
],
);
}
}
创建控制器和界面
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final QuillController _controller = () {
return QuillController.basic(
config: QuillControllerConfig(
clipboardConfig: QuillClipboardConfig(
enableExternalRichPaste: true,
onImagePaste: (imageBytes) async {
if (kIsWeb) {
return null;
}
final newFileName = 'image-file-${DateTime.now().toIso8601String()}.png';
final newPath = path.join(
io.Directory.systemTemp.path,
newFileName,
);
final file = await io.File(
newPath,
).writeAsBytes(imageBytes, flush: true);
return file.path;
},
),
);
}();
final FocusNode _editorFocusNode = FocusNode();
final ScrollController _editorScrollController = ScrollController();
@override
void initState() {
super.initState();
_controller.document = Document.fromJson(kQuillDefaultSample);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Flutter Quill Example'),
actions: [
IconButton(
icon: const Icon(Icons.output),
tooltip: 'Print Delta JSON to log',
onPressed: () {
debugPrint(jsonEncode(_controller.document.toDelta().toJson()));
},
),
],
),
body: SafeArea(
child: Column(
children: [
QuillSimpleToolbar(
controller: _controller,
config: QuillSimpleToolbarConfig(
embedButtons: FlutterQuillEmbeds.toolbarButtons(),
showClipboardPaste: true,
customButtons: [
QuillToolbarCustomButtonOptions(
icon: const Icon(Icons.add_alarm_rounded),
onPressed: () {
_controller.document.insert(
_controller.selection.extentOffset,
TimeStampEmbed(
DateTime.now().toString(),
),
);
_controller.updateSelection(
TextSelection.collapsed(
offset: _controller.selection.extentOffset + 1,
),
ChangeSource.local,
);
},
),
],
buttonOptions: QuillSimpleToolbarButtonOptions(
base: QuillToolbarBaseButtonOptions(
afterButtonPressed: () {
final isDesktop = {
TargetPlatform.linux,
TargetPlatform.windows,
TargetPlatform.macOS
}.contains(defaultTargetPlatform);
if (isDesktop) {
_editorFocusNode.requestFocus();
}
},
),
linkStyle: QuillToolbarLinkStyleButtonOptions(
validateLink: (link) {
return true;
},
),
),
),
),
Expanded(
child: QuillEditor(
focusNode: _editorFocusNode,
scrollController: _editorScrollController,
controller: _controller,
config: QuillEditorConfig(
placeholder: 'Start writing your notes...',
padding: const EdgeInsets.all(16),
embedBuilders: [
...FlutterQuillEmbeds.editorBuilders(
imageEmbedConfig: QuillEditorImageEmbedConfig(
imageProviderBuilder: (context, imageUrl) {
if (imageUrl.startsWith('assets/')) {
return AssetImage(imageUrl);
}
return null;
},
),
videoEmbedConfig: QuillEditorVideoEmbedConfig(
customVideoBuilder: (videoUrl, readOnly) {
return null;
},
),
),
TimeStampEmbedBuilder(),
],
),
),
),
],
),
),
);
}
@override
void dispose() {
_controller.dispose();
_editorScrollController.dispose();
_editorFocusNode.dispose();
super.dispose();
}
}
自定义嵌入块
Flutter Quill 提供了自定义嵌入块的接口,允许用户提供自己的实现:
class TimeStampEmbed extends Embeddable {
const TimeStampEmbed(
String value,
) : super(timeStampType, value);
static const String timeStampType = 'timeStamp';
static TimeStampEmbed fromDocument(Document document) =>
TimeStampEmbed(jsonEncode(document.toDelta().toJson()));
Document get document => Document.fromJson(jsonDecode(data));
}
class TimeStampEmbedBuilder extends EmbedBuilder {
@override
String get key => 'timeStamp';
@override
String toPlainText(Embed node) {
return node.value.data;
}
@override
Widget build(
BuildContext context,
EmbedContext embedContext,
) {
return Row(
children: [
const Icon(Icons.access_time_rounded),
Text(embedContext.node.value.data as String),
],
);
}
}
高级功能与配置
富文本粘贴功能
Flutter Quill 支持从其他应用复制内容并粘贴到编辑器中作为富文本。该功能通过 quill_native_bridge 插件提供对系统剪贴板的访问。
工具栏自定义配置
QuillSimpleToolbar(
controller: _controller,
configurations: QuillSimpleToolbarConfigurations(
toolbarOptions: ToolbarOptions(
fonts: ['Arial', 'Times New Roman'],
formats: [
'bold',
'italic',
'underline',
'strikeThrough',
'header',
'list',
'bullet'
],
),
),
),
数据输入输出
Flutter Quill 使用 Quill Delta 格式来表示文档内容。Delta 格式是一种紧凑而多功能的方法,通过一系列表示插入、删除或格式更改的操作来描述文档更改。
保存文档:
final String json = jsonEncode(_controller.document.toDelta().toJson());
加载文档:
final String json = ...; // 加载先前存储的 JSON Quill Delta
_controller.document = Document.fromJson(jsonDecode(json));
更改只读模式:
_controller.readOnly = true; // 或 false 允许编辑
实际应用案例
编辑器界面截图
自定义字体配置
要使用自己的字体,需要更新你的资源目录并将 items 传递给 QuillToolbarFontFamilyButton 的选项。确保正确配置字体文件路径和字体族名称。
最佳实践建议
- 控制器管理:始终在
dispose方法中正确释放控制器资源 - 文档存储:建议将文档存储为 Delta JSON 格式而非其他格式
- 平台适配:针对不同平台进行适当的配置和测试
- 性能优化:对于大型文档,考虑使用分页加载或虚拟滚动
注意事项
- 目前富文本粘贴功能在 Web 平台上暂不支持
- 从 Delta 转换到 HTML 不是标准功能,建议谨慎使用
- 存储 Delta 作为 HTML 在数据库中,然后在加载文档时将其转换回 Delta 是不推荐的
通过遵循本指南,你将能够快速上手 Flutter Quill 并构建功能强大的富文本编辑应用。该库提供了丰富的定制选项,可以满足各种复杂的文本编辑需求。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考






