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/buttons/buttons
atoms/buttons/circular_button
atoms/buttons/icon_button
atoms/buttons/icon_secondary_button
atoms/buttons/main_button
atoms/buttons/secondary_button
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/buttons/animated_switch_button
molecules/buttons/toggle_button
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
molecules/socials/social_proof
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.