CH 2
CH 2
INTRODUCTION TO FLUTTER
29
30 Introduction to Flutter
1 Install Flutter
Setting up Flutter involves several options to accommodate dierent development environ-
ments and preferences. For developers using Windows, macOS, or Linux, Flutter can be
installed by downloading and extracting the Flutter SDK from the ocial Flutter website,
then conguring environment variables and dependencies. Windows users need to ensure
they have Git and Visual Studio installed, while macOS users should install Xcode to build
iOS apps. Linux users will need to set up additional dependencies like `libglu1-mesa` for
graphical support. For those who prefer an integrated development environment (IDE), An-
droid Studio and Visual Studio Code oer plugins that streamline Flutter development by
providing code completion, debugging, and other essential features. Alternatively, VS Code
is a lightweight option with robust support for Flutter through its extension. Each setup
option provides tools and integrations to help developers eciently build, test, and deploy
Flutter applications.
Development tools: Download and install the Windows version of the following
packages:
https://utlab.io/
https://dartpad.dev/
https://zapp.run/
32 Introduction to Flutter
5. Run the application and test the counter by pressing on the add (+) button, as shown
in Fig. 2.6.
Grasping the structure of a Flutter project is essential for smooth and eective develop-
ment. We'll take a deep dive into the anatomy of a Flutter project, providing you with the
insights needed to navigate and leverage its various components eciently.
Inside the Lib Folder
The lib folder is the core of a Flutter project, containing the Dart code that powers the
app. The main.dart le serves as the app's entry point, while other Dart les break
the code down into manageable parts such as screens, models, services, and widgets.
Following best practices, it's helpful to organize code into subfolders for better structure,
especially as the project becomes more complex.
34 Introduction to Flutter
Pubspec.yaml
The pubspec.yaml le governs the project's conguration settings, dependencies, and
assets. Dependencies, also known as packages, extend the app's functionality and can
be added using the Pub package manager. You also declare assets like images, icons,
and videos here.
code quality. Additionally, the .gitignore le species which les should be excluded
from version control, simplifying the management of your repository.
1. import package:utter/material.dart;
- This line imports the Flutter material design library, which gives access to a set of UI
components (like buttons, text, and layouts) that follow Material Design guidelines by
Google.
5. MaterialApp widget
- This widget sets up the application and its visual design. It includes:
- title: The app's name, which can be shown in the app switcher.
- theme: Denes the overall look and feel of the app, like primary colors.
- home: The main screen of the app, which is another widget called MyHomePage.
8. int_counter = 0;
- This variable keeps track of how many times the user presses the button. It starts at
0.
9. void _incrementCounter()
- This function increments the counter whenever the button is pressed.
- The setState() function is called to tell Flutter to update the screen when the counter
changes.
Introduction to Flutter 37
11. Text widgets and Counter// - Inside the Column, there are two Text widgets. The
rst shows a static message, and the second displays the current value of _counter.
- The counter is updated every time the button is pressed.
This basic template sets up a simple Flutter app with an interactive counter. When you press
the oating button, the number displayed on the screen increments. Flutter uses widgets like
MaterialApp, Scaold, AppBar, and Text to build the user interface. The app demonstrates
how to create stateful widgets, handle events, and update the UI in response to user actions.
Fig. 2.8 shows a screenshot of the Android Studio Environment. Android Studio provides
a powerful environment for developing Flutter apps, with integrated tools that simplify devel-
opment and debugging. One key feature is the use of virtual devices (Android emulators),
which allow developers to test their Flutter apps without needing physical hardware. To set
up a virtual device, go to the AVD Manager, create an emulator, and run the Flutter app to
38 Introduction to Flutter
see it in action. While the app is running, Android Studio's Flutter Inspector helps devel-
opers visualize the widget tree structure in real time. The Widget Tree shows a hierarchical
representation of all the widgets in the app, which is useful for understanding how dier-
ent components are nested and how the UI is constructed. For more detailed analysis, the
Layout Explorer helps inspect the layout of individual widgets, showing padding, margins,
and other spatial properties, while the Widget Details Tree provides an in-depth view of a
widget's properties, including state, size, and rendering details. These tools, combined with
Android Studio's debugging features, make it easier to diagnose and resolve issues during
Flutter app development.
2. Open the Device Manager: Click on the "Device Manager" icon in the toolbar or
navigate to Tools > Device Manager.
3. Start an Android Virtual Device: If you don't already have an AVD running,
create one by clicking on "Create Device" and follow the prompts to set up a virtual
device. Once created, select it from the list and click "Play" to start it.
Introduction to Flutter 39
4. Install the APK: Drag the apk le and drop it on the virtual device.
5. Launch the App: Once the installation is complete, you should see the app icon in
the app drawer of the AVD. Click on the icon to launch and test the app.
By following these steps, you can manually test your Flutter application on an Android
virtual device, allowing you to verify its functionality and performance before deploying it to
physical devices or releasing it to the public.
Style: Material Design is developed by Google and emphasizes clean, bold, and at
visual elements. It is designed around principles of motion and interaction, with a focus
on depth, shadows, and vivid colors.
Widgets: Flutter's Material Design widgets are available through the MaterialApp
class and include components like AppBar, FloatingActionButton, Card, Drawer,
SnackBar, and BottomNavigationBar. These widgets reect Android's native look
and feel but can be customized to suit other platforms.
Common Use Cases: Best for apps targeting Android or those that need to maintain
a consistent cross-platform look regardless of the operating system.
Style: Cupertino design is based on Apple's Human Interface Guidelines and focuses on
a sleek, minimalistic aesthetic with subtle animations, rounded corners, and a general
sense of depth. It prioritizes simplicity, clarity, and intuitive gestures.
Widgets: Flutter provides Cupertino widgets through the CupertinoApp class, which
includes components like CupertinoNavigationBar, CupertinoButton, CupertinoSwitch,
40 Introduction to Flutter
and CupertinoTabBar. These widgets closely resemble native iOS controls, ensuring
that iOS apps feel natural on Apple devices.
Common Use Cases: Ideal for apps targeting iOS users who expect their apps to
behave and look like native iOS applications.
Cupertino Design is best for apps targeting iOS exclusively or when aiming to provide
a native iOS user experience, ensuring the app adheres to Apple's design standards.
In summary, Material Design is more versatile and exible, making it suitable for cross-
platform applications, while Cupertino oers a focused, iOS-native experience. Flutter allows
developers to combine both systems and adapt their UI to the target platform, ensuring
platform-specic behavior without duplicating eort.
Introduction to Flutter 41
Example from the code: In the Dart code provided earlier, the MyApp widget is an
example of a stateless widget.
In this example, the MyApp class extends StatelessWidget. It contains a build method
that returns a widget tree but does not have any state that can change. The UI remains
static once rendered.
Example from the code: The MyHomePage widget in the provided Dart code is a
stateful widget.
@override
_MyHomePageState createState() => _MyHomePageState();
}
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
In this example, the MyHomePage class extends StatefulWidget, and its state is man-
aged by the _MyHomePageState class. The _counter variable is part of the widget's
Introduction to Flutter 43
state, and it changes every time the button is pressed. The setState method triggers
the widget to rebuild, updating the UI to reect the new counter value.
StatefulWidget: Can change based on user interaction or other factors, suitable for
dynamic UI elements.
In the provided code, MyApp is stateless because it does not need to change its state,
while MyHomePage is stateful because it updates the counter when the button is pressed.
void main() {
print('Hello, World!');
}
8.2 Variables
Even in type-safe Dart code, you can declare most variables without explicitly specifying
their type using var. Thanks to type inference, these variables' types are determined by
their initial values:
44 Introduction to Flutter
8.4 Functions
We recommend specifying the types of each function's arguments and return value:
int fibonacci(int n) {
if (n == 0 || n == 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
A shorthand => (arrow) syntax is handy for functions that contain a single statement.
This syntax is especially useful when passing anonymous functions as arguments:
8.5 Comments
Dart comments usually start with //.
8.6 Imports
To access APIs dened in other libraries, use import.
// Importing files
import 'path/to/my_other_file.dart';
8.7 Classes
Here's an example of a class with three properties, two constructors, and a method. One
of the properties can't be set directly, so it's dened using a getter method (instead of a
variable). The method uses string interpolation to print variables' string equivalents inside
of string literals.
class Spacecraft {
String name;
DateTime? launchDate;
// Method.
void describe() {
print('Spacecraft: $name');
// Type promotion doesn't work on getters.
var launchDate = this.launchDate;
if (launchDate != null) {
int years = DateTime.now().difference(launchDate).inDays ~/ 365;
print('Launched: $launchYear ($years years ago)');
} else {
print('Unlaunched');
}
}
}
8.8 Enums
Enums are a way of enumerating a predened set of values or instances in a way that ensures
there cannot be any other instances of that type.
Here is an example of a simple enum that denes a simple list of predened planet types:
8.9 Inheritance
Dart has single inheritance.
8.10 Mixins
Mixins are a way of reusing code in multiple class hierarchies. The following is a mixin
declaration:
Introduction to Flutter 47
mixin Piloted {
int astronauts = 1;
void describeCrew() {
print('Number of astronauts: $astronauts');
}
}
To add a mixin's capabilities to a class, just extend the class with the mixin.
You can create an abstract class to be extended (or implemented) by a concrete class.
Abstract classes can contain abstract methods (with empty bodies).
void describeWithEmphasis() {
print('=========');
describe();
print('=========');
}
}
8.12 Async
Avoid callback issues and make your code much more readable by using async and await.
Everything you can place in a variable is an object, and every object is an instance of
a class. Even numbers, functions, and null are objects. With the exception of null (if
you enable sound null safety), all objects inherit from the Object class.
Version note: Null safety was introduced in Dart 2.12. Using null safety requires a
language version of at least 2.12.
Although Dart is strongly typed, type annotations are optional because Dart can infer
types. In var number = 101, number is inferred to be of type int.
If you enable null safety, variables can't contain null unless you say they can. You
can make a variable nullable by putting a question mark (?) at the end of its type.
For example, a variable of type int? might be an integer, or it might be null. If you
know that an expression never evaluates to null but Dart disagrees, you can add ! to
assert that it isn't null (and to throw an exception if it is). An example: int x =
nullableButNotNullInt!
When you want to explicitly say that any type is allowed, use the type Object? (if
you've enabled null safety), Object, orif you must defer type checking until run-
timethe special type dynamic.
Dart supports generic types, like List<int> (a list of integers) or List<Object> (a list
of objects of any type).
Dart supports top-level functions (such as main()), as well as functions tied to a class
or object (static and instance methods, respectively). You can also create functions
within functions (nested or local functions).
Similarly, Dart supports top-level variables, as well as variables tied to a class or object
(static and instance variables). Instance variables are sometimes known as elds or
properties.
Unlike Java, Dart doesn't have the keywords public, protected, and private. If
an identier starts with an underscore (_), it's private to its library. For details, see
Libraries and imports.
Identiers can start with a letter or underscore (_), followed by any combination of
those characters plus digits.
Introduction to Flutter 49
Dart has both expressions (which have runtime values) and statements (which don't).
For example, the conditional expression condition ? expr1 : expr2 has a value
of expr1 or expr2. Compare that to an if-else statement, which has no value. A
statement often contains one or more expressions, but an expression can't directly
contain a statement.
Dart tools can report two kinds of problems: warnings and errors. Warnings are just
indications that your code might not work, but they don't prevent your program from
executing. Errors can be either compile-time or run-time. A compile-time error prevents
the code from executing at all; a run-time error results in an exception being raised
while the code executes.
Reect on how the code structure and behavior of the above widgets might inuence
their use in dierent scenarios within a Flutter application. How would the choice
between a `StatelessWidget` and a `StatefulWidget` aect the development and func-
tionality of your app?
name: my_flutter_app
description: A new Flutter project
dependencies:
flutter:
sdk: flutter
dev_dependencies:
flutter_test:
sdk: flutter
flutter:
assets:
# List your assets here
(a) Add a dependency for the provider package, specifying its latest version.
(b) Include a list of two assets, such as images or fonts, in the assets section.
Consider how these additions might aect your project's functionality and resource
management.
4. Consider the following incomplete Dart code for a Flutter application, where a `Column`
widget is used to arrange its children:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
home: Scaffold(
appBar: AppBar(
title: Text('Center Alignment Example'),
),
body: Column(
children: <Widget>[
// Add your widgets here
],
// Complete this part to center-align the widgets
),
),
);
}
}
Complete the `Column` widget so that all its child widgets are aligned to the center of
the screen. Use the `mainAxisAlignment` property appropriately to achieve this.
5. Write a Dart function named checkEligibility that takes an integer age as a pa-
rameter and returns a string indicating eligibility for a certain activity. The eligibility
criteria are as follows:
Provide a main function that calls checkEligibility with various ages and prints the
results.
void main() {
// Test the function with various ages
}
6. You have been tasked with developing a simple Flutter application that displays a list
of cards with images and text. As part of the development process, you need to ensure
that the layout and structure of the app are correct across dierent devices, and that
you understand how the widgets are structured and rendered.
Task:
52 Introduction to Flutter
(a) Set up a virtual device: Using Android Studio, create and congure a virtual
device that represents a standard Android phone (e.g., Pixel 4). Launch your
Flutter app on this virtual device. Describe the steps you took to create the
virtual device, launch it, and deploy your Flutter app.
(b) Use the Flutter Inspector: Once the app is running, open the Flutter Inspector
in Android Studio. Explain how the Inspector helps you visualize the widget
hierarchy. Describe how you would use the Inspector to verify that the widget
tree matches your expectations.
(c) Analyze the Widget Tree: Using the widget tree in the Flutter Inspector,
describe how you can identify the parent-child relationships between the widgets
in your app. In your explanation, include how the widget tree helps you understand
the structure and layout of the list of cards with images and text.
(d) Investigate the Layout: Open the Layout Explorer and use it to analyze the
padding, margins, and positioning of the widgets in one of the card items in your
app. Provide a summary of what you found and explain how this tool can help
you rene your layout.
(e) Explore the Widget Details Tree: Finally, use the Widget Details Tree to
examine the properties of one of the text widgets in the card. What properties
can you inspect in the Widget Details Tree, and how can this information be useful
for debugging and optimizing the UI?
Deliverables: