flutter
/

Flutter RichText – Styled Text with Multiple Styles

Last Sync: Today

On this page

10
0%
5 min read
Remaining
5 minleft

Click any section to jump — progress syncs automatically

flutter

Flutter RichText – Styled Text with Multiple Styles

What is RichText?

RichText is a widget that displays text with multiple styles. It uses a tree of TextSpan objects, each with its own style, and optionally WidgetSpan to embed widgets inline. This is the foundation for creating styled text, links, and even interactive elements within a text flow. Unlike Text, which applies a single style to the whole string, RichText gives you fine‑grained control over every part.

Basic RichText with TextSpan

DARTRead-only
1
RichText(
  text: TextSpan(
    text: 'Hello ',
    style: TextStyle(color: Colors.blue, fontSize: 20),
    children: [
      TextSpan(
        text: 'world!',
        style: TextStyle(color: Colors.red, fontWeight: FontWeight.bold),
      ),
    ],
  ),
)

The TextSpan tree builds the text. Each span can have its own style, and nested spans inherit the parent style unless overridden.

Inline Widgets with WidgetSpan

WidgetSpan allows you to embed any widget inside text. This is great for adding inline icons, buttons, or custom components.

DARTRead-only
1
RichText(
  text: TextSpan(
    text: 'Click the ',
    children: [
      WidgetSpan(
        child: GestureDetector(
          onTap: () => print('Icon tapped'),
          child: Icon(Icons.favorite, size: 20, color: Colors.red),
        ),
      ),
      TextSpan(text: ' to like'),
    ],
  ),
)

Handling Tap Events (Links)

You can make parts of the text tappable by adding a TapGestureRecognizer to a TextSpan using recognizer. This is how you create clickable links without using GestureDetector.

DARTRead-only
1
import 'package:flutter/gestures.dart';

RichText(
  text: TextSpan(
    text: 'Read our ',
    children: [
      TextSpan(
        text: 'privacy policy',
        style: TextStyle(color: Colors.blue, decoration: TextDecoration.underline),
        recognizer: TapGestureRecognizer()
          ..onTap = () => print('Privacy policy tapped'),
      ),
      TextSpan(text: '.'),
    ],
  ),
)

Nested Styles and Inheritance

Styles cascade from parent to child. A child span can override inherited properties (like color or fontSize) or add new ones.

DARTRead-only
1
RichText(
  text: TextSpan(
    style: TextStyle(fontSize: 18, color: Colors.black),
    children: [
      TextSpan(text: 'Normal text '),
      TextSpan(
        text: 'bold red',
        style: TextStyle(fontWeight: FontWeight.bold, color: Colors.red),
      ),
      TextSpan(text: ' and '),
      TextSpan(
        text: 'italic green',
        style: TextStyle(fontStyle: FontStyle.italic, color: Colors.green),
      ),
    ],
  ),
)

Text Selection and SelectionControls

RichText supports text selection out of the box if you set selectionColor and use SelectableText.rich instead. For selectable rich text, use SelectableText.rich which takes a TextSpan tree. You can also customize selection controls with SelectionControls.

DARTRead-only
1
SelectableText.rich(
  TextSpan(
    text: 'You can select this text. ',
    children: [
      TextSpan(
        text: 'Click and drag to select.',
        style: TextStyle(fontWeight: FontWeight.bold),
      ),
    ],
  ),
  selectionColor: Colors.lightBlue,
)

Performance Considerations

    • Use const for static TextStyle objects to avoid unnecessary rebuilds.
    • For many spans, consider building the text with a loop and storing the spans in a list.
    • Avoid creating new recognizers on every build; create them once and reuse.
    • When using WidgetSpan, ensure the embedded widgets are lightweight (e.g., small icons, not heavy animations).

Common Mistakes

    • Forgetting to add recognizer to TextSpan: Without it, the text won't respond to taps. Also remember to import flutter/gestures.dart.
    • Using TextSpan without a root style: If you don't set a default style on the root, text may be invisible (no color).
    • Creating recognizers inside the build method: This creates new instances on each rebuild, which can cause memory leaks and performance issues. Create them in initState or as constants.
    • Not handling overflow: RichText does not automatically wrap; use softWrap: true and a proper container width.
    • Mixing RichText with SelectableText.rich incorrectly: RichText is not selectable; use SelectableText.rich for selectable text.

Best Practices

    • Define a base TextStyle for the root TextSpan and override only what changes in children.
    • For interactive spans, create recognizers as member variables and dispose them if necessary (e.g., TapGestureRecognizer).
    • Use WidgetSpan sparingly; if you need many inline widgets, consider using a Wrap or Row for better performance.
    • Always set softWrap: true and overflow: TextOverflow.ellipsis to handle long text gracefully.

Key Takeaways

    • RichText displays text with multiple styles using TextSpan nodes.
    • TextSpan can have style, text, children, and recognizer for taps.
    • WidgetSpan allows embedding widgets inline (icons, buttons).
    • Use SelectableText.rich for selectable rich text.
    • Style inheritance reduces code duplication.
    • Manage recognizers carefully to avoid memory leaks.

Try it yourself

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

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('RichText Demo')),
        body: Padding(
          padding: const EdgeInsets.all(16.0),
          child: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                RichText(
                  text: TextSpan(
                    style: TextStyle(fontSize: 20, color: Colors.black),
                    children: [
                      TextSpan(text: 'Hello '),
                      TextSpan(
                        text: 'world!',
                        style: TextStyle(color: Colors.blue, fontWeight: FontWeight.bold),
                      ),
                    ],
                  ),
                ),
                SizedBox(height: 20),
                RichText(
                  text: TextSpan(
                    style: TextStyle(fontSize: 18),
                    children: [
                      TextSpan(text: 'Tap the '),
                      WidgetSpan(
                        child: GestureDetector(
                          onTap: () => print('Star tapped'),
                          child: Icon(Icons.star, size: 24, color: Colors.amber),
                        ),
                      ),
                      TextSpan(text: ' to like'),
                    ],
                  ),
                ),
                SizedBox(height: 20),
                RichText(
                  text: TextSpan(
                    style: TextStyle(fontSize: 16),
                    children: [
                      TextSpan(text: 'Read our '),
                      TextSpan(
                        text: 'privacy policy',
                        style: TextStyle(color: Colors.blue, decoration: TextDecoration.underline),
                        recognizer: TapGestureRecognizer()
                          ..onTap = () => print('Privacy policy tapped'),
                      ),
                      TextSpan(text: '.'),
                    ],
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

Test Your Knowledge

Q1
of 4

Which widget is used to create text with multiple styles?

A
Text
B
RichText
C
StyledText
D
MultiText
Q2
of 4

How do you embed a widget inside text?

A
WidgetSpan
B
WidgetTextSpan
C
InlineWidget
D
TextSpan.widget
Q3
of 4

What must you import to use `TapGestureRecognizer`?

A
package:flutter/material.dart
B
package:flutter/widgets.dart
C
package:flutter/gestures.dart
D
dart:ui
Q4
of 4

Which widget should you use if you want the text to be selectable?

A
RichText
B
SelectableText.rich
C
SelectableRichText
D
Text.rich

Frequently Asked Questions

What is the difference between `RichText` and `Text.rich`?

RichText is the underlying widget that displays a tree of InlineSpan. Text.rich is a convenience constructor that returns a RichText widget. They are essentially the same; Text.rich is just shorter syntax.

How do I make part of the text clickable without using a `GestureDetector`?

Use a TapGestureRecognizer attached to the TextSpan via the recognizer property. This allows the span to handle taps independently of the rest of the text.

Can I use `RichText` with localization?

Yes. You can build the TextSpan tree dynamically using localized strings. For simple cases, you can combine multiple strings; for complex formatting, consider using a package like intl to generate formatted strings.

How do I apply a gradient to part of the text?

You cannot apply a gradient directly to a TextSpan. You can use a ShaderMask over the entire RichText, but that affects all text. For per‑span gradient, you would need to use a custom painter or multiple RichText widgets stacked, which is not recommended. Usually, a solid color or underline is sufficient for links.

How do I handle text overflow in RichText?

Set overflow: TextOverflow.ellipsis and softWrap: true. If you have WidgetSpan, be aware that they don't support overflow ellipsis; they will clip if the container is too narrow. Ensure your container has enough width or use Flexible in a Row.

Previous

flutter text widget

Next

flutter decoration

Related Content

Need help?

Explore our comprehensive docs or start a chat with our tech experts.