Lesson 20 of 51 beginner

Text, Icons & Images

The Visual Vocabulary of Flutter

Open interactive version (quiz + challenge)

Real-world analogy

If widgets are LEGO bricks, then Text, Icons, and Images are the stickers and decals you put on them. Text is the label maker -- it prints words in any font, size, or color you want. Icons are pre-made symbol stickers from a huge sticker book (Material Icons). Images are photos you glue onto your bricks -- either from your own collection (assets) or downloaded from the internet (network). In team_mvp_kit, these three widgets appear on every single screen.

What is it?

Text, Icons, and Images are the three most fundamental presentation widgets in Flutter. Text renders strings with customizable typography. Icon displays vector symbols from icon fonts. Image loads and displays raster graphics from local assets or remote URLs. These widgets are the visual building blocks that make your app communicate with users. They are lightweight, highly customizable, and designed to work seamlessly with Flutter's theming system.

Real-world relevance

In team_mvp_kit, every screen uses these widgets. Text displays user names, labels, prices, error messages, and descriptions. Icons serve as visual shortcuts in navigation bars, buttons, and status indicators. Images display user avatars, product photos, and branding assets. The project uses Theme.of(context).textTheme for consistent text styling and CircleAvatar for all user profile pictures. Network images are loaded with error handling to gracefully show placeholders when the connection fails.

Key points

Code example

import 'package:flutter/material.dart';

class UserProfileHeader extends StatelessWidget {
  final String name;
  final String email;
  final String? avatarUrl;
  final bool isVerified;

  const UserProfileHeader({
    super.key,
    required this.name,
    required this.email,
    this.avatarUrl,
    this.isVerified = false,
  });

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final colorScheme = theme.colorScheme;

    return Padding(
      padding: const EdgeInsets.all(24),
      child: Column(
        children: [
          Stack(
            children: [
              CircleAvatar(
                radius: 48,
                backgroundColor: colorScheme.primaryContainer,
                backgroundImage: avatarUrl != null
                    ? NetworkImage(avatarUrl!)
                    : null,
                child: avatarUrl == null
                    ? Text(
                        name[0].toUpperCase(),
                        style: theme.textTheme.headlineMedium?.copyWith(
                          color: colorScheme.onPrimaryContainer,
                        ),
                      )
                    : null,
              ),
              if (isVerified)
                Positioned(
                  right: 0,
                  bottom: 0,
                  child: Container(
                    padding: const EdgeInsets.all(2),
                    decoration: BoxDecoration(
                      color: colorScheme.surface,
                      shape: BoxShape.circle,
                    ),
                    child: Icon(
                      Icons.verified,
                      size: 24,
                      color: colorScheme.primary,
                    ),
                  ),
                ),
            ],
          ),
          const SizedBox(height: 16),
          Text(
            name,
            style: theme.textTheme.titleLarge?.copyWith(
              fontWeight: FontWeight.bold,
            ),
          ),
          const SizedBox(height: 4),
          Text(
            email,
            style: theme.textTheme.bodyMedium?.copyWith(
              color: colorScheme.onSurfaceVariant,
            ),
          ),
        ],
      ),
    );
  }
}

Line-by-line walkthrough

  1. 1. Import Flutter material library
  2. 2. Define UserProfileHeader as a StatelessWidget
  3. 3. Declare required name and email, optional avatarUrl, and isVerified flag
  4. 4. Const constructor with named parameters
  5. 5. In build, get theme and colorScheme for consistent styling
  6. 6. Return Padding with 24px on all sides for outer spacing
  7. 7. Column to stack avatar, name, and email vertically
  8. 8. Stack widget to overlay the verified badge on top of the avatar
  9. 9. CircleAvatar with radius 48 for a prominent profile picture
  10. 10. Set backgroundColor to primaryContainer for a themed fallback color
  11. 11. If avatarUrl exists, load it as a NetworkImage for backgroundImage
  12. 12. If no avatar URL, show the first letter of the name as fallback text
  13. 13. Style the fallback letter with headlineMedium in onPrimaryContainer color
  14. 14. Conditionally add verified badge only if isVerified is true
  15. 15. Position the badge at bottom-right of the avatar using Positioned
  16. 16. Wrap the badge icon in a Container with surface-colored circle background
  17. 17. Show the verified icon in primary color at size 24
  18. 18. SizedBox height 16 for spacing between avatar and name
  19. 19. Text widget for name using titleLarge style with bold weight
  20. 20. SizedBox height 4 for tight spacing between name and email
  21. 21. Text widget for email using bodyMedium in a muted variant color

Spot the bug

Image.network(
  'https://example.com/photo.jpg',
  width: 200,
  height: 200,
)

Image.asset(
  'assets/logo.png',
  width: 100,
)
Need a hint?
One will crash if the URL is unreachable. The other might crash if something is missing from a config file.
Show answer
Two issues: (1) Image.network has no errorBuilder, so if the URL fails to load the app shows an error. Add errorBuilder: (context, error, stackTrace) => Icon(Icons.broken_image). (2) Image.asset requires the asset path to be declared in pubspec.yaml under flutter > assets. Without that declaration, Flutter cannot find the file and will throw an error at runtime.

Explain like I'm 5

Think of making a poster for a school event. You need three things: words to tell people what it is about (that is Text), little pictures like arrows and stars to make it eye-catching (those are Icons), and maybe a photo of last year's event to get people excited (that is an Image). In Flutter, every screen is like a poster. Text shows the words, Icons show the symbols, and Images show the pictures. You can make the words big or small, bold or colorful. You can pick from thousands of symbols. And you can load pictures from your phone or from the internet. That is basically it!

Fun fact

Flutter ships with over 2,000 Material icons and over 600 Cupertino (iOS-style) icons built right into the framework -- no external packages needed. The Material icons are actually a font file (MaterialIcons-Regular.otf) that is bundled with every Flutter app. Each Icon widget just renders a character from this font, which is why icons scale perfectly at any size without getting blurry like images would.

Hands-on challenge

Build a ContactListItem widget that displays: a CircleAvatar (with network image and text fallback), a column with the person's name in bold and their phone number in grey below, a verified badge icon if they are verified, and a trailing call IconButton. Use Theme.of(context) for all text styles. Handle the case where the avatar image might fail to load. Create a ListView showing 5 different ContactListItem widgets.

More resources

Open interactive version (quiz + challenge) ← Back to course: Flutter & Dart