Flutter Scale Theme Kit

Pub Version

Look companion for Flutter: semantic colors, authored light/dark, and Material ThemeData. Stock widgets (and flutter_scale_kit SK* widgets) pick up fill, text, and borders.

Package Job Version
flutter_scale_kit Size.w, SKSize, SKCard, ScaleKitBuilder 2.0.0
flutter_scale_theme_kit LookSTTheme, context.st, ThemeData 1.0.1

The two repos sit at the same folder level (flutter_scale_kit next to flutter_scale_theme_kit). They do not depend on each other. An app may install one or both.

This package does not depend on flutter_scale_kit. Install one or both.

  • Dart ^3.7.2, Flutter >=3.35.0
  • Runtime dependency: Flutter SDK only
  • No STButton / STCard — use Flutter widgets (or SK widgets for size)
  • Dark colors are authored. Omit dark: and light is reused. Nothing invents a palette (ColorScheme.fromSeed is not the default)

Install

dependencies:
  flutter_scale_theme_kit: ^1.0.1

With size (recommended for production apps):

dependencies:
  flutter_scale_kit: ^2.0.0
  flutter_scale_theme_kit: ^1.0.1
import 'package:flutter_scale_theme_kit/flutter_scale_theme_kit.dart';

1. Define tokens once

final appST = STTheme(
  colors: STColors(
    primary: STColor(light: Color(0xFF6750A4), dark: Color(0xFFD0BCFF)),
    surface: STColor(light: Color(0xFFFFFFFF), dark: Color(0xFF1E1E1E)),
    background: STColor(light: Color(0xFFF7F7F7), dark: Color(0xFF121212)),
    text: STColor(light: Color(0xFF1C1B1F), dark: Color(0xFFE6E1E5)),
    extra: {'brand': STColor(light: Color(0xFFFFB800))},
  ),
  radius: const STRadius(sm: 8, md: 12, lg: 16),
);

Required colors: primary, surface, background, text. Optional: secondary, border, divider, textSecondary, error, success, warning, info.

2. Wire MaterialApp (and switch theme)

STThemeModeScope(
  builder: (context, mode) {
    return MaterialApp(
      theme: appST.light,
      darkTheme: appST.dark,
      themeMode: mode.mode,
      home: const HomePage(),
    );
  },
);

Anywhere under that app:

context.stMode.setMode(ThemeMode.dark);
context.stMode.setDark(true);    // false → light
context.stMode.toggle();
context.stMode.cycle();          // system → light → dark → system
context.stMode.mode;
context.stMode.isDark(context);

const STThemeModeSwitch();       // stock Switch, forces light/dark

Without STThemeModeScope, pass themeMode yourself. Look still comes from theme / darkTheme.

Scaffold, Card, ElevatedButton, TextField, AppBar, NavigationBar, SearchBar, Slider, Chip, Dialog, and the other mapped Material widgets inherit look from ThemeData. No per-widget color: required.

3. Use tokens in widgets — context.st

Text('Hello', style: TextStyle(color: context.st.text));
Container(
  decoration: BoxDecoration(
    color: context.st.surface,
    borderRadius: context.st.radius.md,
    border: Border.all(color: context.st.border),
  ),
);

Container / SKit.roundedContainerSize are not Material — pass color: context.st.surface.

Colors

Call Meaning
context.st.primary Brand
context.st.secondary Secondary brand
context.st.surface Cards, sheets
context.st.background Scaffold
context.st.border Outlines
context.st.divider Dividers
context.st.text Primary copy
context.st.textSecondary Muted copy
context.st.error / .success / .warning / .info Status
context.st.color('brand') Extra or semantic name

Radius and shadow

Call Type
context.st.radius.md BorderRadius
context.st.radius.mdValue double
context.st.radius.xs.xl same pattern
context.st.shadow.sm / .md / .lg List<BoxShadow>

Typography roles

Text('Title', style: context.st.label);
Text('Muted', style: context.st.sublabel);
Text('Body', style: context.st.description);

Override on STTheme:

typography: STTypography(
  label: STTextToken(fontSize: 14, fontWeight: FontWeight.w600),
  sublabel: STTextToken(fontSize: 12, colorName: 'textSecondary'),
  description: STTextToken(fontSize: 14, height: 1.4, colorName: 'textSecondary'),
),

Named Material slots

components: STComponents(
  card: STComponent(elevation: 0, radius: 16),
  button: STComponent(elevation: 0), // shared by button types
  fab: STComponent(fill: STColor(light: Color(0xFFFF5722))),
  chip: STComponent(radius: 20),
)
context.st.card.fill
context.st.fab.fill
context.st.chip
context.st.container('dialog')

4. Extra styles (second button, product card, price)

No extra widgets. Add tokens, then apply Flutter constructors.

extras: [
  STExtra.button(
    'ghost',
    style: STComponent(
      elevation: 0,
      foreground: STColor(light: Color(0xFF6750A4), dark: Color(0xFFD0BCFF)),
      states: STComponentStates(
        disabled: STComponent(
          foreground: STColor(light: Color(0xFF9E9E9E), dark: Color(0xFF6D6D6D)),
        ),
        pressed: STComponent(
          fill: STColor(light: Color(0x1A6750A4), dark: Color(0x1AD0BCFF)),
        ),
      ),
    ),
  ),
  STExtra.button(
    'destructive',
    style: STComponent(
      fill: STColor(light: Color(0xFFB3261E), dark: Color(0xFFF2B8B5)),
      elevation: 0,
    ),
  ),
  STExtra.card('product', style: STComponent(radius: 20)),
  STExtra.container(
    'highlighted',
    style: STComponent(fill: STColor(light: Color(0xFFFFF8E1), dark: Color(0xFF3E2723))),
  ),
  STExtra.text(
    'price',
    token: STTextToken(fontSize: 18, fontWeight: FontWeight.w600, colorName: 'primary'),
  ),
],

Always available (no generate):

ElevatedButton(
  style: context.st.extraButton('ghost').toButtonStyle(),
  onPressed: save,
  child: const Text('Ghost'),
);
Text('\$48', style: context.st.extraText('price'));
DecoratedBox(
  decoration: context.st.extraCard('product').toBoxDecoration(),
  child: child,
);
color: context.st.color('brand')
color: context.st.extraContainer('highlighted').fill

STComponent.states: hovered, pressed, focused, disabled, selected. Mapped into ButtonStyle (WidgetStateProperty). If you omit disabled, rest fill/ink fades to 38% alpha (same hue). onPressed: null shows disabled.

5. Generate short getters

From a Dart file that already has appST / STExtra.*:

part 'design.g.dart';
dart run flutter_scale_theme_kit:generate
dart run flutter_scale_theme_kit:generate lib/design.dart
dart run flutter_scale_theme_kit:generate watch

Writes design.g.dart (getters only — does not replace appST):

context.st.brand
context.st.ghostButtonStyle
context.st.destructiveButtonStyle
context.st.productCardDecoration
context.st.highlightedContainerColor
context.st.price

JSON is optional authoring input (not loaded at runtime):

dart run flutter_scale_theme_kit:generate st_theme.json
{
  "variable": "appST",
  "colors": {
    "primary": { "light": "#6750A4", "dark": "#D0BCFF" },
    "surface": { "light": "#FFFFFF" },
    "background": { "light": "#F7F7F7" },
    "text": { "light": "#1C1B1F" }
  },
  "extras": {
    "ghost": { "kind": "button", "elevation": 0 },
    "product": { "kind": "card", "radius": 20 },
    "highlighted": { "kind": "container", "fill": { "light": "#FFF8E1" } },
    "price": { "kind": "text", "fontSize": 18, "fontWeight": 600 }
  }
}

kind must be button | card | container | text. Omit file to pick lib/design.dart, design.dart, or st_theme.json. No build_runner.

With flutter_scale_kit

Do not replace appST.light with ResponsiveThemeData.create alone (it drops card/button/input themes). Scale fonts in the app:

ScaleKitBuilder(
  designWidth: 375,
  designHeight: 812,
  child: Builder(
    builder: (context) {
      return STThemeModeScope(
        builder: (context, mode) {
          return MaterialApp(
            theme: appST.light.copyWith(
              textTheme: appST.light.createResponsiveTextTheme(
                appST.light.textTheme,
              ),
            ),
            darkTheme: appST.dark.copyWith(
              textTheme: appST.dark.createResponsiveTextTheme(
                appST.dark.textTheme,
              ),
            ),
            themeMode: mode.mode,
            home: const HomePage(),
          );
        },
      );
    },
  ),
);

Align radius numbers in the app: setRadiusSizes(md: 12) and STRadius(md: 12).

SKit.paddingSize(
  all: SKSize.md,
  child: SKCard(
    child: SKElevatedButton(
      onPressed: save,
      child: SKText('Save', fontSize: 16),
    ),
  ),
);

SKit.roundedContainerSize(
  all: SKSize.md,
  color: context.st.surface,
  borderColor: context.st.border,
);

Never SKContainer(width: 120.w) (double-scale).

AI agent skill

If this package is in the app pubspec.yaml, coding agents must use it for look (STTheme, context.st). If Scale Kit is there too, they merge fonts with createResponsiveTextTheme (see above) — not ResponsiveThemeData.create.

npx skills add fodilfliti/flutter_scale_theme_kit
npx skills add fodilfliti/flutter_scale_kit

Then: Init Flutter Scale Theme Kit with defaults.

Skill source: skills/flutter-scale-theme-kit/

Example

cd example
flutter run

Gallery: light/dark switch (STThemeModeScope), Material widgets, extras, disabled button states.

Not in this package

Figma import, ColorScheme.fromSeed as the default API, STCard / STButton, Cupertino, spacing/.sp (that is flutter_scale_kit).

Libraries

flutter_scale_theme_kit
Look companion for Flutter: semantic colors, light/dark tokens, ThemeData.
generate
JSON → Dart generator. Not part of the runtime barrel.
generate_core
JSON → Dart generator core. No Flutter — safe for dart run.