How to Make TextField in Multiple Colors in Flutter

Alexey Inkin
Akvelon
Published in
3 min readJun 23, 2023

At Akvelon, we made a code editor in Flutter with syntax highlighting. But out of the box the standard TextField widget only works with a single color. Here we explain a simple way to upgrade that.

The TextField widget does not itself impose any styling. Instead, it asks its TextEditingController to produce a styled TextSpan object, which is a piece of text with a style.

The TextField passes its style to TextEditingController, and the default implementation just puts it into the TextSpan object, and this is how the color is normally applied.

To override this, subclass the TextEditingController and override the method:

class GradientTextEditingController extends TextEditingController {
@override
TextSpan buildTextSpan({
required BuildContext context,
TextStyle? style,
bool? withComposing,
}) {
style ??= const TextStyle();
final leftStyle = style.copyWith(color: Colors.red);
final rightStyle = style.copyWith(color: Colors.indigo);
final children = <TextSpan>[];

for (final char in text.characters) {
children.add(
TextSpan(
text: char,
style: TextStyle.lerp(
leftStyle,
rightStyle,
children.length / text.length,
),
),
);
}

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

See the full code here.

You can do much more complex processing. For instance, we made code highlighting by parsing the syntax tree and colorizing keywords, literals, comments, etc. differently:

We start by importing the highlighting and the flutter_highlighting packages we made for another project:

import 'package:flutter_highlighting/themes/vs.dart';
import 'package:highlighting/highlighting.dart';
import 'package:highlighting/languages/java.dart';

Then we parse the text and get a simple form of a syntax tree:

class SyntaxTextEditingController extends TextEditingController {
@override
TextSpan buildTextSpan({
required BuildContext context,
TextStyle? style,
bool? withComposing,
}) {
final highlighted = highlight.parse(text, languageId: java.id);

return TextSpan(
style: style,
children: _buildList(
nodes: highlighted.nodes,
styles: vsTheme, // Built-in theme from flutter_highlighting
ancestorStyle: style,
),
);
}
// ...

Next is to walk the syntax tree and return a TextSpan for each node:

  List<TextSpan>? _buildList({
required List<Node>? nodes,
required Map<String, TextStyle> styles,
TextStyle? ancestorStyle,
}) {
return nodes
?.map(
(node) => _buildNode(
node: node,
styles: styles,
ancestorStyle: ancestorStyle,
),
)
.toList(growable: false);
}

TextSpan _buildNode({
required Node node,
required Map<String, TextStyle> styles,
TextStyle? ancestorStyle,
}) {
final style = styles[node.className] ?? ancestorStyle;

return TextSpan(
text: node.value,
children: _buildList(
nodes: node.children,
styles: styles,
ancestorStyle: style,
),
style: style,
);
}

See the full code here.

So the TextEditingController class is the gate to all sorts of customization. We went farther that road and made an advanced code editor that can do this:

Check it out here if you are interested.

--

--

Alexey Inkin
Alexey Inkin

Written by Alexey Inkin

Google Developer Expert in Flutter. PHP, SQL, TS, Java, C++, professionally since 2003. Open for consulting & dev with my team. Telegram channel: @ainkin_com