Flutter富文本编辑器插件quill_html_editor_v2的使用

发布于 1周前 作者 yuanlaile 来自 Flutter

Flutter富文本编辑器插件quill_html_editor_v2的使用

简介

quill_html_editor_v2 是一个功能强大的HTML富文本编辑器,适用于Android、iOS和Web平台。它基于QuillJs库,提供了丰富的编辑体验。该插件包括了许多更新和改进,以增强功能和性能。

功能特性

  • 高度可定制的编辑器和工具栏:您可以根据需要自定义编辑器和工具栏的外观和行为。
  • 支持Delta格式:您可以使用setDeltagetDelta方法设置和获取内容。
  • 无缝复制粘贴:支持从其他文件或网页中复制粘贴富文本。
  • 可移动的工具栏:工具栏可以放置在页面的任何位置,以满足您的需求。
  • 自定义按钮:您可以为工具栏添加自定义按钮。
  • 嵌入图片、视频和插入表格:支持多媒体内容的嵌入。
  • HTML和Delta格式的文本设置与获取:您可以设置和获取HTML和Delta格式的文本。
  • 集成Google Fonts:提供多种字体选项。

示例图片

1 2

3 4

使用示例

以下是一个完整的示例代码,展示了如何使用quill_html_editor_v2插件创建一个富文本编辑器。

import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:quill_html_editor_v2/quill_html_editor.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(const MaterialApp(debugShowCheckedModeBanner: false, home: MyApp()));
}

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

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  /// 创建一个 QuillEditorController 以访问编辑器方法
  late QuillEditorController controller;

  /// 定义自定义工具栏列表,仅显示选定的样式
  final customToolBarList = [
    ToolBarStyle.bold,
    ToolBarStyle.italic,
    ToolBarStyle.align,
    ToolBarStyle.color,
    ToolBarStyle.background,
    ToolBarStyle.listBullet,
    ToolBarStyle.listOrdered,
    ToolBarStyle.clean,
    ToolBarStyle.addTable,
    ToolBarStyle.editTable,
  ];

  final _toolbarColor = Colors.grey.shade200;
  final _backgroundColor = Colors.white70;
  final _toolbarIconColor = Colors.black87;
  final _editorTextStyle = const TextStyle(
      fontSize: 18,
      color: Colors.black,
      fontWeight: FontWeight.normal,
      fontFamily: 'Roboto');
  final _hintTextStyle = const TextStyle(
      fontSize: 18, color: Colors.black38, fontWeight: FontWeight.normal);

  bool _hasFocus = false;

  @override
  void initState() {
    controller = QuillEditorController();
    controller.onTextChanged((text) {
      debugPrint('listening to $text');
    });
    controller.onEditorLoaded(() {
      debugPrint('Editor Loaded :)');
    });
    super.initState();
  }

  @override
  void dispose() {
    /// 请不要忘记释放控制器
    controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Scaffold(
        backgroundColor: Colors.white,
        resizeToAvoidBottomInset: true,
        body: Column(
          children: [
            ToolBar(
              toolBarColor: _toolbarColor,
              padding: const EdgeInsets.all(8),
              iconSize: 25,
              iconColor: _toolbarIconColor,
              activeIconColor: Colors.greenAccent.shade400,
              controller: controller,
              crossAxisAlignment: WrapCrossAlignment.start,
              direction: Axis.horizontal,
              customButtons: [
                Container(
                  width: 25,
                  height: 25,
                  decoration: BoxDecoration(
                      color: _hasFocus ? Colors.green : Colors.grey,
                      borderRadius: BorderRadius.circular(15)),
                ),
                InkWell(
                    onTap: () => unFocusEditor(),
                    child: const Icon(
                      Icons.favorite,
                      color: Colors.black,
                    )),
                InkWell(
                    onTap: () async {
                      var selectedText = await controller.getSelectedText();
                      debugPrint('selectedText $selectedText');
                      var selectedHtmlText =
                          await controller.getSelectedHtmlText();
                      debugPrint('selectedHtmlText $selectedHtmlText');
                    },
                    child: const Icon(
                      Icons.add_circle,
                      color: Colors.black,
                    )),
              ],
            ),
            Expanded(
              child: QuillHtmlEditor(
                text: "<h1>Hello</h1>This is a quill html editor example 😊",
                hintText: 'Hint text goes here',
                controller: controller,
                isEnabled: true,
                ensureVisible: false,
                minHeight: 500,
                autoFocus: false,
                textStyle: _editorTextStyle,
                hintTextStyle: _hintTextStyle,
                hintTextAlign: TextAlign.start,
                padding: const EdgeInsets.only(left: 10, top: 10),
                hintTextPadding: const EdgeInsets.only(left: 20),
                backgroundColor: _backgroundColor,
                inputAction: InputAction.newline,
                onEditingComplete: (s) => debugPrint('Editing completed $s'),
                loadingBuilder: (context) {
                  return const Center(
                      child: CircularProgressIndicator(
                    strokeWidth: 1,
                    color: Colors.red,
                  ));
                },
                onFocusChanged: (focus) {
                  debugPrint('has focus $focus');
                  setState(() {
                    _hasFocus = focus;
                  });
                },
                onTextChanged: (text) => debugPrint('widget text change $text'),
                onEditorCreated: () {
                  debugPrint('Editor has been loaded');
                  setHtmlText('Testing text on load');
                },
                onEditorResized: (height) =>
                    debugPrint('Editor resized $height'),
                onSelectionChanged: (sel) =>
                    debugPrint('index ${sel.index}, range ${sel.length}'),
              ),
            ),
          ],
        ),
        bottomNavigationBar: Container(
          width: double.maxFinite,
          color: _toolbarColor,
          padding: const EdgeInsets.all(8),
          child: Wrap(
            children: [
              textButton(
                  text: 'Set Text',
                  onPressed: () {
                    setHtmlText('This text is set by you 🫵');
                  }),
              textButton(
                  text: 'Get Text',
                  onPressed: () {
                    getHtmlText();
                  }),
              textButton(
                  text: 'Insert Video',
                  onPressed: () {
                    insertVideoURL(
                        'https://www.youtube.com/watch?v=4AoFA19gbLo');
                    insertVideoURL('https://vimeo.com/440421754');
                    insertVideoURL(
                        'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4');
                  }),
              textButton(
                  text: 'Insert Image',
                  onPressed: () {
                    insertNetworkImage('https://i.imgur.com/0DVAOec.gif');
                  }),
              textButton(
                  text: 'Insert Index',
                  onPressed: () {
                    insertHtmlText("This text is set by the insertText method",
                        index: 10);
                  }),
              textButton(
                  text: 'Undo',
                  onPressed: () {
                    controller.undo();
                  }),
              textButton(
                  text: 'Redo',
                  onPressed: () {
                    controller.redo();
                  }),
              textButton(
                  text: 'Clear History',
                  onPressed: () async {
                    controller.clearHistory();
                  }),
              textButton(
                  text: 'Clear Editor',
                  onPressed: () {
                    controller.clear();
                  }),
              textButton(
                  text: 'Get Delta',
                  onPressed: () async {
                    var delta = await controller.getDelta();
                    debugPrint('delta');
                    debugPrint(jsonEncode(delta));
                  }),
              textButton(
                  text: 'Set Delta',
                  onPressed: () {
                    final Map<dynamic, dynamic> deltaMap = {
                      "ops": [
                        {
                          "insert": {
                            "video":
                                "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"
                          }
                        },
                        {
                          "insert": {
                            "video": "https://www.youtube.com/embed/4AoFA19gbLo"
                          }
                        },
                        {"insert": "Hello"},
                        {
                          "attributes": {"header": 1},
                          "insert": "\n"
                        },
                        {"insert": "You just set the Delta text 😊\n"}
                      ]
                    };
                    controller.setDelta(deltaMap);
                  }),
            ],
          ),
        ),
      ),
    );
  }

  Widget textButton({required String text, required VoidCallback onPressed}) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: MaterialButton(
          shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
          color: _toolbarIconColor,
          onPressed: onPressed,
          child: Text(
            text,
            style: TextStyle(color: _toolbarColor),
          )),
    );
  }

  /// 获取编辑器中的HTML文本
  void getHtmlText() async {
    String? htmlText = await controller.getText();
    debugPrint(htmlText);
  }

  /// 设置编辑器中的HTML文本
  void setHtmlText(String text) async {
    await controller.setText(text);
  }

  /// 插入网络图片
  void insertNetworkImage(String url) async {
    await controller.embedImage(url);
  }

  /// 插入视频URL
  /// 该方法会识别插入的URL并将其转换为可嵌入的URL
  /// 例如:将YouTube视频转换为嵌入视频,Vimeo也是如此
  void insertVideoURL(String url) async {
    await controller.embedVideo(url);
  }

  /// 在指定索引处插入HTML文本
  /// 如果未设置索引,则会在光标位置插入
  void insertHtmlText(String text, {int? index}) async {
    await controller.insertText(text, index: index);
  }

  /// 清除编辑器
  void clearEditor() => controller.clear();

  /// 启用/禁用编辑器
  void enableEditor(bool enable) => controller.enableEditor(enable);

  /// 取消编辑器焦点
  void unFocusEditor() => controller.unFocus();
}

工具栏滚动配置

ToolBar.scroll widget允许您根据指定的direction参数将工具栏按钮显示为单行或单列。默认情况下,direction设置为Axis.horizontal,即按钮排列为单行。要将按钮显示为单列,可以将direction设置为Axis.vertical。例如:

ToolBar.scroll(
  toolBarColor: _toolbarColor,
  controller: controller,
  direction: Axis.vertical,
),

自定义工具栏按钮

您可以自定义工具栏中显示的按钮。通过传递toolBarConfig参数,您可以选择显示特定的按钮。例如:

final customToolBarList = [
  ToolBarStyle.bold,
  ToolBarStyle.italic,
  ToolBarStyle.align,
  ToolBarStyle.color,
];

ToolBar(
  controller: controller,
  toolBarConfig: customToolBarList,
),

此外,您还可以通过customButtons参数添加自定义按钮。例如:

final customButtons = [
  InkWell(onTap: () {}, child: const Icon(Icons.favorite)),
  InkWell(onTap: () {}, child: const Icon(Icons.add_circle)),
];

ToolBar(
  controller: controller,
  customButtons: customButtons,
),

自定义字体

您可以通过定义自定义字体族来设置编辑器中的文本样式。例如,使用Google Fonts中的’Roboto’字体:

final _editorTextStyle = const TextStyle(
  fontSize: 18,
  color: Colors.black,
  fontWeight: FontWeight.normal,
  fontFamily: 'Roboto',
);

获取和设置文本

  • 获取HTML字符串

    String? htmlText = await controller.getText();
    
  • 设置HTML字符串

    await controller.setText(text);
    
  • 获取Delta格式文本

    await controller.getDelta();
    
  • 设置Delta格式文本

    controller.setDelta(deltaMap);
    
  • 插入HTML字符串

    await controller.insertText(text, index: 10);
    
  • 清除编辑器

    controller.clear();
    
  • 启用/禁用编辑器

    controller.enableEditor(true);
    controller.enableEditor(false);
    

更多关于Flutter富文本编辑器插件quill_html_editor_v2的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter富文本编辑器插件quill_html_editor_v2的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何在Flutter项目中使用quill_html_editor_v2插件的示例代码。这个插件允许你实现一个功能丰富的富文本编辑器,并且能够将编辑内容转换为HTML格式。

首先,确保你的Flutter项目已经设置好,并且在pubspec.yaml文件中添加了quill_html_editor_v2依赖:

dependencies:
  flutter:
    sdk: flutter
  quill_html_editor_v2: ^最新版本号  # 请替换为实际的最新版本号

然后,运行flutter pub get来获取依赖。

接下来,我们创建一个包含QuillHtmlEditor的Flutter页面。以下是一个完整的示例代码:

import 'package:flutter/material.dart';
import 'package:quill_html_editor_v2/quill_html_editor_v2.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Quill Html Editor Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: QuillHtmlEditorDemo(),
    );
  }
}

class QuillHtmlEditorDemo extends StatefulWidget {
  @override
  _QuillHtmlEditorDemoState createState() => _QuillHtmlEditorDemoState();
}

class _QuillHtmlEditorDemoState extends State<QuillHtmlEditorDemo> {
  final _controller = QuillController(
    document: Document(),
  )..addListener(() {
    // 这里可以监听文档变化,比如保存内容到本地或进行其他处理
    print('Document changed');
  });

  String? _htmlContent;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Quill Html Editor Demo'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            Expanded(
              child: QuillHtmlEditor(
                controller: _controller,
                focusNode: FocusNode(),
                padding: EdgeInsets.zero,
                placeholder: 'Start typing...',
                readOnly: false,
                toolbarOptions: QuillToolbarOptions(
                  // 配置工具栏选项,可以根据需要添加或移除工具
                  theme: QuillToolbarTheme.light,
                  showIcons: true,
                  items: [
                    ['bold', 'italic', 'underline', 'strike'],
                    ['code-block'],
                    [{'header': 1}, {'header': 2}],
                    [{'indent': '-1'}, {'indent': '+1'}],
                    [{'list': 'ordered'}, {'list': 'bullet'}],
                    [{'script': 'sub'}, {'script': 'super'}],
                    [{'align': []}, {'align': ['left']}, {'align': ['right']}, {'align': ['center']}, {'align': ['justify']}],
                    [{'color': []}, {'background': []}],
                    [{'size': ['small'], 'header': 3}],
                    [{'font': []}, {'background': []}],
                    ['clean'],
                    ['link', 'image', 'video'],
                  ],
                ),
              ),
            ),
            SizedBox(height: 16),
            ElevatedButton(
              onPressed: () {
                // 获取HTML内容
                setState(() {
                  _htmlContent = _controller.documentToHtml();
                });
                // 打印或处理HTML内容
                print(_htmlContent!);
              },
              child: Text('Get HTML Content'),
            ),
            if (_htmlContent != null)
              Padding(
                padding: const EdgeInsets.symmetric(vertical: 16),
                child: Text(
                  'HTML Content:\n$_htmlContent',
                  style: TextStyle(fontSize: 14),
                ),
              ),
          ],
        ),
      ),
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
}

这个示例代码展示了如何使用quill_html_editor_v2插件来创建一个基本的富文本编辑器。主要步骤包括:

  1. pubspec.yaml中添加依赖。
  2. 创建一个QuillController实例来管理编辑器的状态。
  3. 使用QuillHtmlEditor组件来渲染编辑器界面。
  4. 配置工具栏选项以满足需求。
  5. 通过控制器获取编辑器内容并转换为HTML格式。

希望这个示例能帮助你更好地理解和使用quill_html_editor_v2插件。如果有更多问题,欢迎继续提问!

回到顶部