Text & Typography Widgets
The Text Widget
The Text widget is one of the most commonly used widgets in Flutter. It displays a string of text with a single style. For more complex text with multiple styles, Flutter provides RichText and Text.rich.
Basic Text Widget
import 'package:flutter/material.dart';
class TextExamples extends StatelessWidget {
const TextExamples({super.key});
@override
Widget build(BuildContext context) {
return const Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Simple text with default styling'),
Text(
'Bold and larger text',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
Text(
'Colored italic text',
style: TextStyle(
fontSize: 18,
fontStyle: FontStyle.italic,
color: Colors.blue,
),
),
],
);
}
}
TextStyle — Complete Styling Control
TextStyle gives you full control over how text appears. Here are the most important properties:
TextStyle Properties
Text(
'Styled Text Example',
style: TextStyle(
fontSize: 20, // Size in logical pixels
fontWeight: FontWeight.w600, // 100 to 900 (bold = 700)
fontStyle: FontStyle.italic, // normal or italic
color: Colors.deepPurple, // Text color
backgroundColor: Colors.yellow.shade100, // Background highlight
letterSpacing: 2.0, // Space between letters
wordSpacing: 4.0, // Space between words
height: 1.5, // Line height multiplier
decoration: TextDecoration.underline, // Underline, lineThrough, overline
decorationColor: Colors.red, // Decoration line color
decorationStyle: TextDecorationStyle.wavy, // solid, double, dotted, dashed, wavy
decorationThickness: 2.0, // Thickness of decoration
shadows: [
Shadow(
color: Colors.black26,
offset: Offset(2, 2),
blurRadius: 4,
),
],
),
)
FontWeight Scale
Flutter provides named constants for font weights, ranging from thin (100) to black (900):
Font Weight Examples
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Thin (w100)', style: TextStyle(fontWeight: FontWeight.w100)),
Text('Light (w300)', style: TextStyle(fontWeight: FontWeight.w300)),
Text('Regular (w400)', style: TextStyle(fontWeight: FontWeight.w400)),
Text('Medium (w500)', style: TextStyle(fontWeight: FontWeight.w500)),
Text('SemiBold (w600)', style: TextStyle(fontWeight: FontWeight.w600)),
Text('Bold (w700)', style: TextStyle(fontWeight: FontWeight.bold)),
Text('ExtraBold (w800)', style: TextStyle(fontWeight: FontWeight.w800)),
Text('Black (w900)', style: TextStyle(fontWeight: FontWeight.w900)),
],
)
TextAlign & TextDirection
Control how text is aligned within its container using textAlign. Note that alignment works relative to the text’s available width — the Text widget must have a defined width (or be in an expanded layout).
Text Alignment
SizedBox(
width: double.infinity,
child: Column(
children: [
Text('Left aligned', textAlign: TextAlign.left),
Text('Center aligned', textAlign: TextAlign.center),
Text('Right aligned', textAlign: TextAlign.right),
Text(
'Justified text spreads words evenly across the full width '
'of the container making both edges align neatly.',
textAlign: TextAlign.justify,
),
],
),
)
TextOverflow & maxLines
When text is too long for its container, you can control how it overflows using overflow and maxLines:
Handling Text Overflow
Column(
children: [
// Ellipsis at end
Text(
'This is a very long text that will be truncated with an ellipsis at the end when it overflows its container',
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
// Fade out
Text(
'This text fades out at the edge when it overflows its container boundary',
maxLines: 1,
overflow: TextOverflow.fade,
softWrap: false,
),
// Clip text
Text(
'This text is clipped when it exceeds the available space',
maxLines: 1,
overflow: TextOverflow.clip,
),
// Multi-line with limit
Text(
'This is a longer paragraph that will show up to two lines '
'of text and then display an ellipsis if there is still '
'more content to show beyond those two lines.',
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
],
)
TextOverflow.ellipsis is the most commonly used overflow style in production apps. It clearly signals to users that there is more content. Combine it with maxLines to control exactly how many lines are shown.RichText & TextSpan
When you need to display text with multiple styles in a single paragraph, use RichText or the shorthand Text.rich. The TextSpan class represents a section of text with its own style.
RichText with Multiple Styles
// Using Text.rich (preferred shorthand)
Text.rich(
TextSpan(
style: const TextStyle(fontSize: 16, color: Colors.black87),
children: [
const TextSpan(text: 'Flutter is '),
TextSpan(
text: 'beautiful',
style: TextStyle(
color: Colors.blue.shade700,
fontWeight: FontWeight.bold,
),
),
const TextSpan(text: ', '),
TextSpan(
text: 'fast',
style: TextStyle(
color: Colors.green.shade700,
fontWeight: FontWeight.bold,
),
),
const TextSpan(text: ', and '),
TextSpan(
text: 'productive',
style: TextStyle(
color: Colors.orange.shade700,
fontWeight: FontWeight.bold,
decoration: TextDecoration.underline,
),
),
const TextSpan(text: '.'),
],
),
)
// Using RichText directly
RichText(
text: TextSpan(
style: DefaultTextStyle.of(context).style,
children: const [
TextSpan(text: 'Price: '),
TextSpan(
text: '\$29.99',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.green,
),
),
TextSpan(
text: ' \$49.99',
style: TextStyle(
decoration: TextDecoration.lineThrough,
color: Colors.grey,
),
),
],
),
)
RichText directly, it does not inherit the default text style from the widget tree. You must manually set the base style. The Text.rich shorthand inherits the default style automatically, making it the preferred approach in most cases.SelectableText
By default, text in Flutter is not selectable. Use SelectableText when you want users to be able to select and copy text:
Selectable Text Widgets
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Basic selectable text
const SelectableText(
'You can select and copy this text by long pressing on it.',
style: TextStyle(fontSize: 16),
),
const SizedBox(height: 16),
// Selectable with custom cursor and toolbar
SelectableText(
'API Key: sk-abc123def456ghi789',
style: const TextStyle(
fontFamily: 'monospace',
fontSize: 14,
backgroundColor: Color(0xFFF5F5F5),
),
cursorColor: Colors.blue,
showCursor: true,
onTap: () => debugPrint('Text tapped'),
),
const SizedBox(height: 16),
// Rich selectable text
const SelectableText.rich(
TextSpan(
children: [
TextSpan(text: 'Status: '),
TextSpan(
text: 'Active',
style: TextStyle(
color: Colors.green,
fontWeight: FontWeight.bold,
),
),
],
),
),
],
)
DefaultTextStyle
DefaultTextStyle sets a default text style for all Text widgets in its subtree. This is useful for applying consistent styling to a group of text widgets without repeating the style on each one.
DefaultTextStyle Usage
DefaultTextStyle(
style: const TextStyle(
fontSize: 16,
color: Colors.black87,
height: 1.6,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// All these inherit the default style
const Text('First paragraph with default styling.'),
const SizedBox(height: 8),
const Text('Second paragraph inherits the same style.'),
const SizedBox(height: 8),
// Override specific properties
Text(
'Bold text that also inherits base style.',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.blue.shade800,
),
),
],
),
)
Google Fonts Package
The google_fonts package provides access to over 1,000 fonts from Google Fonts. Add it to your pubspec.yaml and use it directly in your styles.
Using Google Fonts
// pubspec.yaml
// dependencies:
// google_fonts: ^6.1.0
import 'package:google_fonts/google_fonts.dart';
class FontShowcase extends StatelessWidget {
const FontShowcase({super.key});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Roboto Slab Heading',
style: GoogleFonts.robotoSlab(
fontSize: 28,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
Text(
'Lato for body text looks clean and modern.',
style: GoogleFonts.lato(
fontSize: 16,
height: 1.6,
),
),
const SizedBox(height: 8),
Text(
'var code = "monospace";',
style: GoogleFonts.firaCode(
fontSize: 14,
color: Colors.green.shade800,
backgroundColor: Colors.grey.shade100,
),
),
const SizedBox(height: 8),
Text(
'Elegant Display Font',
style: GoogleFonts.playfairDisplay(
fontSize: 32,
fontStyle: FontStyle.italic,
),
),
],
);
}
}
// Apply Google Fonts to entire theme
MaterialApp(
theme: ThemeData(
textTheme: GoogleFonts.interTextTheme(),
),
)
GoogleFonts.config.allowRuntimeFetching = false; and bundling fonts as assets. This prevents runtime font downloads and ensures fonts are always available offline.Practical Example: Styled Article
Let’s combine everything to build a beautifully styled article widget:
Styled Article Widget
class StyledArticle extends StatelessWidget {
final String title;
final String author;
final String date;
final String body;
final String category;
const StyledArticle({
super.key,
required this.title,
required this.author,
required this.date,
required this.body,
required this.category,
});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Category badge
Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
decoration: BoxDecoration(
color: Colors.blue.shade50,
borderRadius: BorderRadius.circular(16),
),
child: Text(
category.toUpperCase(),
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w700,
color: Colors.blue.shade700,
letterSpacing: 1.2,
),
),
),
const SizedBox(height: 12),
// Title
Text(
title,
style: const TextStyle(
fontSize: 28,
fontWeight: FontWeight.w800,
height: 1.2,
letterSpacing: -0.5,
),
),
const SizedBox(height: 12),
// Author and date
Text.rich(
TextSpan(
style: TextStyle(fontSize: 14, color: Colors.grey.shade600),
children: [
const TextSpan(text: 'By '),
TextSpan(
text: author,
style: const TextStyle(fontWeight: FontWeight.w600),
),
TextSpan(text: ' \u2022 \$date'),
],
),
),
const SizedBox(height: 20),
const Divider(),
const SizedBox(height: 20),
// Body text
Text(
body,
style: TextStyle(
fontSize: 16,
height: 1.8,
color: Colors.grey.shade800,
),
textAlign: TextAlign.justify,
),
],
),
);
}
}
Practical Example: Rich Text Bio
Build a bio section with mixed styles, links, and interactive spans:
Rich Text Bio Widget
class RichTextBio extends StatelessWidget {
final String name;
final String role;
final String bio;
final List<String> skills;
const RichTextBio({
super.key,
required this.name,
required this.role,
required this.bio,
required this.skills,
});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 4),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
name,
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
Text(
role,
style: TextStyle(
fontSize: 16,
color: Colors.blue.shade600,
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: 16),
Text(
bio,
style: TextStyle(
fontSize: 15,
height: 1.7,
color: Colors.grey.shade700,
),
),
const SizedBox(height: 16),
Text.rich(
TextSpan(
style: const TextStyle(fontSize: 14),
children: [
const TextSpan(
text: 'Skills: ',
style: TextStyle(fontWeight: FontWeight.w600),
),
...skills.asMap().entries.map((entry) => TextSpan(
children: [
TextSpan(
text: entry.value,
style: TextStyle(
color: Colors.blue.shade700,
fontWeight: FontWeight.w500,
),
),
if (entry.key < skills.length - 1)
const TextSpan(text: ' \u2022 '),
],
)),
],
),
),
],
),
);
}
}
Summary
In this lesson, you learned:
- The Text widget displays single-style text with full
TextStylecontrol - TextStyle properties include fontSize, fontWeight, color, letterSpacing, height, decoration, and shadows
- TextAlign controls horizontal alignment; TextOverflow and maxLines handle overflow
- RichText and Text.rich with
TextSpanenable multi-styled text in a single widget - SelectableText allows users to select and copy text content
- DefaultTextStyle applies consistent styling to all Text widgets in a subtree
- The google_fonts package provides access to 1,000+ fonts