Sitemap
Philipp Gerber

Philipp Gerber

Textf 2.0: Ready for Flutter’s Decoupled Future

Headings, live editing in text fields, and one widget for every design system.

5 min readOct 26, 2026

--

When I released Textf 1.0, the idea was simple: replace Text with Textf and write **bold** instead of nesting TextSpans.

A few things have happened since then. Flutter started moving Material and Cupertino out of the framework core. The community asked for headings. And Textf learned to format text while you type it.

Textf 2.0 brings these together. This article covers what’s new.

Press enter or click to view image in full size

Flutter Is Decoupling Its Design Systems

Flutter is moving Material and Cupertino out of the framework core and into their own packages, material_ui and cupertino_ui. For apps, this is mostly a matter of imports. For packages it raises a question: which design system does your package assume?

A package that imports material.dart takes Material along into every app that uses it, including a Cupertino app or an app with its own design system.

Textf 2.0 doesn’t import either. It builds only on Flutter’s core layers (widgets, painting, gestures, foundation) and never reads a design-system theme. Its defaults come from the text they render in, meaning the ambient DefaultTextStyle, in the same way Text works.

The result is that one widget behaves the same everywhere:

WidgetsApp(
color: const Color(0xFF000000),
builder: (context, _) => const DefaultTextStyle(
style: TextStyle(fontSize: 18, color: Color(0xFF222222)),
child: Center(
child: Textf(
'Same **widget**, *any* design system: `code`, '
'==highlights== and [links](https://flutter.dev/).',
),
),
),
)
Press enter or click to view image in full size
Same widget, four design systems.

If your app uses Material today, Textf still works the way you're used to. You just aren't tied to Material anymore.

Headings and Horizontal Rules

In Part 1, I wrote that Textf is not designed for full documents with headings and lists. That’s still true for documents. But short texts often need a little structure: a release note with a title, an onboarding card with a section break, a chat message with a heading.

Textf 2.0 supports ATX headings and thematic breaks:

Textf(
'# Release notes\n'
'## Version 2.0\n'
'Textf now supports **headings** and horizontal rules.\n'
'---\n'
'Still *zero* dependencies.',
)
Press enter or click to view image in full size
Headings and a thematic break, rendered with Textf.
  • # to ###### create headings from H1 to H6, following CommonMark rules.
  • ---, *** or ___ on their own line render as a full-width divider.
  • h1Style to h6Style in TextfOptions style each level. They merge onto the built-in sizes, so changing only the color keeps the heading size.

Heading support started as a community pull request. Thank you!

What didn’t change: Headings and rules apply to a single line. There are no lists, tables, images or block layout. Textf still parses in a single O(N) pass, has zero dependencies, and uses the same drop-in Text API. If you need to render full documents, a full Markdown package is still the right tool.

Formatting While You Type: TextfEditingController

This one has been around since 1.2, but it hasn’t come up in this series yet.

Get Philipp Gerber’s stories in your inbox

Join Medium for free to get updates from this writer.

TextfEditingController is a drop-in replacement for TextEditingController. Attach it to any TextField and formatting renders live as the user types:

final controller = TextfEditingController();
TextField(controller: controller)
Live formatting inside a regular TextField.

The stored value stays plain text with markers. The controller only adds styling on top, so controller.text is exactly what the user typed, and cursor positions match the raw text one to one. IME composing for languages like Chinese, Japanese and Korean works as expected.

You can choose how markers appear:

// Markers always visible, dimmed (default)
TextfEditingController(markerVisibility: MarkerVisibility.always);

// Markers hide when the cursor leaves the formatted span
TextfEditingController(markerVisibility: MarkerVisibility.whenActive);

The best part is that the same syntax works for input and output. What a user types into a TextfEditingController renders the same way in a Textf.

Practical Build: Consent Checkbox

Every app with an account has this sentence. With plain Flutter it means Text.rich, five TextSpans and two TapGestureRecognizers that you have to create and dispose in a StatefulWidget.

With Textf, it’s a StatelessWidget:

class ConsentRow extends StatelessWidget {
const ConsentRow({
required this.value,
required this.onChanged,
super.key,
});

final bool value;
final ValueChanged<bool?> onChanged;

@override
Widget build(BuildContext context) {
return Row(
children: [
Checkbox(value: value, onChanged: onChanged),
Expanded(
child: TextfOptions(
onLinkTap: (url, _) => launchUrl(Uri.parse(url)),
child: Textf(
'I agree to the [**Terms of Service**](https://example.com/terms) '
'and [**Privacy Policy**](https://example.com/privacy).',
),
),
),
],
);
}
}

A consent row with two tappable links.

Localization. The sentence stays a single string, which is what translators need. In languages where the two links come in a different order, they can simply move them. A reader suggested a nice addition: keep the URLs out of what translators edit by passing them as gen-l10n placeholders.

{
"consent": "I agree to the [**Terms of Service**]({termsUrl}) and [**Privacy Policy**]({privacyUrl}).",
"@consent": {
"placeholders": {
"termsUrl": { "type": "String" },
"privacyUrl": { "type": "String" }
}
}
}
AppLocalizations.of(context)!.consent(termsUrl, privacyUrl).textf()

Translators can move the links, but they can’t break or change the addresses.

When to Use Textf

Perfect for:

  • Chat messages and comments, both input and display
  • Notifications, tooltips and UI labels with emphasis
  • Internationalized strings with links and formatting
  • Short structured text: release notes, onboarding cards, announcements
  • Apps on any design system: Material, Cupertino or your own

Not designed for:

  • Full documents with lists, tables or images
  • Fenced code blocks and syntax highlighting
  • HTML rendering

Get Started

  • 📦 pub.dev: pub.dev/packages/textf
  • 🧪 Playground: textf.philippgerber.li
  • ⭐ GitHub: github.com/PhilippHGerber/textfr/textf

Questions, ideas or bugs are always welcome. Happy formatting.

--

--