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

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. SkeletonIgnoreto keep specific widgets visible/interactive inside a skeletonised area.SkeletonUniteto collapse a subtree into a single bone.optimizeForPerformancewraps the skeleton in aRepaintBoundaryfor 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: trueto wrap the skeleton in aRepaintBoundary. - Animation is driven by a single
AnimationControllerperSkeletonizerPlus, not per bone. - Bones are painted as a
ShaderMaskrather 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
- Email: emorylebo@gmail.com
- LinkedIn: godfreylebo
- Portfolio: godfreylebo.dev
- GitHub: @emorilebo
License
MIT — see LICENSE.
Libraries
- skeletonizer_plus
- Skeleton loading states for Flutter with shimmer animations, theme-aware colours, automatic tree analysis, and Sliver support.