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.
Libraries
- atoms/atoms
- Atoms - Composants fondamentaux de l'interface utilisateur Les atomes sont les blocs de construction les plus petits et indivisibles
- atoms/dividers/divider_with_text
- atoms/forms/form_message
- atoms/icons/icon
- atoms/images/abstract_image
- atoms/images/image_circle
- atoms/images/image_square
- atoms/inputs/file_field
- atoms/inputs/password_field
- atoms/inputs/switch_field
- atoms/inputs/text_field
- atoms/interactive/delete_image
- atoms/interactives/removable_content
- foundations/color
- foundations/colors
- foundations/data/auth_form_data
- foundations/data/data
- foundations/data/dialog_data
- foundations/data/profile_form_data
- foundations/foundations
- Foundations - Éléments de base du design system Comprend les couleurs, la typographie, l'espacement et les dimensions standards
- foundations/models/forgot_password_model
- foundations/models/login_model
- foundations/models/models
- foundations/models/profile_model
- foundations/models/register_model
- foundations/responsive/breakpoints
- foundations/responsive/column_system
- foundations/responsive/responsive
- foundations/responsive/responsive_column
- foundations/responsive/responsive_context
- foundations/responsive/responsive_grid
- foundations/responsive/responsive_row
- foundations/sizes
- foundations/spacing
- foundations/typography
- foundations/utils
- molecules/dropdown/dropdown
- molecules/dropdown/dropdown_option
- molecules/forms/form_field
- molecules/forms/form_file_field
- molecules/forms/form_single_image_file_field
- molecules/lists/item
- molecules/lists/item_list
- molecules/molecules
- Molecules - Composants qui combinent plusieurs atomes Les molécules sont des composants qui combinent plusieurs atomes.
- molecules/overlays/dialog
- molecules/overlays/notification
- molecules/pricing/price_default
- organisms/forms/forgot_password_form
- organisms/forms/login_form
- organisms/forms/profile_form
- organisms/forms/register_form
- organisms/marketing/widgets/heading_annoncement
- organisms/organisms
- Organismes - Composants qui combinent plusieurs éléments Les organismes sont des composants qui combinent plusieurs molécules.
- saasfork_design_system
- themes/app_theme
- themes/layouts/default_layout
- themes/layouts/layout_builder
- themes/layouts/layouts
- themes/wrappers/notification_wrapper
- utils/color_utils
- utils/expected_file
- utils/form_utils
- utils/implementations/images/image_service_loader
- utils/implementations/images/image_source
- utils/localization/app_localizations
- utils/pricing/currency
- utils/pricing/period
- utils/providers/providers
- utils/providers/theme_toggle
- utils/services/notification_service
- utils/services/notifications/abstract
- utils/services/notifications/adapter_registry
- utils/services/notifications/adapters/abstract
- utils/services/notifications/adapters/notification_adapter
- utils/services/notifications/animated_notification
- utils/services/notifications/extensions/notification_extension
- utils/services/notifications/overlay_manager
- utils/services/notifications/providers/time_provider
- utils/utils
- views/auth_view
- views/profile_view
- views/views
- Views - Composants qui combinent plusieurs éléments Les vues sont des composants qui présentent plusieurs éléments.