Shimmer.NET 1.2.0

There is a newer version of this package available.
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
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Shimmer.NET" Version="1.2.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Shimmer.NET" Version="1.2.0" />
                    
Directory.Packages.props
<PackageReference Include="Shimmer.NET" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Shimmer.NET --version 1.2.0
                    
#r "nuget: Shimmer.NET, 1.2.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Shimmer.NET@1.2.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Shimmer.NET&version=1.2.0
                    
Install as a Cake Addin
#tool nuget:?package=Shimmer.NET&version=1.2.0
                    
Install as a Cake Tool

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 maskingPorterDuff.Mode.SrcIn shimmer that hugs text and view bounds instead of painting over transparent background.
  • Synchronized sweepingShimmerGroup drives many independent shimmers off one clock for a perfectly unified sweep.
  • Built-in state machineLoading / Content / Error transitions 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

<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 via State / 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)itemCount synchronized post rows inside a ShimmerGroup.

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

  • ShimmerFrameLayout automatically stops its sweep in OnDetachedFromWindow and whenever visibility changes to anything other than Visible (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 OnPause actually fires), call Pause() / Resume() directly; both leave State untouched, so the placeholder layout doesn't flash.
  • ShimmerDrawable reuses its gradient color/position arrays and repositions the shader via Matrix.SetLocalMatrix — no per-frame allocation.
  • Prefer ShimmerGroup over N independent ShimmerFrameLayouts when they should look synchronized: it's one ValueAnimator, not N.
  • In a RecyclerView, set all app: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" (or UseMask = false) and fall back to overlay mode, optionally with SetLayerType(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 actually Visible (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.
  • FindViewById returns null for a Material component: use the concrete type, e.g. Google.Android.Material.Button.MaterialButton, not Android.Widget.Button.
  • A shimmer removed from a ShimmerGroup looks frozen: fixed in 1.2.0 — removing a child from a ShimmerGroup now resets it back to driving its own animator. If you're on an older version, set shimmerView.ShimmerDrawable.IsExternallyDriven = false manually 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 Compatible and additional computed target framework versions.
.NET net10.0-android36.0 is compatible. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.3.1 115 8/2/2026
1.3.0 110 7/30/2026
1.2.0 115 7/24/2026
1.1.1 127 3/26/2026
1.1.0 112 3/26/2026