skeletonizer_plus 1.2.0
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.
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.