【问题标题】:How to format text as we type in TextField widget of flutter?当我们在 Flutter 的 TextField 小部件中输入时如何格式化文本?
【发布时间】:2019-09-23 04:23:32
【问题描述】:

我想在我们输入TextField 时格式化文本。例如,当我在字段中使用下划线_ 时,它们之间的字符应该是斜体。
我想要的是 ma​​rkdown-like 文本格式在 Flutter 的同一个小部件中实时发生。

RichText 可以提供帮助,但它们不可编辑。为此我需要一个 RichTextField,但 Flutter 中不存在类似的东西。

我的结果应该类似于 WhatsApp 的消息字段,应用粗体、斜体、删除线等。

【问题讨论】:

  • 你可以使用onChanged属性textField - flutter.dev/docs/cookbook/forms/text-field-changes
  • 这不会格式化同一字段中的特定单词
  • 只需添加检查以检测分隔符(这就是 whatsapp 的正常工作方式)并转换其间的字符。
  • Flutter 中的 TextField Widget 可以做到吗?我不知道如何实现这一点。你能帮忙吗?

标签: flutter


【解决方案1】:

请参阅在现有“TextField”上实现答案的示例代码。

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

final Color darkBlue = const Color.fromARGB(255, 18, 32, 47);

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
      debugShowCheckedModeBanner: false,
      home: const Scaffold(
        body: Center(
          child: MyWidget(),
        ),
      ),
    );
  }
}

class TextFieldColorizer extends TextEditingController {
  final Map<String, TextStyle> map;
  final Pattern pattern;

  TextFieldColorizer(this.map)
      : pattern = RegExp(
            map.keys.map((key) {
              return key;
            }).join('|'),
            multiLine: true);

  @override
  set text(String newText) {
    value = value.copyWith(
      text: newText,
      selection: TextSelection.collapsed(offset: newText.length),
      composing: TextRange.empty,
    );
  }

  @override
  TextSpan buildTextSpan({TextStyle style, bool withComposing}) {
    final List<InlineSpan> children = [];
    String patternMatched;
    String formatText;
    TextStyle myStyle;
    text.splitMapJoin(
      pattern,
      onMatch: (Match match) {
        myStyle = map[match[0]] ??
            map[map.keys.firstWhere(
              (e) {
                bool ret = false;
                RegExp(e).allMatches(text)
                  ..forEach((element) {
                    if (element.group(0) == match[0]) {
                      patternMatched = e;
                      ret = true;
                      return true;
                    }
                  });
                return ret;
              },
            )];

        if (patternMatched == r"_(.*?)\_") {
          formatText = match[0].replaceAll("_", " ");
        } else if (patternMatched == r'\*(.*?)\*') {
          formatText = match[0].replaceAll("*", " ");
        } else if (patternMatched == "~(.*?)~") {
          formatText = match[0].replaceAll("~", " ");
        } else if (patternMatched == r'```(.*?)```') {
          formatText = match[0].replaceAll("```", "   ");
        } else {
          formatText = match[0];
        }
        children.add(TextSpan(
          text: formatText,
          style: style.merge(myStyle),
        ));
        return "";
      },
      onNonMatch: (String text) {
        children.add(TextSpan(text: text, style: style));
        return "";
      },
    );

    return TextSpan(style: style, children: children);
  }
}

class MyWidget extends StatefulWidget {
  const MyWidget();
  _MyWidgetState createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  final TextEditingController _controller = TextFieldColorizer(
    {
      r"@.\w+": TextStyle(color: Colors.blue, shadows: kElevationToShadow[2]),
      'red': const TextStyle(
          color: Colors.red, decoration: TextDecoration.underline),
      'green': TextStyle(color: Colors.green, shadows: kElevationToShadow[2]),
      'purple': TextStyle(color: Colors.purple, shadows: kElevationToShadow[2]),
      r'_(.*?)\_': TextStyle(
          fontStyle: FontStyle.italic, shadows: kElevationToShadow[2]),
      '~(.*?)~': TextStyle(
          decoration: TextDecoration.lineThrough,
          shadows: kElevationToShadow[2]),
      r'\*(.*?)\*': TextStyle(
          fontWeight: FontWeight.bold, shadows: kElevationToShadow[2]),
      r'```(.*?)```': TextStyle(
          color: Colors.yellow,
          fontFeatures: [const FontFeature.tabularFigures()],
          shadows: kElevationToShadow[2]),
    },
  );

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(10),
      child: TextField(
        maxLines: 5,
        onChanged: (text) {
          final val = TextSelection.collapsed(offset: _controller.text.length);
          _controller.selection = val;
        },
        style: const TextStyle(fontSize: 32),
        controller: _controller,
      ),
    );
  }
}

【讨论】:

  • 如果我们实现 WhatsApp 风格的 Markdown bolditalic、删除线和等宽,会怎么样?
  • 您需要向控制器提供正则表达式,例如这是whatsapp风格的斜体(?anyword i>.
  • 我已更新代码以包含 WhatsApp 样式的 Markdown 粗体、斜体、删除线。如果它解决了您的问题,请接受。
  • 这很好,但我想删除一些行为。目前,当您键入 word 然后按退格键时,前导下划线 _ 会重新出现。我不确定为什么用空格替换它后会发生这种情况?
【解决方案2】:

我发现解决方法很简单。

我只需要扩展/实现TextEditingController 并创建我的TextSpan buildTextSpan({TextStyle style , bool withComposing}) 实现。

这个buildTextSpan 方法负责创建TextField 中显示的文本跨度。

例如检查this code of my package.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-17
    • 1970-01-01
    • 2021-06-13
    • 2019-01-05
    • 2013-12-17
    • 2017-03-06
    • 1970-01-01
    • 2020-06-23
    相关资源
    最近更新 更多