Implémenter Image Picker dans Flutter
Flutter ne permet pas de manière native d’accéder à la galerie photo du téléphone. Vous allez devoir passer par le package image_picker, qui va vous permettre de réaliser cette action. La première étape consiste donc à le télécharger et à l’implémenter sur votre application Flutter.
Installer le package
L’installation du package image_picker peut se faire soit en allant dans votre fichier pubspec.yaml et en y ajoutant la ligne suivante en dessous de dependencies
:
image_picker: ^1.1.2 //Changer par la dernière version disponible
Soit en exécutant la commande suivante dans votre terminal :
flutter pub add image_picker
Une fois ajouté, synchronisez les dépendances avec la commande suivante :
flutter pub get
Configurer image_picker pour iOS
Une fois le package téléchargé, il reste encore quelques étapes avant de pouvoir l’utiliser dans votre application Flutter. En effet, celui-ci est directement disponible pour Android, mais demande quelques ajouts dans le fichier Info.plist pour IOS.
Voici les permissions que vous devez ajouter dans le fichier ios > Runner > Info.plist
, à l’intérieur du tag <dict>
:
<key>NSPhotoLibraryUsageDescription</key> //Permet d'accéder à la galerie
<string>Nous avons besoin d'accéder à votre galerie pour sélectionner des photos.</string>
//Le reste est optionnel, mais vous donne aussi accès à la caméra et au micro
<key>NSCameraUsageDescription</key>
<string>Nous avons besoin d'accéder à votre caméra pour capturer des photos.</string>
<key>NSMicrophoneUsageDescription</key>
<string>Nous avons besoin d'accéder au microphone pour enregistrer des vidéos.</string>
Implémentation du code dans Flutter
Maintenant que image_picker est implémenté, commencez par ajouter la ligne d’importation du package, dans le fichier contenant le widget est charge d’accéder à la galerie photo:
import 'package:image_picker/image_picker.dart';
Voici ensuite les étapes à suivre pour accéder à la galerie :
- Initialiser l’instance d’ImagePicker
Nous devons créer une instance de ImagePicker
pour appeler ses méthodes. Cela se fait généralement dans la classe State
de votre widget.
final ImagePicker _picker = ImagePicker();
Cette instance agit comme un « contrôleur » qui permet de demander des images à la galerie ou à la caméra.
2. Créer une variable pour stocker l’image sélectionnée
Les fichiers tels que les photos peuvent être stockés dans une variable de type File?
. Une fois stockée, vous allez pouvoir réutiliser votre variable, là où vous en aurez besoin :
File? _imageFile;
Astuce : Si vous souhaitez autoriser plusieurs images, vous pouvez utiliser une liste à la place :
List<File> _images = [];
3. Implémenter la méthode pour récupérer l’image
La méthode _pickImage()
appelle le package image_picker
pour ouvrir la galerie et sélectionner une image.
Future<void> _pickImage() async {
final XFile? pickedImage = await _picker.pickImage(source: ImageSource.gallery);
if (pickedImage != null) {
setState(() {
_imageFile = File(pickedImage.path);
});
}
}
await _picker.pickImage
: Ouvre la galerie. Vous pouvez remplacerImageSource.gallery
parImageSource.camera
si vous voulez capturer une photo avec l’appareil photo.XFile? pickedImage
: Représente l’image sélectionnée.pickedImage.path
: Contient le chemin du fichier sélectionné.File(pickedImage.path)
: Convertit le chemin en un fichier manipulable.
4. Construire l’interface utilisateur pour accéder à la galerie
Ici je crée simplement un bouton qui me permet d’ouvrir la galerie et un widget Image qui va contenir ma photo, une fois que je l’ai sélectionnée.
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Image Picker Example')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Afficher l'image sélectionnée
_imageFile != null
? Image.file(_imageFile!, width: 200, height: 200)
: Text('Aucune image sélectionnée'),
SizedBox(height: 20),
// Bouton pour ouvrir la galerie
ElevatedButton(
onPressed: _pickImage,
child: Text('Choisir une image'),
),
],
),
),
);
}
Voici le code complet que vous pouvez adapter selon vos besoins :
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized(); // Initialisation des plugins
runApp(MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: ImagePickerExample(),
);
}
}
class ImagePickerExample extends StatefulWidget {
@override
_ImagePickerExampleState createState() => _ImagePickerExampleState();
}
class _ImagePickerExampleState extends State<ImagePickerExample> {
final ImagePicker _picker = ImagePicker();
File? _imageFile;
Future<void> _pickImage() async {
final XFile? pickedImage = await _picker.pickImage(source: ImageSource.gallery);
if (pickedImage != null) {
setState(() {
_imageFile = File(pickedImage.path);
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Image Picker Example')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_imageFile != null
? Image.file(_imageFile!, width: 200, height: 200)
: Text('Aucune image sélectionnée'),
SizedBox(height: 20),
ElevatedButton(
onPressed: _pickImage,
child: Text('Choisir une image'),
),
],
),
),
);
}
}
Sélectionner plusieurs images à la fois
Si vous souhaitez laisser l’utilisateur sélectionner plusieurs images à la fois, il vous suffit de légèrement changer le code présenté en exemple :
List<File> _images = [];
Future<void> _pickMultipleImages() async {
final List<XFile>? pickedImages = await _picker.pickMultiImage();
if (pickedImages != null) {
setState(() {
_images = pickedImages.map((file) => File(file.path)).toList();
});
}
}
Vous pouvez ensuite afficher toutes les photos dans un widget GridView
Expanded(
// Utilisation de Expanded pour limiter l'espace du GridView
child: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
),
itemCount: _images.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(4.0),
child: Image.file(_images[index]),
);
},
),
)
Stocker une photo
Dans certains cas, vous pourrez avoir besoin que la photo d’un utilisateur soit stockée, afin de ne pas avoir à la lui demander à chaque utilisation. Voici deux manières de le faire.
Sauvegarder une photo localement
Pour sauvegarder localement une photo prise dans la galerie, vous pouvez utiliser le package path_provider de Flutter. Celui-ci vous permet d’accéder au répertoire des documents de l’application, qui est un endroit sûr pour stocker des fichiers persistants.
Commencez par l’installer en ajoutant la dépendance path_provider: ^2.0.5
à votre fichier pubspec.yaml.
Vous pouvez ensuite sauvegarder votre photo, dans les fichiers de votre application, de la manière suivante :
Future<File> saveImageLocally(File imageFile) async {
final directory = await getApplicationDocumentsDirectory();
final String path = directory.path;
// Générer un nom de fichier unique
final String fileName =
'saved_image_${DateTime.now().millisecondsSinceEpoch}.jpg';
// Créer le chemin complet
final String newPath = '$path/$fileName';
// Copier le fichier dans le répertoire permanent
final File newImage = await imageFile.copy(newPath);
print('Image sauvegardée localement : $newPath');
return newImage;
}
Explication :
getApplicationDocumentsDirectory()
: Retourne un répertoire permanent unique à l’application.DateTime.now().millisecondsSinceEpoch
: Génère un nom de fichier unique basé sur l’horodatage actuel.imageFile.copy()
: Copie le fichier à un nouvel emplacement.
Vous devrez ensuite récupérer l’image au démarrage de votre application et la stocker dans une variable de type File, pour pouvoir l’utiliser :
//Cette fonction doit être placé dans votre initState() pour charger la photo quand le widget se construit
Future<File?> loadSavedImage() async {
try {
final directory = await getApplicationDocumentsDirectory();
print("Le répertoire est récupéré : ${directory.path}");
final String path = directory.path;
// Rechercher des fichiers image dans le répertoire
final List<FileSystemEntity> files =
await Directory(path).list().toList();
// Filtrer uniquement les fichiers images (par exemple .png, .jpg)
final imageFiles = files.where((file) {
return file is File &&
(file.path.endsWith('.png') ||
file.path.endsWith('.jpg') ||
file.path.endsWith('.jpeg'));
}).toList();
if (imageFiles.isNotEmpty) {
final String filePath =
imageFiles.first.path; // Charger le premier fichier image trouvé
print('Image trouvée localement : $filePath');
return File(filePath);
}
print("Aucune image trouvée dans le répertoire local");
return null;
} catch (e) {
print("Erreur lors du chargement de l'image : $e");
return null;
}
}
Voici l’implémentation complète :
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:path_provider/path_provider.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized(); // Initialisation des plugins
runApp(MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: LocalImageApp(),
);
}
}
class LocalImageApp extends StatefulWidget {
@override
_LocalImageAppState createState() => _LocalImageAppState();
}
class _LocalImageAppState extends State<LocalImageApp> {
File? _imageFile;
final ImagePicker _picker = ImagePicker();
@override
void initState() {
super.initState();
_loadImage();
}
Future<void> _loadImage() async {
final File? image = await loadSavedImage();
setState(() {
_imageFile = image;
});
}
Future<File> saveImageLocally(File imageFile) async {
final directory = await getApplicationDocumentsDirectory();
final String path = directory.path;
// Générer un nom de fichier unique
final String fileName =
'saved_image_${DateTime.now().millisecondsSinceEpoch}.jpg';
// Créer le chemin complet
final String newPath = '$path/$fileName';
// Copier le fichier dans le répertoire permanent
final File newImage = await imageFile.copy(newPath);
print('Image sauvegardée localement : $newPath');
return newImage;
}
Future<File?> loadSavedImage() async {
try {
final directory = await getApplicationDocumentsDirectory();
print("Le répertoire est récupéré : ${directory.path}");
final String path = directory.path;
// Rechercher des fichiers image dans le répertoire
final List<FileSystemEntity> files =
await Directory(path).list().toList();
// Filtrer uniquement les fichiers images (par exemple .png, .jpg)
final imageFiles = files.where((file) {
return file is File &&
(file.path.endsWith('.png') ||
file.path.endsWith('.jpg') ||
file.path.endsWith('.jpeg'));
}).toList();
if (imageFiles.isNotEmpty) {
final String filePath =
imageFiles.first.path; // Charger le premier fichier image trouvé
print('Image trouvée localement : $filePath');
return File(filePath);
}
print("Aucune image trouvée dans le répertoire local");
return null;
} catch (e) {
print("Erreur lors du chargement de l'image : $e");
return null;
}
}
Future<void> _pickAndSaveImage() async {
final XFile? pickedImage =
await _picker.pickImage(source: ImageSource.gallery);
if (pickedImage != null) {
final File newImage = File(pickedImage.path);
final File savedImage = await saveImageLocally(newImage);
setState(() {
_imageFile = savedImage;
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Sauvegarder une image localement')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_imageFile != null
? Image.file(_imageFile!, width: 200, height: 200)
: Text('Aucune image sauvegardée'),
SizedBox(height: 20),
ElevatedButton(
onPressed: _pickAndSaveImage,
child: Text('Choisir et sauvegarder une image'),
),
],
),
),
);
}
}
Vous pouvez ensuite adapter ce code selon vos besoins, par exemple pour récupérer une photo en particulier, plutôt que la première de la liste.
Sauvegarder une photo dans Firebase
La deuxième manière de stocker votre photo est de la sauvegarder dans le back-end de votre application, par exemple avec la solution Firebase Storage. Si vous ne savez pas comment mettre en place cette outil, j’ai rédigé un guide complet sur comment ajouter Firebase Storage à une application Flutter.
Une fois, Firebase Storage ajouté à votre application, vous pouvez ensuite ajouter toute la logique pour récupérer une photo et la stocker dans votre back-end.
Future<void> _uploadToFirebase() async {
if (_imageFile == null) return; // Si aucune image n'a été choisie, on quitte la fonction
try {
final storageRef = FirebaseStorage.instance.ref(); // Accéder à Firebase Storage
final imageRef = storageRef.child('images/${DateTime.now().millisecondsSinceEpoch}.jpg'); // Créer un nom unique pour l'image et vient la placer dans mon dossier /images
final uploadTask = await imageRef.putFile(_imageFile!); // Uploade l'image vers Firebase Storage
final downloadUrl = await imageRef.getDownloadURL(); // Récupére l'URL de l'image uploadée
setState(() {
_uploadedImageUrl = downloadUrl; // Met à jour l'URL de l'image uploadée et le stock dans une variable pour pouvoir retourner le récupérer dans Storage
});
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Image uploadée avec succès!')));
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Erreur lors de l\'upload : $e')));
}
}
Voici un peu plus d’explications :
Vérification de l’image : On commence par vérifier si _imageFile
est null
. Si c’est le cas, cela signifie que l’utilisateur n’a pas sélectionné d’image, donc la fonction s’arrête.
Référence à Firebase Storage :
FirebaseStorage.instance.ref()
: Cela accède à la racine de Firebase Storage.storageRef.child('images/${DateTime.now().millisecondsSinceEpoch}.jpg')
: Cela crée un chemin unique pour l’image en utilisant un nom basé sur l’horodatage actuel (millisecondsSinceEpoch
).
Uploader le fichier :
await imageRef.putFile(_imageFile!)
: Cette méthode télécharge le fichier dans Firebase Storage à l’emplacement spécifié parimageRef
.
Récupérer l’URL :
await imageRef.getDownloadURL()
: Une fois l’image téléchargée, nous récupérons l’URL publique pour accéder à l’image.
Afficher un message :
- Si le téléchargement réussit, un
SnackBar
est affiché pour indiquer que l’image a été téléchargée avec succès. - Si une erreur se produit, un message d’erreur est affiché dans un
SnackBar
.
Voici le code complet que vous pouvez adapter à vos besoins :
import 'dart:io';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:tutoflutter/firebase_options.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized(); // Initialisation des plugins
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
await FirebaseAppCheck.instance.activate(
androidProvider: AndroidProvider.debug,
appleProvider: AppleProvider.debug,
webProvider: ReCaptchaV3Provider('Remplace_par_votre_clé')
);
await FirebaseAuth.instance.signInWithEmailAndPassword(
email: "emailtest@test.fr",
password: "123456",
);
runApp(MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: FirebaseStorageExample(),
);
}
}
class FirebaseStorageExample extends StatefulWidget {
@override
_FirebaseStorageExampleState createState() => _FirebaseStorageExampleState();
}
class _FirebaseStorageExampleState extends State<FirebaseStorageExample> {
final ImagePicker _picker = ImagePicker();
File? _imageFile;
String? _uploadedImageUrl;
Future<void> _pickImage() async {
final XFile? pickedImage = await _picker.pickImage(source: ImageSource.gallery);
if (pickedImage != null) {
setState(() {
_imageFile = File(pickedImage.path);
});
}
}
Future<void> _uploadToFirebase() async {
if (_imageFile == null) return;
try {
final storageRef = FirebaseStorage.instance.ref();
final imageRef = storageRef.child('images/${DateTime.now().millisecondsSinceEpoch}.jpg');
final uploadTask = await imageRef.putFile(_imageFile!);
final downloadUrl = await imageRef.getDownloadURL();
setState(() {
_uploadedImageUrl = downloadUrl;
});
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Image uploadée avec succès!')));
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Erreur lors de l\'upload : $e')));
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Upload Firebase')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_imageFile != null
? Image.file(_imageFile!, width: 200, height: 200)
: Text('Aucune image sélectionnée'),
SizedBox(height: 20),
ElevatedButton(
onPressed: _pickImage,
child: Text('Choisir une image'),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: _uploadToFirebase,
child: Text('Uploader dans Firebase'),
),
if (_uploadedImageUrl != null)
Padding(
padding: const EdgeInsets.all(8.0),
child: Text('Image URL: $_uploadedImageUrl'),
),
],
),
),
);
}
}