Skeletonizer Plus

pub package License: MIT CI

Skeleton loading states for Flutter — shimmer or pulse animations, theme-aware colours, automatic widget-tree analysis, and Sliver support.

Skeletonizer Plus Showcase

Wrap any widget with SkeletonizerPlus and you get a matching skeleton while data loads. Disable it and the original widget is rendered untouched.

Features

  • Automatic bone generation from common Flutter widgets (Text, Icon, Image, Card, ListTile, CircleAvatar, Container, Row/Column/Padding/Center/Expanded/Flexible/…).
  • Custom bone layouts when you need pixel-precise placeholders — BoneText, BoneRect, BoneCircle, BoneIcon.
  • Shimmer & pulse animations with configurable direction, speed, loops, and colours.
  • Theme-aware — base/highlight colours come from Theme.of(context) automatically.
  • Sliver support via SliverSkeletonizerPlus.
  • SkeletonIgnore to keep specific widgets visible/interactive inside a skeletonised area.
  • SkeletonUnite to collapse a subtree into a single bone.
  • optimizeForPerformance wraps the skeleton in a RepaintBoundary for heavy parent trees.

Install

dependencies:
  skeletonizer_plus: ^1.2.0
flutter pub get

Quick start

import 'package:skeletonizer_plus/skeletonizer_plus.dart';

SkeletonizerPlus(
  enabled: isLoading,
  child: ListView.builder(
    itemCount: items.length,
    itemBuilder: (context, i) => Card(
      child: ListTile(
        leading: const CircleAvatar(child: Icon(Icons.person)),
        title: Text(items[i].title),
        subtitle: Text(items[i].subtitle),
      ),
    ),
  ),
)

When isLoading is true, the widget's tree is analysed and replaced with matching skeleton bones. Flip it to false and the real widget takes over.

Custom bone layout

When automatic generation is too generic, hand-build the skeleton:

SkeletonizerPlus.custom(
  enabled: isLoading,
  bones: const [
    BoneCircle(radius: 30),
    BoneText(words: 4),
    BoneText(words: 6, lines: 2),
    BoneRect(width: 200, height: 100, radius: 12),
    BoneIcon(size: 32),
  ],
  baseColor: Colors.blue.shade200,
  highlightColor: Colors.blue.shade50,
  speed: const Duration(milliseconds: 800),
)

Bone types

Type What it renders
BoneText One or more lines of word-shaped rectangles. Supports fontSize / style for accurate sizing.
BoneRect A rectangle with an optional corner radius.
BoneCircle A circle. Diameter is radius * 2 or min(width, height).
BoneIcon A square with rounded corners.
BoneWidget Renders a real widget (used internally by SkeletonIgnore).

Animation configuration

For full control use SkeletonizerPlus.withConfig:

SkeletonizerPlus.withConfig(
  enabled: isLoading,
  animationConfig: const AnimationConfig(
    type: AnimationType.shimmer,        // or AnimationType.pulse
    direction: ShimmerDirection.ltr,    // ltr | rtl | topDown | bottomUp
    speed: Duration(milliseconds: 1200),
    loop: true,
  ),
  child: YourWidget(),
)

Run for a finite number of cycles:

const AnimationConfig(loop: false, loopCount: 3)

Keep widgets visible inside a skeleton

SkeletonIgnore opts a subtree out of skeletonisation — the wrapped widget renders normally and stays interactive:

SkeletonizerPlus(
  enabled: isLoading,
  child: Row(
    children: [
      const Text('Loading…'),
      SkeletonIgnore(
        child: ElevatedButton(
          onPressed: cancel,
          child: const Text('Cancel'),
        ),
      ),
    ],
  ),
)

Collapse a subtree into one bone

SkeletonUnite replaces the wrapped widget with a single BoneRect so a row of icons (or any cluster of small widgets) doesn't fragment into noise:

SkeletonUnite(
  child: Row(
    children: const [Icon(Icons.star), Icon(Icons.star), Icon(Icons.star)],
  ),
)

Sliver support

Use SliverSkeletonizerPlus inside CustomScrollView. The API mirrors SkeletonizerPlus exactly:

CustomScrollView(
  slivers: [
    SliverSkeletonizerPlus(
      enabled: isLoading,
      child: SliverList(
        delegate: SliverChildBuilderDelegate(
          (context, i) => ListTile(title: Text('Item $i')),
          childCount: items.length,
        ),
      ),
    ),
  ],
)

Theming

Colours default to Colors.grey[300] / Colors.grey[100] in light mode and Colors.grey[800] / Colors.grey[700] in dark mode. Override with baseColor and highlightColor, or supply a full theme: ThemeData(...).

Performance

  • Set optimizeForPerformance: true to wrap the skeleton in a RepaintBoundary.
  • Animation is driven by a single AnimationController per SkeletonizerPlus, not per bone.
  • Bones are painted as a ShaderMask rather than per-pixel — repaints are cheap.

Platform support

Platform Filters & transforms
Android Yes
iOS Yes
Web Yes
macOS Yes
Windows Yes
Linux Yes

API reference

See API_REFERENCE.md.

Author

Godfrey Lebo — Fullstack Developer & Technical PM

License

MIT — see LICENSE.

Libraries

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