Interfaz de chat Flutter: cuadro de entrada TextField buildTextSpan implementa la función @ función de resaltado de visualización

Interfaz de chat Flutter: cuadro de entrada TextField buildTextSpan implementa la función @ función de resaltado de visualización

Cuando un amigo estaba hablando recientemente, mencionó el resaltado de los cuadros de entrada. Es necesario insertar una etiqueta de estilo especial en el campo de texto de Flutter, como por ejemplo: "Por favor @张三 responda". Esta cadena de caracteres se ingresa en el campo de texto. Cuando se ingresa @, aparece una selección de lista de amigos y luego "@张三" está resaltado. en TextField.

https://blog.csdn.net/gloryFlow/article/details/132889374

Las representaciones son las siguientes.

Insertar descripción de la imagen aquí

1. buildTextSpan en TextEditingController

En TextField encontramos el método buildTextSpan de TextEditingController.

La función de buildTextSpan es la siguiente: generar [TextSpan] según el valor de edición actual y, de forma predeterminada, hacer que el texto en el rango compuesto aparezca subrayado. La herencia puede anular este método para personalizar la apariencia del texto.

Heredo TextEditingController aquí y anulo el método buildTextSpan.

List<InlineSpan> textSpans = RichTextHelper.getRichText(value.text);
    if (composingRegionOutOfRange) {
      return TextSpan(style: style, children: textSpans);
    }

El código completo es el siguiente.

import 'package:flutter/material.dart';
import 'package:flutter_lab/rich_text_helper.dart';

class TextFieldController extends TextEditingController {
  /// Builds [TextSpan] from current editing value.
  ///
  /// By default makes text in composing range appear as underlined. Descendants
  /// can override this method to customize appearance of text.
  @override
  TextSpan buildTextSpan(
      {required BuildContext context,
      TextStyle? style,
      required bool withComposing}) {
    assert(!value.composing.isValid ||
        !withComposing ||
        value.isComposingRangeValid);
    // If the composing range is out of range for the current text, ignore it to
    // preserve the tree integrity, otherwise in release mode a RangeError will
    // be thrown and this EditableText will be built with a broken subtree.
    final bool composingRegionOutOfRange =
        !value.isComposingRangeValid || !withComposing;

    print(
        "--- composingRegionOutOfRange:${composingRegionOutOfRange},withComposing:${withComposing},value.isComposingRangeValid:${value.isComposingRangeValid}");
    List<InlineSpan> textSpans = RichTextHelper.getRichText(value.text);
    if (composingRegionOutOfRange) {
      return TextSpan(style: style, children: textSpans);
    }

    print("+++ composingRegionOutOfRange:${composingRegionOutOfRange}");
    final TextStyle composingStyle =
        style?.merge(const TextStyle(decoration: TextDecoration.underline)) ??
            const TextStyle(decoration: TextDecoration.underline);
    return TextSpan(
      style: style,
      children: <TextSpan>[
        TextSpan(text: value.composing.textBefore(value.text)),
        TextSpan(
          style: composingStyle,
          text: value.composing.textInside(value.text),
        ),
        TextSpan(text: value.composing.textAfter(value.text)),
      ],
    );
  }
}

2. Establezca @Resaltado

Para resaltar "@张三" en TextField, se requiere una expresión regular que coincida con la expresión regular de la función @: String regexStr =
r"@[ @]*[ @ ]+[^@]* ";

Usando expresiones regulares

String regexStr =
        r"@[^@]*[^@ ]+[^@]* ";
    RegExp exp = RegExp('$regexStr');

El código específico es el siguiente.

import 'package:flutter/material.dart';

class RichTextHelper {
  //图文混排
  static getRichText(String text) {
    List<InlineSpan> textSpans = [];

    String regexStr =
        r"@[^@]*[^@ ]+[^@]* ";
    RegExp exp = RegExp('$regexStr');

    //正则表达式是否在字符串[input]中有匹配。
    if (exp.hasMatch(text)) {
      Iterable<RegExpMatch> matches = exp.allMatches(text);

      int index = 0;
      int count = 0;
      for (var matche in matches) {
        count++;
        String c = text.substring(matche.start, matche.end);
        //匹配到的东西,如表情在首位
        if (index == matche.start) {
          index = matche.end;
        }
        //匹配到的东西,如表情不在首位
        else if (index < matche.start) {
          String leftStr = text.substring(index, matche.start);
          index = matche.end;
          textSpans.add(
            TextSpan(
              text: spaceWord(leftStr),
              style: getDefaultTextStyle(),
            ),
          );
        }

        //匹配到的网址
        if (RegExp(regexStr).hasMatch(c)) {
          textSpans.add(
            TextSpan(
              text: spaceWord(c),
              style:
              TextStyle(color: Colors.blueAccent, fontSize: 16),
            ),
          );
        }

        //是否是最后一个表情,并且后面是否有字符串
        if (matches.length == count && text.length > index) {
          String rightStr = text.substring(index, text.length);
          textSpans.add(
            TextSpan(
              text: spaceWord(rightStr),
              style: getDefaultTextStyle(),
            ),
          );
        }
      }
    } else {
      textSpans.add(
        TextSpan(
          text: spaceWord(text),
          style: getDefaultTextStyle(),
        ),
      );
    }

    return textSpans;
  }

  static TextStyle getDefaultTextStyle() {
    return TextStyle(
      fontSize: 16,
      fontWeight: FontWeight.w400,
      fontStyle: FontStyle.normal,
      color: Colors.black87,
      decoration: TextDecoration.none,
    );
  }

  static String spaceWord(String text) {
    if (text.isEmpty) return text;
    String spaceWord = '';
    for (var element in text.runes) {
      spaceWord += String.fromCharCode(element);
      spaceWord += '\u200B';
    }
    return spaceWord;
  }
}

3. Crear campo de texto

Cree el TextField que debe mostrarse y configure onTap, onChanged, focusNode, TextEditingController, etc. del cuadro de entrada.

El código se muestra a continuación.

// 输入框
class InputTextField extends StatefulWidget {
  const InputTextField({
    Key? key,
    this.inputOnTap,
    this.inputOnChanged,
    this.inputOnSubmitted,
    this.inputOnEditingCompleted,
    this.autofocus = false,
    required this.textEditingController,
  }) : super(key: key);

  final inputOnTap;
  final inputOnChanged;
  final inputOnSubmitted;
  final inputOnEditingCompleted;
  final bool autofocus;
  final TextEditingController textEditingController;

  @override
  State<InputTextField> createState() => _InputTextFieldState();
}

class _InputTextFieldState extends State<InputTextField> {
  FocusNode editFocusNode = FocusNode();

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
  }

  //获取焦点
  void getFocusFunction(BuildContext context) {
    FocusScope.of(context).requestFocus(editFocusNode);
  }

  //失去焦点
  void unFocusFunction() {
    editFocusNode.unfocus();
  }

  @override
  void dispose() {
    // TODO: implement dispose
    editFocusNode.unfocus();
    editFocusNode.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: EdgeInsets.only(
        left: 10.0,
        top: 5.0,
        bottom: 5.0,
      ),
      constraints: BoxConstraints(
        minHeight: 40.0,
        maxHeight: 120.0,
      ),
      child: TextField(
        onTap: () {
          widget.inputOnTap();
        },
        onChanged: (string) {
          widget.inputOnChanged(string);
        },
        onEditingComplete: () {
          widget.inputOnEditingCompleted();
        },
        onSubmitted: (string) {
          widget.inputOnSubmitted(string);
        },
        minLines: 1,
        maxLines: null,
        keyboardType: TextInputType.multiline,
        textAlignVertical: TextAlignVertical.center,
        autofocus: widget.autofocus,
        focusNode: editFocusNode,
        controller: widget.textEditingController,
        textInputAction: TextInputAction.send,
        decoration: InputDecoration(
          contentPadding: EdgeInsets.symmetric(vertical: 10, horizontal: 8.0),
          filled: true,
          isCollapsed: true,
          floatingLabelBehavior: FloatingLabelBehavior.never,
          hintText: "说点什么吧~",
          hintStyle: TextStyle(
            fontSize: 14,
            fontWeight: FontWeight.w400,
            fontStyle: FontStyle.normal,
            color: ColorUtil.hexColor(0xACACAC),
            decoration: TextDecoration.none,
          ),
          enabledBorder: OutlineInputBorder(
            /*边角*/
            borderRadius: const BorderRadius.all(
              Radius.circular(5.0), //边角为30
            ),
            borderSide: BorderSide(
              color: ColorUtil.hexColor(0xf7f7f7), //边框颜色为绿色
              width: 1, //边线宽度为1
            ),
          ),
          focusedBorder: OutlineInputBorder(
            borderRadius: const BorderRadius.all(
              Radius.circular(5.0), //边角为30
            ),
            borderSide: BorderSide(
              color: ColorUtil.hexColor(0xECECEC), //边框颜色为绿色
              width: 1, //宽度为1
            ),
          ),
        ),
      ),
    );
  }
}

4. Demostración del texto de la tarea TextField

Finalmente podemos configurar el texto en el cuadro de entrada TextField

TextFieldController textEditingController = TextFieldController();

textEditingController.text = "你好@张三 欢迎,哈哈,haha";

El código completo es el siguiente.

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

  @override
  State<TextFieldRich> createState() => _TextFieldRichState();
}

class _TextFieldRichState extends State<TextFieldRich> {
  TextFieldController textEditingController = TextFieldController();

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    textEditingController.text = "你好@张三 欢迎,哈哈,haha";
  }

  @override
  void dispose() {
    // TODO: implement dispose
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    Size scrSize = MediaQuery.of(context).size;
    return Scaffold(
      appBar: AppBar(
        // Here we take the value from the MyHomePage object that was created by
        // the App.build method, and use it to set our appbar title.
        title: Text('TextField测试页面'),
      ),
      body: Container(
        width: scrSize.width,
        height: scrSize.height,
        color: Colors.greenAccent,
        alignment: Alignment.center,
        padding: EdgeInsets.symmetric(vertical: 20.0, horizontal: 20.0),
        child: InputTextField(textEditingController: textEditingController),
      ),
    );
  }
}

En este punto puedes ver que @张三 está resaltado en la representación.

5. Resumen

Interfaz de chat Flutter: cuadro de entrada TextField buildTextSpan implementa la función @ para resaltar la visualización. En este ejemplo hay un problema con el cursor, que no se ha modificado por el momento, tomaré tiempo para modificarlo más tarde.

https://blog.csdn.net/gloryFlow/article/details/132889374

Estudia y registra, sigue mejorando cada día.

Supongo que te gusta

Origin blog.csdn.net/gloryFlow/article/details/132889374
Recomendado
Clasificación