Se connecter à Flutter avec PHP MySQL
Sommaire
- 1- Objectif
- 2- Présentation
- 3- Configuration du projet
- 3.1- Étape 1 : Créer la base de données MySQL
- 3.2- Étape 2 : Créer les scripts PHP
- 3.3- Étape 3 : Créer le projet Flutter
- 3.4- Étape 4 : Créer l'interface de connexion Flutter
- 3.5- Étape 5 : Définir un modèle de données (Client)
- 4- Exercices
- 5- Fichiers réalisés
- 5.1.1- Cours Flutter
Lire et écrire dans Firestore avec Flutter
-
Objectif
- À la fin de ce tutoriel, vous serez capable de :
- Créer une base de données MySQL simple pour l’authentification.
- Écrire un backend PHP pour la connexion à MySQL.
- Échanger des données entre Flutter et PHP via HTTP.
- Créer une interface de connexion Flutter avec vérification côté serveur.
-
Présentation
- Ce tutoriel explique comment connecter une application Flutter à une base de données MySQL à l’aide de scripts PHP comme intermédiaire. Flutter ne peut pas accéder directement à MySQL, d’où l’importance de l’API PHP.
-
Configuration du projet
-
Étape 1 : Créer la base de données MySQL
- Créer la base de données :
- Créer la table
utulisateurs
: - Ajouter un utilisateur de test :
-
Étape 2 : Créer les scripts PHP
- Fichier config.php :
- Fichier dbconnect.php :
- Fichier login.php :
-
Étape 3 : Créer le projet Flutter
- Créer le projet :
- Ajouter la dépendance HTTP dans
pubspec.yaml
: -
Étape 4 : Créer l’interface de connexion Flutter
- Fichier : main.dart
- Description : Ce fichier est le point d’entrée de votre application Flutter. Il lance le widget
MainApp
qui affiche directement la page de connexionLoginPage
. - Fichier : login_page.dart
- Description : Ce fichier gère l’interface de connexion utilisateur. Lorsqu’un utilisateur saisit ses identifiants, l’application envoie une requête POST au script PHP
login.php
. Si les identifiants sont valides, il est redirigé versHomePage
. -
Étape 5 : Définir un modèle de données (Client)
- Fichier : client.dart
- Description : Cette classe représente un objet
Client
. Elle fournit une méthodefromJson
pour transformer une réponse JSON en objet Dart, et une méthodetoJson
pour envoyer les données dans le bon format au serveur. -
Exercices
- Exercice 1 : Créer une page d’inscription Flutter + script PHP
register.php
. - Exercice 2 : Ajouter le hachage de mot de passe avec
password_hash()
côté PHP. - Exercice 3 : Créer une page de profil affichant l’email après connexion.
-
Fichiers réalisés
- connexion.php
- login.php
- main.dart
- pubspec.yaml
- Base de données MySQL : flutter_app / table users
CREATE DATABASE flutter_app;
CREATE TABLE `utilisateurs` (
`id` int NOT NULL AUTO_INCREMENT,
`email` varchar(180) COLLATE utf8mb4_unicode_ci NOT NULL,
`roles` json NOT NULL,
`password` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `UNIQ_IDENTIFIER_EMAIL` (`email`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
<?php
require_once('dbconnect.php');
$email = 'test@example.com';
$password = password_hash('123456', PASSWORD_DEFAULT);
$roles = json_encode(['ROLE_USER']);
$stmt = $pdo->prepare("INSERT INTO utilisateurs (email, password, roles)
VALUES (?, ?, ?)");
$stmt->execute([$email, $password, $roles]);
echo "Utilisateur ajouté.";
?>
<?php
define('DB_HOST', '127.0.0.1'); // l'hôte de la base de données MySQL
define('DB_NAME', 'base_test'); // le nom de la base de données MySQL
define('DB_USER', 'root'); // le nom d'utilisateur MySQL
define('DB_PASSWORD', ''); // le mot de passe MySQLhriadh
define('DB_CHARSET', 'utf8'); // le jeu de caractères pour la base de données MySQL
?>
<?php
require_once('config.php');
// Créer une nouvelle connexion PDO
try {
$pdo = new PDO("mysql:host=".DB_HOST.";dbname=".DB_NAME.";charset=".DB_CHARSET,
DB_USER, DB_PASSWORD);
// Activer le mode d'erreur PDO
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// Activer le mode d'émulation PDO pour désactiver l'utilisation de 'prepared statements'
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
} catch(PDOException $e) {
// En cas d'erreur de connexion à la base de données, afficher un message d'erreur et arrêter le script
die("Erreur de connexion à la base de données: ".$e->getMessage());
}
?>
<?php
// Cela permet à tous les domaines (origines) d’accéder à cette
// ressource via une requête HTTP (notamment via AJAX ou fetch() en
// JavaScript/Flutter).
// Le * (joker) autorise tout le monde. En production, on remplace souvent par un domaine spécifique :
header("Access-Control-Allow-Origin: *");
// Cela autorise les requêtes à inclure certains en-têtes personnalisés (headers), ici Content-Type.
header("Access-Control-Allow-Headers: Content-Type");
// Cela indique que la réponse du serveur est en JSON.
header("Content-Type: application/json");
require_once('dbconnect.php');
// Récupérer les données JSON envoyées par Flutter
$data = json_decode(file_get_contents("php://input"));
$email = $data->email ?? '';
$password = $data->password ?? '';
if (empty($email) || empty($password)) {
echo json_encode(['success' => false, 'message' => 'Email ou mot de passe manquant.']);
exit;
}
// Requête pour trouver l'utilisateur
$stmt = $pdo->prepare("SELECT * FROM utilisateurs WHERE email = ?");
$stmt->execute([$email]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if ($user && password_verify($password, $user['password'])) {
echo json_encode(['success' => true, 'user' => [
'id' => $user['id'],
'email' => $user['email'],
'roles' => json_decode($user['roles'])
]]);
} else {
echo json_encode(['success' => false, 'message' => 'Identifiants invalides']);
}
?>
flutter create flutter_login_app
dependencies:
flutter:
sdk: flutter
http: ^0.13.5
import 'package:flutter/material.dart';
import 'login_page.dart';
void main() {
runApp(const MainApp());
}
class MainApp extends StatelessWidget {
const MainApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: LoginPage(),
);
}
}
import 'dart:convert';
import 'package:exemple01/home_page.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
class LoginPage extends StatefulWidget {
const LoginPage({super.key});
@override
State<LoginPage> createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
final _emailCtrl = TextEditingController();
final _passwordCtrl = TextEditingController();
String errorMsg = '';
final String serverUrl = "http://localhost/flutter/mon_api/login.php";
Future<void> _login() async {
try {
final response = await http.post(
Uri.parse(serverUrl),
headers: {'Content-Type': 'application/json'},
body: jsonEncode({
'email': _emailCtrl.text,
'password': _passwordCtrl.text,
}),
);
print('Statut: ${response.statusCode}');
print('Corps: ${response.body}');
final data = jsonDecode(response.body);
if (data['success'] == true) {
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => const HomePage()),
);
} else {
setState(() {
errorMsg = data['message'];
});
}
} catch (e) {
print('Erreur: $e');
setState(() {
errorMsg = 'Erreur de connexion au serveur.';
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("Login")),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
TextField(controller: _emailCtrl, decoration: const InputDecoration(labelText: "Email")),
TextField(controller: _passwordCtrl, decoration: const InputDecoration(labelText: "Password"), obscureText: true),
const SizedBox(height: 16),
ElevatedButton(onPressed: _login, child: const Text("Login")),
if (errorMsg.isNotEmpty) Text(errorMsg, style: const TextStyle(color: Colors.red)),
],
),
),
);
}
}
class Client {
final int id;
final String name;
final String family;
final int age;
Client({
required this.id,
required this.name,
required this.family,
required this.age,
});
factory Client.fromJson(Map<String, dynamic> json) {
return Client(
id: int.parse(json['id'].toString()),
name: json['name'],
family: json['family'],
age: int.parse(json['age'].toString()),
);
}
Map<String, dynamic> toJson() {
return {'id': id, 'name': name, 'family': family, 'age': age};
}
}