Direct Figma Property Access with Morph

While Morphr style extensions provide a convenient way to apply Figma styles to Flutter widgets, sometimes you need more granular access to specific design properties. The Morph class and the top-level morph() function give you direct access to all properties of your Figma components.

Overview

The morph() function provides a bridge between your Figma components and Flutter code, exposing all visual properties as Flutter-native values:

// Get a reference to a Figma component
final buttonComponent = morph('primary_button');

// Access specific properties
final buttonColor = buttonComponent?.colors.first;
final buttonRadius = buttonComponent?.borderRadius;
final buttonShadows = buttonComponent?.shadows;

Key Benefits

  • Maximum flexibility: Access individual properties rather than applying entire styles
  • Custom theming: Extract specific values from Figma to build custom themes
  • Advanced customization: Combine properties in ways not possible with style extensions
  • Conditional styling: Apply properties based on app state or user preferences
  • Animation: Animate between Figma-defined values
  • Design system foundation: Use Figma properties as the source of truth for your Flutter design system

Usage

The morph() function returns a nullable Morph instance that wraps a Figma component:

// Basic usage
final component = morph('component_name');

// Check if component exists
if (component != null) {
  // Access properties
}

// Null-safe property access with fallback
final primaryColor = morph('primary')?.colors.first ?? Colors.blue;

Available Properties

The Morph class exposes the following properties:

PropertyTypeDescription
idStringComponent ID
nameStringComponent name
typeStringComponent type
isVisibleboolComponent visibility
sizeSizeComponent dimensions
shapeBoxShapeComponent shape (rectangle or circle)
colorsList<Color>Fill colors of the component
borderRadiusBorderRadiusCorner radius of the component
opacitydoubleOpacity value (0.0 - 1.0)
paddingEdgeInsetsInner padding values
strokesList<Color>Border/stroke colors
strokeAlignmentdoubleBorder alignment (-1 inside, 0 center, 1 outside)
leftBorderWidthdoubleLeft border width
rightBorderWidthdoubleRight border width
topBorderWidthdoubleTop border width
bottomBorderWidthdoubleBottom border width
shadowsList<BoxShadow>Shadow effects
imageFiltersList<ImageFilter>Blur effects
gradientsList<Gradient>Fill gradients
fontSizedouble?Text font size (text components only)
fontFamilyString?Text font family (text components only)
fontWeightFontWeight?Text font weight (text components only)
fontStyleFontStyle?Text style (normal or italic, text components only)
letterSpacingdouble?Text letter spacing (text components only)
lineHeightdouble?Text line height (text components only)

Practical Examples

Theme Creation from Figma Components

ThemeData createThemeFromFigma() {
  return ThemeData(
    // Primary and accent colors from Figma
    primaryColor: morph('primary')?.colors.first ?? Colors.blue,
    colorScheme: ColorScheme.light(
      primary: morph('primary')?.colors.first ?? Colors.blue,
      secondary: morph('accent')?.colors.first ?? Colors.orange,
      background: morph('background')?.colors.first ?? Colors.white,
    ),
    
    // Typography from Figma
    textTheme: TextTheme(
      headline1: TextStyle(
        fontSize: morph('heading_1')?.fontSize ?? 24,
        fontWeight: morph('heading_1')?.fontWeight ?? FontWeight.bold,
        color: morph('text_primary')?.colors.first ?? Colors.black,
      ),
      bodyText1: TextStyle(
        fontSize: morph('body')?.fontSize ?? 16,
        fontWeight: morph('body')?.fontWeight ?? FontWeight.normal,
        color: morph('text_primary')?.colors.first ?? Colors.black87,
      ),
    ),
    
    // Other theme elements
    shadowColor: morph('shadow')?.shadows.first.color ?? Colors.black26,
    cardTheme: CardTheme(
      elevation: morph('card')?.shadows.first.blurRadius ?? 4.0,
      shape: RoundedRectangleBorder(
        borderRadius: morph('card')?.borderRadius ?? BorderRadius.circular(8),
      ),
    ),
  );
}

Custom Widget with Figma Properties

class FigmaStyledContainer extends StatelessWidget {
  final Widget child;
  final String figmaComponent;
  
  const FigmaStyledContainer({
    Key? key, 
    required this.child,
    required this.figmaComponent,
  }) : super(key: key);
  
  @override
  Widget build(BuildContext context) {
    final component = morph(figmaComponent);
    
    return Container(
      width: component?.size.width,
      height: component?.size.height,
      padding: component?.padding ?? EdgeInsets.zero,
      decoration: BoxDecoration(
        color: component?.colors.isNotEmpty == true 
            ? component!.colors.first 
            : Colors.white,
        borderRadius: component?.borderRadius ?? BorderRadius.zero,
        boxShadow: component?.shadows ?? [],
        border: component?.strokes.isNotEmpty == true
            ? Border.all(
                color: component!.strokes.first,
                width: component.leftBorderWidth,
              )
            : null,
      ),
      child: child,
    );
  }
}

Conditional Styling

ElevatedButton(
  style: ElevatedButton.styleFrom(
    backgroundColor: isSelected
        ? morph('button_selected')?.colors.first
        : morph('button_normal')?.colors.first,
    shape: RoundedRectangleBorder(
      borderRadius: morph('button_normal')?.borderRadius ?? BorderRadius.zero,
    ),
    elevation: isPressed
        ? (morph('button_pressed')?.shadows.first.blurRadius ?? 1.0) / 2
        : (morph('button_normal')?.shadows.first.blurRadius ?? 4.0) / 2,
  ),
  onPressed: () => {},
  child: Text('Button'),
)

Animation Between Figma Values

AnimatedContainer(
  duration: const Duration(milliseconds: 300),
  curve: Curves.easeInOut,
  width: isExpanded
      ? morph('card_expanded')?.size.width ?? 300
      : morph('card_collapsed')?.size.width ?? 150,
  height: isExpanded 
      ? morph('card_expanded')?.size.height ?? 200
      : morph('card_collapsed')?.size.height ?? 100,
  decoration: BoxDecoration(
    color: isActive
        ? morph('card_active')?.colors.first ?? Colors.blue[100]
        : morph('card_inactive')?.colors.first ?? Colors.grey[100],
    borderRadius: isExpanded
        ? morph('card_expanded')?.borderRadius ?? BorderRadius.circular(16)
        : morph('card_collapsed')?.borderRadius ?? BorderRadius.circular(8),
    boxShadow: isExpanded
        ? morph('card_expanded')?.shadows ?? []
        : morph('card_collapsed')?.shadows ?? [],
  ),
  child: child,
)

Design System Integration

The morph() function is particularly powerful when building a design system:

/// Design system color palette based on Figma
class AppColors {
  static final primary = morph('primary')?.colors.first ?? Colors.blue;
  static final secondary = morph('secondary')?.colors.first ?? Colors.orange;
  static final background = morph('background')?.colors.first ?? Colors.white;
  static final error = morph('error')?.colors.first ?? Colors.red;
  static final success = morph('success')?.colors.first ?? Colors.green;
  
  // Text colors
  static final textPrimary = morph('text_primary')?.colors.first ?? Colors.black87;
  static final textSecondary = morph('text_secondary')?.colors.first ?? Colors.black54;
  static final textDisabled = morph('text_disabled')?.colors.first ?? Colors.black38;
}

/// Design system spacing based on Figma
class AppSpacing {
  static final xs = morph('spacing_xs')?.padding.left ?? 4.0;
  static final sm = morph('spacing_sm')?.padding.left ?? 8.0;
  static final md = morph('spacing_md')?.padding.left ?? 16.0;
  static final lg = morph('spacing_lg')?.padding.left ?? 24.0;
  static final xl = morph('spacing_xl')?.padding.left ?? 32.0;
}

Best Practices

  1. Provide fallbacks: Always include a fallback value when accessing Figma properties
  2. Component naming: Use a consistent naming convention between Figma and your code
  3. Layer of abstraction: Consider creating a design system layer that encapsulates Figma lookups
  4. Feature detection: Check for the presence of expected properties before using them

When to Use morph() vs Style Extensions

Use morph() when...Use style extensions when...
You need access to specific propertiesYou want to apply complete styles
Building custom widgets with Figma propertiesWorking with standard Flutter widgets
Creating animations between Figma statesApplying static styles
Building a custom design systemQuick implementation is a priority
Advanced theming requirementsBasic theming is sufficient
Component properties will be combined in complex waysOne-to-one mapping between Figma and Flutter works well