0% found this document useful (0 votes)
108 views

Flutter

Uploaded by

Giovanni
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
108 views

Flutter

Uploaded by

Giovanni
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 159

Facciamo un’app

prof. Luciano Baresi


[email protected]
BENVENUTI AL POLITECNICO…

Prof. Luciano Baresi


BENVENUTI NEL CAMPUS DI CREMONA

Il Politecnico di Milano è presente a


Cremona dal 1987.

Video di presentazione
del Campus di Cremona
https://youtu.be/L0SDjMFIleo

Prof. Luciano Baresi


IL CAMPUS DI CREMONA: L’OFFERTA FORMATIVA
CORSI DI LAUREA TRIENNALE

INGEGNERIA GESTIONALE

INGEGNERIA INFORMATICA

Prof. Luciano Baresi


IL CAMPUS DI CREMONA: L’OFFERTA FORMATIVA
CORSI DI LAUREA MAGISTRALE

AGRICULTURAL ENGINEERING

MUSIC AND ACOUSTIC ENGINEERING

Prof. Luciano Baresi


OPEN DAY DEL CAMPUS DI CREMONA

3, 4 e 5 MAGGIO 2022

APPUNTAMENTI IN PRESENZA E
ON LINE

per scoprire
Ingegneria Gestionale
Ingegneria Informatica
Agricultural Engineering
Music and Acoustic Engineering

Registrazione Open Day scorso anno


su www.polo-cremona.polimi.it
Futuri studenti > Orientamento

Prof. Luciano Baresi


Complex device

• Accelerometer
• Gyroscope
• Digital compass
• Global Positioning System (GPS)
• Barometer
• Ambient light
• Proximity Sensor
Many different languages

• Objective-C/Swift (iOS)
• Java/Kotlin (Android)
• C# (cross-platform development)
• HTML5/JavaScript (cross-platform development)
Development options
Web-based solution
Progressive Web App
Real app
Native solution
Hybrid solution (PhoneGap)
React Native

• Native mobile apps using JavaScript and React


• Uses the same fundamental UI building blocks as
regular iOS and Android apps
• UI building blocks put together using JavaScript
and React
• Pushed by Facebook
Flutter

• Flutter is a mobile app SDK, complete with


framework, engine, widgets, and tools
• Gives developers easy and productive way to build
and deploy beautiful apps
• Also used for Fuchsia
• Dart (Flutter’s language) can be used to build web
and server applications as well
– Learn Dart once, develop for five platforms
A Brief Introduction to Flutter
Flutter
Many options

https://dart.dev/overview
Dart
• Open-source web programming language developed
by Google
• Class-based, single-inheritance, object-oriented
language with C-style syntax
• Supports interfaces, abstract classes, reified generics,
optional, and strong typing
• For apps targeting mobile and desktop devices
– A Dart VM with just-in-time ( JIT) compilation
– An ahead-of-time (AOT) compiler for producing machine
code
• For apps targeting the web
– A development time compiler (dartdevc)
– A production time compiler (dart2js)
• Both compilers translate Dart into JavaScript
Dart

• Everything you can place in a variable is an object


– Even numbers, functions, and null are objects
• Type annotations are optional
– If no type is expected, use the special type dynamic
• Dart supports generic types, like List<int>
• Dart supports
– Top-level functions (such as main()), as well as functions
tied to a class or object
– Top-level variables, as well as variables tied to a class or
object
• Public, protected, and private properties do not exist
– If an identifier starts with an underscore (_), it is private to
its library
Credits and installation

• Heavily based on https://flutter.dev/docs

• First you must


– Install flutter (v 2.8)
– Install IDE, Simulator, and editor (e.g., VS code)
• xCode, Android Studio, IntelliJ
– Test everything
First app

• flutter create my_app


• cd my_app
• open -a Simulator
– on my Mac
• flutter run
GUI

• Flutter’s UI is built in code


• Widgets are the basic building blocks of a Flutter
UI
– Almost everything in Flutter is a widget
• A widget is an immutable object that describes a
specific part of a UI
• Widgets are composable, meaning, that you can
combine existing widgets to make more
sophisticated widgets
Everything is a widget
Widgets

• You build your UI out of widgets


– Flutter widgets are built using a modern framework that
takes inspiration from React
• Widgets describe what their view should look like
given their current configuration and state
– When a widget’s state changes, the widget rebuilds its
description
– The framework computes the diff against the previous
description to determine the minimal changes needed in
the underlying render tree to transition from one state
to the next
Basic widgets
Minimal app (I)
import 'package:flutter/material.dart';

void main() {
runApp(
Center(
child: Text(
'Hello, world!',
textDirection: TextDirection.ltr,
),
),
);
}
Minimal app

• Function runApp() takes the given Widget and


makes it the root of the widget tree
• The widget tree consists of two widgets,
the Center widget and its child, the Text widget
• The framework forces the root widget to cover
the screen, which means the text “Hello, world”
ends up centered on screen
Something more complete
// Copyright 2018 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {


@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Welcome to Flutter',
home: Scaffold(
appBar: AppBar(
title: Text('Welcome to Flutter'),
),
body: Center(
child: Text('Hello World'),
),
),
);
}
}
Observations

• This example creates a Material app


– Flutter offers a rich set of Material widgets
– iOS-centric applications can use package Cupertino
• The app extends StatelessWidget which makes the
app itself a widget
• The Scaffold widget, from the Material library,
provides a default app bar, title, and a body
property that holds the widget tree for the home
screen
• The body for this example consists of
a Center widget containing a Text child widget
Even more
import 'package:flutter/material.dart’;

void main() {
runApp(
MaterialApp(
title: 'Welcome to Flutter’,
home: Scaffold(
appBar: AppBar(
title: Text('Welcome to Flutter’),
),
body: Center(
child: Text(
'Hello World !’,
style: TextStyle(
fontSize: 30,
fontFamily: 'Futura’,
color: Colors.blue),
),
),
),
),
);
}
Commas

• Always add a trailing comma at the end of a


parameter list
– In functions, methods, and constructors where you care
about keeping the formatting you crafted
• This helps the automatic formatter to insert an
appropriate amount of line breaks for Flutter-style
code
New widgets

• Are subclasses of either StatelessWidget


or StatefulWidget
• A widget’s main job is to implement
function build() to describe the widget in terms of
other, lower-level widgets
– The framework builds those widgets until the process
reaches widgets that represent the
underlying RenderObject, which computes and
describes the geometry of the widget
Stateful vs. stateless widgets

• If a widget can change when the user interacts


with it, it is stateful
– A widget’s state consists of values that can change, like
a slider’s current value or whether a checkbox is
checked
• A widget’s state is stored in a State object, which
separates the widget’s state from its appearance
• When the widget’s state changes, the state object
calls setState(), telling the framework to redraw
the widget
• A stateless widget has no internal state to manage
Rows and Columns

• Row and Column are classes that contain and lay


out widgets
– Widgets inside of a Row or Column are called children
– Row and Column are referred to as parents
Cross axis
Rows and columns
Structure
Another example
Left column
Our first widget

class BlueBox extends StatelessWidget {


@override
Widget build(BuildContext context) {
return Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: Colors.blue,
border: Border.all(),
),
);
}
}
Example
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Welcome to Flutter’,
home: Scaffold(
appBar: AppBar(
title: Text('Welcome to Flutter’),
),
body: Center(
child: Column(
children: [
BlueBox(),
BlueBox(),
BlueBox()
],
),
),
),
);
}
}
Additional attributes

• MainAxisSize (min or max) determines how much


space a Row or Column can occupy on the main
axes
• MainAxisAlignment (start, end, center,
spaceBetween, spaceEvenly, spaceAround)
positions children with respect to main axis
• crossAxisAlignment (many options) positions
children with respect to cross axis
Example

Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.center,
children: [BlueBox(), BiggerBlueBox(), BlueBox()],
),
),
Flexible

• Flexible wraps a widget to let the widget become


resizable
– After inflexible widgets are laid out, the widgets are
resized according to their flex and fit properties
– Flex compares itself against other flex properties before
determining what fraction of the total remaining space
each Flexible widget receives
– Fit determines whether a Flexible widget fills all its extra
space
• FlexFit.loose uses the widget’s preferred size (Default)
• FlexFit.tight forces the widget to fill all its extra space
Flex example

Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
BlueBox(),
Flexible(
fit: FlexFit.tight,
flex: 1,
child: BlueBox(),
),
Flexible(
fit: FlexFit.tight,
flex: 3,
child: BlueBox(),
),
],
),
),
Other options

• Similar to Flexible, Expanded can wrap a widget


and force the widget to fill extra space
• SizedBox can wrap a widget and resizes it using
properties height and width
– It can also use height and width to create empty space
• Spacer can create space between widgets
– Use Spacer to create space using a flex property
– Use SizedBox to create space using a specific number of
logical pixels
SizedBox

children: [
BlueBox(),
SizedBox(width: 50),
SizedBox(
width: 100,
child: BlueBox(),
),
BlueBox(),
]
Spacer widget

child: Row(
children: [
BlueBox(),
Spacer(flex: 1),
BlueBox(),
Spacer(flex: 1),
BlueBox(),
],
)
Icon widget

• Flutter is preloaded with icon packages


for Material and Cupertino applications
body: Center(
child: Row(
children: [
Icon(
Icons.add_circle,
size: 50,
color: Colors.orange,
),
Icon(
Icons.widgets,
size: 50,
color: Colors.red,
),
],
),
),
Image widget

• To use local images you need perimissions

body: Center(
child: Row(
children: [
Image.network('https://...’),
],
),
)
Wireframes

• press "p" in the console


Assets (pubspec.yaml)

• To include all assets under a directory, specify the


directory name with the / character at the end
• Only files located directly in the directory are
included
• To add files located in subdirectories, create an
entry per directory

flutter:
uses-material-design: true
assets:
- images/lake.jpg
- assets/background.png
Example
body: Column(
children: [
Row(
One simple solution
children: [
Icon(),
Column(
children: [
Text(),
Text(),
],
),
],
),
SizedBox(),
Row(
children: [
Text(),
Text(),
],
),
SizedBox(),
Row(
children: [
Icon(), Icon(), Icon(),Icon()
],
),
],
)
return MaterialApp(
title: 'Welcome to Flutter',
home: Scaffold(
appBar: AppBar(
title: Text('Welcome to Flutter'),
),
body: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Row(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Icon(Icons.account_circle, size: 50),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text('Flutter McFlutter',
style: Theme.of(context).textTheme.headline5),
Text('Experienced App Developer'),
],
),
],
),
SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('123 Main Street'),
Text('415-555-0198'),
],
),
SizedBox(height: 16),
Row(
children: [
Icon(Icons.accessibility, size: 30),
Icon(Icons.timer, size: 30),
Icon(Icons.phone_android, size: 30),
Icon(Icons.phone_iphone, size: 30),
],
mainAxisAlignment: MainAxisAlignment.spaceAround,
),
],
),
ListView widget
body: ListView(
padding: EdgeInsets.all(8),
children: [
Container(
height: 50,
color: Colors.amber[600],
child: Center(child: Text('Entry A')),
),
Container(
height: 50,
color: Colors.amber[500],
child: Center(child: Text('Entry B')),
),
Container(
height: 50,
color: Colors.amber[100],
child: Center(child: Text('Entry C')),
),
],
),
primarySwatch

• primarySwatch is not a Color, but a MaterialColor


– It is all the different shades of a color a material app can
use
• PrimaryColor is one of those shades
– It is normally equal to primarySwatch[500]
• It is usually better to define a primarySwatch
instead of a primaryColor
– Some material components may use a different shade of
the primaryColor for things such as shadow, border, ...
Button
(stateless)

body: FlatButton(
color: Colors.blue,
textColor: Colors.white,
disabledColor: Colors.grey,
disabledTextColor: Colors.black,
padding: EdgeInsets.all(8.0),
splashColor: Colors.blueAccent,
onPressed: () {
/*...*/
},
child: Text(
’Flat Button’,
style: TextStyle(fontSize: 20.0),
),
),
Stateful widgets
• Maintain state that might change during the
lifetime of the widget
– Can change their appearance in response to user
events or when they receive data
• Checkbox, Radio, Slider, InkWell, Form,
and TextField are examples
• We always have two classes that extend
StatefulWidget and State
– State consists of values that can change, like a slider’s
current value
– State contains the widget’s mutable state and the
widget’s build() method
– When the widget’s state changes, the state object
calls setState(), telling the framework to redraw the
widget
Initial app
Stateless widget

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {


@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo’,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page’),
);
}
}
StatefulWidget

• This class is the configuration for the state


• It holds the values (title) provided by the parent (
MyApp) and used by the build method of State
• Fields in a widget subclass are always marked final

class MyHomePage extends StatefulWidget {


MyHomePage({Key? key, required this.title}) : super(key: key);

final String title;

@override
_MyHomePageState createState() => _MyHomePageState();
}
Constructors

class Point {
double x = 0;
double y = 0;

Point(double x, double y) {
this.x = x;
this.y = y;
}

Point(this.x, this.y);
}
Functions

• A function can have any number of required


positional parameters
– These can be followed either by named parameters or
by optional positional parameters (but not both)
– Named parameters are optional unless they’re
specifically marked as required
• Although named parameters are optional
parameters, we can annotate them with required
to indicate that the parameter is mandatory
• We can use = to define default values for both
named and positional parameters
– The default values must be compile-time constants
– If no default value is provided, the default value is null
State

• The call to setState tells Flutter that something


has changed in this State, which causes it to rerun
the build method
– The display can then reflect the updated values
– If we changed _counter without calling setState(), build
would not be called and nothing would happen

class _MyHomePageState extends State<MyHomePage> {


int _counter = 0;

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: [
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),
),
);
}
}
build()

• The method is rerun every time setState() is called

• Flutter has been optimized to make rerunning


build methods fast
– It rebuilds anything that needs updating rather than
changing widgets individually
Another example
class FavoriteWidget extends StatefulWidget {
@override
_FavoriteWidgetState createState() => _FavoriteWidgetState();
}

class _FavoriteWidgetState extends State<FavoriteWidget> {


bool _isFavorited = true;
int _favoriteCount = 41;

void _toggleFavorite() {
setState(() {
if (_isFavorited) {
_favoriteCount -= 1;
_isFavorited = false;
} else {
_favoriteCount += 1;
_isFavorited = true;
}
});
}
build()
@override
Widget build(BuildContext context) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: EdgeInsets.all(0),
child: IconButton(
icon: (_isFavorited ? Icon(Icons.star) : Icon(Icons.star_border)),
color: Colors.red[500],
onPressed: _toggleFavorite,
),
),
SizedBox(
width: 18,
child: Container(
child: Text('$_favoriteCount'),
),
),
],
);
}
}
More examples and apps
Who manages the state of a stateful widget

• If the state is user data, for example the checked


or unchecked mode of a checkbox, the state is
best managed by the parent widget
• If the state is aesthetic, for example an animation,
then the state is best managed by the widget itself
• If in doubt, start by managing the state in the
parent widget
Widget itself

• Class _TapboxAState
– Manages state for TapboxA
– Defines boolean _active that determines the box’s
current color
– Defines method _handleTap() that updates _active
when the box is tapped and calls method setState() to
update the UI
– Implements all interactive behavior for the widget
Example
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
appBar: AppBar(
title: Text('Flutter Demo'),
),
body: Center(
child: TapboxA(),
),
),
);
}
}

class TapboxA extends StatefulWidget {

@override
_TapboxAState createState() => _TapboxAState();
}
class _TapboxAState extends State<TapboxA> {
bool _active = false;

void _handleTap() {
setState(() {
_active = !_active;
});
}

Widget build(BuildContext context) {


return GestureDetector(
onTap: _handleTap,
child: Container(
child: Center(
child: Text(
_active ? 'Active' : 'Inactive',
style: TextStyle(fontSize: 32.0, color: Colors.white),
),
),
width: 200,
height: 200,
decoration: BoxDecoration(
color: _active ? Colors.red : Colors.blue,
),
),
);
}
}
Parent widget

• Example
– IconButton is a stateless widget but the parent widget
needs to know whether the button has been tapped

• TapboxB exports its state to its parent through a


callback
– Extends StatelessWidget because its state is handled by its
parent
– When a tap is detected, it notifies the parent
• Class ParentWidgetState
– Manages state _active for TapboxB
– Implements method _handleTapboxChanged() called
when the box is tapped
– When the state changes, calls setState() to update the UI
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class ParentWidget extends StatefulWidget {


@override
_ParentWidgetState createState() => _ParentWidgetState();
}

class _ParentWidgetState extends State<ParentWidget> {


bool _active = false;

void _handleTapboxChanged(bool newValue) {


setState(() {
_active = newValue;
});
}

@override
Widget build(BuildContext context) {
return Container(
child: TapboxB(
active: _active,
onChanged: _handleTapboxChanged,
),
);
}
}
class TapboxB extends StatelessWidget {
TapboxB({Key? key, this.active: false, required this.onChanged})
: super(key: key);

final bool active;


final ValueChanged<bool> onChanged;

void _handleTap() {
onChanged(!active);
}

Widget build(BuildContext context) {


return GestureDetector(
onTap: _handleTap,
child: Container(
child: Center(
child: Text(
active ? 'Active' : 'Inactive',
style: TextStyle(fontSize: 32.0, color: Colors.white),
),
),
width: 200,
height: 200,
decoration: BoxDecoration(
color: active ? Colors.lightGreen[700] : Colors.grey[600],
),
),
);
}
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
appBar: AppBar(
title: Text('Flutter Demo'),
),
body: Center(
child: ParentWidget(),
),
),
);
}
}
External packages

• We cannot use any package freely, but those


already part of the distribution
• We must edit pubspec.yaml to add dependencies
– For example, to pub.dev packages
pubspec.yaml

• Change pubspec.yaml
• Execute flutter pub get
– This pulls the package(s) into your project
– It also auto-generates the pubspec.lock file with a list of
all packages pulled into the project and their version
numbers
dependencies:
flutter:
sdk: flutter

# The following adds the Cupertino Icons font to your application.


# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2
english_words: ^4.0.0
// Copyright 2018 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

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

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {


@override
Widget build(BuildContext context) {
final wordPair = WordPair.random();
return MaterialApp(
title: 'Welcome to Flutter',
home: Scaffold(
appBar: AppBar(
title: Text('Welcome to Flutter'),
),
body: Center(
child: Text(wordPair.asPascalCase),
),
),
);
}
}
Adaptive vs. responsive

• LayoutBuilder
– Examines the constraints to decide what to display
– You can adjust your screen based on device’s width,
height, aspect ratio, and other properties
– When the constraints change, build is run
• MediaQuery
– This gives you the size, orientation, etc, of your device
– More useful if you want to make decisions based on the
complete context
– If something changes, build is run
LayoutBuilder

body: LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
if (constraints.maxWidth > 600) {
return _buildWideContainers();
} else {
return _buildNormalContainer();
}
},
)
MediaQuery

class Home extends StatelessWidget{


@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height * 0.4,
)
);
}
}
Navigation

• Almost all applications comprise diverse screens


• In Flutter, screens and pages are called routes
– A route is just a widget
• Flutter provides options to navigate screens
– Original navigation (imperative)
– Updated navigation (declarative)
• It was difficult to push or pop multiple pages, or remove a
page underneath the current one

• Navigator to navigate to new routes


– An app can use more than one Navigator
Navigator 1.0

• Navigator is a widget that manages a stack of


Route objects
• Route is an object managed by a Navigator that
represents a screen, typically implemented by
classes like MaterialPageRoute
Navigator 1.0

• Navigator.push() to navigate to a second route


– Adds a Route to the stack of routes managed by the
Navigator
– The previous screen is still part of the widget tree, so
any State object associated with it stays around
while the other screen is visible
• Navigator.pop() to return to initial one
– Removes the current Route from the stack of routes
managed by the Navigator
Example (FirstRoute)
class FirstRoute extends StatelessWidget {

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('First Route’),
),
body: Center(
child: ElevatedButton(
child: Text('Open route’),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder:
(context) => SecondRoute()),
);
}
),
),
);
}
}
Example (SecondRoute)
class SecondRoute extends StatelessWidget {

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Second Route"),
),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.pop(context);
},
child: Text('Go back!’),
),
),
);
}
}
Navigation with named routes

• To navigate to the same screen in many parts of


an app
• We can define a named route, and use the named
route for navigation
– Named routes require method Navigator.pushNamed()
Named routes

void main() {
runApp(
MaterialApp(
title: 'Named Routes Demo’,
initialRoute: '/’,
routes: {
'/': (context) => FirstScreen(),
'/second': (context) => SecondScreen(),
},
),
);
}
First route

class FirstScreen extends StatelessWidget {


@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('First Screen'),
),
body: Center(
child: ElevatedButton(
child: Text('Launch screen'),
onPressed: () {
Navigator.pushNamed(context, '/second');
},
),
),
);
}
}
Pass arguments to a named route

• In some cases, when navigating you might also need to


pass arguments to a named route
– For example, you might wish to navigate to the /user
route and pass information about a particular user
• Navigator.pushNamed() has a parameter for this
• We use ModalRoute.of and these steps:
– Define the arguments you need to pass
– Create a widget that extracts the arguments
– Register the widget in the routes table
– Navigate to the widget
• We may also use onGenerateRoute() to extract the
arguments
Step 1

• Define the arguments you need to pass


class ScreenArguments {
final String title;
final String message;

ScreenArguments(this.title, this.message);
}
Step 2
• Create a widget that extracts the arguments
class ExtractArgumentsScreen extends StatelessWidget {

static const routeName = '/extractArguments’;

@override
Widget build(BuildContext context) {
final args = ModalRoute.of(context)!.settings.arguments
as ScreenArguments;

return Scaffold(
appBar: AppBar(
title: Text(args.title),
),
body: Center(
child: Text(args.message),
),
);
}
}
Step 3

• Register the widget in the routes table

class MyApp extends StatelessWidget {


@override
Widget build(BuildContext context) {
return MaterialApp(
routes: {
ExtractArgumentsScreen.routeName:
(context) => ExtractArgumentsScreen(),
},
title: 'Navigation with Arguments',
home: HomeScreen(),
);
}
}
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home Screen'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
child: Text("Navigate to screen that extracts arguments"),
onPressed: () {
Navigator.pushNamed(
context,
ExtractArgumentsScreen.routeName,
arguments: ScreenArguments(
'Extract Arguments Screen',
'This message is extracted in the build method.',
),
);
},
),
],
),
),
);
}
}
PassArgumentsScreen
class PassArgumentsScreen extends StatelessWidget {
static const routeName = '/passArguments’;

final String title;


final String message;

PassArgumentsScreen({
Key? key,
required this.title,
required this.message,
}) : super(key: key);

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: Text(message),
),
);
}
}
Return data from a screen

• In some cases, you might want to return data


from a new screen
– For example, you push a new screen that presents two
options to a user
– When the user taps an option, you want to inform the
first screen of the user’s selection so that it can act on
that information
• We can use Navigator.pop()
Final result
Define the home screen

class HomeScreen extends StatelessWidget {


@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Returning Data Demo'),
),
body: Center(child: SelectionButton()),
);
}
}
Add a button that launches the selection screen

• Launches the SelectionScreen when it is tapped


• Waits for the SelectionScreen to return a result

class SelectionButton extends StatelessWidget {


@override
Widget build(BuildContext context) {
return RaisedButton(
onPressed: () {
_navigateAndDisplaySelection(context);
},
child: Text('Pick an option, any option!'),
);
}
Asyncronous programming

• A future represents the result of an asynchronous


operation
– It can have two states: uncompleted or completed
• To define an async function, add async before the
function body
– We can use keyword await to wait for a future to
complete
– Keyword await only works in async functions
How to use returned data

void _navigateAndDisplaySelection(BuildContext context) async {


final result = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => SelectionScreen()));

ScaffoldMesseger.of(context)
..removeCurrentSnackBar()
..showSnackBar(SnackBar(content: Text("$result")));
}
class SelectionScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Pick an option'),

Show the
),
body: Center(
child: Column(
selectionmainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[

screen
Padding(
padding: EdgeInsets.all(8.0),
child: RaisedButton(
with two onPressed: () {
// Pop here with "Yep"...

buttons
},
child: Text('Yep!'),
),
• Build a selection
),
screen that contains
Padding(
two buttons padding: EdgeInsets.all(8.0),
• When a user tapschild:
a RaisedButton(
onPressed: () {
button, the app closes
// Pop here with "Nope"
the selection screen
},
and lets the homechild: Text('Nope.'),
screen know which
),
button was
… tapped
}
How to return data

• Navigator.pop() accepts an optional second


argument called result

RaisedButton(
onPressed: () {
Navigator.pop(context, 'Yep!');
},
child: Text('Yep!'),
),

RaisedButton(
onPressed: () {
Navigator.pop(context, 'Nope.');
},
child: Text('Nope.'),
),
Send data to a new screen

• Often, you not only want to navigate to a new


screen, but also pass data to the screen
– For example, you might want to pass information about
the item that has been tapped
• For example, you can create a list of todos
– Define a todo class
– Display a list of todos
– Create a detail screen that can display information about
a todo
– Navigate and pass data to the detail screen
Class Todo
import 'package:flutter/material.dart';

class Todo {
final String title;
final String description;

Todo(this.title, this.description);
}

void main() {
runApp(MaterialApp(
title: 'Passing Data',
home: TodosScreen(
todos: List.generate(
20,
(i) => Todo(
'Todo $i',
'A description of what needs to be done for Todo $i',
),
),
),
));
}
class TodosScreen extends StatelessWidget {
final List<Todo> todos;

TodosScreen({Key key, required this.todos}) : super(key: key);

TodosScreen
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Todos'),
),
body: ListView.builder(
itemCount: todos.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(todos[index].title),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailScreen(todo: todos[index]),
),
);
},
);
},
),
);
}
}
DetailScreen
class DetailScreen extends StatelessWidget {
final Todo todo;

DetailScreen({Key key, required this.todo}) : super(key: key);

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(todo.title),
),
body: Padding(
padding: EdgeInsets.all(16.0),
child: Text(todo.description),
),
);
}
}
Example
Tabs

• Create a TabController
• Create the tabs
• Create content for each tab
class TabBarDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: DefaultTabController(
length: 3,
child: Scaffold(
appBar: AppBar(
bottom: TabBar(
tabs: [
Tab(icon: Icon(Icons.directions_car)),
Tab(icon: Icon(Icons.directions_transit)),
Tab(icon: Icon(Icons.directions_bike)),
],
),
title: Text('Tabs Demo'),
),
body: TabBarView(
children: [
Icon(Icons.directions_car),
Icon(Icons.directions_transit),
Icon(Icons.directions_bike),
],
),
),
),
);
}
}
Drawer
return Scaffold(
appBar: AppBar(),
drawer: Drawer(
child: Column(
children: [
DrawerHeader(
child: Center(
child: Text(
'MyApp’,
style: Theme.of(context).textTheme…,
),
),
),
...destinations.map((d) {
return ListTile(
title: Text(d.name),
leading: Icon(d.iconData),
selected: d == destination,
onTap: () => _changePage(d),
);
}),
],
),
),
Fetch data from the Internet

• Fetching data from the internet is necessary for


most apps
• Package http is the solution
• To install the http package, add it to the
dependencies section of the pubspec.yaml
– Execute flutter pub get

dependencies:
http: ^0.13.3
flutter:
sdk: flutter
Example (I)
import 'dart:convert';

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

Future<Post> fetchPost() async {


final response =
await http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts/1'));

if (response.statusCode == 200) {
return Post.fromJson(jsonDecode(response.body));
} else {
throw Exception('Failed to load post’);
}
}
Example (II)
class Post {
final int userId;
final int id;
final String title;
final String body;

Post(
{required this.userId,
required this.id,
required this.title,
required this.body});

factory Post.fromJson(Map<String, dynamic> json) {


return Post(
userId: json['userId’],
id: json['id’],
title: json['title’],
body: json['body’],
);
}
}
Example (III)

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {


@override
_MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {


late Future<Post> futurePost;

@override
void initState() {
super.initState();
futurePost = fetchPost();
}

• It is not recommended to put an API call in a build() method


– Leaving the fetch call in your build() method floods the API with
unnecessary calls and slows down your app
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Fetch Data Example’, Example (IV)
theme: ThemeData(
primarySwatch: Colors.deepOrange,
),
home: Scaffold(
appBar: AppBar(
title: const Text('Fetch Data Example’),
),
body: Center(
child: Padding(
padding: EdgeInsets.all(12.12),
child: FutureBuilder<Post>(
future: futurePost,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data!.title);
} else if (snapshot.hasError) {
return Text('${snapshot.error}’);
}
// By default, show a loading spinner.
return const CircularProgressIndicator();
},
),
),
),
),
);
}
State management

• Flutter rebuilds parts of our UI from scratch


instead of modifying it
– It is fast enough to do that, even on every frame if
needed
• Flutter is declarative, that is, it builds its user
interface to reflect the current state of our app
– A state change triggers a redraw of the user interface
State types
Ephemeral/Local state

• Is the state we can manage in a single widget


• There is no need to serialize it and it does not
change in complex ways
• There is no need to use state management
techniques on this kind of state
• All we need is a StatefulWidget
App state

• We want to share it across many parts of our app,


and we want to keep it between user sessions
– User preferences
– Login info
– Notifications in a social networking app
– Shopping cart in an e-commerce app
– Read/unread state of articles in a news app
• We have many options, and the choice depends
on many aspects
A few different options

• Redux
– A state container approach familiar to many web
developers
• BLoC
– A predictable state management library for Dart
• MobX
– A simple, scalable state management solution
• GetX
– A simplified reactive state management solution
Internationalization (i18n)
Localizations

• Flutter only provides US English localizations


• To add support for other languages, we must
– Include a package called flutter_localizations
– Specify additional MaterialApp (or CupertinoApp)
properties
• As of November 2020, this package supports 78
languages
First (pubspec.yaml)

• flutter pub get

dependencies:
flutter:
sdk: flutter
flutter_localizations: # Add this line
sdk: flutter # Add this line
intl: ^0.17.0 # Add this line

flutter:
uses-material-design: true
generate: true # Add this line
l10n.yaml

• Added to the root directory of the Flutter project


arb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart

• This file configures the localization tool


– The input files are located in ~/lib/l10n
– File app_en.arb provides the template
– Generated localizations are placed in file
app_localizations.dart
Then

• We must ass template app_en.arb to ~/lib/l10n,


{
"helloWorld": "Hello World!",
"@helloWorld": {
"description": "The conventional programmer greeting"
}
}

• Add file app_es.arb in the same directory for


Spanish translation of the same message
{
"helloWorld": "¡Hola Mundo!"
}

• Run the app so that codegen takes place


– Generated files are ~/.dart_tool/flutter_gen/gen_l10n
Resulting code (v1)
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Localizations Sample App’,
localizationsDelegates: [
AppLocalizations.delegate, // Add this line
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: [
Locale('en', ''), // English, no country code
Locale('es', ''), // Spanish, no country code
],
home: MyHomePage(),
);
}

Text(AppLocalizations.of(context)!.helloWorld),
Resulting code (v2)

Widget build(BuildContext context) {


return const MaterialApp(
title: 'Localizations Sample App’,
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
home: MyHomePage(),
);
}

Text(AppLocalizations.of(context)!.helloWorld),
Project structure
Test and debugging

• DevTools is a suite of performance and profiling


tools that run in a browser
• Android Studio/IntelliJ and VS Code support a
built-in source-level debugger
• Flutter inspector allows you to examine a visual
representation of the widget tree, inspect
individual widgets and their property values,
enable the performance overlay, and more
Many more things
OPEN DAY DEL CAMPUS DI CREMONA

3, 4 e 5 MAGGIO 2022

APPUNTAMENTI IN PRESENZA E
ON LINE

per scoprire
Ingegneria Gestionale
Ingegneria Informatica
Agricultural Engineering
Music and Acoustic Engineering

Registrazione Open Day scorso anno


su www.polo-cremona.polimi.it
Futuri studenti > Orientamento

Prof. Luciano Baresi

You might also like