On this page23 sections
  1. Prerequisites
  2. Connection With Previous Lectures
  3. Learning Objectives
  4. 1. Icons
  5. 2. Images From Your Project: Image.asset
  6. 3. Images From the Internet: Image.network
  7. 4. BoxFit — How an Image Fills Its Box
  8. 5. Rounded Images and Avatars
  9. 6. The Four Buttons
  10. 7. Buttons With Icons
  11. 8. Disabled Buttons
  12. 9. Styling Buttons
  13. 10. Responding to a Tap: SnackBar
  14. 11. A Real-World Example
  15. Code Examples
  16. Common Beginner Mistakes
  17. Best Practices
  18. Mini Challenge
  19. Practice Questions
  20. Practice Answers
  21. Quick Revision
  22. Key Takeaways
  23. What's Next?

Lecture 006 — Images, Icons & Buttons

Category3 — Content & Interaction
Lecture006 of 009
Estimated video length20 min
TargetFlutter 3.44 · Dart 3.12
LevelBeginner

Our screens so far are boxes and text, and the few buttons we drew did nothing. This lecture adds the content users actually look at — photos and icons — and buttons that respond when they are tapped.

Contents

Prerequisites · Connection · Objectives · Lesson · Code Examples · Common Mistakes · Best Practices · Mini Challenge · Practice Questions · Answers · Takeaways

Every screenshot and error message in this lecture is real. Each app was run on Flutter 3.44, and each error was produced by the code shown. Screenshots that show a message at the bottom were taken right after tapping the button.


Prerequisites

Students should already understand, from Lectures 001–005:

  • Container, BoxDecoration and TextStyle
  • Row, Column, Expanded, Spacer and spacing
  • Stack and Positioned
  • functions and anonymous functions (() { ... }) from the Dart course

Connection With Previous Lectures

In Lecture 005 the product card used a pink gradient where the product photo should be, and the heart was the only icon we had seen. In Lecture 001 the floating button had onPressed: () {} — tapping it did nothing. A shop, a profile or a weather app needs pictures, a set of familiar symbols, and buttons that answer the user.


Learning Objectives

By the end of this lecture you will be able to:

  • Show Material icons with Icon and change their size and color
  • Add images to a project, declare them in pubspec.yaml and show them with Image.asset
  • Show images from the internet with Image.network
  • Control how an image fills its box with BoxFit
  • Round images with ClipRRect and make avatars with CircleAvatar
  • Use the four main buttons, icon buttons and buttons with icons
  • Disable and style buttons
  • Respond to a tap by showing a SnackBar

1. Icons

Icon draws a symbol from the Material Icons set — more than two thousand of them, included with every Flutter app:

dart
body: const Center(
  child: Row(
    mainAxisAlignment: MainAxisAlignment.center,
    spacing: 16,
    children: [
      Icon(Icons.home),
      Icon(Icons.favorite, color: Colors.red),
      Icon(Icons.star, color: Colors.amber, size: 40),
      Icon(Icons.shopping_cart_outlined, size: 40),
      Icon(Icons.settings_rounded, color: Colors.indigo, size: 56),
    ],
  ),
),
A home icon, a red heart, a yellow star, a cart outline and a large indigo gear in a row
Icon with different colors and sizes
  • Icons.home, Icons.favorite, … are constants of type IconData — the shape of the icon.
  • size defaults to 24; color defaults to the current text color.
  • Many icons come in styles: Icons.shopping_cart_outlined, Icons.settings_rounded, Icons.star_border. Type Icons. in your editor to browse them, or search fonts.google.com/icons.

Analogy — IconData is the stamp; Icon is pressing the stamp onto the page. You can press the same stamp in any size and any ink color.


2. Images From Your Project: Image.asset

An asset is a file shipped inside your app. Three steps:

  1. Create a folder assets/images/ in the project (next to lib/) and copy a photo into it — for example camera.jpg.
  2. Tell Flutter about the folder in pubspec.yaml, under the flutter: section. Indentation matters: two spaces before assets:, four before the dash.
yaml
flutter:
  uses-material-design: true
  assets:
    - assets/images/
  1. Stop the app and run it again (a hot reload does not pick up new assets), then show the file:
dart
body: Image.asset('assets/images/camera.jpg'),
A photo of a vintage camera filling the width of the screen
Image.asset

The path is relative to the project folder and must match the file name exactly, including capital letters and the extension.


3. Images From the Internet: Image.network

Image.network downloads the picture while the app runs:

dart
body: Image.network('https://picsum.photos/id/96/800/500'),
A photo of a game controller filling the width of the screen
Image.network

The image appears once it has downloaded, so for a moment the space is empty. The device needs an internet connection. On Android, debug builds already have permission to use the internet; before you publish an app, add <uses-permission android:name="android.permission.INTERNET"/> to android/app/src/main/AndroidManifest.xml, or network images stay empty in the release build.

Asset or network? Use assets for pictures that are part of the app (logos, illustrations); use network for pictures that change or come from a server (products, profile photos).


4. BoxFit — How an Image Fills Its Box

When an image and its box have different shapes, fit decides what happens. Here a square photo goes into a wide box:

dart
body: Padding(
  padding: const EdgeInsets.all(16),
  child: Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    spacing: 8,
    children: [
      const Text('BoxFit.cover'),
      Container(
        width: 280,
        height: 110,
        color: Colors.grey.shade300,
        child: Image.network(
          'https://picsum.photos/id/30/600/600',
          fit: BoxFit.cover,
        ),
      ),
      const Text('BoxFit.contain'),
      Container(
        width: 280,
        height: 110,
        color: Colors.grey.shade300,
        child: Image.network(
          'https://picsum.photos/id/30/600/600',
          fit: BoxFit.contain,
        ),
      ),
      const Text('BoxFit.fill'),
      Container(
        width: 280,
        height: 110,
        color: Colors.grey.shade300,
        child: Image.network(
          'https://picsum.photos/id/30/600/600',
          fit: BoxFit.fill,
        ),
      ),
    ],
  ),
),
Three versions of a mug photo: cropped to fill, fully visible with grey bars, and stretched
BoxFit.cover, contain and fill
  • BoxFit.cover — fills the whole box and crops what sticks out. The most common choice.
  • BoxFit.contain — shows the whole image; empty bars fill the rest.
  • BoxFit.fill — stretches the image to the box. Almost always looks wrong.

You can also set width and height directly on the Image.


5. Rounded Images and Avatars

BoxDecoration's borderRadius does not round a child image. ClipRRect (clip rounded rectangle) cuts its child to rounded corners. For round profile pictures, CircleAvatar is the ready-made widget:

dart
body: Padding(
  padding: const EdgeInsets.all(16),
  child: Row(
    spacing: 24,
    children: [
      ClipRRect(
        borderRadius: BorderRadius.circular(16),
        child: Image.network(
          'https://picsum.photos/id/431/800/600',
          width: 160,
          height: 120,
          fit: BoxFit.cover,
        ),
      ),
      const CircleAvatar(
        radius: 50,
        backgroundImage: NetworkImage(
          'https://picsum.photos/id/64/300/300',
        ),
      ),
    ],
  ),
),
A coffee photo with rounded corners next to a circular portrait
ClipRRect and CircleAvatar

CircleAvatar takes an image provider — NetworkImage(url) or AssetImage(path) — rather than an Image widget, and radius is half its width. Without an image it can show a child, usually initials.


6. The Four Buttons

Material Design has four main text buttons, from most to least important:

dart
body: Center(
  child: Column(
    mainAxisSize: MainAxisSize.min,
    spacing: 12,
    children: [
      FilledButton(onPressed: () {}, child: const Text('FilledButton')),
      ElevatedButton(
        onPressed: () {},
        child: const Text('ElevatedButton'),
      ),
      OutlinedButton(
        onPressed: () {},
        child: const Text('OutlinedButton'),
      ),
      TextButton(onPressed: () {}, child: const Text('TextButton')),
    ],
  ),
),
A filled, a raised, an outlined and a plain text button stacked in the center
The four Material buttons
  • FilledButton — the main action of a screen (Pay, Save).
  • ElevatedButton — a raised button for important actions on busy backgrounds.
  • OutlinedButton — a secondary action (Cancel, Back).
  • TextButton — the least important action (Skip, Forgot password?).

Every button needs onPressed — the function that runs on a tap — and a child.


7. Buttons With Icons

Each button has an .icon constructor that takes an icon and a label. IconButton is a button that is only an icon:

dart
body: Center(
  child: Column(
    mainAxisSize: MainAxisSize.min,
    spacing: 12,
    children: [
      FilledButton.icon(
        onPressed: () {},
        icon: const Icon(Icons.shopping_cart),
        label: const Text('Add to cart'),
      ),
      OutlinedButton.icon(
        onPressed: () {},
        icon: const Icon(Icons.share),
        label: const Text('Share'),
      ),
      Row(
        mainAxisSize: MainAxisSize.min,
        children: [
          IconButton(
            onPressed: () {},
            icon: const Icon(Icons.favorite_border),
          ),
          IconButton.filled(
            onPressed: () {},
            icon: const Icon(Icons.add),
          ),
          IconButton.outlined(
            onPressed: () {},
            icon: const Icon(Icons.remove),
          ),
        ],
      ),
    ],
  ),
),
An Add to cart button with a cart icon, a Share button, and three round icon buttons
FilledButton.icon, OutlinedButton.icon and IconButton

IconButton.filled and IconButton.outlined give icon buttons a background or a border; IconButton.filledTonal gives a soft tinted background.


8. Disabled Buttons

Pass null as onPressed and the button is disabled — greyed out and not tappable:

dart
body: const Center(
  child: Column(
    mainAxisSize: MainAxisSize.min,
    spacing: 12,
    children: [
      FilledButton(onPressed: null, child: Text('Pay now')),
      OutlinedButton(onPressed: null, child: Text('Cancel')),
    ],
  ),
),
Greyed-out Pay now and Cancel buttons
onPressed: null

That is why onPressed is required but nullable: it forces you to decide whether the button works.


9. Styling Buttons

Each button class has a styleFrom method that builds its style:

dart
body: Padding(
  padding: const EdgeInsets.all(16),
  child: Column(
    crossAxisAlignment: CrossAxisAlignment.stretch,
    spacing: 12,
    children: [
      ElevatedButton(
        onPressed: () {},
        style: ElevatedButton.styleFrom(
          backgroundColor: Colors.deepOrange,
          foregroundColor: Colors.white,
          padding: const EdgeInsets.symmetric(vertical: 16),
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(8),
          ),
        ),
        child: const Text('Checkout', style: TextStyle(fontSize: 18)),
      ),
      OutlinedButton(
        onPressed: () {},
        style: OutlinedButton.styleFrom(
          foregroundColor: Colors.deepOrange,
          side: const BorderSide(color: Colors.deepOrange, width: 2),
          minimumSize: const Size.fromHeight(52),
        ),
        child: const Text('Continue shopping'),
      ),
    ],
  ),
),
A full-width deep orange Checkout button above a full-width orange outlined Continue shopping button
ElevatedButton.styleFrom and OutlinedButton.styleFrom
  • backgroundColor / foregroundColor — fill and text/icon color.
  • padding and minimumSize — Size.fromHeight(52) means "52 tall, as wide as allowed".
  • shape — RoundedRectangleBorder with a borderRadius; side — the outline.

CrossAxisAlignment.stretch made both buttons full width.


10. Responding to a Tap: SnackBar

onPressed is a normal Dart function, so it can do anything. A simple, visible response is a SnackBar — a short message that slides up from the bottom and disappears after a few seconds:

dart
body: Center(
  child: FilledButton(
    onPressed: () {
      ScaffoldMessenger.of(
        context,
      ).showSnackBar(const SnackBar(content: Text('Hello, Sara!')));
    },
    child: const Text('Say hello'),
  ),
),
A Say hello button with a dark bar at the bottom reading Hello, Sara!
After tapping Say hello

ScaffoldMessenger.of(context) finds the app's messenger — context is the build method's parameter from Lecture 001, which finally has a job. Changing what the screen shows after a tap (a counter going up, a heart turning red) needs a StatefulWidget, a topic that comes after this course; for now buttons answer with messages.


11. A Real-World Example

A product page for a shop: a rounded network photo, a name, a star rating built from icons, the price, a wishlist icon button and an Add to cart button that confirms with a SnackBar.

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

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(colorSchemeSeed: Colors.brown),
      home: const ProductScreen(),
    );
  }
}

class ProductScreen extends StatelessWidget {
  const ProductScreen({super.key});

  @override
  Widget build(BuildContext context) {
    String product = 'Coffee Mug';
    int price = 1200;
    double rating = 4.5;

    return Scaffold(
      appBar: AppBar(title: const Text('Coffee Mug')),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          spacing: 12,
          children: [
            ClipRRect(
              borderRadius: BorderRadius.circular(16),
              child: Image.network(
                'https://picsum.photos/id/30/600/600',
                height: 260,
                fit: BoxFit.cover,
              ),
            ),
            Text(
              product,
              style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
            ),
            Row(
              children: [
                const Icon(Icons.star, color: Colors.amber),
                const Icon(Icons.star, color: Colors.amber),
                const Icon(Icons.star, color: Colors.amber),
                const Icon(Icons.star, color: Colors.amber),
                const Icon(Icons.star_half, color: Colors.amber),
                const SizedBox(width: 8),
                Text('$rating'),
                const Spacer(),
                Text(
                  'Rs. $price',
                  style: const TextStyle(
                    fontSize: 20,
                    fontWeight: FontWeight.w600,
                  ),
                ),
              ],
            ),
            Row(
              spacing: 12,
              children: [
                IconButton.outlined(
                  onPressed: () {},
                  icon: const Icon(Icons.favorite_border),
                ),
                Expanded(
                  child: FilledButton.icon(
                    onPressed: () {
                      ScaffoldMessenger.of(context).showSnackBar(
                        SnackBar(content: Text('$product added to cart')),
                      );
                    },
                    icon: const Icon(Icons.shopping_cart),
                    label: const Text('Add to cart'),
                  ),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}
A coffee mug photo with rounded corners, Coffee Mug, four and a half stars, 4.5, Rs. 1200, a heart button and an Add to cart button, with the message Coffee Mug added to cart at the bottom
Product page after tapping Add to cart

SnackBar is not const here because its text uses the product variable.


Code Examples

Example 1 — Weather Icons

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

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(colorSchemeSeed: Colors.indigo),
      home: const HomeScreen(),
    );
  }
}

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Example 1')),
      body: const Padding(
        padding: EdgeInsets.all(24),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            Icon(Icons.wb_sunny, color: Colors.orange, size: 48),
            Icon(Icons.cloud, color: Colors.blueGrey, size: 48),
            Icon(Icons.water_drop, color: Colors.blue, size: 48),
            Icon(Icons.ac_unit, color: Colors.lightBlue, size: 48),
          ],
        ),
      ),
    );
  }
}
A sun, a cloud, a water drop and a snowflake icon in color
Example 1

Example 2 — A Full-Width Rounded Photo

width: double.infinity makes the image as wide as the padding allows; BoxFit.cover crops it to 220 pixels tall.

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

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(colorSchemeSeed: Colors.indigo),
      home: const HomeScreen(),
    );
  }
}

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Example 2')),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: ClipRRect(
          borderRadius: BorderRadius.circular(20),
          child: Image.network(
            'https://picsum.photos/id/431/800/600',
            height: 220,
            width: double.infinity,
            fit: BoxFit.cover,
          ),
        ),
      ),
    );
  }
}
A wide coffee photo with rounded corners
Example 2

Example 3 — A Contact Row

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

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(colorSchemeSeed: Colors.indigo),
      home: const HomeScreen(),
    );
  }
}

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Contacts')),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Row(
          spacing: 16,
          children: [
            const CircleAvatar(
              radius: 32,
              backgroundImage: NetworkImage(
                'https://picsum.photos/id/64/300/300',
              ),
            ),
            const Expanded(
              child: Text(
                'Sara Khan',
                style: TextStyle(fontSize: 20, fontWeight: FontWeight.w600),
              ),
            ),
            IconButton(onPressed: () {}, icon: const Icon(Icons.call)),
            IconButton(onPressed: () {}, icon: const Icon(Icons.message)),
          ],
        ),
      ),
    );
  }
}
A round photo of Sara, the name Sara Khan, and phone and message icons
Example 3

Example 4 — Avatars Without Photos

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

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(colorSchemeSeed: Colors.indigo),
      home: const HomeScreen(),
    );
  }
}

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Example 4')),
      body: const Padding(
        padding: EdgeInsets.all(16),
        child: Row(
          spacing: 16,
          children: [
            CircleAvatar(radius: 32, child: Text('A')),
            CircleAvatar(
              radius: 32,
              backgroundColor: Colors.teal,
              foregroundColor: Colors.white,
              child: Text('Z'),
            ),
            CircleAvatar(radius: 32, child: Icon(Icons.person)),
          ],
        ),
      ),
    );
  }
}
Three circles: A, a teal Z and a person icon
Example 4

Example 5 — Cart Quantity and Order

The total is calculated inside the SnackBar text.

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

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(colorSchemeSeed: Colors.green),
      home: const CartScreen(),
    );
  }
}

class CartScreen extends StatelessWidget {
  const CartScreen({super.key});

  @override
  Widget build(BuildContext context) {
    String item = 'Notebook';
    int quantity = 2;
    int price = 120;

    return Scaffold(
      appBar: AppBar(title: const Text('Cart')),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          spacing: 16,
          children: [
            Row(
              children: [
                Expanded(
                  child: Text(item, style: const TextStyle(fontSize: 18)),
                ),
                IconButton.outlined(
                  onPressed: () {},
                  icon: const Icon(Icons.remove),
                ),
                Padding(
                  padding: const EdgeInsets.symmetric(horizontal: 12),
                  child: Text(
                    '$quantity',
                    style: const TextStyle(fontSize: 18),
                  ),
                ),
                IconButton.filled(
                  onPressed: () {},
                  icon: const Icon(Icons.add),
                ),
              ],
            ),
            SizedBox(
              width: double.infinity,
              child: FilledButton(
                onPressed: () {
                  ScaffoldMessenger.of(context).showSnackBar(
                    SnackBar(
                      content: Text('Order placed: Rs. ${quantity * price}'),
                    ),
                  );
                },
                child: const Text('Place order'),
              ),
            ),
          ],
        ),
      ),
    );
  }
}
Notebook with minus, 2 and plus buttons, a full-width Place order button and the message Order placed: Rs. 240
Example 5 after tapping Place order

Example 6 — Bank Quick Actions

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

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(colorSchemeSeed: Colors.indigo),
      home: const BankScreen(),
    );
  }
}

class BankScreen extends StatelessWidget {
  const BankScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Bank')),
      body: Padding(
        padding: const EdgeInsets.all(24),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: [
            Column(
              children: [
                IconButton.filledTonal(
                  onPressed: () {},
                  iconSize: 32,
                  icon: const Icon(Icons.send),
                ),
                const Text('Send'),
              ],
            ),
            Column(
              children: [
                IconButton.filledTonal(
                  onPressed: () {},
                  iconSize: 32,
                  icon: const Icon(Icons.call_received),
                ),
                const Text('Receive'),
              ],
            ),
            Column(
              children: [
                IconButton.filledTonal(
                  onPressed: () {},
                  iconSize: 32,
                  icon: const Icon(Icons.receipt_long),
                ),
                const Text('Pay bills'),
              ],
            ),
          ],
        ),
      ),
    );
  }
}
Three tinted icon buttons labelled Send, Receive and Pay bills
Example 6

Common Beginner Mistakes

1. An asset that is missing or not declared

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

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(colorSchemeSeed: Colors.indigo),
      home: const HomeScreen(),
    );
  }
}

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Oops')),
      body: Center(child: Image.asset('assets/images/shoes.jpg')),
    );
  }
}
A small framed error message in the middle of the screen
The asset could not be loaded
output
══╡ EXCEPTION CAUGHT BY IMAGE RESOURCE SERVICE ╞═════════════════
The following assertion was thrown resolving an image codec:
Unable to load asset: "assets/images/shoes.jpg".
The asset does not exist or has empty data.

Check three things: the file really is at that path, the folder is listed under assets: in pubspec.yaml (with correct indentation), and you restarted the app after changing pubspec.yaml.

2. Forgetting onPressed

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

void main() {
  runApp(
    MaterialApp(
      home: Scaffold(
        body: Center(child: FilledButton(child: const Text('Pay'))),
      ),
    ),
  );
}
output
error • The named parameter 'onPressed' is required, but there's no corresponding argument. Try adding the required argument • lib/main.dart:7:29 • missing_required_argument

Every button needs onPressed. Use () {} while you build the screen, or null to disable it.

3. Passing IconData where a widget is expected

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

void main() {
  runApp(
    MaterialApp(
      home: Scaffold(
        body: Center(child: IconButton(onPressed: () {}, icon: Icons.add)),
      ),
    ),
  );
}
output
error • The argument type 'IconData' can't be assigned to the parameter type 'Widget'.  • lib/main.dart:7:64 • argument_type_not_assignable

Icons.add is only the shape. Wrap it: icon: const Icon(Icons.add).

4. Writing an icon name as a string

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

void main() {
  runApp(
    const MaterialApp(
      home: Scaffold(body: Center(child: Icon('home'))),
    ),
  );
}
output
error • A value of type 'String' can't be assigned to a parameter of type 'IconData?' in a const constructor. Try using a subtype, or removing the keyword 'const' • lib/main.dart:6:47 • const_constructor_param_type_mismatch
error • The argument type 'String' can't be assigned to the parameter type 'IconData?'.  • lib/main.dart:6:47 • argument_type_not_assignable

Icons are constants, not text: Icon(Icons.home).

5. Expecting borderRadius to round an image

A Container with BoxDecoration(borderRadius: …) around an Image rounds the container, but the photo still has square corners on top of it. Use ClipRRect.


Best Practices

  • Use one filled button per screen for the main action; make the others outlined or text buttons.
  • Use BoxFit.cover with a fixed height for photos in cards and banners.
  • Name asset files in lowercase with underscores (profile_photo.png), and keep them in assets/images/.
  • Make tap targets big enough — at least 48 × 48 pixels. IconButton already is.
  • Give a quick response to every tap (a SnackBar, a new screen), so the user knows it worked.

Mini Challenge

Build a weather screen: in the center, a 96-pixel amber Icons.wb_sunny, then 30°C in 48-pixel bold text, Lahore in 20-pixel text, and an outlined Refresh button with Icons.refresh. Tapping it shows the SnackBar Updated: Lahore 30°C. Store the city and the temperature in variables.

Try it yourself before opening the solution.

Show solution
dart
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(colorSchemeSeed: Colors.orange),
      home: const WeatherScreen(),
    );
  }
}

class WeatherScreen extends StatelessWidget {
  const WeatherScreen({super.key});

  @override
  Widget build(BuildContext context) {
    String city = 'Lahore';
    int celsius = 30;

    return Scaffold(
      appBar: AppBar(title: const Text('Weather')),
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          spacing: 12,
          children: [
            const Icon(Icons.wb_sunny, color: Colors.amber, size: 96),
            Text(
              '$celsius°C',
              style: const TextStyle(fontSize: 48, fontWeight: FontWeight.bold),
            ),
            Text(city, style: const TextStyle(fontSize: 20)),
            OutlinedButton.icon(
              onPressed: () {
                ScaffoldMessenger.of(context).showSnackBar(
                  SnackBar(content: Text('Updated: $city $celsius°C')),
                );
              },
              icon: const Icon(Icons.refresh),
              label: const Text('Refresh'),
            ),
          ],
        ),
      ),
    );
  }
}
A sun icon, 30°C, Lahore and a Refresh button, with Updated: Lahore 30°C at the bottom
Mini challenge solution after tapping Refresh

How it works

  1. A Column with MainAxisSize.min inside Center keeps everything in the middle.
  2. OutlinedButton.icon combines the icon and the label.
  3. onPressed calls ScaffoldMessenger.of(context).showSnackBar(...) with the variables interpolated into the message.

Practice Questions

Beginner

  1. Show a red Icons.favorite icon, 64 pixels, in the center.
  2. Show assets/images/mug.jpg as a 200 × 200 image in the center, cropped to fill.
  3. Show a network image with 16-pixel rounded corners and 16 pixels of padding.

Application

  1. Show Sara's profile photo as a CircleAvatar with a radius of 50.
  2. Show a full-width Login ElevatedButton with 16 pixels of padding around it.
  3. Show a Cancel TextButton and a disabled Submit FilledButton side by side.
  4. Store city = 'Karachi' and celsius = 34. Show a filled icon button with Icons.thermostat (size 40) that shows Karachi: 34°C in a SnackBar.

Challenge

  1. Build a product card for a Coffee Mug costing 1200: a rounded asset photo 240 pixels tall, a row with the name (bold, 22) and the price, and two equal buttons — an outlined Wishlist and a filled Buy that shows Added Coffee Mug to cart.

Practice Answers

Question 1 — Solution

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

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(colorSchemeSeed: Colors.indigo),
      home: const HomeScreen(),
    );
  }
}

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Question 1')),
      body: const Center(
        child: Icon(Icons.favorite, color: Colors.red, size: 64),
      ),
    );
  }
}
A large red heart in the center
Question 1

Question 2 — Solution

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

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(colorSchemeSeed: Colors.indigo),
      home: const HomeScreen(),
    );
  }
}

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Question 2')),
      body: Center(
        child: Image.asset(
          'assets/images/mug.jpg',
          width: 200,
          height: 200,
          fit: BoxFit.cover,
        ),
      ),
    );
  }
}
A square photo of a mug in the center
Question 2

Question 3 — Solution

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

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(colorSchemeSeed: Colors.indigo),
      home: const HomeScreen(),
    );
  }
}

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Question 3')),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: ClipRRect(
          borderRadius: BorderRadius.circular(16),
          child: Image.network('https://picsum.photos/id/96/800/500'),
        ),
      ),
    );
  }
}
A game controller photo with rounded corners
Question 3

Question 4 — Solution

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

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(colorSchemeSeed: Colors.indigo),
      home: const HomeScreen(),
    );
  }
}

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Question 4')),
      body: const Center(
        child: CircleAvatar(
          radius: 50,
          backgroundImage: NetworkImage('https://picsum.photos/id/64/300/300'),
        ),
      ),
    );
  }
}
A round portrait in the center
Question 4

Question 5 — Solution

SizedBox(width: double.infinity) makes the button full width. ElevatedButton.styleFrom( minimumSize: const Size.fromHeight(48)) would work too.

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

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(colorSchemeSeed: Colors.indigo),
      home: const HomeScreen(),
    );
  }
}

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Question 5')),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: SizedBox(
          width: double.infinity,
          child: ElevatedButton(onPressed: () {}, child: const Text('Login')),
        ),
      ),
    );
  }
}
A full-width Login button near the top
Question 5

Question 6 — Solution

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

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(colorSchemeSeed: Colors.indigo),
      home: const HomeScreen(),
    );
  }
}

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Question 6')),
      body: Center(
        child: Row(
          mainAxisSize: MainAxisSize.min,
          spacing: 12,
          children: [
            TextButton(onPressed: () {}, child: const Text('Cancel')),
            const FilledButton(onPressed: null, child: Text('Submit')),
          ],
        ),
      ),
    );
  }
}
A Cancel text button next to a greyed-out Submit button
Question 6

Question 7 — Solution

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

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(colorSchemeSeed: Colors.indigo),
      home: const TemperatureScreen(),
    );
  }
}

class TemperatureScreen extends StatelessWidget {
  const TemperatureScreen({super.key});

  @override
  Widget build(BuildContext context) {
    String city = 'Karachi';
    int celsius = 34;

    return Scaffold(
      appBar: AppBar(title: const Text('Question 7')),
      body: Center(
        child: IconButton.filled(
          onPressed: () {
            ScaffoldMessenger.of(
              context,
            ).showSnackBar(SnackBar(content: Text('$city: $celsius°C')));
          },
          iconSize: 40,
          icon: const Icon(Icons.thermostat),
        ),
      ),
    );
  }
}
A round filled thermometer button and the message Karachi: 34°C at the bottom
Question 7 after tapping the button

Question 8 — Solution

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

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(colorSchemeSeed: Colors.brown),
      home: const ProductScreen(),
    );
  }
}

class ProductScreen extends StatelessWidget {
  const ProductScreen({super.key});

  @override
  Widget build(BuildContext context) {
    String product = 'Coffee Mug';
    int price = 1200;

    return Scaffold(
      appBar: AppBar(title: const Text('Question 8')),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          spacing: 12,
          children: [
            ClipRRect(
              borderRadius: BorderRadius.circular(16),
              child: Image.asset(
                'assets/images/mug.jpg',
                height: 240,
                fit: BoxFit.cover,
              ),
            ),
            Row(
              children: [
                Expanded(
                  child: Text(
                    product,
                    style: const TextStyle(
                      fontSize: 22,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                ),
                Text('Rs. $price', style: const TextStyle(fontSize: 20)),
              ],
            ),
            Row(
              spacing: 12,
              children: [
                Expanded(
                  child: OutlinedButton(
                    onPressed: () {},
                    child: const Text('Wishlist'),
                  ),
                ),
                Expanded(
                  child: FilledButton(
                    onPressed: () {
                      ScaffoldMessenger.of(context).showSnackBar(
                        SnackBar(content: Text('Added $product to cart')),
                      );
                    },
                    child: const Text('Buy'),
                  ),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}
A mug photo, Coffee Mug, Rs. 1200, Wishlist and Buy buttons and the message Added Coffee Mug to cart
Question 8 after tapping Buy

Quick Revision

  1. What is the difference between Icons.home and Icon(Icons.home)?
  2. Which file must list your asset folder?
  3. When should you use Image.network instead of Image.asset?
  4. Which BoxFit fills the box and crops the rest?
  5. How do you give an image rounded corners?
  6. What does CircleAvatar's backgroundImage expect?
  7. Name the four main Material buttons, from most to least important.
  8. How do you disable a button?

Key Takeaways

  • Icon(Icons.x, size: …, color: …) draws one of the built-in Material icons.
  • Image.asset shows files declared in pubspec.yaml; Image.network downloads from a URL.
  • fit: BoxFit.cover fills a box by cropping; ClipRRect rounds an image's corners.
  • CircleAvatar shows a round photo (NetworkImage/AssetImage) or initials.
  • FilledButton, ElevatedButton, OutlinedButton, TextButton, IconButton — each has an onPressed; null disables it. styleFrom styles it.
  • ScaffoldMessenger.of(context).showSnackBar(...) gives quick feedback after a tap.

What's Next?

Lecture 007 — ListView. Every screen so far fits on one phone screen. A chat, a contact list or a shop has far more items than that. Next we make content scroll with ListView, build rows quickly with ListTile, and create lists from Dart data with ListView.builder.