mervey_ui_kit

Adaptive Material/Cupertino design system for Flutter apps. Bundles a semantic color palette, typography tokens, spacing/radius scales, and a set of cross-platform components that pick the right backend per platform.

The package is brand-agnostic. Host apps wire their colors and typography in through AppTheme.light / AppTheme.dark overrides.

Requirements

  • Dart SDK >= 3.6.0
  • Flutter >= 3.27.0

The Flutter floor is dictated by Color.withValues(alpha:), which replaced the deprecated withOpacity in Flutter 3.27.

Installation

dependencies:
  mervey_ui_kit: ^0.7.2

Theme setup

import 'package:flutter/material.dart';
import 'package:mervey_ui_kit/mervey_ui_kit.dart';

void main() {
  runApp(
    MaterialApp(
      theme: AppTheme.light(),       // neutral Material defaults
      darkTheme: AppTheme.dark(),
      builder: AppTheme.wrapWithCupertino, // optional, for iOS Cupertino
      home: const _Demo(),
    ),
  );
}

Branding

Pass a custom palette:

final palette = AppColorPalette.light(
  brandPrimary: const Color(0xFFF3E123),
  onBrand: const Color(0xFF111111),
  textPrimary: const Color(0xFF111111),
);

MaterialApp(
  theme: AppTheme.light(palette: palette),
  darkTheme: AppTheme.dark(palette: AppColorPalette.dark(brandPrimary: ...)),
);

All factory arguments are named and optional — overrides are surgical, the rest stays Material-flavoured. The palette also expands to a Material 3 ColorScheme with the surface-container ramp, outline / outlineVariant and surfaceTint filled in, so M3 widgets stay aligned with the brand.

Configure the brand once: AppTheme.wrapWithCupertino inherits the palette already installed by AppTheme.light / .dark, so the Cupertino side (CupertinoButton, navigation, action text, …) picks up brandPrimary without re-passing it. Adaptive components such as AppButton read the brand straight off context.colors, so they stay branded on iOS even without the builder.

Typography

The kit ships with system-font defaults on every platform — no third-party fonts are bundled, and there are no licensing strings attached. For a branded look, bundle your fonts in your app's pubspec.yaml and pass them through:

AppTheme.light(
  typography: const AppTypographyTokens(
    scale: AppTextScale.md,
    accentFont: 'Inter',         // null → system font
    secondaryFont: 'Inter',
    monospaceFont: 'JetBrains Mono',
  ),
);

Use AppTextScale.sm / .md / .lg for a compact / default / roomy text rhythm without touching individual styles. The scale applies everywhere — component text, the Material TextTheme the kit builds, and the Cupertino text styles.

Components

Every component is a StatelessWidget/StatefulWidget that reads tokens from the active theme.

Category Widgets
Buttons AppButton, AppGlassButton, AppCircleIconButton, AppIconActionButton, AppResendButton
Inputs AppTextField, AppAmountField, AppSearchField, AppOtpField, showAppInputDialog
Cards & surfaces AppCard, AppTappableCard, AppCoinActionCard, AppFieldShell + AppFieldLabel, AppFieldTile, AppInfoBanner
Sheets & dialogs showAppModalSheet, AppSheetHeader, AppSheetDragHandle, AppDialog + showAppDialog
Navigation AppScaffold, AppNavbar, AppBackBar, AppPillTabs
Lists & structure AppDetailRow, AppSectionHeader, AppSectionCaption, AppOnboardingHeader
Indicators AppPill, AppStatusPill, AppBadgedIcon, AppAvatar, AppAvatarInitials, AppSwitch, AppSkeletonBar
Feedback showAppSnackBar / context.showSnack, showAppToast, showAppTopToast, showAppCopiedToast
States AppLoadingState, AppErrorState, AppEmptyState

Platform adaptivity

Some components swap to a Cupertino backend on iOS for native feel; the rest are Material-only and render the same on every platform (Material widgets look natural on iOS too).

Adaptive (Material on Android, Cupertino on iOS): AppButton, AppTextField, AppAmountField, AppDialog / showAppDialog, AppNavbar, AppScaffold. Everything else renders the same Material look everywhere.

AppSwitch is deliberately not adaptive — since 0.7.4 it renders one custom toggle on every platform so the kit's on/off state looks identical across Android and iOS.

AppSearchField is intentionally Material-only — for iOS-pure search flows prefer Flutter's CupertinoSearchTextField.

Context extensions

Pull tokens from any BuildContext:

final colors = context.colors;         // AppColorPalette
final typography = context.typography; // AppTypographyTokens
final icons = context.icons;           // AppIconTokens

Since 0.7.0 these getters are safe under themes that were not built by AppTheme: they fall back to neutral defaults matching the ambient brightness instead of throwing. Kit components degrade to an unbranded look inside host screens with a custom theme, consumer tests, and dialogs under a foreign navigator — they never crash the app.

Migrating to 0.7.0

Components without the App prefix were renamed for the library-wide naming convention. The old names still compile as @Deprecated aliases and will be removed in 1.0.0 — migrate with a find/replace:

Deprecated Replacement
AvatarInitials AppAvatarInitials
BadgedIcon AppBadgedIcon
IconActionButton AppIconActionButton
LoadingState / ErrorState / EmptyState AppLoadingState / AppErrorState / AppEmptyState
showInputDialog showAppInputDialog

See CHANGELOG for the rest of 0.7.0: theme-fallback safety, the disabled/loading button treatment, formatter cursor fixes, text-scale routing, and accessibility improvements.

Example app

See example/ for a runnable showcase with a light/dark switch and a tour of every component.

License

MIT — see LICENSE.

Libraries

mervey_ui_kit
Mervey shared UI kit.