Sommaire
1
1. Introduction à Flutter
2. Widgets everywhere
3. Le langage Dart
4. Gestion du State
5. Retours d'expériences & à priori
featuring...
Jamy, t'es où?
J'ai un gros souci (comme d'hab)!
J'ai promis à ma mère de lui développer une appli mobile pour son commerce d'herbes provençales, mais...
Développer en Android ET en iOS, ça coûte un bras!
Et React Native, c'est un peu has been!
1. Introduction
2
Ah mais bouge pas mon p'tit Fred, j'ai la solution à tes problèmes!
C'est nouveau, ça vient de sortir, et c'est from Google!
1. Introduction
3
Et pourquoi c'est si bien que ça Jamy?
Mais pour plein de raisons mon petit Fred!
SDK officiel de dev Fuchsia
Multi device sans bridge JS
Moteur de rendu custom
Basé sur Dart
Everything is widget
Communauté
Tooling
1. Introduction
5
. Made by Google
. Version 1.0 en Décembre 2018
. SDK de développement officiel de Fuchsia
. Utilise les langage & SDK Dart
1. Introduction
6
. Moteur de rendu custom
. Gère chaque pixel à l'écran
. Utilise la librairie Skia
. Plus besoin de bridge JS
. Compilé AOT en code natif ARM
1. Introduction
7
. Ecriture de composants thémés Material & Cupertino
. Pas (encore) de switch automatique...
. Besoin de reproduire les widgets natifs :
1. Introduction
8
Ca m'a l'air super ça Jamy!
Mais plus concrétement, ça ressemble à quoi?
Bah c'est pas compliqué : des Widgets et du Dart
Ca m'avance pas trop...
2. Widgets everywhere
9
. Good look and feel
. Fast (very)
. Customisable & extensible
2. Widgets everywhere
10
. Petit trade off :
. Moteur de rendu embarqué dans le livrable
. Taille min d'APK : environ 4.7Mb
2. Widgets everywhere
11
new Card(
child: new Center(
child: new Column(
children: [
new Text('Pizza Time!')),
new Icon(Icons.star, color: Colors.yellow)
]
)
)
). Pour ceux qui n'ont pas fait de React...
. Voici à quoi ressemblent les Widgets :
. TOUT EST WIDGET
. WIDGET TREE
2. Widgets everywhere
12
@override
Widget build(BuildContext context){
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Text('You have pushed the button this many times:'),
new Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: new FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: new Icon(Icons.add),
),
);
}. Exemple de widget tree "complet" :
2. Widgets everywhere
13
MyApp
MaterialApp
MyHomePage
Scaffold
AppBar
Text
Center
Column
FloatingActionButton
Icon
Text
Text
Phase de Layout
Constraints
Layout Details
2. Widgets everywhere
14
C'est ça Fred!
OK Jamy, c'est easy donc...
J'empile mes Widgets et c'est tout?
Et selon le besoin, des StatelessWidget, des StatefulWidget, des InheritedWidget, etc
Euh... ?
2. Widgets everywhere
15
C'est pas compliqué Fred...
. La StatelessWidget effectue le rendu de ce qu'on lui passe.
class TitleWidget extends StatelessWidget {
const TitleWidget(this.title, {
Key key,
}): assert(title != null), super(key:key);
final String title;
@override
Widget build(BuildContext context){
return Text(title, style: Styles.title);
}
}2. Widgets everywhere
16
. Le StatefulWidget gère un State interne... ;)
class CounterWidget extends StatefulWidget {
CounterWidget({ Key key, this.initialCount }): super(key: key);
final number initialCount;
@override
_CounterWidgetState createState() => new _CounterWidgetState();
}
class _CounterWidgetState extends State<CounterWidget> {
number _count;
@override
void initState() {
super.initState();
this._count = this.widget.initialCount;
}
@override
Widget build(BuildContext context){
return RaisedButton.icon(
icon: const Icon(Icons.add, size: 18.0),
label: const Text('Current count is $_counter'),
onPressed: () => setState(() => this._counter++),
);
}
}2. Widgets everywhere
17
Tiens Fred, question!
Un formulaire de login...
Stateless ou Stateful?
Euh... ?
2. Widgets everywhere
18
. L'InheritedWidget permet le partage de données.
. Uniquement pour ses enfants dans le Widget tree.
class MyInheritedWidget extends InheritedWidget {
MyInheritedWidget({
Key key,
@required Widget child,
this.user,
}): super(key: key, child: child);
final User user;
static MyInheritedWidget of(BuildContext context) {
return context.inheritFromWidgetOfExactType(MyInheritedWidget);
}
@override
bool updateShouldNotify(MyInheritedWidget oldWidget) => user!= oldWidget.user;
}2. Widgets everywhere
19
. L'InheritedWidget permet le partage de données.
. Uniquement pour ses enfants dans le Widget tree.
class ParentWidget... {
...
@override
Widget build(BuildContext context) {
return new MyInheritedWidget(
user: user,
child: ChildWidget(...)
);
}
}
class ChildWidget... {
...
@override
Widget build(BuildContext context){
final MyInheritedWidget inheritedWidget = MyInheritedWidget.of(context);
return new Container(
color: inheritedWidget.user.name,
);
}
}2. Widgets everywhere
20
Les plus :
. Facile à prendre en main
. Maintenabilité, factorisation
. Contrôle total sur le rendu de son application et ses mises à jour
Les moins :
. Beaucoup de widgets...
. Attention aux performances
. Slivers
3. Le langage Dart
21
. Dart 2.0
. Orienté objet
. Mono thread
Gestion de l'asynchronisme avec les Future
On m'a dit que Kotlin/TS étaient mieux...
. Performant, compilation en code natif
3. Le langage Dart
22
. Facilités de développement :
user ??= User();score = json['score']?.toDouble() ?? 0;List<String>()
..addAll(_existingUsers)
..addAll(newUsers)
..sort(_sortUsers)
..forEach((user) => print("User : ${user.name}"));class TextCollapsible extends StatefulWidget {
const TextCollapsible(
this.text,
{
Key key,
this.numDisplayChars = 150
}
) : assert(text != null),
super(key: key);3. Le langage Dart
23
. Mixins power :
. Une interface avec implémentation
class CircularPercentIndicatorState extends State<CircularPercentIndicator>
with SingleTickerProviderStateMixin {
...
_animationController = AnimationController(
vsync : this,
...mixin SingleTickerProviderStateMixin on State implements TickerProvider {
...
@override
Ticker createTicker(TickerCallback onTick) {
...3. Le langage Dart
24
. Hooks power :
class Example extends HookWidget {
final Duration duration;
const Example({Key key, @required this.duration})
: assert(duration != null),
super(key: key);
@override
Widget build(BuildContext context) {
final controller = useAnimationController(duration: duration);
return Container();
}
}3. Le langage Dart
25
. S'intègre parfaitement aux patterns de de développement Flutter :
final onTextChanged = PublishSubject<String>();
final Stream<List<User>> _users$ = onTextChanged
.distinct()
.debounce(const Duration(milliseconds: 250))
.switchMap<List<User>>((String term) => _searchUsers(term))
.startWith([]); @override
Widget build(BuildContext context) {
return StreamBuilder<List<User>>(
stream: _users$,
builder: (BuildContext context,
AsyncSnapshot<List<User>> snapshot) {
final List<User> users = snapshot.data;
.... Aparté sur la librairie RxDart :
3. Le langage Dart
26
Les plus :
. Rapidité de développement
. Performances
. Facilité de prise en main
Les moins :
. Pas aussi poussé qu'un Kotlin ou un Typescript?
. Pas de spread operator ou de destructuring ;)
4. Gestion du State
27
Jamy, j'ai bien compris le fonctionnement des widgets...
Mais une question me taraude quand même...
Mes objets métiers globaux, je les mets où?
J'aurais bien une réponse Fred...
4. Gestion du State
28
Y a de la DI avec Flutter & Dart?
Bah je vais faire des singletons alors!
Tu sors.
Nan
4. Gestion du State
29
. Je crée mon StateContainer avec un InhéritedWidget.
class AppStateContainer extends StatefulWidget {
...
static AppStateContainerState of(BuildContext context) {
return ...;
}
}
class AppStateContainerState extends State<AppStateContainer> {
AppState _state;
User get user => state.user;
void addProduct(Product product) { setState(() => _state.user.addProduct(product)) }
@override
Widget build(BuildContext context) {
return new _InheritedStateContainer(data : _state, child: ...);
}
}
class _InheritedStateContainer extends InheritedWidget {
final AppStateContainerState data;
...
@override bool updateShouldNotify(_InheritedStateContainer old) => true;
}4. Gestion du State
30
. Je crée mon StateContainer avec un InhéritedWidget.
. Oui, mais... ?
class AppState {
bool isLoading;
User user;
Cart cart;
List<Product> products;
...
AppState({
this.isLoading = false,
this.user,
this.cart,
this.products
});class UserCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
final AppStateContainerState state =
AppStateContainer.of(context);
return new Text('${state.user?.name}');
}
}class CartWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final AppStateContainerState state =
AppStateContainer.of(context);
...
return RaisedButton(
child: Text('Add Item'),
onPressed: () => state.addProduct(item)
);
}4. Gestion du State
31
Un InheritedWidget pour chaque donnée du State...
Oui, et c'est pour ça que d'autres alternatives existent.
C'est un peu lourd ça non?
4. Gestion du State
32
. Gestion du State global de l'application.
. flutter_redux :
4. Gestion du State
33
. flutter_redux :
StoreProvider
State
StoreConnector
Widgets
Actions
Reducers
Middleware
4. Gestion du State
34
. Séparation UI - logique métier
. BLoC :
. Gestion du State local
. Basé sur les Streams :
. StreamController
. Sinks
. Streams
4. Gestion du State
35
. BLoC :
class CounterBloc {
final StreamController<int> _counterController = StreamController<int>();
Stream<int> get getCount => _counterController.stream;
void updateCount(int newCount) {
_counterController.sink.add(newCount);
}
void dispose() {
_counterController.close();
}
}. Création du BLoC :
4. Gestion du State
36
. BLoC :
@override
Widget build(BuildContext context) {
return StreamBuilder<int>(
stream: bloc.getCount,
initialData: 0,
builder: (context, snapshot) => Row(
children: [
Text('${snapshot.data}'),
RaisedButton(
onPressed: () => bloc.updateCount(snapshot.data + 1),
child: Text('Increment')
)
]
)
);
}. Utilisation dans mes widgets :
4. Gestion du State
37
. Combo flutter_redux & "BLoC"
. Le premier pour le State GLOBAL
. Le deuxième pour le State LOCAL
38
Jamy, j'ai quand même très peur...
Y a mon pote qui est expert mobile, il m'a dit que Flutter n'a aucun avenir...
Ah oui, il a eu une mauvaise expérience avec Flutter?
Non, il a lu un thread Reddit.
5. Retours d'expérience et à priori
39
Plugins pour la suite Intellij & VSCode
Jamy, Flutter c'est super récent, il doit y avoir 0 tooling :/
Flutter CLI
HOT HOT RELOADING
UI, CPU, Memory debug tools
etc...
5. Retours d'expérience et à priori
40
Communauté & doc très importantes
Jamy, et la communauté? Vais-je être seul avec mes questions?
5. Retours d'expérience et à priori
41
Nope. 7Mo en release pour une appli de 60+ widgets
Jamy, on m'a dit que la taille des livrables était monstrueuse :/
Jamy, on m'a dit que les perfs étaient pourries :/
En dev, oui. Mais aucun souci en release.
5. Retours d'expérience et à priori
5. Retours d'expérience et à priori
43
Jamy, et à choisir avec React Native?
etc...
En conclusion...
44
. Flutter est une solution viable pour la création d'applications mobiles multi-devices complexes.
. Est-ce le meilleur choix entre natif et React? Bah ça dépend ;)
Super ta conclusion...
En conclusion...
45
. Les plus :
. Moteur de rendu custom
. Hot reloading
. Widgets
. Dart
. Les moins :
. Moteur de rendu custom
. Widgets
. Debug & errors