skeletonizer_plus 1.2.0 copy "skeletonizer_plus: ^1.2.0" to clipboard
skeletonizer_plus: ^1.2.0 copied to clipboard

Skeleton loading states for Flutter with shimmer animations, theme-aware colours, automatic tree analysis, and Sliver support.

example/lib/main.dart

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

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Skeletonizer Plus Example',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
        useMaterial3: true,
      ),
      darkTheme: ThemeData.dark(useMaterial3: true),
      themeMode: ThemeMode.system,
      home: const HomePage(),
    );
  }
}

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

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  bool _isLoading = true;
  int _selectedIndex = 0;

  @override
  void initState() {
    super.initState();
    Future.delayed(const Duration(seconds: 3), () {
      if (mounted) setState(() => _isLoading = false);
    });
  }

  void _toggleLoading() => setState(() => _isLoading = !_isLoading);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Skeletonizer Plus Example'),
        actions: [
          IconButton(
            icon: Icon(_isLoading ? Icons.pause : Icons.play_arrow),
            onPressed: _toggleLoading,
            tooltip: 'Toggle Loading',
          ),
        ],
      ),
      body: IndexedStack(
        index: _selectedIndex,
        children: [
          AutomaticSkeletonExample(isLoading: _isLoading),
          CustomBonesExample(isLoading: _isLoading),
          ListViewExample(isLoading: _isLoading),
          SliverExample(isLoading: _isLoading),
          NewFeaturesExample(isLoading: _isLoading),
        ],
      ),
      bottomNavigationBar: NavigationBar(
        selectedIndex: _selectedIndex,
        onDestinationSelected: (index) =>
            setState(() => _selectedIndex = index),
        destinations: const [
          NavigationDestination(icon: Icon(Icons.auto_awesome), label: 'Automatic'),
          NavigationDestination(icon: Icon(Icons.build), label: 'Custom'),
          NavigationDestination(icon: Icon(Icons.list), label: 'List'),
          NavigationDestination(icon: Icon(Icons.view_carousel), label: 'Sliver'),
          NavigationDestination(icon: Icon(Icons.new_releases), label: 'New'),
        ],
      ),
    );
  }
}

class AutomaticSkeletonExample extends StatelessWidget {
  const AutomaticSkeletonExample({required this.isLoading, super.key});

  final bool isLoading;

  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      padding: const EdgeInsets.all(16),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          const Text(
            'Automatic Skeleton Generation',
            style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
          ),
          const SizedBox(height: 16),
          SkeletonizerPlus(
            enabled: isLoading,
            child: const Card(
              child: Padding(
                padding: EdgeInsets.all(16),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Row(
                      children: [
                        CircleAvatar(radius: 24, child: Icon(Icons.person)),
                        SizedBox(width: 16),
                        Expanded(
                          child: Column(
                            crossAxisAlignment: CrossAxisAlignment.start,
                            children: [
                              Text(
                                'John Doe',
                                style: TextStyle(
                                  fontSize: 18,
                                  fontWeight: FontWeight.bold,
                                ),
                              ),
                              Text('Software Engineer'),
                            ],
                          ),
                        ),
                      ],
                    ),
                    SizedBox(height: 16),
                    Text(
                      'This is a sample card with multiple text elements and an '
                      'avatar. The skeletonizer detects each element and '
                      'generates matching bones.',
                    ),
                  ],
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

class CustomBonesExample extends StatelessWidget {
  const CustomBonesExample({required this.isLoading, super.key});

  final bool isLoading;

  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      padding: const EdgeInsets.all(16),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          const Text(
            'Custom Bone Layout',
            style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
          ),
          const SizedBox(height: 16),
          SkeletonizerPlus.custom(
            enabled: isLoading,
            bones: const [
              BoneCircle(radius: 30),
              BoneText(words: 4),
              BoneText(words: 6, lines: 2),
              BoneRect(width: 300, height: 200, radius: 12),
              BoneIcon(size: 32),
            ],
            baseColor: Colors.blue.shade200,
            highlightColor: Colors.blue.shade50,
            speed: const Duration(milliseconds: 800),
          ),
        ],
      ),
    );
  }
}

class ListViewExample extends StatelessWidget {
  const ListViewExample({required this.isLoading, super.key});

  final bool isLoading;

  @override
  Widget build(BuildContext context) {
    return SkeletonizerPlus(
      enabled: isLoading,
      child: ListView.builder(
        itemCount: 20,
        itemBuilder: (context, index) => Card(
          margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
          child: ListTile(
            leading: const CircleAvatar(child: Icon(Icons.person)),
            title: Text('Item ${index + 1}'),
            subtitle: Text('This is item number ${index + 1}'),
            trailing: const Icon(Icons.arrow_forward_ios),
          ),
        ),
      ),
    );
  }
}

class SliverExample extends StatelessWidget {
  const SliverExample({required this.isLoading, super.key});

  final bool isLoading;

  @override
  Widget build(BuildContext context) {
    return CustomScrollView(
      slivers: [
        const SliverAppBar(
          expandedHeight: 200,
          pinned: true,
          flexibleSpace: FlexibleSpaceBar(title: Text('Sliver Example')),
        ),
        SliverSkeletonizerPlus(
          enabled: isLoading,
          child: SliverList(
            delegate: SliverChildBuilderDelegate(
              (context, index) => Card(
                margin:
                    const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
                child: ListTile(
                  leading: const CircleAvatar(child: Icon(Icons.person)),
                  title: Text('Item ${index + 1}'),
                  subtitle: Text('This is item number ${index + 1}'),
                ),
              ),
              childCount: 20,
            ),
          ),
        ),
      ],
    );
  }
}

class NewFeaturesExample extends StatelessWidget {
  const NewFeaturesExample({required this.isLoading, super.key});

  final bool isLoading;

  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      padding: const EdgeInsets.all(16),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          const Text(
            'SkeletonIgnore — keep widgets visible',
            style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
          ),
          const SizedBox(height: 8),
          SkeletonizerPlus(
            enabled: isLoading,
            child: Card(
              child: Padding(
                padding: const EdgeInsets.all(16),
                child: Row(
                  children: [
                    const Icon(Icons.person, size: 40),
                    const SizedBox(width: 16),
                    Expanded(
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          const Text('Skeletonized Text'),
                          const SizedBox(height: 8),
                          SkeletonIgnore(
                            child: ElevatedButton(
                              onPressed: () {},
                              child: const Text('I am Ignored!'),
                            ),
                          ),
                        ],
                      ),
                    ),
                  ],
                ),
              ),
            ),
          ),
          const SizedBox(height: 24),
          const Text(
            'SkeletonUnite — collapse a subtree',
            style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
          ),
          const SizedBox(height: 8),
          SkeletonizerPlus(
            enabled: isLoading,
            child: const Card(
              child: Padding(
                padding: EdgeInsets.all(16),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text('Three icons united into one bone:'),
                    SizedBox(height: 8),
                    SkeletonUnite(
                      child: Row(
                        children: [
                          Icon(Icons.star),
                          SizedBox(width: 8),
                          Icon(Icons.star),
                          SizedBox(width: 8),
                          Icon(Icons.star),
                        ],
                      ),
                    ),
                  ],
                ),
              ),
            ),
          ),
          const SizedBox(height: 24),
          const Text(
            'Pulse Animation',
            style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
          ),
          const SizedBox(height: 8),
          SkeletonizerPlus.withConfig(
            enabled: isLoading,
            animationConfig: const AnimationConfig(type: AnimationType.pulse),
            child: const Card(
              child: ListTile(
                leading: Icon(Icons.favorite),
                title: Text('Pulsing Skeleton'),
                subtitle: Text('Opacity fade instead of shimmer.'),
              ),
            ),
          ),
        ],
      ),
    );
  }
}
4
likes
160
points
163
downloads

Documentation

API reference

Publisher

verified publishergodfreylebo.dev

Weekly Downloads

Skeleton loading states for Flutter with shimmer animations, theme-aware colours, automatic tree analysis, and Sliver support.

Repository (GitHub)
View/report issues

Topics

#skeleton #loading #shimmer #placeholder #ui

License

MIT (license)

Dependencies

flutter

More

Packages that depend on skeletonizer_plus