Skip to content

Bases de l'interface utilisateur Flutter

Flutter utilise un système de widgets composables pour créer des interfaces utilisateur. Chaque élément de l’interface est un widget, des éléments simples comme le texte aux structures complexes comme les écrans complets.

Le Scaffold fournit la structure de base d’un écran dans une application Material Design. Il offre des emplacements prédéfinis pour les éléments courants de l’interface utilisateur.

L’AppBar permet d’afficher :

  • le titre de l’application
  • à gauche : généralement un bouton retour
  • une liste de boutons d’actions
import 'package:flutter/material.dart';
void main() => runApp(
MaterialApp(
home: Scaffold(
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.arrow_left),
onPressed: () {},
),
title: const Text('Home'),
actions: [
IconButton(
icon: Icon(Icons.info),
onPressed: () => print('Infos'),
),
],
),
),
debugShowCheckedModeBanner: false,
),
);

Les scaffold permettent d’afficher un drawer : un panneau latéral modal (drawer et endDrawer).

Lorsqu’un Drawer est présent, l’AppBar intègre par défaut un bouton “burger”.

Scaffold(
appBar: AppBar(
title: const Text('Drawer Demo'),
),
drawer: Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: const <Widget>[
DrawerHeader(
decoration: BoxDecoration(
color: Colors.blue,
),
child: Text(
'Drawer Header',
style: TextStyle(
color: Colors.white,
fontSize: 24,
),
),
),
ListTile(
leading: Icon(Icons.message),
title: Text('Messages'),
),
ListTile(
leading: Icon(Icons.account_circle),
title: Text('Profile'),
),
ListTile(
leading: Icon(Icons.settings),
title: Text('Settings'),
),
],
),
),
)

Le scaffold intègre un emplacement pour un FloatingActionButton (FAB). Ce bouton est positionné en bas à droite ou en bas au milieu, “au-dessus” du contenu du body.

import 'package:flutter/material.dart';
void main() {
runApp(const MaterialApp(home: MainScreen()));
}
class MainScreen extends StatefulWidget {
const MainScreen({Key? key}) : super(key: key);
@override
State<MainScreen> createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
int value = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
floatingActionButton: FloatingActionButton(
onPressed: () => print('ADD'),
child: const Icon(Icons.add),
),
);
}
}

L’AppBar permet également l’affichage d’une barre d’onglets scrollable/swipable.

import 'package:flutter/material.dart';
void main() {
runApp(const MaterialApp(home: MainScreen()));
}
class MainScreen extends StatefulWidget {
const MainScreen({Key? key}) : super(key: key);
@override
_MainScreenState createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen>
with SingleTickerProviderStateMixin {
final tabs = const [
Tab(text: 'Livres'),
Tab(text: 'Films'),
Tab(text: 'Disques'),
];
late TabController tabController;
@override
void initState() {
tabController = TabController(
length: tabs.length,
initialIndex: 0,
vsync: this,
);
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
bottom: TabBar(
controller: tabController,
tabs: tabs,
isScrollable: true,
),
),
body: TabBarView(
controller: tabController,
children: const <Widget>[
Center(child: Text('Livres')),
Center(child: Text('Films')),
Center(child: Text('Musique')),
],
),
);
}
}

Le BottomNavigationBar permet de créer une navigation par onglets en bas de l’écran.

import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
home: MainScreen(icons),
));
}
List<Icon> icons = const [
Icon(Icons.music_note),
Icon(Icons.book),
];
class MainScreen extends StatefulWidget {
final List<Icon> icons;
const MainScreen(this.icons, {Key? key}) : super(key: key);
@override
_MainScreenState createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
int pageIndex = 0;
late List<Widget> subviews;
@override
void initState() {
subviews = widget.icons;
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.shifting,
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.music_note),
label: 'Music',
),
BottomNavigationBarItem(
icon: Icon(Icons.book),
label: 'Livre',
),
],
currentIndex: pageIndex,
fixedColor: Colors.cyan,
unselectedItemColor: Colors.grey.shade500,
onTap: (index) => setState(() => pageIndex = index),
),
body: Center(child: subviews[pageIndex]),
);
}
}

Le widget Text permet d’afficher du texte avec différentes options de style.

const Text(
'Hello Flutter',
style: TextStyle(color: Colors.blue),
textAlign: TextAlign.start,
maxLines: 3,
overflow: TextOverflow.ellipsis,
)

Pour créer du texte avec différents styles dans le même widget :

const Text.rich(
TextSpan(
text: 'Hello',
children: [
TextSpan(
text: 'Flutter',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 32,
color: Colors.white,
backgroundColor: Colors.cyan,
),
),
],
),
style: TextStyle(color: Colors.blue),
textAlign: TextAlign.start,
maxLines: 3,
overflow: TextOverflow.ellipsis,
)

Le widget Image dispose de 4 constructeurs :

  • Image.network
  • Image.asset
  • Image.file
  • Image.memory

Il permet de définir :

  • une taille
  • un coefficient d’échelle
  • une teinte
  • l’opacité
  • etc…

Pour embarquer des assets (images, polices, textes), on les déclare dans le fichier pubspec.yaml :

flutter:
uses-material-design: true
assets:
- assets/logo.png
- assets/config.json
- packages/my_assets/assets/image.png

Pour utiliser cet asset :

Image.asset('assets/dart.png'),
final config = await rootBundle.loadString('assets/config.json');

Flutter fournit une large collection d’icônes Material Design :

Icon(Icons.music_note)

Consultez material.io/icons pour voir toutes les icônes disponibles.

Material Design propose une palette de MaterialColors. Chacune de ces couleurs contient plusieurs nuances.

Colors.cyan
Colors.cyan.shade100
Colors.cyan.shade500
Colors.cyan.shade900
Colors.cyanAccent
Colors.cyanAccent.shade100

Ce widget a la particularité d’aller à l’encontre du principe de composition. En effet, ce widget intègre la logique de plusieurs autres widgets et permet de déclarer :

  • un padding
  • une marge
  • un alignment
  • une taille : la largeur width et la hauteur height
  • une couleur color
  • une decoration de type BoxDecoration, pouvant définir :
    • un borderRadius
    • un contour
  • une contrainte constraints
Container(
width: 300,
height: 300,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.red, width: 3),
boxShadow: const [
BoxShadow(blurRadius: 8, spreadRadius: 2)
],
gradient: LinearGradient(
colors: [Colors.cyan, Colors.blueGrey]
),
),
alignment: Alignment.center,
child: Text(
'Hello, World!',
style: Theme.of(context).textTheme.headline4,
),
);

Card est un container Material ombré et légèrement arrondi :

Card(
elevation: 5,
color: Colors.grey.shade300,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Text('Settings : $category'),
),
ElevatedButton(
onPressed: context.pop,
child: const Text('Fermer'),
)
],
),
),
)

Le widget Padding permet d’ajouter des espaces autour d’un widget enfant, exprimé à l’aide de EdgeInsets :

Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: const [
Padding(
padding: EdgeInsets.all(8.0),
child: Icon(Icons.add),
),
Text('Ajouter'),
],
)

Le widget Center centre son enfant dans l’espace disponible :

Center(
child: Text('Contenu centré'),
)

Le widget SizedBox permet de contraindre la taille de son enfant ou de créer un espace vide :

// Contraindre la taille
SizedBox(
width: 200,
height: 100,
child: Container(color: Colors.blue),
)
// Créer un espace vide
SizedBox(height: 20) // Espace vertical de 20 pixels

Ces widgets de composition de base sont essentiels pour créer des mises en page précises et bien structurées dans Flutter.