Flutter Mémo Rapide
Flutter - Build Apps for Any Screen
Flutter est la boîte à outils d'interface utilisateur de Google pour créer de belles applications compilées nativement pour mobile, web et desktop à partir d'un seul code source. Il est reconnu pour son interface utilisateur expressive, son cycle de développement rapide et ses performances natives.
[No text to translate]Table des Matières
- Installation
- Démarrage
- Concepts Principaux
- Widgets
- Mises en Page
- Navigation
- Gestion d’État
- Réseau
- Persistance
- Animations
- Intégration Plateforme
- Tests
- Débogage
- Déploiement
- Meilleures Pratiques
- Résolution de Problèmes
Would you like me to continue translating the remaining sections? I can proceed with the translations for sections 4-20 if you confirm.```bash
Download Flutter SDK from https://flutter.dev/docs/get-started/install
Extract the zip file
Add Flutter to your path (for the current terminal session)
export PATH=“$PATH:pwd/flutter/bin”
Add Flutter to your path permanently (macOS/Linux)
echo ‘export PATH=“$PATH:[PATH_TO_FLUTTER_GIT_DIRECTORY]/flutter/bin”’ >> ~/.zshrc source ~/.zshrc
Add Flutter to your path permanently (Windows)
Update your system’s environment variables
### Platform Setup
#### Android Setup
```bash
# Install Android Studio
# Download from https://developer.android.com/studio
# Set up Android emulator
# Open Android Studio > AVD Manager > Create Virtual Device
# Accept Android licenses
flutter doctor --android-licenses
iOS Setup (macOS only)
# Install Xcode
# Download from Mac App Store
# Install CocoaPods
sudo gem install cocoapods
# Set up iOS simulator
open -a Simulator
Flutter Doctor
# Check your environment and see a report of the status of your Flutter installation
flutter doctor
# Verbose output
flutter doctor -v
Getting Started
Create a New Project
# Create a new Flutter project
flutter create my_app
# Navigate to the project directory
cd my_app
# Create a project with a specific organization
flutter create --org com.example my_app
# Create a project with a specific template
flutter create -t module my_module
Run the App
# Run the app on an available device
flutter run
# Run on a specific device
flutter run -d chrome
flutter run -d 'iPhone 12'
# Run in release mode
flutter run --release
# Run with hot reload
# Press 'r' in the terminal
# Run with hot restart
# Press 'R' in the terminal
Project Structure
my_app/
├── android/ # Android-specific code
├── ios/ # iOS-specific code
├── lib/ # Main Dart code
│ ├── main.dart # App entry point
│ ├── screens/ # Screen widgets
│ ├── widgets/ # Reusable widgets
│ ├── models/ # Data models
│ ├── services/ # API services
│ └── utils/ # Utility functions
├── test/ # Test files
├── pubspec.yaml # Project dependencies and metadata
└── README.md # Project documentation
Core Concepts
Dart Language Basics
// Variables
var name = 'Flutter';
String language = 'Dart';
int year = 2017;
double version = 2.12;
bool isAwesome = true;
// Functions
int add(int a, int b) {
return a + b;
}
// Arrow syntax
int multiply(int a, int b) => a * b;
// Classes
class Person {
String name;
int age;
Person(this.name, this.age);
void sayHello() {
print('Hello, my name is $name and I am $age years old.');
}
}
// Asynchronous programming
Future<String> fetchData() async {
await Future.delayed(Duration(seconds: 2));
return 'Data fetched';
}
void main() async {
var person = Person('John Doe', 30);
person.sayHello();
print('Fetching data...');
var data = await fetchData();
print(data);
}
Everything is a Widget
import 'package:flutter/material.dart';
void main() {
runApp(
MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('My First Flutter App'),
),
body: Center(
child: Text('Hello, Flutter!'),
),
),
),
);
}
BuildContext
// BuildContext provides information about the location of a widget in the widget tree.
// It is used to access theme data, navigation, and other inherited widgets.
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return RaisedButton(
onPressed: () {
Scaffold.of(context).showSnackBar(
SnackBar(content: Text('Button pressed')),
);
},
child: Text('Press Me'),
);
}
}
Stateless vs. Stateful Widgets
// StatelessWidget: A widget that does not require mutable state.
class MyStatelessWidget extends StatelessWidget {
final String title;
MyStatelessWidget({required this.title});
@override
Widget build(BuildContext context) {
return Text(title);
}
}
// StatefulWidget: A widget that has mutable state.
class MyStatefulWidget extends StatefulWidget {
@override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Text('Counter: $_counter'),
RaisedButton(
onPressed: _incrementCounter,
child: Text('Increment'),
),
],
);
}
}
Widgets
Basic Widgets
import 'package:flutter/material.dart';
class BasicWidgets extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Basic Widgets')),
body: SingleChildScrollView(
padding: EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Text
Text('This is a Text widget', style: TextStyle(fontSize: 18)),
SizedBox(height: 20),
// Icon
Icon(Icons.favorite, color: Colors.red, size: 30),
SizedBox(height: 20),
// Image
Image.network('https://flutter.dev/images/flutter-logo-sharing.png'),
SizedBox(height: 20),
// Asset Image
Image.asset('assets/images/logo.png'),
SizedBox(height: 20),
// Button
RaisedButton(
onPressed: () {},
child: Text('RaisedButton'),
),
SizedBox(height: 10),
FlatButton(
onPressed: () {},
child: Text('FlatButton'),
),
SizedBox(height: 10),
IconButton(
onPressed: () {},
icon: Icon(Icons.thumb_up),
),
SizedBox(height: 20),
// TextField
TextField(
decoration: InputDecoration(
labelText: 'Enter your name',
border: OutlineInputBorder(),
),
),
SizedBox(height: 20),
// Checkbox
CheckboxListTile(
title: Text('Accept terms and conditions'),
value: true,
onChanged: (value) {},
),
SizedBox(height: 20),
// Radio
RadioListTile(
title: Text('Option 1'),
value: 1,
groupValue: 1,
onChanged: (value) {},
),
SizedBox(height: 20),
// Switch
SwitchListTile(
title: Text('Enable notifications'),
value: true,
onChanged: (value) {},
),
SizedBox(height: 20),
// Slider
Slider(
value: 0.5,
onChanged: (value) {},
),
SizedBox(height: 20),
// Progress Indicator
CircularProgressIndicator(),
SizedBox(height: 10),
LinearProgressIndicator(),
],
),
),
);
}
}
Material Components
import 'package:flutter/material.dart';
class MaterialComponents extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Material Components')),
drawer: Drawer(
child: ListView(
children: [
DrawerHeader(child: Text('Drawer Header')),
ListTile(title: Text('Item 1')),
ListTile(title: Text('Item 2')),
],
),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Card
Card(
child: Padding(
padding: EdgeInsets.all(16.0),
child: Text('This is a Card'),
),
),
SizedBox(height: 20),
// Chip
Chip(
avatar: CircleAvatar(child: Text('A')),
label: Text('Chip'),
),
SizedBox(height: 20),
// DataTable
DataTable(
columns: [
DataColumn(label: Text('ID')),
DataColumn(label: Text('Name')),
],
rows: [
DataRow(cells: [DataCell(Text('1')), DataCell(Text('John'))]),
DataRow(cells: [DataCell(Text('2')), DataCell(Text('Jane'))]),
],
),
SizedBox(height: 20),
// SnackBar
RaisedButton(
onPressed: () {
Scaffold.of(context).showSnackBar(
SnackBar(content: Text('This is a SnackBar')),
);
},
child: Text('Show SnackBar'),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {},
child: Icon(Icons.add),
),
bottomNavigationBar: BottomNavigationBar(
items: [
BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
BottomNavigationBarItem(icon: Icon(Icons.search), label: 'Search'),
BottomNavigationBarItem(icon: Icon(Icons.person), label: 'Profile'),
],
),
);
}
}
Layouts
Single-Child Layout Widgets
import 'package:flutter/material.dart';
class SingleChildLayouts extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Single-Child Layouts')),
body: Column(
children: [
// Container
Container(
color: Colors.blue,
padding: EdgeInsets.all(16.0),
margin: EdgeInsets.all(16.0),
child: Text('Container'),
),
// Center
Center(
child: Text('Center'),
),
// Padding
Padding(
padding: EdgeInsets.all(16.0),
child: Text('Padding'),
),
// Align
Align(
alignment: Alignment.bottomRight,
child: Text('Align'),
),
// SizedBox
SizedBox(
width: 200,
height: 100,
child: Card(child: Center(child: Text('SizedBox'))),
),
// AspectRatio
AspectRatio(
aspectRatio: 16 / 9,
child: Container(color: Colors.green),
),
// ConstrainedBox
ConstrainedBox(
constraints: BoxConstraints(maxWidth: 200, maxHeight: 100),
child: Container(color: Colors.red),
),
],
),
);
}
}
Multi-Child Layout Widgets
import 'package:flutter/material.dart';
class MultiChildLayouts extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Multi-Child Layouts')),
body: Column(
children: [
// Row
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [Text('Row 1'), Text('Row 2'), Text('Row 3')],
),
// Column
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [Text('Column 1'), Text('Column 2'), Text('Column 3')],
),
// Stack
Stack(
children: [
Container(width: 100, height: 100, color: Colors.red),
Container(width: 80, height: 80, color: Colors.green),
Container(width: 60, height: 60, color: Colors.blue),
],
),
// ListView
Expanded(
child: ListView(
children: [
ListTile(title: Text('Item 1')),
ListTile(title: Text('Item 2')),
ListTile(title: Text('Item 3')),
],
),
),
// GridView
Expanded(
child: GridView.count(
crossAxisCount: 2,
children: List.generate(4, (index) => Center(child: Text('Item $index'))),
),
),
],
),
);
}
}
Navigation
Navigator 1.0 (Imperative)
// Navigate to a new screen
Navigator.push(
context,
MaterialPageRoute(builder: (context) => DetailsScreen()),
);
// Navigate back
Navigator.pop(context);
// Navigate with data
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailsScreen(item: item),
),
);
// Receive data from a screen
final result = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => SelectionScreen()),
);
// Send data back
Navigator.pop(context, 'Yes');
Navigator 2.0 (Declarative)
// pubspec.yaml
dependencies:
flutter:
sdk: flutter
go_router: ^3.0.0
// main.dart
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp.router(
routeInformationParser: _router.routeInformationParser,
routerDelegate: _router.routerDelegate,
);
}
final _router = GoRouter(
routes: [
GoRoute(
path: '/',
builder: (context, state) => HomeScreen(),
),
GoRoute(
path: '/details/:id',
builder: (context, state) => DetailsScreen(id: state.params['id']!),
),
],
);
}
// Navigate with go_router
context.go('/details/123');
context.push('/details/456');
State Management
Provider
// pubspec.yaml
dependencies:
provider: ^6.0.0
// main.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => Counter(),
child: MyApp(),
),
);
}
class Counter with ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
}
// Usage in a widget
class CounterScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Provider Example')),
body: Center(
child: Text('Count: ${context.watch<Counter>().count}'),
),
floatingActionButton: FloatingActionButton(
onPressed: () => context.read<Counter>().increment(),
child: Icon(Icons.add),
),
);
}
}
```### BLoC (Business Logic Component)
```dart
// pubspec.yaml
dependencies:
flutter_bloc: ^8.0.0
// counter_event.dart
abstract class CounterEvent {}
class Increment extends CounterEvent {}
// counter_bloc.dart
import 'package:bloc/bloc.dart';
import 'counter_event.dart';
class CounterBloc extends Bloc<CounterEvent, int> {
CounterBloc() : super(0) {
on<Increment>((event, emit) => emit(state + 1));
}
}
// main.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
void main() {
runApp(
BlocProvider(
create: (context) => CounterBloc(),
child: MyApp(),
),
);
}
// Usage in a widget
class CounterScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('BLoC Example')),
body: Center(
child: BlocBuilder<CounterBloc, int>(
builder: (context, count) => Text('Count: $count'),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => context.read<CounterBloc>().add(Increment()),
child: Icon(Icons.add),
),
);
}
}
```### Riverpod
```dart
// pubspec.yaml
dependencies:
flutter_riverpod: ^1.0.0
// main.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
final counterProvider = StateProvider((ref) => 0);
void main() {
runApp(ProviderScope(child: MyApp()));
}
// Usage in a widget
class CounterScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.watch(counterProvider);
return Scaffold(
appBar: AppBar(title: Text('Riverpod Example')),
body: Center(
child: Text('Count: $count'),
),
floatingActionButton: FloatingActionButton(
onPressed: () => ref.read(counterProvider.state).state++,
child: Icon(Icons.add),
),
);
}
}
```## Mise en Réseau
### Package http
```dart
// pubspec.yaml
dependencies:
http: ^0.13.0
// services/api_service.dart
import 'package:http/http.dart' as http;
import 'dart:convert';
class ApiService {
Future<Map<String, dynamic>> fetchData() async {
final response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts/1'));
if (response.statusCode == 200) {
return json.decode(response.body);
} else {
throw Exception('Failed to load data');
}
}
}
```### Package dio
```dart
// pubspec.yaml
dependencies:
dio: ^4.0.0
// services/api_service.dart
import 'package:dio/dio.dart';
class ApiService {
final dio = Dio();
Future<Map<String, dynamic>> fetchData() async {
try {
final response = await dio.get('https://jsonplaceholder.typicode.com/posts/1');
return response.data;
} catch (e) {
throw Exception('Failed to load data');
}
}
}
```## Persistance
### shared_preferences
```dart
// pubspec.yaml
dependencies:
shared_preferences: ^2.0.0
// utils/storage.dart
import 'package:shared_preferences/shared_preferences.dart';
class Storage {
Future<void> saveUsername(String username) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('username', username);
}
Future<String?> getUsername() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString('username');
}
}
```### sqflite
```dart
// pubspec.yaml
dependencies:
sqflite: ^2.0.0
path: ^1.8.0
// services/database_service.dart
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
class DatabaseService {
late Database _database;
Future<void> initDatabase() async {
_database = await openDatabase(
join(await getDatabasesPath(), 'my_database.db'),
onCreate: (db, version) {
return db.execute(
'CREATE TABLE users(id INTEGER PRIMARY KEY, name TEXT, age INTEGER)',
);
},
version: 1,
);
}
Future<void> insertUser(Map<String, dynamic> user) async {
await _database.insert('users', user, conflictAlgorithm: ConflictAlgorithm.replace);
}
Future<List<Map<String, dynamic>>> getUsers() async {
return await _database.query('users');
}
}
```## Animations
### Animations Implicites
```dart
class ImplicitAnimation extends StatefulWidget {
@override
_ImplicitAnimationState createState() => _ImplicitAnimationState();
}
class _ImplicitAnimationState extends State<ImplicitAnimation> {
bool _isBig = false;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => setState(() => _isBig = !_isBig),
child: AnimatedContainer(
duration: Duration(seconds: 1),
width: _isBig ? 200 : 100,
height: _isBig ? 200 : 100,
color: _isBig ? Colors.blue : Colors.red,
),
);
}
}
```### Animations Explicites
```dart
class ExplicitAnimation extends StatefulWidget {
@override
_ExplicitAnimationState createState() => _ExplicitAnimationState();
}
class _ExplicitAnimationState extends State<ExplicitAnimation> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(vsync: this, duration: Duration(seconds: 2));
_animation = Tween<double>(begin: 0, end: 300).animate(_controller)
..addListener(() => setState(() {}));
_controller.forward();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
width: _animation.value,
height: _animation.value,
color: Colors.green,
);
}
}
```## Intégration de Plateforme
### MethodChannel
```dart
// Dart side
import 'package:flutter/services.dart';
const platform = MethodChannel('com.example.myapp/channel');
Future<String> getBatteryLevel() async {
try {
final int result = await platform.invokeMethod('getBatteryLevel');
return 'Battery level at $result % .';
} on PlatformException catch (e) {
return "Failed to get battery level: '${e.message}'.";
}
}
// Android side (MainActivity.java)
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.plugin.common.MethodChannel;
public class MainActivity extends FlutterActivity {
private static final String CHANNEL = "com.example.myapp/channel";
@Override
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
super.configureFlutterEngine(flutterEngine);
new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL)
.setMethodCallHandler(
(call, result) -> {
if (call.method.equals("getBatteryLevel")) {
// ... get battery level
result.success(batteryLevel);
}
}
);
}
}
```## Tests
### Tests Unitaires
```dart
// test/counter_test.dart
import 'package:test/test.dart';
import 'package:my_app/counter.dart';
void main() {
test('Counter value should be incremented', () {
final counter = Counter();
counter.increment();
expect(counter.value, 1);
});
}
```### Tests de Widgets
```dart
// test/widget_test.dart
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:my_app/main.dart';
void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
await tester.pumpWidget(MyApp());
expect(find.text('0'), findsOneWidget);
expect(find.text('1'), findsNothing);
await tester.tap(find.byIcon(Icons.add));
await tester.pump();
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsOneWidget);
});
}
```### Tests d'Intégration
```dart
// test_driver/app_test.dart
import 'package:flutter_driver/flutter_driver.dart';
import 'package:test/test.dart';
void main() {
group('Counter App', () {
FlutterDriver driver;
setUpAll(() async {
driver = await FlutterDriver.connect();
});
tearDownAll(() async {
if (driver != null) {
driver.close();
}
});
test('increments the counter', () async {
final counterTextFinder = find.byValueKey('counter');
final buttonFinder = find.byValueKey('increment');
expect(await driver.getText(counterTextFinder), '0');
await driver.tap(buttonFinder);
expect(await driver.getText(counterTextFinder), '1');
});
});
}
```## Débogage
### DevTools
```bash
# Open DevTools from the terminal
flutter pub global activate devtools
flutter pub global run devtools
# Or from VS Code / Android Studio
```### Journalisation
```dart
import 'dart:developer' as developer;
void myFunction() {
developer.log('This is a log message', name: 'my.app.category');
}
```## Déploiement
### Android
```bash
# Build an APK
flutter build apk
# Build an App Bundle
flutter build appbundle
```### iOS
```bash
# Build an IPA
flutter build ipa
```### Web
```bash
# Build for web
flutter build web
```## Meilleures Pratiques
- **Suivez les directives Dart efficaces** : Écrivez du code Dart propre et maintenable.
- **Organisez la structure de votre projet** : Gardez votre code organisé et facile à naviguer.
- **Utilisez une solution de gestion d'état** : Choisissez une solution de gestion d'état qui convient à la complexité de votre application.
- **Écrivez des tests** : Écrivez des tests unitaires, de widgets et d'intégration pour vous assurer que votre application fonctionne correctement.
- **Optimisez les performances** : Utilisez DevTools pour identifier et corriger les goulots d'étranglement de performance.
- **Gérez les erreurs avec élégance** : Utilisez des blocs try-catch et des widgets d'erreur pour gérer les erreurs.
## Résolution de Problèmes
### Problèmes Courants
- **Problèmes spécifiques à la plateforme** : Vérifiez la configuration de votre plateforme (Android Studio, Xcode).
- **Conflits de dépendances** : Exécutez `flutter pub deps`pour vérifier les conflits de dépendances.
- **Erreurs de compilation** : Exécutez `flutter clean`puis
Would you like me to fill in the specific commands for items 19 and 20, or do you want to provide those?`flutter pub get`pour nettoyer vos fichiers de build.
---
## Résumé
Flutter est un toolkit d'interface utilisateur puissant et flexible pour créer des applications multiplateformes de haute qualité. Ses principales caractéristiques incluent :
- **Développement Rapide** : Le rechargement à chaud vous permet de voir les modifications instantanément.
- **Interface Utilisateur Expressive** : Créez des interfaces utilisateur magnifiques et personnalisées avec un ensemble riche de widgets.
- **Performance Native** : Les applications Flutter sont compilées en code natif, offrant une excellente performance.
- **Base de Code Unique** : Écrivez une fois, exécutez sur mobile, web et desktop.
- **Communauté Croissante** : Une communauté large et active fournit du support et un écosystème riche de packages.
Flutter est un excellent choix pour les développeurs qui souhaitent créer des applications performantes et élégantes pour plusieurs plateformes avec une base de code unique.
<script>
function copyToClipboard() {
const commands = document.querySelectorAll('code');
let allCommands = '';
commands.forEach(cmd => allCommands += cmd.textContent + '\n');
navigator.clipboard.writeText(allCommands);
alert('All commands copied to clipboard!');
}
function generatePDF() {
window.print();
}
</script>