Tired of skeleton loaders that flash gray then pop into the real colors?
This package keeps it small: color-matched Flutter skeleton widgets that match the color of the content they replace, not just its shape.
Skeleton— an ambient scope that marks a subtree as loading.SkeletonText— wraps aText; bone color follows the text's own style color. Pass apreviewlistenable to reflow the bone's width as partial text arrives, before the real content is ready.SkeletonImage— drop-in forImage; bone color is sampled from the image's average pixel color, and bone size matches the image's own decoded dimensions whenwidth/heightaren't given.SkeletonBox— wraps aContainer; bone color and size follow the container's owncolor/width/height.SkeletonBone— the low-level shimmer bone used to build custom color-matched placeholders.
width/height are optional everywhere — omit them and each widget sizes
itself from its content (or the space its parent gives it) instead of a
generic placeholder box.
Every bone animates as a shimmer sweep by default. Set style on the
ambient Skeleton to change it for a whole subtree, or on an individual
SkeletonText/SkeletonImage/SkeletonBox/SkeletonBone to override just
that one:
Skeleton(loading: isLoading, style: SkeletonStyle.pulse, child: const SizedBox());SkeletonStyle |
Effect |
|---|---|
shimmer |
Gradient highlight sweeping left to right (default). |
wave |
Gradient highlight sweeping top to bottom. |
sheen |
A narrow, brighter band sweeping diagonally. |
pulse |
Opacity fades in and out. |
breathe |
A soft glow that grows and shrinks from the center. |
solid |
A flat, unanimated fill. |
Add the package to pubspec.yaml
flutter pub add skeleton_tintor
dependencies:
skeleton_tint: ^0.2.1 # x-release-please-versionThen import the package.
import 'package:skeleton_tint/skeleton_tint.dart';Wrap a subtree with Skeleton and swap in the color-matched widgets:
Skeleton(
loading: isLoading,
child: Column(
children: [
SkeletonImage(
image: NetworkImage(user.avatarUrl),
borderRadius: BorderRadius.circular(24),
),
SkeletonText(
child: Text(
user.name,
style: const TextStyle(fontSize: 16, color: Colors.black87),
),
),
SkeletonBox(
child: Container(
color: Colors.blue.shade50,
width: 120,
height: 32,
child: PriceTag(product.price),
),
),
],
),
);Toggle loading on the ambient Skeleton and every descendant
SkeletonText/SkeletonImage/SkeletonBox switches between its bone and
the real widget automatically.
Every property below is optional unless marked required; full docs are on each constructor and on pub.dev.
| Widget | Property | Type | Default | Notes |
|---|---|---|---|---|
Skeleton |
loading |
bool |
required | Whether descendants render their bone instead of real content. |
style |
SkeletonStyle |
shimmer |
Default bone animation style for descendants that don't set their own style. |
|
SkeletonText |
child |
Text |
required | The real Text widget the bone replaces. |
width |
double? |
null |
Max width to wrap lines at. Defaults to the space the parent gives it. | |
preview |
ValueListenable<String?>? |
null |
Partial text known before child's data is final; the bone reflows live as it updates. |
|
borderRadius |
BorderRadius? |
null |
Defaults to a small radius scaled from font size; pass a large radius for a pill shape (e.g. price-tag-like text). | |
style |
SkeletonStyle? |
null |
Overrides the ambient Skeleton.style for this bone. |
|
SkeletonImage |
image |
ImageProvider |
required | The real image; sampled once per instance for its average color. |
width / height |
double? |
null |
Default to the image's own decoded size once known. | |
fit |
BoxFit? |
null |
Forwarded to the real Image once loaded. |
|
borderRadius |
BorderRadius |
BorderRadius.zero |
Applied to both the bone and the real image. | |
style |
SkeletonStyle? |
null |
Overrides the ambient Skeleton.style for this bone. |
|
SkeletonBox |
child |
Container |
required | The real Container the bone replaces. |
color |
Color? |
null |
Defaults to child's own Container.color. |
|
width / height |
double? |
null |
Default to child's own tight size, if any. |
|
borderRadius |
BorderRadius |
BorderRadius.zero |
Bone corner radius. | |
style |
SkeletonStyle? |
null |
Overrides the ambient Skeleton.style for this bone. |
|
SkeletonBone |
color |
Color |
required | The bone's base shimmer color. |
width / height |
double? |
null |
Fill the parent's bounded size, or a fixed extent when unbounded. | |
borderRadius |
BorderRadius |
BorderRadius.circular(4) |
Bone corner radius. | |
style |
SkeletonStyle |
shimmer |
See Bone styles. |
The repo includes a runnable example in example/ with
Loading/Loaded tabs to compare the bone and real content side by
side.
cd example
fvm flutter runThis project uses FVM to pin the Flutter SDK (see
.fvmrc).
fvm flutter pub get
fvm flutter analyze
fvm flutter testPull requests are welcome. If you change public behavior or the documented API, keep the README and example app in sync. See CONTRIBUTING.md for details.
Bug reports and feature requests are best opened in the GitHub issue tracker.
This project is licensed under the MIT License.
