Shimmer.NET
1.2.0
See the version list below for details.
dotnet add package Shimmer.NET --version 1.2.0
NuGet\Install-Package Shimmer.NET -Version 1.2.0
<PackageReference Include="Shimmer.NET" Version="1.2.0" />
<PackageVersion Include="Shimmer.NET" Version="1.2.0" />
<PackageReference Include="Shimmer.NET" />
paket add Shimmer.NET --version 1.2.0
#r "nuget: Shimmer.NET, 1.2.0"
#:package Shimmer.NET@1.2.0
#addin nuget:?package=Shimmer.NET&version=1.2.0
#tool nuget:?package=Shimmer.NET&version=1.2.0
Shimmer.NET
A high-performance, pure C# shimmer/skeleton-loading library for .NET Android. No Java/Kotlin bindings — the gradient engine, view widgets, and state machine are all native C#.
- Content-aware masking —
PorterDuff.Mode.SrcInshimmer that hugs text and view bounds instead of painting over transparent background. - Synchronized sweeping —
ShimmerGroupdrives many independent shimmers off one clock for a perfectly unified sweep. - Built-in state machine —
Loading/Content/Errortransitions with cross-fade, plus a generated retry view. - Theme-aware by default — colors adapt to light/dark mode out of the box, no configuration required.
Contents
- Installation
- Core components
- XML attributes
- Runtime API reference
- Lifecycle & performance
- Masking mode
- Examples
- Troubleshooting
- Contributing
Installation
<PackageReference Include="Shimmer.NET" Version="1.2.0" />
Targets net9.0-android and net10.0-android. Requires the .NET Android workload.
Core components
ShimmerDrawable
The core engine: a Drawable that builds a LinearGradient/RadialGradient shader (base → highlight → base) and repositions it every animation tick. Exposes every visual/timing knob (Direction, Shape, Intensity, Dropoff, Angle, Duration, RepeatDelay, RepeatCount, RepeatMode, Interpolator, AutoStart) so you can drive it manually if you're not using ShimmerFrameLayout.
ShimmerFrameLayout
A FrameLayout (XML tag shimmer.net.widgets.ShimmerFrameLayout) that owns a ShimmerDrawable and applies it over its children. Supports:
- Overlay mode (default): draws the shimmer on top of your placeholder views.
- Mask mode (
app:shimmer_use_mask="true"): the shimmer only paints where child content already has non-transparent pixels — see Masking mode. - A
ShimmerState(Loading/Content/Error) state machine viaState/SetState(...), with automatic alpha cross-fading. ShowError(message, retryText, onRetry)to generate and show a retry view without building your own layout.
ShimmerGroup
A LinearLayout that runs a single shared ValueAnimator and pushes its progress into every descendant ShimmerFrameLayout, so a profile header, a card, and a list can all sweep in lockstep. Children are tracked lazily and re-scanned only when the view hierarchy actually changes.
ShimmerRecyclerView
A thin RecyclerView wrapper. ShowLoading(layoutId, count) swaps in a throwaway adapter that inflates layoutId as pure, unbound placeholders; HideLoading() restores whatever adapter was set beforehand.
ShimmerPresets
Static factories that build a ready-to-add ShimmerFrameLayout (or ShimmerGroup) entirely in code — no XML needed:
ShimmerPresets.CreateProfile(context)— avatar + two text lines.ShimmerPresets.CreatePost(context)— image block + text lines.ShimmerPresets.CreateList(context, itemCount = 5)—itemCountsynchronized post rows inside aShimmerGroup.
All three pick their placeholder color from ShimmerTheme, so they adapt to dark mode automatically.
XML attributes
All attributes live on ShimmerFrameLayout under the app: namespace.
| Attribute | Type | Notes |
|---|---|---|
shimmer_base_color |
color | Defaults to a theme-aware light gray / deep graphite if omitted. |
shimmer_highlight_color |
color | Defaults to a theme-aware highlight if omitted. |
shimmer_base_alpha |
integer (0–255) | Overrides the base color's alpha channel. |
shimmer_highlight_alpha |
integer (0–255) | Overrides the highlight color's alpha channel. |
shimmer_angle |
float | Tilt of a Linear gradient, in degrees. |
shimmer_duration |
integer (ms) | Sweep duration. Default 1000. |
shimmer_repeat_count |
integer | -1 for infinite (default). |
shimmer_repeat_mode |
enum: restart, reverse |
|
shimmer_repeat_delay |
integer (ms) | Pause between sweeps. |
shimmer_intensity |
float (0–1) | Width of the highlight band. |
shimmer_dropoff |
float (0–1) | Edge softness of the highlight. |
shimmer_direction |
enum: left_to_right, right_to_left, top_to_bottom, bottom_to_top |
|
shimmer_shape |
enum: linear, radial |
|
shimmer_use_mask |
boolean | Enables content-aware PorterDuff masking. |
shimmer_is_loading |
boolean | Initial IsLoading/State value. |
shimmer_auto_start |
boolean | Whether the animator starts as soon as it's (re)created. Default true. |
Any attribute you omit falls back to its default rather than overwriting a previously-set value — this matters if you construct the drawable in code first and then apply a style.
Runtime API reference
ShimmerDrawable
bool IsLoading { get; set; } // setting true calls Start()
void Start(); // ensures the animator exists and (re)starts it
void Stop(); // cancels the animator without resetting state
bool IsStarted { get; }
float Progress { get; set; } // used by ShimmerGroup when IsExternallyDriven
bool IsExternallyDriven { get; set; } // true while a ShimmerGroup owns the clock
bool AutoStart { get; set; }
Color BaseColor { get; set; }
Color HighlightColor { get; set; }
float Angle { get; set; }
long Duration { get; set; }
long RepeatDelay { get; set; }
int RepeatCount { get; set; }
ValueAnimatorRepeatMode RepeatMode { get; set; }
float Intensity { get; set; } // clamped 0..1
float Dropoff { get; set; } // clamped 0..1
ShimmerDirection Direction { get; set; }
ShimmerShape Shape { get; set; }
bool UseMask { get; set; }
IInterpolator Interpolator { get; set; }
ShimmerFrameLayout
ShimmerDrawable ShimmerDrawable { get; }
ShimmerState State { get; set; } // Loading | Content | Error
bool IsLoading { get; set; } // convenience wrapper over State
void SetState(ShimmerState state, bool animate = true);
void SetErrorView(View errorView);
void ShowError(string message, string retryText = "Retry", Action? onRetry = null);
void Pause(); // stop the sweep, keep State as-is
void Resume(); // resume if still Loading
ShimmerRecyclerView
void ShowLoading(int loadingLayoutId, int itemCount = 10);
void HideLoading();
Lifecycle & performance
ShimmerFrameLayoutautomatically stops its sweep inOnDetachedFromWindowand whenever visibility changes to anything other thanVisible(window backgrounded, an ancestor hidden, an off-screen ViewPager page), then resumes automatically when visible again — no wiring required.- For explicit control (e.g. you want to pause slightly before
OnPauseactually fires), callPause()/Resume()directly; both leaveStateuntouched, so the placeholder layout doesn't flash. ShimmerDrawablereuses its gradient color/position arrays and repositions the shader viaMatrix.SetLocalMatrix— no per-frame allocation.- Prefer
ShimmerGroupover N independentShimmerFrameLayouts when they should look synchronized: it's oneValueAnimator, not N. - In a
RecyclerView, set allapp:shimmer_*attributes via XML/style rather than one-by-one in code —ShimmerFrameLayout.Init()batches XML attributes into a single shader/animator rebuild, but setting individual properties on an already-inflated drawable rebuilds on each call.
Masking mode
Mask mode uses Canvas.SaveLayer() + PorterDuffXfermode(PorterDuff.Mode.SrcIn) so the shimmer only appears over pixels your placeholder views already drew — ideal for text-shaped or irregularly-shaped skeletons. Trade-offs:
- Requires an extra offscreen layer per frame, so it costs more than overlay mode.
- On some GPU/API-level combinations you may see artifacts; if so, set
shimmer_use_mask="false"(orUseMask = false) and fall back to overlay mode, optionally withSetLayerType(LayerType.Software, null).
Examples
XML (linear shimmer, mask mode):
<shimmer.net.widgets.ShimmerFrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:shimmer_is_loading="true"
app:shimmer_use_mask="true"
app:shimmer_duration="1500"
app:shimmer_direction="left_to_right"
app:shimmer_shape="linear"
app:shimmer_intensity="0.3"
app:shimmer_dropoff="0.5">
</shimmer.net.widgets.ShimmerFrameLayout>
Runtime tweaks:
var shimmer = FindViewById<Shimmer.NET.Widgets.ShimmerFrameLayout>(Resource.Id.shimmer_main);
shimmer.ShimmerDrawable.Duration = 1200;
shimmer.ShimmerDrawable.Intensity = 0.4f;
shimmer.ShimmerDrawable.Direction = Shimmer.NET.Drawables.ShimmerDirection.TopToBottom;
shimmer.IsLoading = true;
State transitions with a retry view:
shimmer.State = ShimmerState.Loading;
// ...network call fails...
shimmer.ShowError("Couldn't load your feed.", "Retry", onRetry: () => ReloadFeed());
// ...network call succeeds...
shimmer.SetState(ShimmerState.Content, animate: true);
Code-only skeleton, no XML:
var list = Shimmer.NET.Helpers.ShimmerPresets.CreateList(this, itemCount: 6);
container.AddView(list);
Troubleshooting
- No animation visible: confirm
IsLoading/State == Loading, that the container has non-zero bounds, and that the view is actuallyVisible(an invisible view now pauses its sweep by design — see Lifecycle & performance). - Visual artifacts in mask mode: switch to overlay mode (
shimmer_use_mask="false") or force a software layer, as described above. FindViewByIdreturnsnullfor a Material component: use the concrete type, e.g.Google.Android.Material.Button.MaterialButton, notAndroid.Widget.Button.- A shimmer removed from a
ShimmerGrouplooks frozen: fixed in 1.2.0 — removing a child from aShimmerGroupnow resets it back to driving its own animator. If you're on an older version, setshimmerView.ShimmerDrawable.IsExternallyDriven = falsemanually after removal.
Contributing
See library_roadmap.md for the current feature roadmap and ideas under consideration. Issues and PRs welcome at the repository.
License
Shimmer.NET is released under the MIT License.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0-android36.0 is compatible. |
-
net10.0-android36.0
- Xamarin.AndroidX.RecyclerView (>= 1.4.0.5)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.