Que fait flutter-apply-architecture-best-practices ?
Conçoit une application Flutter en utilisant l'approche en couches recommandée (UI, Logique, Données). À utiliser lors de la structuration d'un nouveau projet ou du refactoring pour l'évolutivité.
Présentation
Cette compétence vous guide pour structurer votre application Flutter selon les meilleures pratiques architecturales, en séparant les responsabilités en trois couches principales :
- Couche UI : widgets, pages, routes, gestionnaires d'état (ex.
StatefulWidget,Bloc,Provider). - Couche Logique : repositories, use cases, services métier.
- Couche Données : sources de données (API, base de données locale, fichiers).
Pourquoi cette approche ?
- Maintenabilité : chaque couche a une responsabilité unique, facilitant les modifications.
- Testabilité : les couches peuvent être testées indépendamment.
- Évolutivité : ajouter de nouvelles fonctionnalités sans impacter le reste du code.
- Réutilisabilité : les composants métier peuvent être réutilisés dans différents contextes.
Utilisation
- Planification : analysez les besoins de votre application et identifiez les couches nécessaires.
- Structure des dossiers : créez une arborescence reflétant les couches (ex.
lib/ui/,lib/logic/,lib/data/). - Implémentation : développez les composants de chaque couche en respectant les dépendances (la couche UI dépend de la logique, qui dépend des données).
- Tests : écrivez des tests unitaires pour la logique et des tests d'intégration pour l'UI.
Exemple
// Couche Données : un repository
class UserRepository {
Future<User> getUser(int id) async {
final response = await http.get(Uri.parse('https://api.example.com/users/$id'));
return User.fromJson(jsonDecode(response.body));
}
}
// Couche Logique : un use case
class GetUserUseCase {
final UserRepository repository;
GetUserUseCase(this.repository);
Future<User> call(int id) => repository.getUser(id);
}
// Couche UI : un widget
class UserWidget extends StatelessWidget {
final GetUserUseCase useCase;
final int userId;
UserWidget({required this.useCase, required this.userId});
@override
Widget build(BuildContext context) {
return FutureBuilder<User>(
future: useCase(userId),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
} else if (snapshot.hasError) {
return Text('Erreur : ${snapshot.error}');
} else {
return Text('Utilisateur : ${snapshot.data!.name}');
}
},
);
}
}