flywind 0.1.1 copy "flywind: ^0.1.1" to clipboard
flywind: ^0.1.1 copied to clipboard

A Tailwind-like utility-first Flutter component library that brings the power and flexibility of Tailwind CSS to Flutter development.

Flywind #

A Tailwind-like utility-first Flutter component library that brings the power and flexibility of Tailwind CSS to Flutter development.

πŸš€ Features #

  • Flexible Value System: Accepts int, double, String, and Color objects for maximum flexibility
  • Multiple Input Types: Support for numeric values, token names, CSS units, and direct theme access
  • String-Based Unit System: Flexible CSS-like units supporting px, rem, em, %, and plain numbers
  • Utility-First Components: FlyText and FlyContainer with Tailwind-like method chaining
  • Theme Integration: Seamless integration with Flutter's ThemeData extensions via FlyTheme
  • Type-Safe Access: Full autocomplete support with direct property access (colors.blue500)
  • Robust Error Handling: Comprehensive validation with helpful error messages and graceful fallbacks
  • Flexible API: Both dot notation (colors.blue600) and bracket notation (colors['blue600'])
  • Design Token System: Centralized typed tokens with FlySpacingToken, FlyColorToken, and FlyRadiusToken
  • Method Chaining: Fluent API for clean, readable code composition
  • CSS Unit Support: Handles px, em, rem, and % units with proper validation

πŸ“¦ Components #

FlyText #

A text widget with Tailwind-like utilities for styling:

FlyText('Hello World')
  .p('16')        // padding: 16px
  .px('24')       // horizontal padding: 24px
  .m('8')         // margin: 8px
  .color('blue600') // text color
  .rounded('lg')   // border radius: 8px

FlyContainer #

A container widget with background color, padding, and margin utilities:

FlyContainer(
  child: FlyText('Content'),
)
  .bg('red600')   // background color
  .p('16')        // padding: 16px
  .m('8')         // margin: 8px
  .rounded('xl')  // border radius: 12px

🎨 Design System #

Syntax & Supported Value Types #

Flywind supports multiple input types for maximum flexibility:

Colors

// Color objects
FlyText('Hello').color(Colors.blue)
FlyText('Hello').bg(Colors.red.shade100)

// Token names (from FlyColorToken)
FlyText('Hello').color('blue500')
FlyText('Hello').bg('red100')

// Hex colors
FlyText('Hello').color('#FF5733')
FlyText('Hello').bg('#00FF00')

// Direct theme access
final colors = FlyTheme.of(context).colors;
FlyText('Hello').color(colors.blue500)  // Type-safe access

Spacing (Padding & Margin)

// Numeric values
FlyText('Hello').p(16)        // int
FlyText('Hello').px(16.0)     // double
FlyText('Hello').my(8)        // int
FlyText('Hello').mt(12.5)     // double

// Token names (from FlySpacingToken)
FlyText('Hello').p('s3')    // Uses spacing token
FlyText('Hello').mx('px')  // Uses spacing token

// CSS units
FlyText('Hello').p('16px')    // pixels
FlyText('Hello').px('1.5em')  // em units (1.5 * 16px = 24px)
FlyText('Hello').py('2rem')   // rem units (2 * 16px = 32px)
FlyText('Hello').m('50%')     // percentage (0.5 as decimal)

// Direct theme access
final spacing = FlyTheme.of(context).spacing;
FlyText('Hello').p(spacing.s3)           // Type-safe access
FlyText('Hello').m(spacing['s3'] ?? 16)  // Bracket access with fallback

Border Radius

// Numeric values
FlyText('Hello').rounded(8)     // int
FlyText('Hello').rounded(8.0)   // double
FlyText('Hello').roundedT(4)    // int
FlyText('Hello').roundedL(12.5) // double

// Token names (from FlyRadiusToken)
FlyText('Hello').rounded('md')  // Uses radius token
FlyText('Hello').roundedT('sm') // Uses radius token
FlyText('Hello').roundedTl('lg') // Uses radius token

// CSS units
FlyText('Hello').rounded('8px') // pixels
FlyText('Hello').roundedT('0.5em') // em units
FlyText('Hello').roundedL('1rem')  // rem units

// Direct theme access
final radius = FlyTheme.of(context).radius;
FlyText('Hello').rounded(radius.md)  // Type-safe access

Combined Examples

// Complex styling with mixed types
FlyText('Hello World')
  .color('blue500')           // Token name
  .p(16)                      // int
  .mx('large')                // Token name
  .rounded('md')              // Token name
  .bg('#F0F0F0');             // Hex color

// Container with mixed values
FlyContainer(
  child: FlyText('Content'),
)
  .bg(Colors.blue.shade50)    // Color object
  .p('20px')                  // CSS unit
  .m(8)                       // int
  .rounded('xl');             // Token name

// Using direct theme access
final theme = FlyTheme.of(context);
FlyText('Token-based')
  .p(theme.spacing.s4)        // Direct token access
  .color(theme.colors.blue500) // Direct token access
  .rounded(theme.radius.md);   // Direct token access

String-Based Unit System #

Flywind uses a flexible string-based unit system that supports multiple CSS-like formats:

// Plain numbers (assumed pixels)
FlyText('Hello').p('16')        // 16px

// Explicit pixel values
FlyText('Hello').p('16px')      // 16px

// Rem values (16px base)
FlyText('Hello').p('1rem')      // 16px
FlyText('Hello').p('1.5rem')    // 24px

// Em values (16px base)
FlyText('Hello').p('1em')       // 16px
FlyText('Hello').p('0.5em')     // 8px

// Percentage values (returned as decimal)
FlyText('Hello').p('50%')       // 0.5 (50%)

// Mixed unit types
FlyText('Hello')
  .pl('1rem')    // 16px left
  .pr('10px')    // 10px right
  .pt('0.5em')   // 8px top
  .pb('10')      // 10px bottom

Spacing Scale #

Flexible string-based unit system supporting multiple formats:

  • Plain numbers: p('16') / m('8') = 16px / 8px (assumed pixels)
  • Pixel values: p('16px') / m('8px') = 16px / 8px
  • Rem values: p('1rem') / m('0.5rem') = 16px / 8px (16px base)
  • Em values: p('1em') / m('0.5em') = 16px / 8px (16px base)
  • Percentage values: p('50%') / m('25%') = 50% / 25% (returned as decimal)

Examples:

FlyText('Hello')
  .p('20')        // 20px
  .px('1.5rem')   // 24px (1.5 * 16)
  .py('0.5em')    // 8px (0.5 * 16)
  .m('10%')       // 10% margin

Color Palette #

Comprehensive color system with semantic naming:

  • Grays: gray50, gray100, gray200, ..., gray800
  • Primary Colors: blue600, red600, green600, yellow600
  • Extended Colors: purple600, teal600, orange600
  • Basic Colors: white, black

Border Radius Scale #

Matching Tailwind CSS border radius system:

  • rounded('none') = 0px
  • rounded('sm') = 2px (0.125rem)
  • rounded('') = 4px (0.25rem) - default
  • rounded('md') = 6px (0.375rem)
  • rounded('lg') = 8px (0.5rem)
  • rounded('xl') = 12px (0.75rem)
  • rounded('2xl') = 16px (1rem)
  • rounded('3xl') = 24px (1.5rem)
  • rounded('full') = 9999px (circular)

Border Radius Directions #

  • Uniform: rounded('lg') - all corners
  • Directional: roundedT('xl'), roundedR('md'), roundedB('lg'), roundedL('sm')
  • Individual: roundedTl('2xl'), roundedTr('lg'), roundedBl('md'), roundedBr('xl')

πŸ›‘οΈ Error Handling & Validation #

Flywind provides comprehensive error handling with helpful messages:

Input Validation #

// ❌ These will throw helpful errors:

// Invalid hex colors
FlyText('Hello').color('#GGGGGG')  // "Invalid hex color: contains non-hex characters"
FlyText('Hello').color('#12345')   // "Invalid hex color: must be 6 or 8 characters"

// Negative values
FlyText('Hello').p(-10)            // "Value resolved to negative number. Must be non-negative."

// Invalid percentages
FlyText('Hello').p('150%')         // "Invalid percentage: must be between 0% and 100%"

// Invalid units
FlyText('Hello').p('10xyz')        // "Invalid unit: expected format like '10px', '1.5em'"

// Invalid token names
FlyText('Hello').color('invalid')  // "Cannot resolve color value: expected Color object, hex string, or token name. Available tokens: red50, blue100, ..."

Graceful Fallbacks #

// Safe bracket access with fallbacks
FlyText('Hello').m(spacing['s3'] ?? 16)  // Falls back to 16 if token doesn't exist

// Theme access with error handling
try {
  final colors = FlyTheme.of(context).colors;
  FlyText('Hello').color(colors.blue500);
} catch (e) {
  // Graceful fallback
  FlyText('Hello').color(Colors.blue);
}

πŸ› οΈ Usage #

1. Setup your app with FlyTheme #

Option A: Use Default Theme

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
        useMaterial3: true,
        extensions: [
          FlyTheme.defaultTheme, // Uses default colors and spacing
        ],
      ),
      home: const HomePage(),
    );
  }
}

Option B: Use Custom Theme

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
        useMaterial3: true,
        extensions: [
          FlyConfig.createTheme(), // Creates theme with custom colors/spacing
        ],
      ),
      home: const HomePage(),
    );
  }
}

2. Access theme values with context.flywind #

class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    final flywind = context.flywind; // Access Flywind theme
    final spacing = flywind.spacing;
    final colors = flywind.colors;
    final borderRadius = flywind.borderRadius;
    
    return Scaffold(
      body: Column(
        children: [
          // Use utility components with method chaining
          FlyText('Welcome to Flywind')
            .p('16')
            .m('8')
            .color('blue600')
            .rounded('lg'),
          
          // Access theme values directly
          ElevatedButton(
            style: ElevatedButton.styleFrom(
              backgroundColor: colors.blue200, // Custom color with autocomplete
              padding: EdgeInsets.all(spacing.s4), // Type-safe spacing
            ),
            onPressed: () {},
            child: Text('Custom Button'),
          ),
          
          // Container with background, padding, margin, and border radius
          FlyContainer(
            child: FlyText('Card Content').color('white'),
          )
            .bg('gray800')
            .p('24')
            .px('32')
            .m('12')
            .mx('16')
            .rounded('xl'),
        ],
      ),
    );
  }
}

πŸ§ͺ Testing #

All Tests Passing - 80+ comprehensive tests covering all components and utilities.

Test Coverage #

  • FlyUnitParser: Parses string-based units (px, rem, em, %, plain numbers) with comprehensive error handling
  • FlyPadding Utility: Resolves uniform and directional padding, handles mixed combinations with string units
  • FlyMargin Utility: Resolves uniform and directional margin, handles mixed combinations with string units
  • FlyColor Utility: Resolves colors from theme, applies to TextStyle and Container
  • FlyRounded Utility: Resolves uniform, directional, and individual corner border radius
  • FlyStyle Class: copyWith, hasPadding/Margin/BorderRadius properties, apply method
  • FlyText Widget: Renders, applies padding/color, handles method chaining with string units
  • FlyContainer Widget: Renders, applies background/padding, handles method chaining with string units

Running Tests #

# Run all tests
flutter test

# Run with coverage
flutter test --coverage

🎯 Key Concepts #

Custom Configuration #

Define your custom colors and spacing in FlyConfig:

class FlyConfig {
  static const Map<String, Color> customColors = {
    'blue200': Color(0xFF8B5CF6),     // Purple
    'brand': Color(0xFF10B981),    // Green
    'accent': Color(0xFFF59E0B),   // Orange
  };

  static const Map<String, double> customSpacing = {
    'blue200': 64.0,
    'brand': 80.0,
    'large': 96.0,
  };

  static FlyTheme createTheme() {
    return FlyTheme.withCustom(
      customColors: customColors,
      customSpacing: customSpacing,
    );
  }
}

Type-Safe Access #

Access theme values with full autocomplete support:

final flywind = context.flywind;
final spacing = flywind.spacing;
final colors = flywind.colors;

// Type-safe dot notation
spacing.s4        // 16.0
colors.blue600    // Color(0xFF2563EB)
colors.blue200       // Custom color with autocomplete

// Bracket notation still works
spacing[4]        // 16.0
colors['blue600'] // Color(0xFF2563EB)

Fluent API #

Method chaining allows for clean, readable code:

FlyText('Hello')
  .p('12')        // Returns FlyText
  .px('16')       // Returns FlyText
  .m('8')         // Returns FlyText
  .color('blue')  // Returns FlyText
  .rounded('lg')  // Returns FlyText

Padding vs Margin #

Understanding the difference between padding and margin:

  • Padding: Space inside the element (background color extends into padding area)
  • Margin: Space outside the element (background color does not extend into margin area)
FlyContainer(
  child: FlyText('Content'),
)
  .bg('blue600')  // Background color
  .p('16')        // Padding: background extends here
  .m('8')         // Margin: background does NOT extend here
  .rounded('lg')  // Border radius: applied to background

Utility-First Approach #

Instead of creating custom styles, use utility classes:

// Instead of custom styling
Container(
  padding: EdgeInsets.all(16),
  margin: EdgeInsets.all(8),
  decoration: BoxDecoration(
    color: Colors.blue,
    borderRadius: BorderRadius.circular(8),
  ),
  child: Text('Hello'),
)

// Use utilities
FlyContainer(
  child: FlyText('Hello'),
)
  .p('16')        // padding: 16px
  .m('8')         // margin: 8px
  .bg('blue600')  // background color
  .rounded('lg')  // border radius: 8px

Theme Integration #

Flywind integrates seamlessly with Flutter's theme system:

// Access theme values in any widget
Widget build(BuildContext context) {
  final flywind = context.flywind;
  
  return Container(
    padding: EdgeInsets.all(flywind.spacing.s4),
    decoration: BoxDecoration(
      color: flywind.colors.blue200,
      borderRadius: BorderRadius.circular(flywind.borderRadius.lg),
    ),
    child: Text('Styled with theme'),
  );
}

πŸš€ Getting Started #

Quick Setup #

# 1. Add to your Flutter project
flutter pub add flywind

# 2. Activate CLI globally (one-time setup)
dart pub global activate flywind

# 3. Initialize FlyWind in your project
fly init

# 4. Generate design tokens
fly generate

Project Structure #

This repository contains:

  • Library code: The main Flywind package in the root directory
  • Example app: A complete Flutter app demonstrating Flywind usage in the example/ directory

Running the Example #

You can run the example app to see Flywind in action:

# Option 1: Use the provided script
./run_example.sh

# Option 2: Manual steps
cd example
flutter pub get
flutter run

Development Setup #

  1. Clone the repository
  2. Run the example app:
    cd example
    flutter pub get
    flutter run
    
  3. Explore the code in example/main.dart
  4. Run the tests:
    flutter test
    

πŸ”§ Troubleshooting #

Build Issues and Stale Files #

If you encounter warnings about "stale files located outside of the allowed root paths" or other build-related issues, try these cleanup steps:

# Clean all build artifacts and cache
flutter clean

# Reinstall dependencies
flutter pub get

# Verify your Flutter environment
flutter doctor

This will remove all build artifacts, clear the cache, and reinstall dependencies, resolving most build-related issues.

🀝 Contributing #

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality
  4. Ensure all tests pass
  5. Submit a pull request

πŸ“„ License #

This project is licensed under the MIT License.


Flywind - Bringing Tailwind's utility-first approach to Flutter development. 🎨✨

1
likes
140
points
112
downloads

Publisher

verified publisherflyui.xyz

Weekly Downloads

A Tailwind-like utility-first Flutter component library that brings the power and flexibility of Tailwind CSS to Flutter development.

Repository (GitHub)
View/report issues

Topics

#flutter #tailwind #utility-first #ui-components #design-system

Documentation

Documentation
API reference

License

MIT (license)

Dependencies

awesome_flutter_extensions, build, dotted_border, flutter, flycolor, path, source_gen, yaml

More

Packages that depend on flywind