saasfork_design_system 0.0.6
saasfork_design_system: ^0.0.6 copied to clipboard
A Flutter UI kit for SaaSFork apps and projects.
SaasFork Design System #
Un système de design complet pour les applications Flutter de SaasFork, offrant des composants cohérents et réutilisables.
Installation #
dependencies:
saasfork_design_system:
path: ../packages/saasfork_design_system
Composants disponibles #
Atoms #
Les éléments les plus basiques de notre système de design.
Boutons
SaasFork Design System offre plusieurs types de boutons pour différents contextes:
// Bouton principal
SFMainButton(
label: 'Valider',
onPressed: () => print('Bouton principal pressé'),
size: ComponentSize.md, // xs, sm, md, lg, xl
color: AppColors.primary, // Optionnel
);
// Bouton secondaire
SFSecondaryButton(
label: 'Annuler',
onPressed: () => print('Bouton secondaire pressé'),
size: ComponentSize.md,
);
// Bouton lien
SFLinkButton(
label: 'En savoir plus',
onPressed: () => print('Lien pressé'),
size: ComponentSize.md,
);
// Bouton circulaire
SFCircularButton(
icon: Icons.add,
onPressed: () => print('Bouton circulaire pressé'),
size: ComponentSize.md,
iconColor: Colors.white,
backgroundColor: AppColors.primary, // Optionnel
);
// Bouton secondaire avec icône
SFSecondaryIconButton(
icon: Icons.login,
label: 'Se connecter',
onPressed: () => print('Bouton avec icône pressé'),
);
Champs de texte
// Champ texte simple
SFTextField(
placeholder: 'Votre email',
controller: emailController,
isInError: false,
size: ComponentSize.md,
);
// Champ mot de passe
SFPasswordField(
placeholder: 'Votre mot de passe',
controller: passwordController,
isInError: false,
size: ComponentSize.md,
);
Icônes et séparateurs
// Icône stylisée
SFIcon(
icon: Icons.warning,
color: AppColors.warning,
iconType: SFIconType.rounded,
);
// Séparateur avec texte
SFDividerWithText(text: 'ou continuer avec'),
Molecules #
Combinaisons d'atomes pour créer des composants plus complexes.
Champ de formulaire
SFFormfield(
label: 'Email',
isRequired: true,
input: SFTextField(
placeholder: 'Entrez votre email',
controller: emailController,
isInError: hasError,
),
errorMessage: hasError ? 'Email invalide' : null,
hintMessage: 'Nous ne partagerons jamais votre email',
);
Notifications
// Dans votre widget
SFNotification(
title: 'Succès',
message: 'Votre profil a été mis à jour',
icon: Icons.check_circle_outline_rounded,
iconColor: AppColors.success,
onClose: () => print('Notification fermée'),
);
// Ou avec les extensions (plus simple)
context.showSuccess(
title: 'Enregistré',
message: 'Vos modifications ont été enregistrées',
);
context.showError(
title: 'Erreur',
message: 'Impossible de se connecter au serveur',
);
context.showInfo(
message: 'Une mise à jour est disponible',
);
context.showWarning(
title: 'Attention',
message: 'Votre abonnement expire bientôt',
);
Dialogs
showDialog(
context: context,
builder: (context) => SFDialog(
title: 'Désactiver le compte',
message: 'Êtes-vous sûr de vouloir désactiver votre compte? Cette action ne peut pas être annulée.',
icon: Icons.warning_amber_rounded,
onCancel: () => Navigator.of(context).pop(),
onDeactivate: () {
// Action de désactivation
Navigator.of(context).pop();
},
additionalData: {
'desactivate_button': 'Désactiver',
'cancel_button': 'Annuler',
},
),
);
Organisms #
Combinaisons de molécules pour créer des composants plus complexes.
Formulaires
// Formulaire de connexion
SFLoginForm(
additionalData: {
'label_email': 'Adresse e-mail',
'placeholder_email': 'Entrez votre email',
'error_email_invalid': 'Adresse email invalide',
'label_password': 'Mot de passe',
'placeholder_password': 'Entrez votre mot de passe',
'error_password_length': 'Le mot de passe doit contenir au moins 6 caractères',
'login_button': 'Se connecter',
},
onSubmit: (loginData) {
// Traiter les données de connexion
print('Email: ${loginData['email']}');
print('Password: ${loginData['password']}');
},
size: ComponentSize.md,
);
// Formulaire d'inscription
SFRegisterForm(
additionalData: {
'label_email': 'Email',
'placeholder_email': 'Entrez votre email',
'error_email_invalid': 'Adresse email invalide',
'label_password': 'Mot de passe',
'placeholder_password': 'Créez un mot de passe',
'error_password_length': 'Le mot de passe doit contenir au moins 6 caractères',
'register_button': 'Créer un compte',
},
onSubmit: (registerData) {
// Traiter les données d'inscription
},
);
// Formulaire de profil
SFProfileForm(
additionalData: {
'label_email': 'E-mail',
'placeholder_email': 'Entrez votre email',
'error_email_invalid': 'Adresse email invalide',
'label_username': 'Nom d\'utilisateur',
'placeholder_username': 'Entrez votre nom d\'utilisateur',
'error_username_required': 'Le nom d\'utilisateur est requis',
'save_button': 'Enregistrer',
},
profileModel: ProfileModel(
email: 'user@example.com',
username: 'johndoe',
),
onSubmit: (profileData) {
// Traiter les données du profil
},
);
Views #
Composants de niveau supérieur qui intègrent plusieurs organismes.
Vue d'authentification
SFAuthView(
additionalData: AuthFormData(
authNotAccount: AuthLinkData(
text: "Vous n'avez pas encore de compte ? ",
link: "S'inscrire",
),
authAlreadyExists: AuthLinkData(
text: "Vous avez déjà un compte ? ",
link: "Se connecter",
),
authForgotPassword: AuthLinkData(
link: "Mot de passe oublié ?",
),
dividerText: "ou continuer avec",
),
onLogin: (loginModel) {
// Traiter la connexion
print('Email: ${loginModel.email}');
},
onRegister: (registerModel) {
// Traiter l'inscription
print('Email: ${registerModel.email}');
},
onForgotPassword: (forgotPasswordModel) {
// Traiter la récupération de mot de passe
print('Email: ${forgotPasswordModel.email}');
},
onGoogleConnect: () {
// Connexion avec Google
},
onGithubConnect: () {
// Connexion avec GitHub
},
);
Vue de profil
SFProfileView(
additionalData: ProfileFormData(
// Personnalisation des libellés et messages
),
profileModel: ProfileModel(
email: 'user@example.com',
username: 'johndoe',
),
onSubmit: (profileModel) {
// Traiter la mise à jour du profil
print('Email mis à jour: ${profileModel.email}');
print('Username mis à jour: ${profileModel.username}');
},
children: [
// Widgets additionnels à afficher sous le formulaire de profil
SFSecondaryButton(
label: 'Déconnexion',
onPressed: () => print('Déconnexion'),
),
],
);
Services et Utilitaires #
Service de notifications #
// Configuration du service
NotificationService.setInstance(
NotificationService.internal(OverlayNotificationManager()),
);
// Utilisation directe du service
NotificationService().showNotification(
context,
SFNotification(
title: 'Succès',
message: 'Votre profil a été mis à jour',
icon: Icons.check_circle_outline_rounded,
iconColor: AppColors.success,
),
duration: const Duration(seconds: 4),
margin: const EdgeInsets.only(top: 16, right: 16),
);
// Fermer toutes les notifications
NotificationService().closeAllNotifications();
// Utilisation des extensions (recommandée)
context.showSuccess(
title: 'Enregistré',
message: 'Vos modifications ont été enregistrées',
);
Thèmes #
// Application du thème global
MaterialApp(
theme: AppTheme.lightTheme,
darkTheme: AppTheme.darkTheme,
themeMode: ThemeMode.system, // ou .light ou .dark
home: MyHomePage(),
);
Layouts #
SaasFork Design System fournit un système de layouts flexible pour structurer vos applications de manière cohérente.
SFDefaultLayout #
Le layout principal utilise un pattern builder pour permettre une grande flexibilité dans la construction de l'interface:
SFDefaultLayout(
// Définit la structure du layout via un builder
builder: SFLayoutBuilder(
// Constructeur d'en-tête (optionnel)
headerBuilder: (context, ref) => AppBar(
title: Text('Mon application'),
actions: [
IconButton(
icon: Icon(Icons.notifications),
onPressed: () => print('Notifications'),
),
],
),
// Constructeur de drawer latéral (optionnel)
drawerBuilder: (context, ref) => ListView(
children: [
DrawerHeader(child: Text('Menu')),
ListTile(
leading: Icon(Icons.home),
title: Text('Accueil'),
onTap: () => print('Accueil'),
),
ListTile(
leading: Icon(Icons.settings),
title: Text('Paramètres'),
onTap: () => print('Paramètres'),
),
],
),
// Constructeur de contenu (obligatoire)
contentBuilder: (context, ref) => SingleChildScrollView(
child: Column(
children: [
Text('Contenu principal'),
// Autres widgets...
],
),
),
// Constructeur de pied de page (optionnel)
footerBuilder: (context, ref) => Container(
height: 60,
color: Theme.of(context).colorScheme.surface,
child: Center(
child: Text('© 2023 - SaasFork'),
),
),
),
// Padding du contenu principal (optionnel)
contentPadding: EdgeInsets.all(16.0),
);
Exemple d'utilisation avec une barre de navigation
// Définition d'une navbar pour le header
Widget _buildNavBar(BuildContext context, WidgetRef ref) {
return SFNavBar(
leading: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.bolt, color: AppColors.primary),
SizedBox(width: 8),
Text('SaasFork', style: AppTypography.titleMedium),
],
),
links: [
SFNavLink(
label: 'Tableau de bord',
onPress: () => print('Dashboard'),
),
SFNavLink(
label: 'Utilisateurs',
onPress: () => print('Users'),
),
],
actions: [
SFCircularButton(
icon: Icons.notifications,
onPressed: () => print('Notifications'),
size: ComponentSize.sm,
),
],
);
}
// Utilisation dans le layout
SFDefaultLayout(
builder: SFLayoutBuilder(
headerBuilder: _buildNavBar,
contentBuilder: (context, ref) => Center(
child: Text('Contenu de l\'application'),
),
),
);
Fondations #
Couleurs #
// Utilisation des couleurs prédéfinies
Container(
color: AppColors.primary,
child: Text('Texte sur fond primaire'),
);
// Variations de couleurs
Text(
'Message d\'erreur',
style: TextStyle(color: AppColors.red.s400),
),
Espacements #
// Utilisation des espacements standardisés
Padding(
padding: const EdgeInsets.all(AppSpacing.md),
child: Text('Texte avec padding moyen'),
);
// Dans un Column ou Row avec l'extension spacing
Column(
spacing: AppSpacing.md, // Ajoute un espace standardisé entre les éléments
children: [
Text('Premier élément'),
Text('Deuxième élément'),
Text('Troisième élément'),
],
);
Typographie #
Text(
'Titre principal',
style: AppTypography.headlineLarge,
);
Text(
'Corps de texte',
style: AppTypography.bodyMedium,
);
// Typographie adaptative selon la taille du composant
Text(
'Bouton',
style: AppTypography.getScaledStyle(
AppTypography.labelLarge,
ComponentSize.md,
),
);
Système Responsive #
Notre système responsive permet d'adapter facilement vos layouts à différentes tailles d'écran.
Grille Responsive
// Une grille qui adapte automatiquement le nombre de colonnes selon la taille d'écran
ResponsiveGrid(
// Surcharger le nombre de colonnes par défaut par type d'écran (optionnel)
mobileColumns: 1,
tabletColumns: 2,
desktopColumns: 3,
largeDesktopColumns: 4,
// Espacements et marges
spacing: 16.0,
margin: EdgeInsets.all(16.0),
padding: EdgeInsets.all(8.0),
// Contenu de la grille
children: [
// Vos widgets ici
Card(child: Text("Élément 1")),
Card(child: Text("Élément 2")),
Card(child: Text("Élément 3")),
],
);
Lignes et Colonnes Responsives
// Création d'une ligne responsive avec des colonnes
ResponsiveRow(
spacing: 16.0,
wrap: true, // Passe à la ligne si l'espace est insuffisant
margin: EdgeInsets.symmetric(vertical: 16.0),
children: [
// Colonne responsive qui occupe 6/12 sur mobile, 4/12 sur tablet, 3/12 sur desktop
ResponsiveColumn(
xs: 6, // Mobile (fraction sur 12)
sm: 4, // Tablet (fraction sur 12)
md: 3, // Desktop (fraction sur 12)
child: Container(color: Colors.red, height: 100),
),
// Colonne responsive qui occupe 6/12 sur mobile, 8/12 sur tablet, 9/12 sur desktop
ResponsiveColumn(
xs: 6,
sm: 8,
md: 9,
padding: EdgeInsets.all(8.0),
child: Container(color: Colors.blue, height: 100),
),
],
);
Conteneur Responsive
// Conteneur responsive qui s'adapte selon la taille d'écran
ResponsiveContainer(
maxWidth: 1200.0, // Largeur max (optionnel)
padding: EdgeInsets.all(16.0), // Padding adapté automatiquement sur mobile
center: true, // Centre le contenu si maxWidth est défini
child: Text("Contenu centré avec largeur maximale"),
);
Extensions Responsives
// Détecter la taille d'écran actuelle
if (context.isMobile) {
// Logique spécifique aux mobiles
} else if (context.isTablet) {
// Logique spécifique aux tablettes
} else if (context.isDesktop) {
// Logique spécifique aux desktops
}
// Valeur adaptative selon la taille d'écran
final padding = context.responsive(
mobile: 8.0,
tablet: 16.0,
desktop: 24.0,
largeDesktop: 32.0,
);
// Accéder aux marges et espacements adaptés
final horizontalMargin = context.horizontalMargin;
final gap = context.gap;
// Padding adapté à la taille d'écran
Container(
padding: context.responsivePadding, // Padding horizontal & vertical
child: Text("Contenu avec padding adaptatif"),
);
// Nombre de colonnes de la grille actuelle
final columns = context.columns; // 1-12 selon la taille d'écran
Pricing Components #
SFPriceDefault #
SFPriceDefault est un composant permettant d'afficher un prix avec devise et période de facturation, utilisable dans différents contextes comme les pages de tarification ou les récapitulatifs de commande.
Fonctionnalités
- Affichage formaté d'un prix avec sa devise
- Support pour différentes devises avec positionnement personnalisable du symbole
- Affichage optionnel de la période de facturation
- Adaptation automatique selon différentes tailles (xs, sm, md, lg, xl)
- Optimisé pour l'accessibilité et la performance
Exemples d'utilisation
// Utilisation du code de devise au lieu du symbole
SFPriceDefault(
value: 15000,
currency: SFCurrency.eur,
period: SFPricePeriod.year,
useCurrencyCode: true, // Affiche "EUR" au lieu de "€"
size: ComponentSize.lg,
)
List Components #
SFItemList #
SFItemList permet d'afficher facilement une liste d'éléments avec des icônes, idéale pour les fonctionnalités d'un produit, les listes de bénéfices ou les résumés de plan tarifaire.
Fonctionnalités
- Affichage d'une liste verticale d'éléments avec icônes et libellés
- Personnalisation des icônes par élément ou utilisation d'une icône par défaut
- Support pour différentes tailles de composants
- Personnalisation de la couleur des icônes
- Optimisé pour les listes statiques dans un layout
Exemples d'utilisation
// Combinaison avec icônes personnalisées et icône par défaut
SFItemList(
items: [
SFItemListData(label: "Sauvegarde automatique", icon: Icons.backup),
SFItemListData(label: "Intégration Slack"), // Utilisera l'icône par défaut
SFItemListData(label: "Gestion des accès", icon: Icons.security),
],
defaultIcon: Icons.check_circle,
iconColor: Theme.of(context).colorScheme.secondary,
)
Contribution #
Pour contribuer au design system, veuillez consulter notre guide de contribution et suivre nos conventions de codage.
License #
SaasFork Design System est la propriété de SaasFork. Tous droits réservés.