Flutter增强文本输入功能插件extended_text_field的使用

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

Flutter增强文本输入功能插件extended_text_field的使用

概述

extended_text_field 是Flutter官方TextField组件的一个第三方扩展库,它提供了丰富的功能来创建特殊文本(如内联图片、@somebody、自定义背景等),并且支持快速构建富文本、自定义选择工具栏和句柄。

pub package GitHub stars GitHub forks GitHub license GitHub issues

Web demo for ExtendedTextField

主要特性对比

Feature ExtendedTextField TextField
内联图片和文本混合 支持,允许显示内联图片和混合文本 仅支持显示文本,但有文本选择问题
复制实际值 支持,启用复制文本的实际值 不支持
快速构建富文本 支持,基于文本格式快速构建富文本 不支持

使用方法

添加依赖

pubspec.yaml文件中添加以下依赖:

dependencies:
  extended_text_field: ^11.0.1-ohos

示例代码

基本用法

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Extended Text Field Example')),
        body: Padding(
          padding: const EdgeInsets.all(8.0),
          child: ExtendedTextField(
            controller: TextEditingController(),
            decoration: InputDecoration(
              labelText: 'Enter text',
              border: OutlineInputBorder(),
            ),
            specialTextSpanBuilder: MySpecialTextSpanBuilder(),
          ),
        ),
      ),
    );
  }
}

class MySpecialTextSpanBuilder extends SpecialTextSpanBuilder {
  @override
  TextSpan build(String data, {TextStyle? textStyle, SpecialTextGestureTapCallback? onTap}) {
    var textSpan = super.build(data, textStyle: textStyle, onTap: onTap);
    return textSpan;
  }

  @override
  SpecialText? createSpecialText(String flag, {TextStyle? textStyle, SpecialTextGestureTapCallback? onTap, int? index}) {
    if (flag == null || flag.isEmpty) return null;

    if (isStart(flag, AtText.flag)) {
      return AtText(textStyle, onTap,
          start: index! - (AtText.flag.length - 1),
          showAtBackground: true);
    } else if (isStart(flag, EmojiText.flag)) {
      return EmojiText(textStyle, start: index! - (EmojiText.flag.length - 1));
    } else if (isStart(flag, DollarText.flag)) {
      return DollarText(textStyle, onTap,
          start: index! - (DollarText.flag.length - 1));
    }
    return null;
  }
}

class AtText extends SpecialText {
  static const String flag = "@";
  final bool showAtBackground;

  AtText(TextStyle? textStyle, SpecialTextGestureTapCallback? onTap,
      {required this.showAtBackground, required int start})
      : super(flag, " ", textStyle, onTap: onTap);

  @override
  InlineSpan finishText() {
    TextStyle textStyle =
        this.textStyle?.copyWith(color: Colors.blue, fontSize: 16.0);

    final String atText = toString();

    return showAtBackground
        ? BackgroundTextSpan(
            background: Paint()..color = Colors.blue.withOpacity(0.15),
            text: atText,
            actualText: atText,
            start: start,

            ///caret can move into special text
            deleteAll: true,
            style: textStyle,
            recognizer: (TapGestureRecognizer()
              ..onTap = () {
                if (onTap != null) onTap(atText);
              }))
        : SpecialTextSpan(
            text: atText,
            actualText: atText,
            start: start,
            style: textStyle,
            recognizer: (TapGestureRecognizer()
              ..onTap = () {
                if (onTap != null) onTap(atText);
              }));
  }
}

内联图片

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Inline Image Example')),
        body: Padding(
          padding: const EdgeInsets.all(8.0),
          child: ExtendedTextField(
            controller: TextEditingController(),
            decoration: InputDecoration(
              labelText: 'Enter text with inline images',
              border: OutlineInputBorder(),
            ),
            specialTextSpanBuilder: MySpecialTextSpanBuilder(),
          ),
        ),
      ),
    );
  }
}

class MySpecialTextSpanBuilder extends SpecialTextSpanBuilder {
  @override
  TextSpan build(String data, {TextStyle? textStyle, SpecialTextGestureTapCallback? onTap}) {
    var textSpan = super.build(data, textStyle: textStyle, onTap: onTap);
    return textSpan;
  }

  @override
  SpecialText? createSpecialText(String flag, {TextStyle? textStyle, SpecialTextGestureTapCallback? onTap, int? index}) {
    if (flag == null || flag.isEmpty) return null;

    if (isStart(flag, "[img]")) {
      return ImageTextSpan(textStyle, onTap,
          start: index! - ("[img]".length - 1),
          imageProvider: AssetImage("assets/image.png"));
    }
    return null;
  }
}

class ImageTextSpan extends SpecialText {
  final ImageProvider imageProvider;

  ImageTextSpan(TextStyle? textStyle, SpecialTextGestureTapCallback? onTap,
      {required this.imageProvider, required int start})
      : super("[img]", "", textStyle, onTap: onTap);

  @override
  InlineSpan finishText() {
    return ImageSpan(
      imageProvider,
      imageWidth: 24.0,
      imageHeight: 24.0,
      margin: EdgeInsets.only(left: 2.0, bottom: 0.0, right: 2.0),
      start: start,
      actualText: "[img]",
    );
  }
}

自定义选择工具栏

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Custom Toolbar Example')),
        body: Padding(
          padding: const EdgeInsets.all(8.0),
          child: ExtendedTextField(
            controller: TextEditingController(),
            decoration: InputDecoration(
              labelText: 'Enter text',
              border: OutlineInputBorder(),
            ),
            selectionControls: MyTextSelectionControls(),
          ),
        ),
      ),
    );
  }
}

class MyTextSelectionControls extends MaterialTextSelectionControls {
  @override
  Size getHandleSize(double textLineHeight) => Size(22.0, 22.0);

  @override
  Widget buildToolbar(
    BuildContext context,
    RenderBox renderBox,
    Offset globalEditableRegionOffset,
    double textLineHeight,
    List<TextSelectionPoint> endpoints,
    TextSelectionDelegate delegate,
    ClipboardStatusNotifier? clipboardStatus,
    Offset? lastSecondaryTapDownPosition,
  ) {
    return AdaptiveTextSelectionToolbar.editableText(
      editableTextState: delegate as ExtendedEditableTextState,
    );
  }
}

注意事项

  1. 不支持 RTL 文本方向:当 TextDirection.rtl 时,不会处理特殊文本,且图片位置计算可能会有问题。
  2. 不支持隐藏文本:当 obscureTexttrue 时,不会处理特殊文本。

通过以上内容,您可以快速上手并充分利用 extended_text_field 插件的强大功能。希望这些示例能帮助您更好地理解和使用该插件!


更多关于Flutter增强文本输入功能插件extended_text_field的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter增强文本输入功能插件extended_text_field的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何使用 extended_text_field 插件来增强 Flutter 文本输入功能的代码示例。extended_text_field 是一个功能强大的 Flutter 插件,它扩展了默认的 TextField 小部件,提供了更多自定义和高级功能,比如自定义文本样式、正则表达式验证、点击链接等。

首先,确保你已经在 pubspec.yaml 文件中添加了 extended_text_field 依赖:

dependencies:
  flutter:
    sdk: flutter
  extended_text_field: ^x.y.z  # 替换为最新版本号

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

接下来是一个简单的示例,展示如何使用 ExtendedTextField 来创建一个带有正则表达式验证和自定义样式的文本输入字段:

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('ExtendedTextField Example'),
        ),
        body: Center(
          child: Padding(
            padding: const EdgeInsets.all(16.0),
            child: CustomTextField(),
          ),
        ),
      ),
    );
  }
}

class CustomTextField extends StatefulWidget {
  @override
  _CustomTextFieldState createState() => _CustomTextFieldState();
}

class _CustomTextFieldState extends State<CustomTextField> {
  final TextEditingController _controller = TextEditingController();
  final FocusNode _focusNode = FocusNode();

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

  @override
  Widget build(BuildContext context) {
    return ExtendedTextField(
      controller: _controller,
      focusNode: _focusNode,
      decoration: InputDecoration(
        border: OutlineInputBorder(),
        labelText: 'Enter text with regex validation',
        helperText: 'Only alphabets and numbers are allowed',
      ),
      maxLength: 20,
      maxLengthEnforced: true,
      specialText: SpecialText.all(
        regex: RegExp(r'[^a-zA-Z0-9]'),
        style: TextStyle(color: Colors.red, decoration: TextDecoration.underline),
        start: 0,
      ),
      onChanged: (text) {
        // Handle text changes if needed
        print('Current text: $text');
      },
      onSubmitted: (text) {
        // Handle text submission if needed
        print('Submitted text: $text');
      },
      validator: (text) {
        if (!RegExp(r'^[a-zA-Z0-9]*$').hasMatch(text)) {
          return 'Only alphabets and numbers are allowed';
        }
        return null;
      },
    );
  }
}

在这个示例中,我们做了以下几件事:

  1. 创建 ExtendedTextField

    • 使用 TextEditingControllerFocusNode 来控制文本输入和焦点。
    • 设置输入装饰(InputDecoration),包括边框、标签文本和辅助文本。
  2. 正则表达式验证

    • 使用 SpecialText.all 来标记不符合正则表达式的文本(这里是不符合 [a-zA-Z0-9] 的字符),并为这些字符设置特定的样式(红色下划线)。
    • 使用 validator 回调函数在表单提交时进行验证,如果不符合正则表达式,返回错误信息。
  3. 事件处理

    • 使用 onChanged 回调函数处理文本变化事件。
    • 使用 onSubmitted 回调函数处理文本提交事件。

这个示例展示了如何使用 extended_text_field 插件来增强文本输入功能,包括自定义样式、正则表达式验证等。你可以根据具体需求进一步自定义和扩展这个示例。

回到顶部