skeletix 1.1.0 copy "skeletix: ^1.1.0" to clipboard
skeletix: ^1.1.0 copied to clipboard

A strictly declarative RenderObject automated skeleton generator. Intelligently scans layouts to draw precision placeholders without rigid boilerplate.

example/lib/main.dart

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

void main() {
  // Global configuration example - setting separate brand defaults for light and dark modes
  SkeletixTheme.configure(
    // Light Mode Defaults
    skeletonColor: const Color(0xFFF0F0F0),
    shimmerColor: Colors.white54,
    // Dark Mode Defaults
    darkSkeletonColor: const Color(0xFF242424),
    darkShimmerColor: Colors.white10,
  );

  runApp(const MyApp());
}

// ============================================================================
// DATA MODELS
// ============================================================================

class BannerAd {
  final String? imageUrl;
  BannerAd(this.imageUrl);
}

class Category {
  final String? title;
  final String? iconUrl;
  Category(this.title, this.iconUrl);
}

class Product {
  final String? title;
  final String? imageUrl;
  final String? price;
  Product(this.title, this.imageUrl, this.price);
}

class EcomData {
  final List<BannerAd> banners;
  final List<Category> categories;
  final List<Product> products;

  EcomData({
    required this.banners,
    required this.categories,
    required this.products,
  });
}

// ============================================================================
// APP ENTRY
// ============================================================================

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

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  ThemeMode _themeMode = ThemeMode.light;

  void _toggleTheme() {
    setState(() {
      _themeMode = _themeMode == ThemeMode.light
          ? ThemeMode.dark
          : ThemeMode.light;
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'SkeletiX E-commerce Demo',
      debugShowCheckedModeBanner: false,
      themeMode: _themeMode,
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      darkTheme: ThemeData(
        colorScheme: ColorScheme.fromSeed(
          seedColor: Colors.deepPurple,
          brightness: Brightness.dark,
        ),
        useMaterial3: true,
      ),
      home: EcomHomePage(onToggleTheme: _toggleTheme),
    );
  }
}

class EcomHomePage extends StatefulWidget {
  final VoidCallback onToggleTheme;
  const EcomHomePage({super.key, required this.onToggleTheme});

  @override
  State<EcomHomePage> createState() => _EcomHomePageState();
}

class _EcomHomePageState extends State<EcomHomePage> {
  bool _isLoading = true;
  String? _error;
  EcomData? _data;

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

  void _fetchApiData() async {
    setState(() {
      _isLoading = true;
      _error = null;
      _data = null;
    });

    await Future.delayed(const Duration(seconds: 4));

    if (mounted) {
      setState(() {
        _isLoading = false;
        _data = EcomData(
          banners: [
            BannerAd(
              'https://images.unsplash.com/photo-1607082348824-0a96f2a4b9da?auto=format&fit=crop&w=1000&q=80',
            ),
          ],
          categories: [
            Category('Clothing', 'https://picsum.photos/seed/clothing/150'),
            Category(
              'Electronics',
              'https://picsum.photos/seed/electronics/150',
            ),
            Category('Home', 'https://picsum.photos/seed/home/150'),
            Category('Beauty', 'https://picsum.photos/seed/beauty/150'),
          ],
          products: [
            Product(
              'Wireless Headphones',
              'https://picsum.photos/seed/headphone/400',
              '\$299.99',
            ),
            Product(
              'Smart Watch Series 8',
              'https://picsum.photos/seed/watch/400',
              '\$399.00',
            ),
            Product(
              'Minimalist Desk Lamp',
              'https://picsum.photos/seed/lamp/400',
              '\$45.50',
            ),
            Product(
              'Ergonomic Chair',
              'https://picsum.photos/seed/chair/400',
              '\$199.99',
            ),
          ],
        );
      });
    }
  }

  void _simulateError() async {
    setState(() {
      _isLoading = true;
      _error = null;
      _data = null;
    });

    await Future.delayed(const Duration(seconds: 2));

    if (mounted) {
      setState(() {
        _isLoading = false;
        _error = "Network Failed: Could not connect to the store servers.";
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    final isDark = Theme.of(context).brightness == Brightness.dark;

    return Scaffold(
      backgroundColor: isDark ? const Color(0xFF121212) : Colors.white,
      appBar: AppBar(
        title: const Text(
          'SkeletiX Store',
          style: TextStyle(fontWeight: FontWeight.bold),
        ),
        backgroundColor: isDark ? const Color(0xFF1E1E1E) : Colors.white,
        surfaceTintColor: Colors.transparent,
        elevation: 0,
        actions: [
          IconButton(
            icon: Icon(isDark ? Icons.light_mode : Icons.dark_mode),
            onPressed: widget.onToggleTheme,
            tooltip: "Toggle Theme",
          ),
          IconButton(
            icon: const Icon(Icons.refresh),
            onPressed: _fetchApiData,
            tooltip: "Retry Connection",
          ),
          IconButton(
            icon: const Icon(Icons.error_outline),
            onPressed: _simulateError,
            tooltip: "Fail Connection",
          ),
        ],
      ),
      body: SkeletiX(
        loading: _isLoading,
        error: _error,
        onRetry: () => _fetchApiData(),
        child: _buildEcomLayout(),
      ),
    );
  }

  /// ABSOLUTE PURE DECLARATIVE LAYOUT!
  /// Notice there is ZERO "_isLoading" logic anywhere inside this method.
  /// SkeletiX inherently understands how to structurally map our data gaps!
  Widget _buildEcomLayout() {
    final isDark = Theme.of(context).brightness == Brightness.dark;

    // 1. Array Fallbacks
    // When _data is null during loading, we temporarily yield arrays of nulls.
    final banners = _data?.banners ?? List.generate(1, (_) => BannerAd(null));
    final categories =
        _data?.categories ??
        List.generate(4, (_) => Category('Category', null));
    final products =
        _data?.products ??
        List.generate(
          4,
          (_) => Product('Loading Product Title...', null, '\$00.00'),
        );

    return ListView(
      padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
      children: [
        // --- PROMO BANNERS ---
        SizedBox(
          height: 160,
          child: PageView.builder(
            itemCount: banners.length,
            itemBuilder: (context, index) {
              final banner = banners[index];
              return ClipRRect(
                borderRadius: BorderRadius.circular(16),
                child: SkeletixImage.network(
                  banner.imageUrl,
                  fit: BoxFit.cover,
                  width: double.infinity,
                ),
              );
            },
          ),
        ),
        const SizedBox(height: 32),

        // --- CATEGORIES ---
        const Text(
          'Shop by Category',
          style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
        ),
        const SizedBox(height: 16),
        SizedBox(
          height: 100,
          child: ListView.builder(
            scrollDirection: Axis.horizontal,
            itemCount: categories.length,
            itemBuilder: (context, index) {
              final category = categories[index];
              return Padding(
                padding: const EdgeInsets.only(right: 20),
                child: Column(
                  children: [
                    CircleAvatar(
                      radius: 35,
                      backgroundColor: Colors.transparent,
                      backgroundImage: category.iconUrl != null
                          ? NetworkImage(category.iconUrl!)
                          : null,
                    ),
                    const SizedBox(height: 8),
                    Text(
                      category.title ?? '',
                      style: const TextStyle(fontWeight: FontWeight.w500),
                    ),
                  ],
                ),
              );
            },
          ),
        ),
        const SizedBox(height: 24),

        // --- TRENDING PRODUCTS ---
        const Text(
          'Trending Products',
          style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
        ),
        const SizedBox(height: 16),
        GridView.builder(
          shrinkWrap: true,
          physics: const NeverScrollableScrollPhysics(),
          gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
            crossAxisCount: 2,
            crossAxisSpacing: 16,
            mainAxisSpacing: 16,
            childAspectRatio: 0.72,
          ),
          itemCount: products.length,
          itemBuilder: (context, index) {
            final product = products[index];
            return Card(
              color: isDark ? const Color(0xFF1E1E1E) : const Color(0xFFF8F9FA),
              elevation: 0,
              shape: RoundedRectangleBorder(
                borderRadius: BorderRadius.circular(16),
              ),
              child: Padding(
                padding: const EdgeInsets.all(12),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    // Safe Image Container
                    Expanded(
                      child: ClipRRect(
                        borderRadius: BorderRadius.circular(12),
                        child: SkeletixImage.network(
                          product.imageUrl,
                          width: double.infinity,
                          fit: BoxFit.cover,
                        ),
                      ),
                    ),
                    const SizedBox(height: 12),
                    // Safe Texts
                    Text(
                      product.title ?? '',
                      style: const TextStyle(
                        fontWeight: FontWeight.w600,
                        fontSize: 13,
                      ),
                      maxLines: 2,
                    ),
                    const SizedBox(height: 4),
                    Text(
                      product.price ?? '',
                      style: TextStyle(
                        color: isDark
                            ? Colors.deepPurpleAccent
                            : Colors.deepPurple,
                        fontWeight: FontWeight.w900,
                        fontSize: 16,
                      ),
                    ),
                    const SizedBox(height: 12),
                    // Framework buttons are inherently structurally analyzed!
                    SizedBox(
                      width: double.infinity,
                      height: 36,
                      child: ElevatedButton(
                        style: ElevatedButton.styleFrom(
                          backgroundColor: Colors.deepPurple,
                          foregroundColor: Colors.white,
                          shape: RoundedRectangleBorder(
                            borderRadius: BorderRadius.circular(8),
                          ),
                        ),
                        onPressed: () {},
                        child: const Text('Add to Cart'),
                      ),
                    ),
                  ],
                ),
              ),
            );
          },
        ),
        const SizedBox(height: 32),
      ],
    );
  }
}
7
likes
160
points
138
downloads

Documentation

API reference

Publisher

verified publishermanishdevan.com

Weekly Downloads

A strictly declarative RenderObject automated skeleton generator. Intelligently scans layouts to draw precision placeholders without rigid boilerplate.

Repository (GitHub)
View/report issues

Topics

#skeleton #loading #shimmer #ui #animation

License

Apache-2.0 (license)

Dependencies

flutter

More

Packages that depend on skeletix