๐ŸŽฏ UI Skeleton Factory

A powerful Flutter package that automatically generates Skeleton Loading UI from any existing Flutter Widget without manually writing skeleton layouts.

pub package License: MIT

โœจ Features

  • ๐ŸŽฏ Automatic Generation: Convert any widget to skeleton with zero configuration
  • ๐ŸŽจ Layout Preservation: Maintains original widget sizes and structure
  • โœจ Shimmer Animation: Optional smooth shimmer effect for visual feedback
  • โšก Performance: Optimized for smooth scrolling and minimal overhead
  • ๐Ÿ”ง Flexible API: Multiple ways to use (widget, static, extension)
  • ๐ŸŽญ Smart Mapping: Intelligently maps widgets to appropriate skeleton representations
  • ๐Ÿ“ฆ Lightweight: No heavy dependencies, pure Flutter implementation

๐Ÿš€ Quick Start

Installation

Add to your pubspec.yaml:

dependencies:
  ui_skeleton_factory: ^0.0.1

Then run:

flutter pub get

Basic Usage

There are three ways to use UI Skeleton Factory:

Method 1: SkeletonFactory Widget

SkeletonFactory(
  isLoading: true,
  child: ProductCard(product: product),
)

Method 2: Skeleton.from() Static Method

Skeleton.from(
  ProductCard(product: fakeProduct),
)

Method 3: Extension API

ProductCard(product: product).skeleton(isLoading: true)

๐Ÿ“– Comprehensive Examples

Example 1: Product Card with Skeleton

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

class ProductScreen extends StatefulWidget {
  @override
  State<ProductScreen> createState() => _ProductScreenState();
}

class _ProductScreenState extends State<ProductScreen> {
  bool _isLoading = true;
  Product? _product;

  @override
  void initState() {
    super.initState();
    _loadProduct();
  }

  Future<void> _loadProduct() async {
    // Simulate API call
    await Future.delayed(Duration(seconds: 2));
    setState(() {
      _product = Product(/* ... */);
      _isLoading = false;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Product Details')),
      body: SkeletonFactory(
        isLoading: _isLoading,
        child: ProductCard(product: _product ?? Product.placeholder()),
      ),
    );
  }
}

Example 2: ListView with Skeleton Items

ListView.builder(
  itemCount: isLoading ? 10 : items.length,
  itemBuilder: (context, index) {
    if (isLoading) {
      return Skeleton.from(
        UserListTile(user: User.placeholder()),
      );
    }
    return UserListTile(user: items[index]);
  },
)

Example 3: Using Extension API

Column(
  crossAxisAlignment: CrossAxisAlignment.start,
  children: [
    Text('Title').skeleton(isLoading: isLoading),
    SizedBox(height: 8),
    Text('Subtitle').skeleton(isLoading: isLoading),
    SizedBox(height: 16),
    ElevatedButton(
      onPressed: () {},
      child: Text('Action'),
    ).skeleton(isLoading: isLoading),
  ],
)

Example 4: Conditional Skeleton

// Using skeletonIf for cleaner conditional logic
Text('Hello World').skeletonIf(isLoading)

// Equivalent to:
// isLoading ? Skeleton.from(Text('Hello World')) : Text('Hello World')

โš™๏ธ Configuration

Custom Colors and Appearance

SkeletonFactory(
  isLoading: true,
  config: SkeletonConfig(
    shimmer: true,
    baseColor: Colors.grey[300],
    highlightColor: Colors.grey[100],
    shimmerDuration: Duration(milliseconds: 1500),
    borderRadius: 8.0,
  ),
  child: MyWidget(),
)

Performance Mode (No Shimmer)

For better performance in lists or complex layouts:

// Using predefined performance config
Skeleton.fast(MyWidget())

// Or custom config without shimmer
SkeletonFactory(
  isLoading: true,
  config: SkeletonConfig(shimmer: false),
  child: MyWidget(),
)

Global Configuration

You can use SkeletonConfig.defaultConfig or SkeletonConfig.performanceConfig:

// Default with shimmer
Skeleton.auto(MyWidget())

// Performance mode without shimmer
Skeleton.fast(MyWidget())

๐ŸŽจ Supported Widgets

UI Skeleton Factory intelligently maps Flutter widgets to skeleton representations:

Original Widget Skeleton Representation
Text Rectangular bar with font-based height
Image / Image.network Solid container box
Icon Circle
CircleAvatar Circle matching avatar size
ElevatedButton / TextButton Rounded rectangle
FloatingActionButton Circle
Card Container with rounded corners
ListTile Complete tile layout with leading/title/subtitle
Checkbox / Radio / Switch Small box/circle
Row / Column Preserves layout and spacing
Stack Maintains positioning
Padding / Center / Align Preserves layout constraints
Expanded / Flexible Maintains flex properties

Layout Preservation

The package automatically preserves:

  • โœ… Widget dimensions (width & height)
  • โœ… Padding and margins
  • โœ… Alignment and positioning
  • โœ… Flex properties (Expanded, Flexible)
  • โœ… Row/Column structure and spacing
  • โœ… Stack positioning

๐Ÿ› ๏ธ Advanced Usage

Custom Skeleton Widget

If you want complete control, provide a custom skeleton:

SkeletonFactory(
  isLoading: true,
  skeleton: MyCustomSkeletonWidget(),
  child: MyActualWidget(),
)

Using Individual Skeleton Components

import 'package:ui_skeleton_factory/ui_skeleton_factory.dart';

// Skeleton line (for text)
SkeletonLine(
  config: SkeletonConfig(),
  width: 150,
  height: 16,
)

// Skeleton avatar (circular)
SkeletonAvatar(
  config: SkeletonConfig(),
  size: 40,
)

// Skeleton box (for images)
SkeletonBox(
  config: SkeletonConfig(),
  aspectRatio: 16 / 9,
)

// Generic skeleton container
SkeletonContainer(
  width: 100,
  height: 50,
  config: SkeletonConfig(),
  borderRadius: 8,
  isCircular: false,
)

Extension Methods for Specific Widgets

// Text to skeleton line
Text('Title').toSkeletonLine(widthFactor: 0.8)

// Icon to skeleton circle
Icon(Icons.star).toSkeletonCircle()

// Image to skeleton box
Image.network('url').toSkeletonBox(aspectRatio: 16/9)

// CircleAvatar to skeleton avatar
CircleAvatar(radius: 20).toSkeletonAvatar()

๐Ÿ“ Architecture

The package follows a clean, modular architecture:

lib/
 โ”œโ”€ core/
 โ”‚   โ”œโ”€ skeleton_parser.dart      // Traverses widget tree
 โ”‚   โ”œโ”€ widget_mapper.dart        // Maps widgets โ†’ skeletons
 โ”‚   โ”œโ”€ size_resolver.dart        // Preserves layout sizes
 โ”‚   โ””โ”€ skeleton_context.dart     // Configuration
 โ”œโ”€ widgets/
 โ”‚   โ”œโ”€ skeleton_container.dart   // Base skeleton components
 โ”‚   โ”œโ”€ shimmer_effect.dart       // Shimmer animation
 โ”‚   โ”œโ”€ skeleton_wrapper.dart     // Loading wrapper logic
 โ”‚   โ”œโ”€ skeleton_factory.dart     // Main widget API
 โ”‚   โ””โ”€ skeleton.dart             // Static API
 โ””โ”€ extensions/
     โ””โ”€ skeleton_extension.dart   // Extension methods

๐ŸŽฏ Design Principles

  1. Zero Configuration: Works out of the box with sensible defaults
  2. Layout Preservation: Skeletons match original widget dimensions
  3. Performance First: Optimized for lists and complex layouts
  4. Flexible API: Multiple usage patterns for different preferences
  5. Type Safety: Full Dart type safety and null safety support

๐Ÿงช Testing

The package includes comprehensive tests covering:

  • Widget mapping logic
  • Layout preservation
  • Configuration handling
  • Extension methods
  • Edge cases and error handling

Run tests with:

flutter test

๐Ÿ“ฑ Example App

The package includes a full example app demonstrating:

  1. Basic Demo: Simple widgets with skeleton
  2. Product Card: Real-world e-commerce card
  3. ListView Demo: Scrollable list with skeleton items
  4. Extension API: Using extension methods
  5. Custom Config: Custom colors and settings

Run the example:

cd example
flutter run

๐Ÿค Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

๐Ÿ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

๐ŸŒŸ Show Your Support

If you find this package useful, please give it a โญ๏ธ on GitHub!

๐Ÿ“ฎ Feedback

For bugs, questions, and feature requests, please create an issue on GitHub.


Made with โค๏ธ by Flutter developers, for Flutter developers