Skip to content

Flutter Integration

The design system generates a Dart file at build/flutter/naluma_tokens.dart containing type-safe color constants. This file is the bridge between the token JSON and the Flutter app’s ThemeData.


The Style Dictionary config (config/style-dictionary.config.mjs) includes a flutter platform that transforms primitive color tokens into a Dart class:

tokens/primitive/color.json
|
Style Dictionary (flutter transformGroup)
|
build/flutter/naluma_tokens.dart

Run from the repository root:

Terminal window
npm run tokens

This regenerates all platform outputs, including the Dart file.


The generated naluma_tokens.dart exports an abstract final class with static Color constants:

// DO NOT EDIT — generated by Style Dictionary
import 'package:flutter/material.dart';
abstract final class NalumaColors {
/// Primary light background
static const primitiveColorWarmWhite = Color(0xFFFBFAF1);
/// Secondary background, soft accent areas
static const primitiveColorPaleWheat = Color(0xFFF9DFAE);
/// Borders, dividers, accent highlights
static const primitiveColorWarmGold = Color(0xFFF2C087);
// ... all primitive colors
}

The class is abstract final to prevent instantiation — it serves as a namespace for color constants.


Import the generated file and wire the colors into your app’s ThemeData:

import 'package:naluma_app/generated/naluma_tokens.dart';
ThemeData nalumaLightTheme() {
return ThemeData(
scaffoldBackgroundColor: NalumaColors.primitiveColorWarmWhite,
colorScheme: ColorScheme.light(
primary: NalumaColors.primitiveColorBurntSiennaDeep,
onPrimary: NalumaColors.primitiveColorWhite,
secondary: NalumaColors.primitiveColorDarkBurgundy,
surface: NalumaColors.primitiveColorWhite,
onSurface: NalumaColors.primitiveColorWarmDark,
error: const Color(0xFFDC2626),
),
appBarTheme: AppBarTheme(
backgroundColor: NalumaColors.primitiveColorPaleWheat,
foregroundColor: NalumaColors.primitiveColorWarmDark,
),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
backgroundColor: NalumaColors.primitiveColorBurntSiennaDeep,
foregroundColor: NalumaColors.primitiveColorWhite,
minimumSize: const Size.fromHeight(44), // Accessibility min touch target
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8), // primitive.radius.md
),
),
),
);
}
ThemeData nalumaDarkTheme() {
return ThemeData.dark().copyWith(
scaffoldBackgroundColor: NalumaColors.primitiveColorWarmCharcoal,
colorScheme: ColorScheme.dark(
primary: NalumaColors.primitiveColorBurntSiennaDeep,
onPrimary: NalumaColors.primitiveColorWhite,
secondary: NalumaColors.primitiveColorPaleWheat, // Flipped for dark mode
surface: NalumaColors.primitiveColorDarkSurface,
onSurface: NalumaColors.primitiveColorWarmWhite,
error: const Color(0xFFDC2626),
),
);
}

The app uses two Google Fonts families. Bundle them as assets rather than fetching at runtime to avoid a flash of unstyled text.

Download variable-weight .ttf files for:

  • Literata (headings) — weights 300—800
  • Plus Jakarta Sans (body) — weights 300—800
flutter:
fonts:
- family: Literata
fonts:
- asset: assets/fonts/Literata-VariableFont.ttf
- family: PlusJakartaSans
fonts:
- asset: assets/fonts/PlusJakartaSans-VariableFont.ttf
TextTheme nalumaTextTheme() {
return TextTheme(
displayLarge: const TextStyle(
fontFamily: 'Literata',
fontSize: 48,
fontWeight: FontWeight.w600,
height: 1.15, // line-height.snug
letterSpacing: -0.01 * 48, // tight-1 in px
),
headlineLarge: const TextStyle(
fontFamily: 'Literata',
fontSize: 36,
fontWeight: FontWeight.w600,
height: 1.2,
letterSpacing: -0.005 * 36,
),
bodyLarge: const TextStyle(
fontFamily: 'PlusJakartaSans',
fontSize: 18,
fontWeight: FontWeight.w400,
height: 1.7,
),
bodyMedium: const TextStyle(
fontFamily: 'PlusJakartaSans',
fontSize: 16,
fontWeight: FontWeight.w400,
height: 1.7,
),
bodySmall: const TextStyle(
fontFamily: 'PlusJakartaSans',
fontSize: 14,
fontWeight: FontWeight.w400,
height: 1.65,
),
labelLarge: const TextStyle(
fontFamily: 'PlusJakartaSans',
fontSize: 16,
fontWeight: FontWeight.w600,
letterSpacing: 0.01 * 16, // button-lg
),
);
}

The settle easing curve maps to Flutter’s Cubic:

class NalumaMotion {
static const settleCurve = Cubic(0.16, 1, 0.3, 1);
static const settleDuration = Duration(milliseconds: 600);
static const defaultCurve = Cubic(0.4, 0, 0.2, 1);
static const fastDuration = Duration(milliseconds: 150);
static const normalDuration = Duration(milliseconds: 250);
static const slowDuration = Duration(milliseconds: 400);
}

The generated Dart file should be committed to the Flutter app repository (not the design system repo). The recommended workflow:

  1. Edit token JSON in naluma-design-system/tokens/
  2. Run npm run tokens to regenerate all outputs
  3. Copy build/flutter/naluma_tokens.dart into the Flutter app’s lib/generated/ directory
  4. Review the diff for any breaking changes before committing

A future CI step could automate step 3 by opening a PR against the Flutter app repo whenever tokens change.