figmage 0.1.0-dev.4 copy "figmage: ^0.1.0-dev.4" to clipboard
figmage: ^0.1.0-dev.4 copied to clipboard

A CLI tool for generating Figma styles for Flutter

🧙‍♂️ Figmage 🧙 #

A CLI tool for generating Figma styles for Flutter

Empowered by whynotmake.it Coverage Badge Powered by Mason melos

What's in the box 🎁 #

Figmage is a magical CLI tool that helps you generate a flutter package from your Figma Design System. It uses the Figma APIs to fetch your design tokens from published styles, as well as variables, with full modes support.

So a variables section like this: Example Screenshot of a Variables Section

Is only a short figmage forge run away from becoming code like this:

// colors.dart
import 'package:flutter/material.dart';
@immutable
class ColorsMyCollection extends ThemeExtension<ColorsMyCollection> {
  const ColorsMyCollection({
    required this.background,
    required this.primary,
  });

  const ColorsMyCollection.dark()
      : background = const Color(0xff665555),
        primary = const Color(0xffef86a6);

  const ColorsMyCollection.light()
      : background = const Color(0xfffff4f4),
        primary = const Color(0xff7d4052);

  final Color background;

  final Color primary;

  @override
  ColorsMyCollection copyWith([
    Color? background,
    Color? primary,
  ]) {
    /// ...
  }

  @override
  ColorsMyCollection lerp(
    ColorsMyCollection other,
    double t,
  ) {
    /// ...
  } 
}

extension ColorsMyCollectionBuildContextX on BuildContext {
  ColorsMyCollection get colorsMyCollection =>
      Theme.of(this).extension<ColorsMyCollection>()!;
}

Features #

  • ✨ Generate a Flutter package from your Figma Design System
  • 🎨 Supports many types of tokens:
    • 🌈 Color styles and variables
    • 🖋️ Typography styles (with optional google_fonts support!)
    • 🔢 Number variables, which can be generated as Paddings, and Spacers as well
  • 🌓 Modes support for variables: Generate different tokens for different themes (e.g. dark/light)
  • 📦 Package generation: All your tokens end up in one convenient package. Depend on it from your app, and update it whenever neccessary!
  • 🤝 Seamless integration with Themes from material.dart: Generated classes are ThemeExtensions, so they can be integrated into your app's theme easily!
  • 🎯 Quick access using BuildContext extensions.
  • 🔮 Portable: figmage is a pure dart package, so it can easily be integrated into your CI/CD pipelines, to automatically fetch the latest tokens of your project for you!

Installation 💻 #

❗ In order to start using Figmage you must have the Dart SDK installed on your machine.

For the easiest usage, install figmage via dart pub global activate:

dart pub global activate figmage

How to generate 🏭 #

🚀 Quick start #

This command will generate a new package at the specified output path using the provided Figma API token and file ID:

figmage forge <path> --token <token> --fileId <fileId>

Note

🤔 Wait, what's a token? And what's this file ID? Where do I get them?

To interact with the Figma API, you'll need an access token. Check out the Figma Docs to learn how to create yours.

The fileId is part of the URL when you open a Figma file. Just look in your browser's address bar when you have your design system file open:

➡ figma.com/file/your-file-id-is-here/more-not-so-interesting-stuff

🎨 Details #

If you require more control over the generated code, create a figmage.yaml file in the directory from which you're running the command. Below is an example, along with descriptions of what each flag accomplishes:

# Default: "figmage_package". The name of the generated Dart package.
packageName: "figmage_package"
# Required. The Figma file ID from which to generate tokens.
fileId: "YOUR_FIGMA_FILE_ID"
# Default: ''. Description of the generated package.
packageDescription: "A package generated from Figma designs."
# Default: '.'. Directory to generate the package in, relative to this config file.
packageDir: "./generated"
# Default: true. Determines whether to drop unresolvable values. When true, values that cannot be resolved (e.g., an alias pointing to a missing variable) are omitted, ensuring all tokens are resolvable in all modes (e.g., light and dark mode). When false, unresolved variables are included but will return null. Defaults to false.
dropUnresolved: true
colors:
  # Default: true. Whether to generate color tokens.
  generate: true
  # Default: []. Specific paths to generate color tokens from.
  from:
    - "colors/path"
typography:
  # Default: true. Whether to generate typography tokens.
  generate: true
  # Default: []. Specific paths to generate typography tokens from.
  from:
    - "typography/path"
  # Default: true. Whether to use Google Fonts for font families.
  useGoogleFonts: true
numbers:
  # Default: false. Whether to generate number tokens (for spacers, paddings, borders).
  generate: false
  # Default: []. Specific paths to generate number tokens from.
  from:
    - "numbers/path"
spacers:
  # Default: false. Whether to generate spacer tokens from all generated number tokens (see above).
  generate: false
paddings:
  # Default: false. Whether to generate padding tokens from all generated number tokens (see above).
  generate: false

Warning

Spacers and Paddings will always be generated for all included number tokens for now. Any from paths specified for spacers and paddings will be ignored. See this issue for more information.

How to use 📲 #

🚀 Quick start #

To integrate your newly minted token package into your app, simply add it to your pubspec.yaml file:

figmage_package:
  path: path/to/your/package

Incorporate design tokens into your code as follows:

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.light().copyWith(
        extensions: <ThemeExtension<dynamic>>[
          ColorsDesignSystem.dark(), // <- added
          TypographyDesignSystem.standard(), // <- added
        ],
      ),
      darkTheme: ThemeData.dark().copyWith(
        extensions: <ThemeExtension<dynamic>>[
          ColorsDesignSystem.dark(), // <- added
          TypographyDesignSystem.standard(), // <- added
        ],
      ),
      themeMode: isLightTheme ? ThemeMode.light : ThemeMode.dark,
      home: Home(
        isLightTheme: isLightTheme,
        toggleTheme: toggleTheme,
      ),
    );
  }

Leverage the tokens within your widgets like so:

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final colors = context.colorsDesignSystem;
    final typography = context.typographyDesignSystem;

    return Container(
        color: colors.primary,
        child: Text('Hello world!', style: typography.body1),
    )
    ...

🎨 Details #

Design tokens are encapsulated within classes based on their type and collection for variable-based tokens or style group name for style-based tokens. Each class implements BuildContext extension which can be used to propagate the design tokens through your app.