Module 6: Text, Images, and Buttons

The Content Widgets That Make Screens Worth Looking At

Building UI Screens

Text, images, and buttons on a mobile screen Layout widgets are the skeleton. Text, images, and buttons are the skin, the face, and the handshake.

🎯

Teach: How to display styled text, load images from multiple sources, and create interactive buttons with Flutter's built-in widget library. See: Text with multiple styles in a single paragraph, network images with rounded corners, and buttons for every occasion. Feel: That you have the pieces to build screens that actually look like real apps.

🎙️

Last module, you learned how to arrange things on screen. But arranging empty boxes isn't very exciting. Today you fill those boxes with content that users actually see and interact with -- styled text, images pulled from the internet or bundled with your app, and buttons that do things when tapped. By the end of this module, you'll build a product card that could sit in any e-commerce app. Let's go.


Text and TextStyle: More Than Just Words

🎯

Teach: The Text widget displays strings, and TextStyle gives you fine-grained control over size, weight, color, spacing, and overflow behavior. See: A Text widget styled with fontSize, fontWeight, color, letterSpacing, and ellipsis overflow handling. Feel: That styling text in Flutter is straightforward and gives you more control than CSS.

🎙️

The humble Text widget is probably the widget you'll use more than any other. But "display some text" barely scratches the surface. Flutter gives you fine-grained control over every aspect of how text appears -- size, weight, color, spacing, decoration, overflow behavior. Learning TextStyle is like learning to use a word processor's formatting toolbar, except you do it in code.

At its simplest, Text takes a string:

Text('Hello, Flutter!')

But the real power is in TextStyle:

Text(
  'Hello, Flutter!',
  style: TextStyle(
    fontSize: 24,
    fontWeight: FontWeight.bold,
    color: Colors.blue,
    letterSpacing: 1.5,
    fontStyle: FontStyle.italic,
    decoration: TextDecoration.underline,
  ),
)

The TextStyle Properties You'll Use Most

Property What It Does Example Values
fontSize Size in logical pixels 12, 16, 24, 32
fontWeight How thick the characters are FontWeight.bold, FontWeight.w300
color Text color Colors.blue, Color(0xFF123456)
letterSpacing Space between letters 1.0, 2.0, -0.5
height Line height as a multiplier 1.5 (150% of font size)
fontStyle Normal or italic FontStyle.italic
decoration Underline, strikethrough, etc. TextDecoration.underline

Text Overflow

What happens when text is too long? You control it:

Text(
  'This is a very long piece of text that might not fit on a single line...',
  maxLines: 2,
  overflow: TextOverflow.ellipsis,
)

TextOverflow.ellipsis adds "..." when text is clipped. Other options: clip (just cuts off), fade (fades out), visible (overflows visibly).


RichText: Multiple Styles in One Block

🎯

Teach: RichText and TextSpan let you apply different styles to different parts of a single text block -- no need for separate Text widgets crammed into a Row. See: A sentence where individual words have different colors, weights, and decorations. Feel: That building "Terms and Conditions" links or highlighted keywords is straightforward.

Sometimes you need one word bold, another word colored, and the rest normal -- all in the same sentence. That's RichText with TextSpan:

RichText(
  text: TextSpan(
    text: 'Hello ',
    style: TextStyle(color: Colors.black, fontSize: 18),
    children: [
      TextSpan(
        text: 'Flutter',
        style: TextStyle(
          fontWeight: FontWeight.bold,
          color: Colors.blue,
        ),
      ),
      TextSpan(text: '! Welcome aboard.'),
    ],
  ),
)

The parent TextSpan sets the default style. Children inherit that style and can override specific properties. The children flow inline -- no line breaks between them -- just like words in a sentence.

When to Use RichText vs. Multiple Text Widgets

Use RichText when styled text should flow together as a paragraph. Use separate Text widgets when items are distinct elements that need their own layout (like a title and subtitle stacked vertically).

🎙️

Here's a rule of thumb: if the text reads as one sentence or paragraph, use RichText. If the text pieces are separate visual elements, use separate Text widgets in a Column. You'll develop the instinct for which is which pretty quickly.


Images: From the Internet and Your App

🎯

Teach: Image.network loads images from URLs at runtime, Image.asset loads bundled images instantly, and ClipRRect adds rounded corners to any widget. See: Network images with BoxFit options, asset images from the app bundle, and ClipRRect rounding image corners. Feel: That loading and displaying images in Flutter is surprisingly simple compared to other frameworks.

Images make apps feel alive. Flutter gives you two main ways to load them.

Image.network: Loading from the Internet

Image.network(
  'https://picsum.photos/200',
  width: 200,
  height: 200,
  fit: BoxFit.cover,
)

This fetches an image from the internet and displays it. Simple. The fit property controls how the image fills its container:

BoxFit Behavior
cover Fill the box completely, crop if needed
contain Fit inside the box, may leave empty space
fill Stretch to fill exactly (may distort)
fitWidth Match the width, height adjusts
fitHeight Match the height, width adjusts

Image.asset: Bundled with Your App

For images shipped with your app, put them in an assets/images/ folder and register them in pubspec.yaml:

flutter:
  assets:
    - assets/images/

Then load them:

Image.asset(
  'assets/images/logo.png',
  width: 100,
)

Asset images load instantly (no network delay) and work offline.

ClipRRect: Rounded Image Corners

Want rounded corners on an image? Wrap it in ClipRRect:

ClipRRect(
  borderRadius: BorderRadius.circular(12),
  child: Image.network(
    'https://picsum.photos/200',
    width: 200,
    height: 200,
    fit: BoxFit.cover,
  ),
)
💡

ClipRRect clips ANY widget to rounded corners, not just images. The name stands for "Clip Rounded Rectangle" -- the double R is not a typo.

🎙️

Images are one of those areas where Flutter feels gentle compared to native development. On iOS or Android, you're dealing with caches, resolution variants, lazy loading, and a half-dozen framework classes. In Flutter, Image.network loads from a URL. Image.asset loads from your bundle. BoxFit decides how the image fills its container. That's the whole API you need for ninety percent of image use cases. When you get to more advanced needs like persistent caching or placeholder images, we'll reach for the cached_network_image package in Module 17 — but for now, the built-in Image widget does the job.


Icons and IconButton

🎯

Teach: Flutter includes hundreds of Material Icons built in, and IconButton wraps any icon with tap handling and an ink splash effect. See: Icons displayed in different colors and sizes, and an IconButton that responds to taps with a visual ripple. Feel: Delight that you get a full icon library for free without downloading anything.

🎙️

Flutter ships with the entire Material Icons set built right in. No downloading icon packs, no importing SVGs. Just use the Icons class and you have hundreds of icons at your fingertips. And when you need an icon that does something when tapped, wrap it in an IconButton.

Icons

Icon(Icons.favorite, color: Colors.red, size: 30)
Icon(Icons.star, color: Colors.amber, size: 24)
Icon(Icons.home, color: Colors.blue, size: 32)

The Icons class has hundreds of options. In your IDE, type Icons. and scroll through autocomplete to explore.

IconButton

An icon that responds to taps:

IconButton(
  onPressed: () {
    print('Heart tapped!');
  },
  icon: Icon(Icons.favorite),
  color: Colors.red,
  iconSize: 32,
)

IconButton adds a circular ink splash effect on tap. It's the standard way to make icons interactive.


Buttons: The Right Button for the Job

🎯

Teach: Flutter provides different button types for different emphasis levels -- elevated for primary actions, text for low-emphasis, outlined for medium-emphasis. See: Each button type side by side, with custom styling applied. Feel: That choosing the right button type is a design decision, not a random choice.

Flutter's Material button types follow a hierarchy of visual emphasis:

ElevatedButton: Primary Actions

The most visually prominent button. Raised with a shadow. Use for the main action on a screen.

ElevatedButton(
  onPressed: () { print('Buy now!'); },
  child: Text('Buy Now'),
)

TextButton: Low-Emphasis Actions

Flat, no background, no border. Use for secondary actions or in dialogs.

TextButton(
  onPressed: () { print('Learn more'); },
  child: Text('Learn More'),
)

OutlinedButton: Medium-Emphasis Actions

Has a border but no fill. Visually between elevated and text.

OutlinedButton(
  onPressed: () { print('Add to cart'); },
  child: Text('Add to Cart'),
)

FloatingActionButton: The Standout

A circular button that floats above the content. Typically one per screen for the primary action.

Scaffold(
  floatingActionButton: FloatingActionButton(
    onPressed: () { print('Add!'); },
    child: Icon(Icons.add),
  ),
  body: ...,
)

Button Styling with styleFrom

All buttons can be customized with their styleFrom method:

ElevatedButton(
  onPressed: () {},
  style: ElevatedButton.styleFrom(
    backgroundColor: Colors.teal,
    foregroundColor: Colors.white,
    padding: EdgeInsets.symmetric(horizontal: 32, vertical: 16),
    shape: RoundedRectangleBorder(
      borderRadius: BorderRadius.circular(12),
    ),
    textStyle: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
  ),
  child: Text('Styled Button'),
)

Notice the pattern: ElevatedButton.styleFrom(), TextButton.styleFrom(), OutlinedButton.styleFrom(). Each button type has its own styleFrom factory.

🎙️

A quick design tip: don't use ElevatedButton for everything. Having a visual hierarchy of button importance is what makes interfaces feel polished. The primary action gets ElevatedButton. Secondary actions get TextButton or OutlinedButton. If everything is elevated, nothing stands out.


There Are No Dumb Questions

🎯

Teach: Answers to common questions about button types, image loading, text selection, and icon-text buttons. See: Clear explanations that resolve the confusion beginners have about content widgets. Feel: That these are normal questions everyone asks, and the answers are straightforward.

Q: Why does Flutter have so many button types? Can't I just style one button differently?

A: You could, but the different button classes encode design intent. When another developer reads your code, TextButton immediately communicates "this is a low-emphasis action." It also means the button automatically follows your app's theme correctly without manual styling.

Q: What's the difference between Image.network and Image.asset?

A: Image.network downloads from a URL at runtime -- it needs internet and has loading delay. Image.asset loads from files bundled in your app -- instant, works offline, but increases app size. Use network for user-generated content and dynamic images. Use assets for logos, icons, and images that never change.

Q: Can I make text selectable?

A: Yes! Use SelectableText instead of Text. It has the same API but lets users highlight and copy the text. Regular Text widgets are not selectable by default.

Q: How do I add a loading indicator while a network image loads?

A: Use the loadingBuilder parameter on Image.network, or use the FadeInImage widget which shows a placeholder while loading. We'll cover more advanced patterns later in the course.

Q: What if I want a button with both an icon and text?

A: All button types have an .icon constructor: ElevatedButton.icon(onPressed: ..., icon: Icon(Icons.add), label: Text('Add')).

🎙️

The button-types question is worth re-reading. I know it looks like busywork — why not one button class with a style parameter? But the difference between a good-looking app and a generic-looking app is visual hierarchy, and visual hierarchy comes from using the right button at the right time. ElevatedButton for the main action. OutlinedButton for secondary actions. TextButton for tertiary or cancel actions. IconButton when space is tight and the icon is unambiguous. Pick the right type, and your theming does the visual work for you.


Sharpen Your Pencil: Product Card

🎯

Teach: How to combine Text, Image.network, Icon, multiple button types, Card, and layout widgets into a polished product card component. See: A shopping-app-quality product card with an image, rating stars, description, price, and action buttons. Feel: Pride that you can build something that looks like it belongs in a real e-commerce app.

This is a meaty exercise that combines everything from this module -- and a few layout widgets from last module too. You'll build a product card that could sit in any shopping app.

Setup

flutter create text_images_buttons

Build lib/product_card.dart

Create a StatelessWidget called ProductCard that takes these parameters: - String name (required) - String description (required) - String imageUrl (required) - double price (required) - double rating (required)

The widget returns a Card with elevation: 4 and rounded corners (borderRadius: 12), with margin of 16. Inside:

  1. An Image.network using imageUrl -- height 200, full width, BoxFit.cover, wrapped in ClipRRect with rounded top corners.

  2. A Padding of 16 containing a Column (crossAxisAlignment: start):

  3. Product name -- fontSize 20, bold
  4. SizedBox(height: 4)
  5. Rating row: rating.round() star icons (amber, size 18) followed by the rating as text (grey)
  6. SizedBox(height: 8)
  7. Description -- maxLines 2, ellipsis overflow, grey color
  8. SizedBox(height: 12)
  9. Price and buttons row (spaceBetween):
    • Price text -- fontSize 22, bold, teal
    • Three buttons: IconButton (favorite, red), OutlinedButton ("Details"), ElevatedButton ("Add to Cart", teal background)

Build lib/product_screen.dart

Create a ProductScreen with: - AppBar titled 'Shop' - FloatingActionButton with Icons.shopping_cart - Body: SingleChildScrollView with a Column of at least 3 ProductCard widgets using different data and https://picsum.photos/seed/productN/400/300 URLs

Wire up main.dart

Set ProductScreen as the home screen with primarySwatch: Colors.teal.

🔄

Where this fits: The ProductCard exercise combines Text, TextStyle, Image.network, Icon, ElevatedButton, OutlinedButton, IconButton, Card, Row, Column, Padding, SizedBox, and ClipRRect. If you can build this, you have a solid grasp of Flutter's fundamental content and layout widgets.

🎙️

I want you to notice something when you finish this exercise. Count the number of widget classes you used. It's probably around fifteen. None of them were complicated individually — Text, Icon, ClipRRect, Image.network — but composing them gave you a card that would pass for production code. That's the Flutter pattern in miniature. Small, focused widgets, stacked and nested, producing something polished. The hardest part is not learning the widgets. It's internalizing the instinct to compose instead of configure. This exercise is where that instinct starts forming.


📝 Module Quiz

Test what you learned. 45 seconds per question. Passing score is set per module. Your best attempt is saved in your browser so you can track progress -- nothing is sent to a server.

Wrapping Up

🎯

Teach: A recap of all content widgets covered -- Text, RichText, Images, Icons, and Buttons -- and how they complete the picture alongside layout widgets. See: The full toolkit: layout for arrangement, content for display, and buttons for interaction. Feel: Ready to build screens that look and feel like real apps.

🎙️

You now know how to put content on screen and make it look good. Text with TextStyle gives you full typographic control. RichText lets you mix styles in a single block. Images load from the network or your app bundle. Icons give you hundreds of glyphs for free. And buttons come in flavors that match every level of visual emphasis. Combined with the layout widgets from last module, you have everything you need to build screens that look like real apps. Next up: making those screens interactive with StatefulWidget.

💡

Every screen in every app is built from three things: layout widgets for arrangement, content widgets for display, and interactive widgets for user actions. You now have all three.

1 / 1