Files
monisuo/.agents/skills/superdesign-flutter/SKILL.md
sion123 22128e66ce docs(skills): 移除技能元数据中的版本信息
删除superdesign-flutter技能文档中的version、original和adapted_for元数据字段,简化技能定义结构。
2026-03-28 18:09:07 +08:00

15 KiB

name, description
name description
superdesign-flutter Flutter Design Skill - SuperDesign adapted for Flutter/Dart development. Optimized for Material Design 3 and Flutter widgets.

Flutter Design Skill

Use this skill when creating Flutter UI components, screens, or any Flutter UI design work.

This is an adapted version of SuperDesign for Flutter, converting web/CSS patterns to Flutter/Dart equivalents.

Design Workflow

Follow this structured approach for Flutter UI design:

  • Layout Design — Think through widget structure, create ASCII wireframes
  • Theme Design — Define colors, text styles, spacing, shadows
  • Animation Design — Plan micro-interactions and transitions
  • Implementation — Generate Flutter/Dart code

1. Layout Design

Before coding, sketch the widget tree in ASCII format:

┌─────────────────────────────────────┐
│      Scaffold / AppBar              │
├─────────────────────────────────────┤
│                                     │
│      Hero Section (Column)          │
│     - Title Text                    │
│     - CTA ElevatedButton            │
│                                     │
├─────────────────────────────────────┤
│  Row (3x Feature Cards)             │
│  ┌─────┐ ┌─────┐ ┌─────┐           │
│  │Card │ │Card │ │Card │           │
│  └─────┘ └─────┘ └─────┘           │
├─────────────────────────────────────┤
│      BottomNavigationBar            │
└─────────────────────────────────────┘

2. Theme Guidelines

Color Rules:

  • NEVER use generic blue (Colors.blue) — it looks dated
  • Prefer Material Design 3 color system (ColorScheme)
  • Use Theme.of(context).colorScheme for semantic colors
  • Support both light and dark mode with ColorScheme

OKLCH to Flutter Color Conversion:

Convert OKLCH values to Flutter Color using this helper:

import 'package:flutter/material.dart';

/// Convert OKLCH to Flutter Color
/// Example: oklch(0.205 0 0) → Color
Color oklchToColor(double l, double c, double h) {
  // Simplified conversion - for accurate conversion use a color library
  // This is a placeholder implementation
  final hue = h;
  final chroma = c;
  final lightness = l;
  
  // Convert to HSL then to RGB
  // For production, use package:flutter_color or similar
  return HSLColor.fromAHSL(1.0, hue, chroma, lightness).toColor();
}

// Modern Dark Mode Colors (Vercel/Linear style)
class ModernDarkColors {
  static const background = Color(0xFFFFFFFF);  // oklch(1 0 0)
  static const foreground = Color(0xFF252525);  // oklch(0.145 0 0)
  static const primary = Color(0xFF343434);     // oklch(0.205 0 0)
  static const secondary = Color(0xFFF7F7F7);   // oklch(0.970 0 0)
  static const muted = Color(0xFFF7F7F7);       // oklch(0.970 0 0)
  static const border = Color(0xFFEBEBEB);      // oklch(0.922 0 0)
}

Flutter Theme Setup:

MaterialApp(
  theme: ThemeData(
    useMaterial3: true,
    colorScheme: ColorScheme.fromSeed(
      seedColor: Color(0xFF343434),  // Modern neutral
      brightness: Brightness.light,
    ),
    fontFamily: 'Inter',
  ),
  darkTheme: ThemeData(
    useMaterial3: true,
    colorScheme: ColorScheme.fromSeed(
      seedColor: Color(0xFF343434),
      brightness: Brightness.dark,
    ),
    fontFamily: 'Inter',
  ),
);

Font Selection (Google Fonts):

Use google_fonts package:

# pubspec.yaml
dependencies:
  google_fonts: ^6.1.0
import 'package:google_fonts/google_fonts.dart';

// Sans-serif fonts
final inter = GoogleFonts.inter();
final roboto = GoogleFonts.roboto();
final poppins = GoogleFonts.poppins();
final montserrat = GoogleFonts.montserrat();
final outfit = GoogleFonts.outfit();
final dmSans = GoogleFonts.dmSans();

// Monospace fonts
final jetBrainsMono = GoogleFonts.jetBrainsMono();
final firaCode = GoogleFonts.firaCode();
final sourceCodePro = GoogleFonts.sourceCodePro();

Spacing System:

/// Spacing scale based on 4.0 (instead of 0.25rem)
class Spacing {
  static const double xs = 4.0;
  static const double sm = 8.0;
  static const double md = 16.0;
  static const double lg = 24.0;
  static const double xl = 32.0;
  static const double xxl = 48.0;
}

// Usage
Padding(
  padding: EdgeInsets.all(Spacing.md),  // 16.0
  child: Text('Content'),
)

3. Theme Patterns

Modern Dark Mode (Vercel/Linear style):

ThemeData(
  useMaterial3: true,
  colorScheme: ColorScheme(
    brightness: Brightness.light,
    background: Color(0xFFFFFFFF),
    onBackground: Color(0xFF252525),
    primary: Color(0xFF343434),
    onPrimary: Color(0xFFFBFBFB),
    secondary: Color(0xFFF7F7F7),
    onSecondary: Color(0xFF252525),
    surface: Color(0xFFFFFFFF),
    onSurface: Color(0xFF252525),
    outline: Color(0xFFEBEBEB),
  ),
  cardTheme: CardTheme(
    elevation: 0,
    shape: RoundedRectangleBorder(
      borderRadius: BorderRadius.circular(10.0),  // 0.625rem
    ),
  ),
);

Neo-Brutalism (90s revival):

ThemeData(
  useMaterial3: true,
  colorScheme: ColorScheme(
    brightness: Brightness.light,
    background: Color(0xFFFFFFFF),
    onBackground: Color(0xFF000000),
    primary: Color(0xFFFF6B35),     // Vibrant orange
    secondary: Color(0xFF4ECDC4),   // Teal
    tertiary: Color(0xFF5B5F97),    // Purple
    outline: Color(0xFF000000),
  ),
  cardTheme: CardTheme(
    elevation: 0,
    shape: RoundedRectangleBorder(
      borderRadius: BorderRadius.circular(0),  // No radius
      side: BorderSide(color: Colors.black, width: 2),
    ),
  ),
  elevatedButtonTheme: ElevatedButtonThemeData(
    style: ElevatedButton.styleFrom(
      elevation: 0,
      shadowColor: Colors.transparent,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(0),
      ),
    ),
  ),
);

Glassmorphism:

class GlassCard extends StatelessWidget {
  final Widget child;
  
  const GlassCard({required this.child});
  
  @override
  Widget build(BuildContext context) {
    return ClipRRect(
      borderRadius: BorderRadius.circular(16.0),
      child: BackdropFilter(
        filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
        child: Container(
          decoration: BoxDecoration(
            color: Colors.white.withOpacity(0.1),
            border: Border.all(
              color: Colors.white.withOpacity(0.2),
              width: 1,
            ),
            borderRadius: BorderRadius.circular(16.0),
          ),
          child: child,
        ),
      ),
    );
  }
}

4. Animation Guidelines

Flutter Animation Equivalents:

// Button press animation (150ms)
class AnimatedButton extends StatefulWidget {
  @override
  _AnimatedButtonState createState() => _AnimatedButtonState();
}

class _AnimatedButtonState extends State<AnimatedButton>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _scaleAnimation;
  
  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: Duration(milliseconds: 150),
      vsync: this,
    );
    _scaleAnimation = Tween<double>(begin: 1.0, end: 0.95).animate(
      CurvedAnimation(parent: _controller, curve: Curves.easeInOut),
    );
  }
  
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTapDown: (_) => _controller.forward(),
      onTapUp: (_) => _controller.reverse(),
      onTapCancel: () => _controller.reverse(),
      child: ScaleTransition(
        scale: _scaleAnimation,
        child: ElevatedButton(...),
      ),
    );
  }
}

// Fade in animation (400ms)
class FadeInWidget extends StatefulWidget {
  final Widget child;
  const FadeInWidget({required this.child});
  @override
  _FadeInWidgetState createState() => _FadeInWidgetState();
}

class _FadeInWidgetState extends State<FadeInWidget>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _opacity;
  late Animation<Offset> _slide;
  
  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: Duration(milliseconds: 400),
      vsync: this,
    );
    _opacity = Tween<double>(begin: 0.0, end: 1.0).animate(_controller);
    _slide = Tween<Offset>(begin: Offset(0, 0.05), end: Offset.zero)
        .animate(CurvedAnimation(parent: _controller, curve: Curves.easeOut));
    _controller.forward();
  }
  
  @override
  Widget build(BuildContext context) {
    return SlideTransition(
      position: _slide,
      child: FadeTransition(opacity: _opacity, child: widget.child),
    );
  }
}

Common Animation Durations:

class AnimationDurations {
  static const Duration buttonPress = Duration(milliseconds: 150);
  static const Duration hover = Duration(milliseconds: 200);
  static const Duration fadeIn = Duration(milliseconds: 400);
  static const Duration slideIn = Duration(milliseconds: 350);
  static const Duration bounce = Duration(milliseconds: 600);
  static const Duration pageTransition = Duration(milliseconds: 300);
}

5. Widget Patterns

Responsive Layout:

class ResponsiveLayout extends StatelessWidget {
  final Widget mobile;
  final Widget? tablet;
  final Widget? desktop;
  
  const ResponsiveLayout({
    required this.mobile,
    this.tablet,
    this.desktop,
  });
  
  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        if (constraints.maxWidth >= 1024) {
          return desktop ?? tablet ?? mobile;
        } else if (constraints.maxWidth >= 768) {
          return tablet ?? mobile;
        } else {
          return mobile;
        }
      },
    );
  }
}

// Usage
ResponsiveLayout(
  mobile: MobileLayout(),
  tablet: TabletLayout(),
  desktop: DesktopLayout(),
)

Icons:

// Use Material Icons (built-in)
Icon(Icons.home)
Icon(Icons.settings)

// Or use Cupertino Icons
Icon(CupertinoIcons.house)
Icon(CupertinoIcons.settings)

// For custom icons, use flutter_svg
import 'package:flutter_svg/flutter_svg.dart';
SvgPicture.asset('assets/icons/custom_icon.svg')

Images:

// Network images with error handling
Image.network(
  'https://images.unsplash.com/photo-xxx?w=800&h=600',
  errorBuilder: (context, error, stackTrace) {
    return Icon(Icons.error);
  },
  loadingBuilder: (context, child, loadingProgress) {
    if (loadingProgress == null) return child;
    return CircularProgressIndicator();
  },
)

// Asset images
Image.asset('assets/images/placeholder.png')

// Use cached_network_image for better performance
CachedNetworkImage(
  imageUrl: 'https://images.unsplash.com/photo-xxx?w=800&h=600',
  placeholder: (context, url) => CircularProgressIndicator(),
  errorWidget: (context, url, error) => Icon(Icons.error),
)

6. Accessibility

// Semantic labels for screen readers
Semantics(
  label: 'Submit button',
  button: true,
  enabled: true,
  child: ElevatedButton(...),
)

// Minimum touch target (44x44)
MaterialButton(
  minWidth: 44,
  height: 44,
  child: Text('Button'),
  onPressed: () {},
)

// Sufficient color contrast (4.5:1 minimum)
// Use tools like https://webaim.org/resources/contrastchecker/

7. Component Design Tips

Cards:

Card(
  elevation: 2,  // Subtle shadow
  shape: RoundedRectangleBorder(
    borderRadius: BorderRadius.circular(12.0),
  ),
  child: Padding(
    padding: EdgeInsets.all(16.0),  // p-4
    child: Column(...),
  ),
)

// Hover effect (web only)
InkWell(
  onTap: () {},
  hoverColor: Colors.transparent,
  splashColor: Colors.transparent,
  child: MouseRegion(
    cursor: SystemMouseCursors.click,
    child: AnimatedContainer(
      duration: Duration(milliseconds: 200),
      transform: Matrix4.identity()..translate(0.0, _isHovered ? -2.0 : 0.0),
      decoration: BoxDecoration(
        boxShadow: [
          BoxShadow(
            color: Colors.black.withOpacity(_isHovered ? 0.15 : 0.1),
            blurRadius: _isHovered ? 12 : 8,
            offset: Offset(0, _isHovered ? 4 : 2),
          ),
        ],
      ),
      child: Card(...),
    ),
  ),
)

Buttons:

// Primary button
ElevatedButton(
  style: ElevatedButton.styleFrom(
    minimumSize: Size(44, 44),  // Touch target
    padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12),
  ),
  onPressed: () {},
  child: Text('Primary'),
)

// Secondary button
OutlinedButton(
  style: OutlinedButton.styleFrom(
    minimumSize: Size(44, 44),
  ),
  onPressed: () {},
  child: Text('Secondary'),
)

// Ghost button
TextButton(
  style: TextButton.styleFrom(
    minimumSize: Size(44, 44),
  ),
  onPressed: () {},
  child: Text('Ghost'),
)

Forms:

TextFormField(
  decoration: InputDecoration(
    labelText: 'Email',  // Clear label above input
    border: OutlineInputBorder(),
    focusedBorder: OutlineInputBorder(
      borderSide: BorderSide(color: Theme.of(context).colorScheme.primary, width: 2),
    ),
    errorBorder: OutlineInputBorder(
      borderSide: BorderSide(color: Theme.of(context).colorScheme.error),
    ),
  ),
  validator: (value) {
    if (value == null || value.isEmpty) {
      return 'Please enter email';
    }
    return null;
  },
)

// Spacing between fields
SizedBox(height: 16.0)

Navigation:

// Bottom navigation bar
BottomNavigationBar(
  currentIndex: _currentIndex,
  onTap: (index) => setState(() => _currentIndex = index),
  items: [
    BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
    BottomNavigationBarItem(icon: Icon(Icons.settings), label: 'Settings'),
  ],
)

// Navigation Rail (for tablets)
NavigationRail(
  selectedIndex: _selectedIndex,
  onDestinationSelected: (index) => setState(() => _selectedIndex = index),
  destinations: [
    NavigationRailDestination(icon: Icon(Icons.home), label: Text('Home')),
    NavigationRailDestination(icon: Icon(Icons.settings), label: Text('Settings')),
  ],
)

8. Flutter-Specific Best Practices

Performance:

// Use const constructors when possible
const Text('Static text')
const SizedBox(height: 16)

// Use ListView.builder for long lists
ListView.builder(
  itemCount: items.length,
  itemBuilder: (context, index) {
    return ListTile(title: Text(items[index]));
  },
)

// Avoid rebuilding widgets with const
const MyStaticWidget()

State Management:

// Simple state with StatefulWidget
setState(() {
  _counter++;
});

// For complex state, use provider, riverpod, or bloc
// Example with provider:
Consumer<MyModel>(
  builder: (context, model, child) {
    return Text('${model.value}');
  },
)

Quick Reference

Element Flutter Recommendation
Primary font Inter, Outfit, DM Sans (google_fonts)
Code font JetBrains Mono, Fira Code (google_fonts)
Border radius 8.0 - 16.0 (modern), 0 (brutalist)
Shadow BoxShadow with elevation 2-4
Spacing 4.0 base unit (Spacing class)
Animation 150-400ms, Curves.easeOut
Colors ColorScheme.fromSeed (Material 3)
Touch targets Minimum 44x44

Based on SuperDesign patterns — https://superdesign.dev
Adapted for Flutter/Dart — Material Design 3