Avez-vous une question? (216) 97 656 803 hajjriadh@gmail.com
Flutter

Se connecter à Flutter avec Spring Boot

Un cours complet pour maîtriser Se connecter à Flutter avec Spring Boot.

0 chapitres 0 QCM Accès gratuit
 
  1. 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 Spring Boot pour la connexion à MySQL.
      • Échanger des données entre Flutter et Spring Boot.
      • Créer une interface de connexion Flutter avec vérification côté serveur.
  2. Présentation

    • Ce tutoriel explique comment connecter une application Flutter à une base de données MySQL à l'aide d'un backend Spring Boot. Flutter ne peut pas accéder directement à MySQL, d'où l'importance de l'API Spring Boot.
  3. Configuration du projet Spring Boot

    1. Étape 1 : Créer la base de données MySQL
      • Créer la base de données :
      • CREATE DATABASE flutter_app;
      • Créer la table utilisateurs:
      • CREATE TABLE `utilisateurs` (
          `id` int NOT NULL AUTO_INCREMENT,
          `email` varchar(180) NOT NULL,
          `roles` json NOT NULL,
          `password` varchar(255) NOT NULL,
          PRIMARY KEY (`id`),
          UNIQUE KEY `UNIQ_EMAIL` (`email`)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
    2. Étape 2 : Créer le projet Spring Boot
      • Utilisez Spring Initializr (https://start.spring.io/) avec les dépendances :
      • Configure le projet :
        • Project : Maven (recommandé)
        • Language : Java
        • Spring Boot : laisse la dernière version stable
      • Project Metadata :
        • Group : com.example
        • Artifact : myapi
        • Packaging : Jar
        • Java : 17 (ou 21 si tu as installé la version récente)
      • Ajouter les dépendances :
        • Spring Web
        • Spring Data JPA
        • MySQL Driver
        • (optionnel) Spring Security si tu veux gérer l’authentification
      • Clique sur Generate → ça télécharge un fichier .zip
      • Décompresse et ouvre le projet dans IntelliJ IDEA ou Eclipse
    3. Étape 3 : Configuration de la base de données
      • Fichier application.properties:
          spring.datasource.url=jdbc:mysql://localhost:3306/flutter_app
          spring.datasource.username=root
          spring.datasource.password=
          
          spring.jpa.hibernate.ddl-auto=update
          spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
          spring.jpa.show-sql=true
          
          # CORS configuration
          spring.web.cors.allowed-origins=http://localhost:3000,http://10.0.2.2
          spring.web.cors.allowed-methods=GET,POST,PUT,DELETE,OPTIONS
          spring.web.cors.allowed-headers=*
          spring.web.cors.allow-credentials=true
      • Étape 4 : Créer l'entité Utilisateur
          @Entity
          @Table(name = "utilisateurs")
          public class Utilisateur {
              @Id
              @GeneratedValue(strategy = GenerationType.IDENTITY)
              private Long id;
              
              @Column(unique = true, nullable = false)
              private String email;
              
              @Column(nullable = false)
              private String password;
              
              @ElementCollection(fetch = FetchType.EAGER)
              private List roles = new ArrayList<>();
              
              // Constructeurs, getters et setters
              public Utilisateur() {}
              
              public Utilisateur(String email, String password, List roles) {
                  this.email = email;
                  this.password = password;
                  this.roles = roles;
              }
              
              // Getters et setters pour tous les champs
          }
      • Étape 5 : Créer le Repository
          @Repository
          public interface UtilisateurRepository extends JpaRepository {
              Optional findByEmail(String email);
              Boolean existsByEmail(String email);
          }
      • Étape 6 : Configuration de sécurité
          @Configuration
          @EnableWebSecurity
          public class SecurityConfig {
              
              @Bean
              public PasswordEncoder passwordEncoder() {
                  return new BCryptPasswordEncoder();
              }
              
              @Bean
              public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
                  http
                      .cors().and()
                      .csrf().disable()
                      .authorizeHttpRequests(authz -> authz
                          .requestMatchers("/api/auth/**").permitAll()
                          .anyRequest().authenticated()
                      )
                      .sessionManagement(session -> session
                          .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                      );
                  
                  return http.build();
              }
          }
      • Étape 7 : Créer le contrôleur d'authentification
          @RestController
          @RequestMapping("/api/auth")
          @CrossOrigin(origins = "*")
          public class AuthController {
              
              @Autowired
              private UtilisateurRepository utilisateurRepository;
              
              @Autowired
              private PasswordEncoder passwordEncoder;
              
              @PostMapping("/login")
              public ResponseEntity> login(@RequestBody LoginRequest loginRequest) {
                  try {
                      Optional utilisateurOpt = utilisateurRepository.findByEmail(loginRequest.getEmail());
                      
                      if (utilisateurOpt.isEmpty()) {
                          return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
                              .body(new AuthResponse(false, "Identifiants invalides", null));
                      }
                      
                      Utilisateur utilisateur = utilisateurOpt.get();
                      
                      if (!passwordEncoder.matches(loginRequest.getPassword(), utilisateur.getPassword())) {
                          return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
                              .body(new AuthResponse(false, "Mot de passe incorrect", null));
                      }
                      
                      // Créer une réponse avec les informations utilisateur
                      UserResponse userResponse = new UserResponse(
                          utilisateur.getId(),
                          utilisateur.getEmail(),
                          utilisateur.getRoles()
                      );
                      
                      return ResponseEntity.ok(new AuthResponse(true, "Connexion réussie", userResponse));
                      
                  } catch (Exception e) {
                      return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                          .body(new AuthResponse(false, "Erreur serveur: " + e.getMessage(), null));
                  }
              }
              
              // Classes internes pour les requêtes et réponses
              public static class LoginRequest {
                  private String email;
                  private String password;
                  
                  // Getters et setters
                  public String getEmail() { return email; }
                  public void setEmail(String email) { this.email = email; }
                  public String getPassword() { return password; }
                  public void setPassword(String password) { this.password = password; }
              }
              
              public static class AuthResponse {
                  private boolean success;
                  private String message;
                  private UserResponse user;
                  
                  // Constructeurs, getters et setters
                  public AuthResponse(boolean success, String message, UserResponse user) {
                      this.success = success;
                      this.message = message;
                      this.user = user;
                  }
                  
                  // Getters et setters
              }
              
              public static class UserResponse {
                  private Long id;
                  private String email;
                  private List roles;
                  
                  // Constructeurs, getters et setters
                  public UserResponse(Long id, String email, List roles) {
                      this.id = id;
                      this.email = email;
                      this.roles = roles;
                  }
                  
                  // Getters et setters
              }
          }
      • Étape 8 : Créer un script pour insérer un utilisateur de test
          @Component
          public class DataLoader implements CommandLineRunner {
              
              @Autowired
              private UtilisateurRepository utilisateurRepository;
              
              @Autowired
              private PasswordEncoder passwordEncoder;
              
              @Override
              public void run(String... args) throws Exception {
                  // Vérifier si l'utilisateur test existe déjà
                  if (utilisateurRepository.findByEmail("nodejs@example.com").isEmpty()) {
                      Utilisateur utilisateur = new Utilisateur();
                      utilisateur.setEmail("nodejs@example.com");
                      utilisateur.setPassword(passwordEncoder.encode("123456"));
                      utilisateur.setRoles(Arrays.asList("ROLE_USER"));
                      
                      utilisateurRepository.save(utilisateur);
                      System.out.println("Utilisateur test créé avec succès");
                  }
              }
          }
  4. Créer le projet Flutter

    1. Étape 1 : Créer le projet Flutter
        flutter create flutter_login_app
      • Ajouter la dépendance HTTP dans pubspec.yaml:
      • dependencies:
          flutter:
            sdk: flutter
          http: ^0.13.5
      • Étape 2 : Créer un service d'authentification
        • Fichier : auth_service.dart
        import 'dart:convert';
        import 'package:http/http.dart' as http;
        
        class AuthService {
          static const String baseUrl = "http://10.0.2.2:8080/api/auth";
        
          static Future> login(String email, String password) async {
            try {
              final response = await http.post(
                Uri.parse('$baseUrl/login'),
                headers: {'Content-Type': 'application/json'},
                body: jsonEncode({
                  'email': email,
                  'password': password,
                }),
              );
        
              final responseData = jsonDecode(response.body);
              return responseData;
            } catch (e) {
              throw Exception('Erreur de connexion: $e');
            }
          }
        }
      • Étape 3 : Créer l'interface de connexion Flutter
        • Fichier : main.dart
        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(),
            );
          }
        }
      • Fichier : login_page.dart
          import 'dart:convert';
          import 'package:flutter/material.dart';
          import 'package:http/http.dart' as http;
          import 'home_page.dart';
          import 'auth_service.dart';
          
          class LoginPage extends StatefulWidget {
            const LoginPage({super.key});
          
            @override
            State createState() => _LoginPageState();
          }
          
          class _LoginPageState extends State {
            final _emailCtrl = TextEditingController();
            final _passwordCtrl = TextEditingController();
            String errorMsg = '';
            bool isLoading = false;
          
            Future _login() async {
              setState(() {
                isLoading = true;
                errorMsg = '';
              });
          
              try {
                final response = await AuthService.login(
                  _emailCtrl.text.trim(),
                  _passwordCtrl.text.trim(),
                );
          
                if (response['success'] == true) {
                  Navigator.pushReplacement(
                    context,
                    MaterialPageRoute(builder: (context) => const HomePage()),
                  );
                } else {
                  setState(() {
                    errorMsg = response['message'] ?? 'Identifiants incorrects';
                  });
                }
              } catch (e) {
                setState(() {
                  errorMsg = 'Erreur de connexion au serveur: $e';
                });
              } finally {
                setState(() {
                  isLoading = false;
                });
              }
            }
          
            @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"),
                        keyboardType: TextInputType.emailAddress,
                      ),
                      TextField(
                        controller: _passwordCtrl,
                        decoration: const InputDecoration(labelText: "Password"),
                        obscureText: true,
                      ),
                      const SizedBox(height: 16),
                      isLoading
                          ? const CircularProgressIndicator()
                          : ElevatedButton(
                              onPressed: _login,
                              child: const Text("Login"),
                            ),
                      if (errorMsg.isNotEmpty)
                        Padding(
                          padding: const EdgeInsets.only(top: 12.0),
                          child: Text(errorMsg, style: const TextStyle(color: Colors.red)),
                        ),
                    ],
                  ),
                ),
              );
            }
          }
      • Étape 4 : Créer un modèle Utilisateur pour Flutter
          class Utilisateur {
            final int id;
            final String email;
            final List roles;
          
            Utilisateur({
              required this.id,
              required this.email,
              required this.roles,
            });
          
            factory Utilisateur.fromJson(Map json) {
              return Utilisateur(
                id: json['id'],
                email: json['email'],
                roles: List.from(json['roles']),
              );
            }
          
            Map toJson() => {
              'id': id,
              'email': email,
              'roles': roles,
            };
          }
    2. Exercices

      • Exercice 1 : Ajoutez une fonctionnalité d'inscription avec hachage de mot de passe côté Spring Boot.
      • Exercice 2 : Implémentez JWT (JSON Web Tokens) pour une authentification stateless
      • Exercice 3 : Créez une page de profil affichant l'email après connexion
      • Exercice 4 : Ajoutez la gestion des erreurs et les messages de validation