Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to format text as we type in TextField widget of flutter?

Tags:

flutter

I want to format text when we type in TextField. For example, when I use underscores _ in the field, the characters between them should get italic.
What I want is a markdown-like text formatting to happen live in the same widget in Flutter.

RichText could help, but they're not editable. I'd need a RichTextField to for that, but something like it doesn't exist in Flutter.

My result should be like WhatsApp's message field which applies bold, italic, strikethrough, etc.

like image 984
Syed Mushaheed Avatar asked May 05 '19 07:05

Syed Mushaheed


People also ask

How do you change the text style in TextField Flutter?

Here is the step by step instructions: Step 1: Locate the file where you have placed the TextField widget. Step 2: Inside the TextField widget, add the style parameter and assign the TextField widget. Step 3: Inside the TextField widget, add the color parameter and set the color of your choice.

How do you AutoFill TextField in Flutter?

Also, as you already mentioned, you need to configure an AutoFill service in the system settings. Working sample code: @override Widget build(BuildContext context) => Scaffold( appBar: AppBar( title: Text("Login"), ), body: AutofillGroup( child: Column( children: [ TextField( autofillHints: [AutofillHints.

How do you wrap text in TextField Flutter?

Here's how you wrap text on overflow in Flutter:Step 1: Make sure your Text widget is inside the Row widget. Step 2: Wrap your Text widget inside the Expanded widget. Step 3: Run your app.

What is input formatter in Flutter?

TextInputFormatter translates the field value into text and the text into the field value. In this article, you'll learn how to format data in TextFormField and TextField. When the user enters something into an input field.


1 Answers

Please see sample code on implementing the answer on an existing 'TextField'.

Screen Recording

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,
      ),
    );
  }
}
like image 63
bluenile Avatar answered Sep 28 '22 05:09

bluenile