flywind 0.1.1
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, andColorobjects 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:
FlyTextandFlyContainerwith Tailwind-like method chaining - Theme Integration: Seamless integration with Flutter's
ThemeDataextensions viaFlyTheme - 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, andFlyRadiusToken - 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')= 0pxrounded('sm')= 2px (0.125rem)rounded('')= 4px (0.25rem) - defaultrounded('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 #
- Clone the repository
- Run the example app:
cd example flutter pub get flutter run - Explore the code in
example/main.dart - 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 #
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass
- Submit a pull request
π License #
This project is licensed under the MIT License.
Flywind - Bringing Tailwind's utility-first approach to Flutter development. π¨β¨