On this page23 sections
- Prerequisites
- Connection With Previous Lectures
- Learning Objectives
- 1. Icons
- 2. Images From Your Project: Image.asset
- 3. Images From the Internet: Image.network
- 4. BoxFit — How an Image Fills Its Box
- 5. Rounded Images and Avatars
- 6. The Four Buttons
- 7. Buttons With Icons
- 8. Disabled Buttons
- 9. Styling Buttons
- 10. Responding to a Tap: SnackBar
- 11. A Real-World Example
- Code Examples
- Common Beginner Mistakes
- Best Practices
- Mini Challenge
- Practice Questions
- Practice Answers
- Quick Revision
- Key Takeaways
- What's Next?
Lecture 006 — Images, Icons & Buttons
| Category | 3 — Content & Interaction |
| Lecture | 006 of 009 |
| Estimated video length | 20 min |
| Target | Flutter 3.44 · Dart 3.12 |
| Level | Beginner |
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,BoxDecorationandTextStyleRow,Column,Expanded,SpacerandspacingStackandPositioned- 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
Iconand change their size and color - Add images to a project, declare them in
pubspec.yamland show them withImage.asset - Show images from the internet with
Image.network - Control how an image fills its box with
BoxFit - Round images with
ClipRRectand make avatars withCircleAvatar - 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:
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),
],
),
),
Icons.home,Icons.favorite, … are constants of typeIconData— the shape of the icon.sizedefaults to 24;colordefaults to the current text color.- Many icons come in styles:
Icons.shopping_cart_outlined,Icons.settings_rounded,Icons.star_border. TypeIcons.in your editor to browse them, or search fonts.google.com/icons.
Analogy —
IconDatais the stamp;Iconis 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:
- Create a folder
assets/images/in the project (next tolib/) and copy a photo into it — for examplecamera.jpg. - Tell Flutter about the folder in
pubspec.yaml, under theflutter:section. Indentation matters: two spaces beforeassets:, four before the dash.
flutter:
uses-material-design: true
assets:
- assets/images/- Stop the app and run it again (a hot reload does not pick up new assets), then show the file:
body: Image.asset('assets/images/camera.jpg'),
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:
body: Image.network('https://picsum.photos/id/96/800/500'),
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:
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,
),
),
],
),
),
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:
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',
),
),
],
),
),
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:
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')),
],
),
),
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:
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),
),
],
),
],
),
),
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:
body: const Center(
child: Column(
mainAxisSize: MainAxisSize.min,
spacing: 12,
children: [
FilledButton(onPressed: null, child: Text('Pay now')),
OutlinedButton(onPressed: null, child: Text('Cancel')),
],
),
),
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:
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'),
),
],
),
),
backgroundColor/foregroundColor— fill and text/icon color.paddingandminimumSize—Size.fromHeight(52)means "52 tall, as wide as allowed".shape—RoundedRectangleBorderwith aborderRadius;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:
body: Center(
child: FilledButton(
onPressed: () {
ScaffoldMessenger.of(
context,
).showSnackBar(const SnackBar(content: Text('Hello, Sara!')));
},
child: const Text('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.
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'),
),
),
],
),
],
),
),
);
}
}
SnackBar is not const here because its text uses the product variable.
Code Examples
Example 1 — Weather Icons
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),
],
),
),
);
}
}
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.
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,
),
),
),
);
}
}
Example 3 — A Contact Row
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)),
],
),
),
);
}
}
Example 4 — Avatars Without Photos
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)),
],
),
),
);
}
}
Example 5 — Cart Quantity and Order
The total is calculated inside the SnackBar text.
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'),
),
),
],
),
),
);
}
}
Example 6 — Bank Quick Actions
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'),
],
),
],
),
),
);
}
}
Common Beginner Mistakes
1. An asset that is missing or not declared
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')),
);
}
}
══╡ 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
import 'package:flutter/material.dart';
void main() {
runApp(
MaterialApp(
home: Scaffold(
body: Center(child: FilledButton(child: const Text('Pay'))),
),
),
);
}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_argumentEvery button needs onPressed. Use () {} while you build the screen, or null to disable it.
3. Passing IconData where a widget is expected
import 'package:flutter/material.dart';
void main() {
runApp(
MaterialApp(
home: Scaffold(
body: Center(child: IconButton(onPressed: () {}, icon: Icons.add)),
),
),
);
}error • The argument type 'IconData' can't be assigned to the parameter type 'Widget'. • lib/main.dart:7:64 • argument_type_not_assignableIcons.add is only the shape. Wrap it: icon: const Icon(Icons.add).
4. Writing an icon name as a string
import 'package:flutter/material.dart';
void main() {
runApp(
const MaterialApp(
home: Scaffold(body: Center(child: Icon('home'))),
),
);
}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_assignableIcons 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.coverwith a fixed height for photos in cards and banners. - Name asset files in lowercase with underscores (
profile_photo.png), and keep them inassets/images/. - Make tap targets big enough — at least 48 × 48 pixels.
IconButtonalready 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
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'),
),
],
),
),
);
}
}
How it works
- A
ColumnwithMainAxisSize.mininsideCenterkeeps everything in the middle. OutlinedButton.iconcombines the icon and the label.onPressedcallsScaffoldMessenger.of(context).showSnackBar(...)with the variables interpolated into the message.
Practice Questions
Beginner
- Show a red
Icons.favoriteicon, 64 pixels, in the center. - Show
assets/images/mug.jpgas a 200 × 200 image in the center, cropped to fill. - Show a network image with 16-pixel rounded corners and 16 pixels of padding.
Application
- Show Sara's profile photo as a
CircleAvatarwith a radius of 50. - Show a full-width Login
ElevatedButtonwith 16 pixels of padding around it. - Show a Cancel
TextButtonand a disabled SubmitFilledButtonside by side. - Store
city = 'Karachi'andcelsius = 34. Show a filled icon button withIcons.thermostat(size 40) that showsKarachi: 34°Cin aSnackBar.
Challenge
- Build a product card for a
Coffee Mugcosting1200: 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 showsAdded Coffee Mug to cart.
Practice Answers
Question 1 — Solution
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),
),
);
}
}
Question 2 — Solution
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,
),
),
);
}
}
Question 3 — Solution
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'),
),
),
);
}
}
Question 4 — Solution
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'),
),
),
);
}
}
Question 5 — Solution
SizedBox(width: double.infinity) makes the button full width. ElevatedButton.styleFrom( minimumSize: const Size.fromHeight(48)) would work too.
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')),
),
),
);
}
}
Question 6 — Solution
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')),
],
),
),
);
}
}
Question 7 — Solution
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),
),
),
);
}
}
Question 8 — Solution
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'),
),
),
],
),
],
),
),
);
}
}
Quick Revision
- What is the difference between
Icons.homeandIcon(Icons.home)? - Which file must list your asset folder?
- When should you use
Image.networkinstead ofImage.asset? - Which
BoxFitfills the box and crops the rest? - How do you give an image rounded corners?
- What does
CircleAvatar'sbackgroundImageexpect? - Name the four main Material buttons, from most to least important.
- How do you disable a button?
Key Takeaways
Icon(Icons.x, size: …, color: …)draws one of the built-in Material icons.Image.assetshows files declared inpubspec.yaml;Image.networkdownloads from a URL.fit: BoxFit.coverfills a box by cropping;ClipRRectrounds an image's corners.CircleAvatarshows a round photo (NetworkImage/AssetImage) or initials.FilledButton,ElevatedButton,OutlinedButton,TextButton,IconButton— each has anonPressed;nulldisables it.styleFromstyles 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.