AI & Technology

Figma to Flutter: A Practical Design Handoff Guide (2026)

Simon Dziak
Simon Dziak
Owner & Head Developer
October 12, 2026•8 min

The fastest way to get from Figma to Flutter is not a code generator. It is a shared vocabulary. If colors, type, spacing and components have the same names in Figma and in your Dart code, every screen becomes a translation exercise instead of a guessing game. This guide shows the handoff we use at App369, step by step.

Why "export to Flutter" rarely survives contact with production

Figma-to-code plugins can produce a widget tree that looks right in a screenshot. The trouble starts on the second screen. Generated code usually hard-codes sizes and hex values, uses absolute positioning where Flutter wants flexible layouts, and knows nothing about your state management, routing, accessibility or dark mode. You end up rewriting it.

Generated code is still useful as a reference for a tricky layout. But the durable part of the handoff is the design system underneath the screens, and that you should build by hand, once.

Step 1: Agree on tokens in Figma first

Before any Flutter work starts, the design file should define its decisions as variables, not as loose hex values on layers. Figma variables can hold colors, numbers, strings and booleans, are grouped into collections, and can have several modes, so one variable resolves to different values in, for example, a light and a dark theme (Figma: guide to variables).

A practical minimum for an app:

  • Color: semantic names (surface, onSurface, primary, onPrimary, error, outline), with light and dark modes. Avoid names like blue-500 in components; keep raw palette colors in their own collection and point semantic variables at them.
  • Spacing and radius: a short number scale (for example 4, 8, 12, 16, 24, 32) used everywhere.
  • Typography: named text styles (titleLarge, bodyMedium, labelSmall) instead of one-off sizes.

If the designer uses Material 3 naming for color roles and text styles, the mapping to Flutter in the next step is almost one-to-one.

Step 2: Map tokens to ThemeData

In Flutter, the Material equivalents live on ThemeData: ColorScheme for color roles and TextTheme for text styles. Build one ThemeData for light and one for dark from the Figma values, and pass both to MaterialApp (theme and darkTheme).

final lightScheme = ColorScheme(
  brightness: Brightness.light,
  primary: Color(0xFF1F5EFF),
  onPrimary: Colors.white,
  surface: Colors.white,
  onSurface: Color(0xFF0F172A),
  error: Color(0xFFB3261E),
  onError: Colors.white,
  secondary: Color(0xFF475569),
  onSecondary: Colors.white,
);

final lightTheme = ThemeData(
  colorScheme: lightScheme,
  textTheme: appTextTheme,
  useMaterial3: true,
);

Widgets then read Theme.of(context).colorScheme.primary, never a hex value. When the designer changes a variable in Figma, you change one line in Dart.

Step 3: Put everything else in a ThemeExtension

Real designs always have tokens Material doesn't name: a success color, a card shadow, a brand gradient, your spacing scale. Flutter's answer is ThemeExtension, an interface for adding custom properties to ThemeData. You subclass it, define your fields, and implement copyWith and lerp, so values animate smoothly when the theme changes (Flutter API: ThemeExtension).

@immutable
class AppTokens extends ThemeExtension<AppTokens> {
  const AppTokens({required this.success, required this.cardRadius});

  final Color success;
  final double cardRadius;

  @override
  AppTokens copyWith({Color? success, double? cardRadius}) => AppTokens(
        success: success ?? this.success,
        cardRadius: cardRadius ?? this.cardRadius,
      );

  @override
  AppTokens lerp(ThemeExtension<AppTokens>? other, double t) {
    if (other is! AppTokens) return this;
    return AppTokens(
      success: Color.lerp(success, other.success, t)!,
      cardRadius: lerpDouble(cardRadius, other.cardRadius, t)!,
    );
  }
}

// Register: ThemeData(extensions: const [AppTokens(...)])
// Read:     Theme.of(context).extension<AppTokens>()!

Keep the Dart names identical to the Figma variable names. That one habit removes most handoff questions.

Step 4: Build components, then screens

Go through the Figma components page and build each component as a Flutter widget before building any screen: buttons with all their states, inputs, list rows, cards, dialogs, empty states. For each one, check:

  • States: default, pressed, disabled, loading, error. Figma files often show only the default state; ask for the rest before you build.
  • Layout intent: does it hug its content or fill the width? Auto layout in Figma maps naturally to Row, Column, Padding and Expanded/Flexible. Absolute positioning in Figma usually means "ask the designer".
  • Text scaling: Flutter respects the user's system font size. Test components at a large text scale, because a label that fits in Figma at 100% can wrap or overflow on a real phone.
  • Touch targets: small icons need a larger tappable area than their visual size.

Screens then become mostly composition, which is where Flutter is fast.

Step 5: Lock the design with golden tests

Once a component matches the design, protect it. Flutter's golden tests render a widget to an image and compare it with a stored reference image: matchesGoldenFile checks a widget against the golden file, and running flutter test --update-goldens creates or updates the references (Flutter API: matchesGoldenFile).

testWidgets('PrimaryButton matches design', (tester) async {
  await tester.pumpWidget(MaterialApp(
    theme: lightTheme,
    home: const Center(child: PrimaryButton(label: 'Continue')),
  ));
  await expectLater(
    find.byType(PrimaryButton),
    matchesGoldenFile('goldens/primary_button_light.png'),
  );
});

Render each key component in light and dark mode. When someone changes a token or a widget, the failing golden shows exactly what moved. Goldens can differ slightly between operating systems because of font rendering, so generate and check them on one platform, usually in CI.

Step 6: Review on real devices, not in Figma

Designs are drawn on one screen size. Before calling a screen done, check it on a small phone, a large phone and, if you ship there, a tablet and the web, in both light and dark mode, with a large system font. Most handoff bugs show up here: text that wraps, keyboards covering inputs, safe areas on notched phones.

A short handoff checklist

  • Figma variables for color (with light and dark modes), spacing, radius and type.
  • The same names in ColorScheme, TextTheme and one ThemeExtension.
  • Every component built and reviewed in all its states before screens.
  • Golden tests for key components in light and dark mode.
  • A device review with large text before release.

Getting help

App369 builds Flutter apps from Figma designs, and can also set up the design system and golden tests for a team that already has a Flutter app. See Flutter app development, get a ballpark with the app cost calculator, or read how the Workspace369 case study app is put together.

Want this done at a fixed price?

Fixed scope, fixed price, paid upfront. The work happens in writing; no call needed.

Tags
#figma to flutter #flutter design system #flutter theming #figma variables #flutter golden tests #design handoff
Share:

Related Resources

Related Articles