Flutter
Flutter
Video di presentazione
del Campus di Cremona
https://youtu.be/L0SDjMFIleo
INGEGNERIA GESTIONALE
INGEGNERIA INFORMATICA
AGRICULTURAL ENGINEERING
3, 4 e 5 MAGGIO 2022
APPUNTAMENTI IN PRESENZA E
ON LINE
per scoprire
Ingegneria Gestionale
Ingegneria Informatica
Agricultural Engineering
Music and Acoustic Engineering
• 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
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
void main() {
runApp(
Center(
child: Text(
'Hello, world!',
textDirection: TextDirection.ltr,
),
),
);
}
Minimal app
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
Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.center,
children: [BlueBox(), BiggerBlueBox(), BlueBox()],
),
),
Flexible
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
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
body: Center(
child: Row(
children: [
Image.network('https://...’),
],
),
)
Wireframes
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
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());
}
@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
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()
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
• 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(),
),
),
);
}
}
@override
_TapboxAState createState() => _TapboxAState();
}
class _TapboxAState extends State<TapboxA> {
bool _active = false;
void _handleTap() {
setState(() {
_active = !_active;
});
}
• Example
– IconButton is a stateless widget but the parent widget
needs to know whether the button has been tapped
@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);
void _handleTap() {
onChanged(!active);
}
• 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
import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';
• 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
@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
void main() {
runApp(
MaterialApp(
title: 'Named Routes Demo’,
initialRoute: '/’,
routes: {
'/': (context) => FirstScreen(),
'/second': (context) => SecondScreen(),
},
),
);
}
First route
ScreenArguments(this.title, this.message);
}
Step 2
• Create a widget that extracts the arguments
class ExtractArgumentsScreen extends StatelessWidget {
@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
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
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
RaisedButton(
onPressed: () {
Navigator.pop(context, 'Yep!');
},
child: Text('Yep!'),
),
RaisedButton(
onPressed: () {
Navigator.pop(context, 'Nope.');
},
child: Text('Nope.'),
),
Send data to a new screen
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
@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;
@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
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;
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});
@override
void initState() {
super.initState();
futurePost = fetchPost();
}
• 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
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
Text(AppLocalizations.of(context)!.helloWorld),
Resulting code (v2)
Text(AppLocalizations.of(context)!.helloWorld),
Project structure
Test and debugging
3, 4 e 5 MAGGIO 2022
APPUNTAMENTI IN PRESENZA E
ON LINE
per scoprire
Ingegneria Gestionale
Ingegneria Informatica
Agricultural Engineering
Music and Acoustic Engineering