COLLEGE OF INFORMATION AND COMPUTING SCIENCES
Flutter
Fundamentals
Mobile Programming
FLUTTER csua
TABLE OF DART BASICS
CONTENTS FLUTTER ARCHITECTURE
BUILDING UI WITH WIDGETS
THEMES & UI ENHANCEMENTS
HANDLING USER INPUT & FORMS
NAVIGATION & MULTI-SCREEN APPS
DEPLOYING APP
Mobile Programming
Dart Basics Declaring Variables
Before diving into Flutter, it is Data Types in Dart
essential to understand the
Dart programming language,
as it's the foundation of Functions & Control Flow
Flutter apps.
Object Oriented Programming
Asynchronous Programming
Declaring Variables
In Dart, variables can be explicitly typed or inferred using var or dynamic.
// Using var (Type inference)
// Explicit types
int age = 25; var message = "Hello, Flutter!";
double price = 99.99;
String name = "John Doe"; // Using dynamic (Can change types)
bool isFlutterFun = true; dynamic anything = "This is a string";
anything = 42; // Now it's an int
Data Types in Dart
Dart has several built-in data types:
Numbers → int, double
Text → String
Boolean → bool
Lists (Arrays) → List
Maps (Key-Value Pair) → Map
Sets → Set
Functions
Functions are reusable blocks of code. Dart supports named parameters and
default values.
Basic Function Function w/ Arrow Function
Parameters (Short hand)
void greet() { int add(int a, int b) { int multiply
print("Hello, Dart!"); return a + b; (int x, int y) => x * y;
} }
print(add(5, 3)); //
Output: 8
Control Flow
Dart supports standard control structures like if, switch, and loops.
If Else For Loop While Loop
int score = 85; for (int i = 1; i <= 5; int n = 1;
if (score >= 90) { i++) { while (n <= 3) {
print("A+ Grade"); print("Count: $i"); print("Number: $n");
} else if (score >= 75) { } n++;
print("B Grade"); }
} else {
print("Fail");
}
OOP Concepts Used in the To-Do App
Encapsulation: Tasks are stored in a Task class.
Abstraction: The UI interacts with the Task model without worrying
about implementation details.
Inheritance: StatefulWidget is extended to manage state.
Polymorphism: Methods like addTask and removeTask allow
different operations on tasks.
OOP Concepts Used in the To-Do App
Task Model
class Task {
String title;
bool isCompleted;
Task({required this.title, this.isCompleted = false});
void toggleCompleted() {
isCompleted = !isCompleted;
}
}
OOP Concepts Used in the To-Do App
Main Method TodoApp Class
void main() { class TodoApp extends StatelessWidget {
runApp(TodoApp()); @override
} Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'To-Do App',
theme: ThemeData(primarySwatch: Colors.green),
home: TodoScreen(),
);
}
}
Asynchronous Programming
(Future, async/await)
A Future represents a value that is not available yet but will be in the future. It can be in one
of two states:
Uncompleted – The asynchronous operation is still in progress.
Completed – The operation is done, and it has either a value or an error.
The async keyword allows a function to perform asynchronous tasks, and await is used to
pause execution until the Future completes.
We have two types of loading: Lazy Loading & Eager Loading
Mobile Programming FLUTTER csua
The Three
Flutter Major
Layers:
Architecture
Flutter follows a layered
architecture with a Flutter Framework (UI & Business Logic)
declarative UI framework.
The architecture can be Flutter Engine (Rendering & Graphics)
broken down into three
major layers Embedder (Platform Integration)
Mobile Programming FLUTTER csua
Flutter Framework (UI & Business Logic)
Flutter Engine (Rendering & Graphics)
Embedder (Platform Integration)
Flutter Framework (UI & Business Logic)
The Flutter Framework is the topmost layer, written in Dart, and is responsible for the
UI and business logic of Flutter applications. It consists of several key components:
Widgets: The fundamental building blocks of a Flutter app. Flutter follows a declarative
UI approach, where UI elements are defined as widgets (StatelessWidget and
StatefulWidget).
Rendering Layer: Converts widget trees into render objects, which describe the layout
and painting of UI elements.
Animation & Gestures: Provides built-in support for smooth animations and gesture
handling.
Flutter Framework (UI & Business Logic)
State Management: Supports various approaches like Provider, Bloc, Riverpod, and
GetX to manage application state.
Flutter Engine (Rendering & Graphics)
The Flutter Engine is written in C++ and is responsible for rendering and graphics. It
acts as the bridge between the Dart framework and low-level system operations. Key
components include:
Skia Graphics Engine: Renders Flutter UI using a hardware-accelerated 2D
graphics engine.
Dart Runtime & JIT/AOT Compiler: Runs Dart code efficiently, with JIT (Just-In-
Time) compilation for hot reload and AOT (Ahead-Of-Time) compilation for
optimized performance in production.
Text Rendering: Uses libraries like HarfBuzz for complex text layout rendering.
Platform Channels: Enables communication between Dart and native code
(Android/iOS).
Embedder (Platform Integration)
The Embedder is the lowest layer, responsible for integrating Flutter with the target
platform (Android, iOS, Windows, macOS, Linux, and Fuchsia). It handles:
Native API Calls: Uses platform channels to communicate with OS-specific
services (camera, location, file system, etc.).
Event Loop & Input Handling: Captures touch, keyboard, and mouse input and
forwards them to the Flutter framework.
Window Management: Controls how the Flutter application runs within a native
app shell.
Mobile Programming FLUTTER csua
BUILDING UI Basic Widgets
WITH WIDGETS
Flutter is a widget-based Input Widgets
framework, meaning everything
you see on the screen is a
widget. Widgets define both the
UI elements and their behavior. Lists & Grids
There are different types of
widgets in Flutter, categorized
based on their functionality.
Basic Widgets
Container(
padding: EdgeInsets.all(16),
margin: EdgeInsets.all(8),
decoration: BoxDecoration(color: Colors.blue, borderRadius:
BorderRadius.circular(10)),
child: Text('Hello Flutter', style: TextStyle(color: Colors.white)),
)
Basic Widgets
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Icon(Icons.star, color: Colors.orange),
Icon(Icons.favorite, color: Colors.red),
Icon(Icons.thumb_up, color: Colors.blue),
],
)
Basic Widgets
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Title', style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
Text('Subtitle', style: TextStyle(fontSize: 16)),
],
)
Basic Widgets
Basic Widgets
Stack(
children: [
Positioned(top: 50, left: 50, child: Icon(Icons.star, size: 50, color: Colors.yellow)),
Positioned(top: 80, left: 80, child: Icon(Icons.circle, size: 50, color: Colors.blue)),
],
)
Text('Hello, Flutter!', style: TextStyle(fontSize: 20, color: Colors.blue))
Basic Widgets
Basic Widgets
Image.network('https://flutter.dev/images/flutter-logo-sharing.png', width: 100)
Image.asset('assets/images/logo.png', width: 100)
Input Widgets
TextField(
obscureText: true
decoration: InputDecoration(labelText: 'Enter your name', border:
OutlineInputBorder()),
)
ElevatedButton(
onPressed: () {
print('Button Clicked');
},
child: Text('Click Me'),
)
Input Widgets
Input Widgets
IconButton(
icon: Icon(Icons.favorite, color: Colors.red),
onPressed: () {
print('Favorite Clicked');
},
)
Checkbox(
value: true,
onChanged: (bool? newValue) {},
)
Input Widgets
Input Widgets
Switch(
value: true,
onChanged: (bool value) {},
)
Slider(
value: 50,
min: 0,
max: 100,
onChanged: (double value) {},
)
Input Widgets
Input Widgets
DropdownButton<String>(
value: 'Item 1',
items: ['Item 1', 'Item 2', 'Item 3']
.map((String item) => DropdownMenuItem(value: item, child: Text(item)))
.toList(),
onChanged: (String? newValue) {},
)
Input Widgets
Lists & Grids
ListView( ListTile(
children: [ leading: Icon(Icons.person),
ListTile( title: Text('Jane Smith'),
leading: Icon(Icons.person), subtitle: Text('Data Scientist'),
title: Text('John Doe'), ),
subtitle: Text('Software Engineer'), ],
), )
Lists & Grids
Lists & Grids
ListView.builder( GridView.count(
itemCount: 10, crossAxisCount: 2,
itemBuilder: (context, index) { children: List.generate(6, (index) {
return ListTile( return Card(
leading: Icon(Icons.star), child: Center(child: Text('Item
title: Text('Item $index'), $index')),
); );
}, }),
) )
Mobile Programming csua
THEMES & UI ENHANCEMENTS
THEMES IN UI Optional:
FLUTTER ENHANCEMENT Dynamic Theme
TIPS Switching
Basic Theme Setup Use Consistent Padding and
Margins You can use a package like
provider or flutter_bloc to toggle
between themes at runtime.
Mobile Programming THEMES & UI ENHANCEMENTS
csua
Basic Theme Setup
THEMES IN FLUTTER @override
Widget build(BuildContext context) {
return MaterialApp(
class MyApp extends StatelessWidget {
title: 'Flutter Themed App',
final ThemeData lightTheme = ThemeData(
theme: lightTheme,
brightness: Brightness.light,
darkTheme: darkTheme,
primarySwatch: Colors.green,
themeMode: ThemeMode.system, // or .light / .dark
scaffoldBackgroundColor: Colors.white,
home: HomePage(),
textTheme: TextTheme(
);
headlineSmall: TextStyle(fontSize: 22, fontWeight: FontWeight.bold),
}
bodyMedium: TextStyle(fontSize: 16),
),
);
final ThemeData darkTheme = ThemeData(
brightness: Brightness.dark,
primarySwatch: Colors.orange,
scaffoldBackgroundColor: Colors.black,
textTheme: TextTheme(
headlineSmall: TextStyle(fontSize: 22, fontWeight: FontWeight.bold,
color: Colors.white),
bodyMedium: TextStyle(fontSize: 16, color: Colors.white70),
),
);
Mobile Programming THEMES & UI ENHANCEMENTS
csua
UI ENHANCEMENT TIPS
Use Consistent Padding Use Custom Reusable
and Margins Widgets
Create a constants.dart:
class TitleText extends StatelessWidget {
const double defaultPadding = 16.0; final String text;
const double defaultRadius = 12.0; const TitleText(this.text);
@override
Responsive UI with Widget build(BuildContext context) {
MediaQuery return Text(
text,
double width = MediaQuery.of(context).size.width; style: Theme.of(context).textTheme.headlineSmall,
);
if (width < 600) { }
// show mobile layout }
} else {
// show tablet or web layout
}
Mobile Programming THEMES & UI ENHANCEMENTS
csua
Optional: Dynamic Theme Switching
Theme Event ThemeBloc
abstract class ThemeEvent {} import 'package:flutter_bloc/flutter_bloc.dart';
class ToggleTheme extends ThemeEvent {} class ThemeBloc extends Bloc<ThemeEvent, ThemeState> {
ThemeBloc() : super(ThemeState(ThemeMode.light)) {
on<ToggleTheme>((event, emit) {
final newTheme =
Theme State state.themeMode == ThemeMode.light ? ThemeMode.dark : ThemeMode.light;
emit(ThemeState(newTheme));
import 'package:flutter/material.dart'; });
}
class ThemeState { }
final ThemeMode themeMode;
ThemeState(this.themeMode);
}
Mobile Programming csua
HANDLING USER INPUT & FORMS
Main Method Stateful class “UserFormPage” State Class of the “UserFormPage
import 'package:flutter/material.dart'; class UserFormPage extends StatefulWidget { class _UserFormPageState extends
const UserFormPage({super.key}); State<UserFormPage> {
void main() { final _formKey = GlobalKey<FormState>();
runApp(const MyApp()); @override final _nameController = TextEditingController();
} State<UserFormPage> createState() => final _emailController = TextEditingController();
_UserFormPageState();
class MyApp extends StatelessWidget { } @override
const MyApp({super.key}); void dispose() {
_nameController.dispose();
@override _emailController.dispose();
Widget build(BuildContext context) { super.dispose();
return MaterialApp( }
title: 'Flutter Form Demo',
debugShowCheckedModeBanner: false,
home: const UserFormPage(),
);
}
}
Inside the State Class csua
SubmitForm Method Build Context Email Input TextFormField
@override /// Email Input
void _submitForm() { Widget build(BuildContext context) { TextFormField(
if (_formKey.currentState!.validate()) { return Scaffold( controller: _emailController,
final name = _nameController.text; appBar: AppBar(title: const Text('User decoration: const
final email = _emailController.text; Form')), InputDecoration(labelText: 'Email'),
body: Padding( keyboardType:
padding: const EdgeInsets.all(16), TextInputType.emailAddress,
ScaffoldMessenger.of(context).showSnackBar( child: Form( validator: (value) {
SnackBar(content: Text('Submitted: key: _formKey, if (value == null || value.isEmpty) {
$name, $email')), child: Column( return 'Please enter your email';
); children: [ } else if
} /// Name Input (!RegExp(r'\S+@\S+\.\S+').hasMatch(value)) {
} TextFormField( return 'Please enter a valid email';
controller: _nameController, }
decoration: const return null;
InputDecoration(labelText: 'Name'), },
Last: Submit Button validator: (value) { ),
if (value == null || value.isEmpty) {
return 'Please enter your name';
/// Submit Button }
ElevatedButton( return null;
onPressed: _submitForm, },
child: const Text('Submit'), ),
)
📦 Code → Analogy Mapping: csua
GlobalKey<FormState> is like the remote control for a TV (Form)
🖥️ The Form: 📺 Without the remote:
Think of the Form as a TV. You'd have to walk up to the TV (form) and
It has many settings (like inputs for name, email, etc.). manually interact with each control (each
field).
🕹️ The GlobalKey<FormState>: That’s messy, especially if your TV has no
buttons (like Flutter forms don't validate on
It's the remote control. their own).
With it, you can:
🟢
Turn on validation → like pressing the power button
Reset the form → like hitting the reset or home button ✅ With the remote (GlobalKey):
🔁
Save input values → like saving a favorite channel ❤️ You just press a button
(_formKey.currentState!.validate()) and the
whole TV (form) responds.
All fields get checked (like turning on
subtitles, adjusting brightness, etc., all at
once).
📦 Code → Analogy Mapping: csua
GlobalKey<FormState> is like the remote control for a TV (Form)
Mobile Programming csua
Navigation & Multi-Screen Apps in flutter
In Flutter, navigation and multi-screen apps are commonly managed using a Navigator
and routes. This allows you to move between different pages (also known as screens
or views) within your app.
Mobile Programming csua
Basic Navigation with Navigator
First Screen Second Screen Using Named Routes
// Second Screen
// First Screen ElevatedButton( SETUP IN THE MAIN:
ElevatedButton( onPressed: () {
onPressed: () { Navigator.pop(context); // Go back void main() {
Navigator.push( }, runApp(MaterialApp(
context, child: Text('Back'), initialRoute: '/',
MaterialPageRoute(builder: (context) => ); routes: {
SecondScreen()), '/': (context) => HomePage(),
); '/dashboard': (context) => DashboardPage(),
}, },
child: Text('Go to Second Screen'), ));
); }
NAVIGATION:
Navigator.pushNamed(context, '/dashboard');
Mobile Programming csua
Passing & Receiving data Between
Screen
Passing Data Between Screens Receive data:
Navigator.push( class SecondScreen extends StatelessWidget {
context, final String data;
MaterialPageRoute(
builder: (context) => SecondScreen(data: 'Hello!'), SecondScreen({required this.data});
),
); @override
Widget build(BuildContext context) {
return Text(data);
}
}
Mobile Programming csua
Returning Data
Push and await result Return data to the first screen
// Push second screen and await result // In SecondScreen
final result = await Navigator.push( Navigator.pop(context, 'This is the result');
context,
MaterialPageRoute(builder: (context) =>
SecondScreen()),
);
// Use result
print(result);
Mobile Programming csua
Next Topic
Software Design
Patterns