03-Module 3
03-Module 3
Mobile Programming
MODULE 3:
BUILDING USER INTERFACES WITH
FLUTTER
Module 3
• Introduction to UI Design
• Introduction to Basic Flutter Widgets
• UI Styles: Declarative style vs Imperative style
• Comparison between Flutter's Scaffold and Android’s Native
Activities/Fragments
• Flutter Layout Widgets
– Single-child Layout Widgets
– Multi-child Layout Widgets
• Aligning Widgets
• Designing Your Layout in Flutter
• Navigation and routing
• Options to Pass Data Between Screens in Flutter
• CodeLab: Creating layouts in Flutter
Reference: https://docs.flutter.dev/ui
2
Introduction to UI Design
3
UI Design
Introduction
▪ Unlike other frameworks, Flutter uses Dart for both App functions
and UI
4
In Flutter, the main root of an application is often a
StatelessWidget even if there are parts of the
UI Design widget tree that need to update (like changing text).
This is possible because of Flutter's architecture,
Widgets which separates the UI description from the state
management.
https://docs.utter.dev/reference/widgets
5
Stateless Widgets Accessibility Value Widgets
do not contain
states hence they
can be updated
Animation and Motion Layout widgets
only when its
parent changes.
Assets, Images, and Icons Navigation widgets
Once created,
stateless widgets Async Other Widgets
Stateless vs Stateful
Based on Functions
Classic Categories
cannot be updated
means they are Basic Widgets
immutable, they
have to be created Input
again by supplying
new data in order Cupertino
to see the
changes. Interaction Models
Stateful
Widgets can hold Layout
the states
internally, so it can Painting and Effects
be updated
whenever its Scrolling
states changes and
also whenever its Styling
parent changes. It
is mutable widget,
so it can be drawn
Text
multiple times in
its lifetime.
6
https://docs.utter.dev/reference/widgets
Lifecycle of Widgets
https://medium.com/gytworkz/flutter-widget-lifecycle-everything-you-need-to-know-629d01ca4a09 7
Lifecycle of Widgets
What are Widget
Lifecycle Methods? createState()
https://medium.com/gytworkz/flutter-widget-lifecycle-everything-you-need-to-know-629d01ca4a09 8
Widget Lifecycle Methods
createState()
initState()
didChangeDependencies()
build()
didUpdateWidget()
setState()
deactivate()
dispose()
https://medium.com/gytworkz/flutter-widget-lifecycle-everything-you-need-to-know-629d01ca4a09 9
createState()
https://medium.com/gytworkz/flutter-widget-lifecycle-everything-you-need-to-know-629d01ca4a09 10
initState()
https://medium.com/gytworkz/flutter-widget-lifecycle-everything-you-need-to-know-629d01ca4a09 11
build()
This method is called after the state object is initialized. It is used to build the
widget tree. This gets called each time the widget is rebuilt this can happen after
initState, didChangeDependencies, didUpdateWidget, or when the state is
changed via a call to setState.
@override
Widget build(BuildContext context) {
print("build");
return Scaffold(
appBar: AppBar(
title: const Text("Lifecycle Demo"),
),
body: Container(
child: Column(
children: [
Text(_counter.toString()),
ElevatedButton(onPressed: _increment, child: const Text("Increment"))
],
)),
);
}
https://medium.com/gytworkz/flutter-widget-lifecycle-everything-you-need-to-know-629d01ca4a09 12
didChangeDependencies()
@override
void didChangeDependencies() {
print("didChangeDependencies");
super.didChangeDependencies();
}
*Base class for widgets that efficiently propagate information down the tree.
https://medium.com/gytworkz/flutter-widget-lifecycle-everything-you-need-to-know-629d01ca4a09 13
didUpdateWidget()
@override
void didUpdateWidget(covariant MyPage oldWidget)
{
print("didUpdateWidget");
super.didUpdateWidget(oldWidget);
}
https://medium.com/gytworkz/flutter-widget-lifecycle-everything-you-need-to-know-629d01ca4a09 14
deactivate()
@override
void deactivate() {
print("deactivate");
super.deactivate();
}
https://medium.com/gytworkz/flutter-widget-lifecycle-everything-you-need-to-know-629d01ca4a09 15
dispose()
@override
void dispose() {
print("dispose");
super.dispose();
}
https://medium.com/gytworkz/flutter-widget-lifecycle-everything-you-need-to-know-629d01ca4a09 16
Basic Widgets
17
UI Design
Basic widgets
Text
•The Text widget lets you create a run of styled text within your application.
Row, Column
•These flex widgets let you create flexible layouts in both the horizontal (Row) and vertical
(Column) directions. The design of these objects is based on the web's flexbox layout
model.
Stack
•Instead of being linearly oriented (either horizontally or vertically), a Stack widget lets
you place widgets on top of each other in paint order. You can then use the Positioned
widget on children of a Stack to position them relative to the top, right, bottom, or left
edge of the stack. Stacks are based on the web's absolute positioning layout model.
Container
•The Container widget lets you create a rectangular visual element. A container can be
decorated with a BoxDecoration, such as a background, a border, or a shadow. A
Container can also have margins, padding, and constraints applied to its size. In addition,
a Container can be transformed in three-dimensional space using a matrix.
18
UI Design
Using Material Components
▪ Flutter provides a number of widgets that help you build apps that
follow Material Design.
19
UI Design
Using Material Components
import 'package:flutter/material.dart'; class MyScaffold extends StatelessWidget {
const MyScaffold({super.key});
class MyAppBar extends StatelessWidget {
const MyAppBar({required this.title, super.key}); @override
Widget build(BuildContext context) {
// Fields in a Widget subclass are always marked "final". // Material is a conceptual piece
// of paper on which the UI appears.
final Widget title; return Material(
// Column is a vertical, linear layout.
@override child: Column(
Widget build(BuildContext context) { children: [
return Container( MyAppBar(
height: 56, // in logical pixels title: Text(
padding: const EdgeInsets.symmetric(horizontal: 8), 'Example title',
decoration: BoxDecoration(color: Colors.blue[500]), style: Theme.of(context) //
// Row is a horizontal, linear layout. .primaryTextTheme
child: Row( .titleLarge,
children: [ ),
const IconButton( ),
icon: Icon(Icons.menu), const Expanded(
tooltip: 'Navigation menu', child: Center(
onPressed: null, // null disables the button child: Text('Hello, world!'),
), ),
// Expanded expands its child ),
// to fill the available space. ],
Expanded( ),
child: title, );
), }
const IconButton( }
icon: Icon(Icons.search),
tooltip: 'Search', void main() {
onPressed: null, runApp(
), const MaterialApp(
], title: 'My app', // used by the OS task switcher
), home: SafeArea(
); child: MyScaffold(),
} ),
} ),
);
}
20
UI Design import 'package:flutter/material.dart';
@override
Most applications include some form of user Widget build(BuildContext context) {
return GestureDetector(
interaction with the system. The first step in building onTap: () {
an interactive application is to detect input gestures. },
print('MyButton was tapped!');
void main() {
runApp(
const MaterialApp(
home: Scaffold(
body: Center(
child: MyButton(),
),
),
),
);
}
21
UI Design
Changing widgets in response to input
▪ In order to build more complex experiences—for example, to react in more interesting ways to
user input—applications typically carry some state.
▪ Flutter uses StatefulWidgets to capture this idea.
▪ StatefulWidgets are special widgets that know how to generate State objects, which are then
used to hold state. Consider this basic example, using the ElevatedButton mentioned earlier:
23
Flutter has a
collection of visual,
structural, platform,
and interactive
widgets.
https://docs.flutter.dev/ui/widgets
24
UI Styles: Declarative style
vs Imperative style
25
UI Styles
Declarative style vs Imperative style
• The focus is on managing the sequence of operations that modify the UI,
with the developer directly controlling each UI update step.
26
UI Styles
Declarative style vs Imperative style
27
Comparison between Flutter's
Scaffold and Android’s Native
Activities/Fragments
28
UI Design
Flutter's Scaffold and Android’s Native Activities/Fragments
▪ UI Structure Management
▪ State Management
▪ Navigation
29
Layout Widgets: Single-child
& Multi-child Widgets
30
Layouts in Flutter
Everything is a Widget!
31
Layout widgets
Single-child Multi-child
layout widgets layout widgets
Sliver widgets
32
Layout widgets
33
Layout widgets
34
Layout widgets
35
Aligning Widgets
36
Lay out multiple widgets vertically and horizontally
37
Lay out multiple widgets vertically and horizontally
38
Aligning widgets
• You control how a row or column aligns its children using the mainAxisAlignment
and crossAxisAlignment properties.
• For a row, the main axis runs horizontally and the cross axis runs vertically.
• For a column, the main axis runs vertically and the cross axis runs horizontally.
39
Aligning widgets
• When you add images to your project, you need to update the pubspec.yaml file to
access them—this example uses Image.asset to display the images. You don't need
to do this if you're referencing online images using Image.network.
Row(
mainAxisAlignment:
MainAxisAlignment.spaceEvenly,
children: [
Image.asset('images/pic1.jpg'),
Image.asset('images/pic2.jpg'),
Image.asset('images/pic3.jpg'),
],
);
40
Designing Your Layout in
Flutter
41
UI Design
Designing Your Layout in Flutter
42
Navigation and routing
43
The Navigator widget displays screens as a stack using
the correct transition animations for the target
platform. To navigate to a new screen, access the
Using the
Navigator through the route's BuildContext and call Navigator
imperative methods such as push() or pop() 44
Flutter applications with advanced navigation and routing
requirements (such as a web app that uses direct links to each screen,
or an app with multiple Navigator widgets) should use a routing
Using the
package such as go_router that can parse the route path and
configure the Navigator whenever the app receives a new deep link.
Router
45
Options to Pass Data Between
Screens in Flutter
46
Ex01: Using Constructor Arguments
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
47
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home Screen'),
),
body: Center(
child: ElevatedButton(
onPressed: () {
// Navigating and passing data
Navigator.push(
context,
MaterialPageRoute(builder: (context) => DetailScreen(data: 'Hello World')),
);
},
child: Text('Go to Detail Screen'),
),
),
);
}
}
48
class DetailScreen extends StatelessWidget {
final String data;
DetailScreen({required this.data});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar AppBar(
title Text('Detail Screen'),
),
body Center(
child Text(data, style TextStyle(fontSize 24)),
),
);
}
}
49
Ex02: Using Navigator.push and
Navigator.pop
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
50
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home Screen'),
),
body: Center(
child: ElevatedButton(
onPressed: () async {
// Navigating and retrieving data
final result = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => SelectionScreen()),
);
print(result); // Outputs: Selected Data
// Show result in a SnackBar
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Result: $result')),
);
},
child: Text('Go to Second Screen'),
),
),
);
} 51
}
// Second screen returning data
class SelectionScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Selection Screen'),
),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.pop(context, 'Data = 55');
},
child: Text('Send Data to first screen'),
),
),
);
}
}
52
Ex03: Using Named Routes with Arguments
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
return Scaffold(
appBar: AppBar(
title: Text('Detail Screen'),
),
body: Center(
child: Text(
data,
style: TextStyle(fontSize: 24),
),
),
);
}
}
DataStore._internal();
}
return Scaffold(
appBar: AppBar(
title: Text('Second Screen'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Shared Data: ${store.sharedData}',
style: TextStyle(fontSize: 24),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: () {
// Update the shared data
store.sharedData = 'Updated Data';
// Optionally navigate back
Navigator.pop(context);
},
child: Text('Update Data and Go Back'),
),
],
),
),
);
}
}
61
CodeLab: Creating layouts in
Flutter
62
Your Second Flutter app
https://dartpad.dev/?id=e7076b40fb17a0fa899f9f7a154a02e8
63
End of Module 3