Bringing Liquid Glass to Flutter without hand-rolling BackdropFilter
Flutter UI kit implementing Apple's iOS 26 Liquid Glass design language - a comprehensive glass widget library with real shader-based blur, physics-driven jelly animations, and dynamic lighting. Works on every platform out of the box.
At a glance
- What is it?
- sdegenaar/liquid_glass_widgets is a Flutter UI kit that implements Apple's Liquid Glass design language with real shader-based blur, physics-driven jelly animations and dynamic lighting, wrapped in a scaffold that handles background, z-ordering and accessibility for you. It works, but it demands a recent Flutter and treats the scaffold, not the card, as the unit of design.
- Who is it for?
- Adopt liquid_glass_widgets if you are building a Flutter app that should read as iOS 26 Liquid Glass and you want the blur, the refraction and the accessibility handling already solved rather than approximated with a stack of BackdropFilters. Do not adopt it if you cannot move to Flutter 3.41 or later, because the shader APIs are the point of the library.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly Dart, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A port of a platform design language, not a blur recipe
Liquid Glass is Apple's visual language for iOS 26, and this package is an attempt to bring it to Flutter. The README describes real shader-based blur, physics-driven jelly animations and dynamic lighting, and the repository topics list the technical ingredients: fragment-shader, impeller, glassmorphism, ios26 and liquid-glass.
The distinction from a generic glassmorphism kit is the shader. A BackdropFilter blur is a single sampled effect, and a stack of them is what you get when someone wants frosted panels and does not have a platform equivalent. A fragment shader can do refraction, edge highlights, thickness-dependent distortion and lighting that responds to what is behind the surface. The package ships a shaders/ directory, and the README notes it uses newer shader APIs that improve significantly with each Flutter release, which is both the reason it looks the way it does and the reason it is tied to a recent engine.
So the audience is a Flutter team that wants its app to read as a first-class iOS 26 interface rather than as a Material app with rounded corners. The licence is MIT, the package is published on pub.dev as liquid_glass_widgets, and the description says it works on every platform out of the box, which in practice means the desktop and web builds get a lighter shader set rather than the full premium one.
There is a second audience in the README that is unusual to find. The project ships a skill file for AI coding agents, on the grounds that models frequently hallucinate obsolete APIs or write manual BackdropFilter stacks instead of using the official component suite. That is a candid statement about a real problem, and it means the package is partly a reference implementation as well as a widget library.
Two steps: initialize() then wrap()
The setup is described as two steps, and the entire setup.
Adding the dependency is the usual pub.dev declaration, which pins the minor version:
dependencies:
liquid_glass_widgets: ^1.14.0flutter pub getThen the app needs two calls. The first pre-warms the shaders, the second wraps the app. The README's example imports the package alongside the Flutter SDK and then does this:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await LiquidGlassWidgets.initialize();
runApp(LiquidGlassWidgets.wrap(child: const MyApp()));
}The claim about initialize() is specific and worth taking seriously. It performs 100% non-blocking async disk-to-RAM I/O with zero GPU draw calls and zero rasterization, so the OS window always presents immediately, and on Android, iOS and macOS all premium shaders are preloaded at startup for instant Frame 1 rendering.
That last clause is the platform split in miniature. On Windows, Linux and Web, the default strategy skips unused premium shaders, and the warm-up mode can be overridden, with auto as the default for smart per-platform preload, always to force all shaders everywhere, and never to skip heavy shader preload entirely. There is also an enablePerformanceMonitor flag, defaulting to true, that controls a debug raster monitor.
The reasoning is sound: a shader that has to be compiled during the first frame is a shader that produces a stutter at exactly the moment the user is judging whether your app is fast.
MaterialApp users must pass a brightnessResolver
There is a trap in this package, and the README puts it in a callout rather than a footnote, which is the right place for it.
If your app is a MaterialApp, you have to add one line so the glass widgets honour your ThemeMode rather than the raw OS brightness. Without it, shadows and borders can disappear when the device is in Dark Mode even if your app is set to Light Mode, and the same inversion happens in the other direction. CupertinoApp users can omit it.
The reason is not cosmetic. A glass surface is defined in part by how it responds to light, and light has two possible sources in a Flutter app: the system setting and your app's theme. Most applications in production set a theme explicitly, and the OS setting is only a fallback. A glass library that reads the OS brightness by default will therefore disagree with your app about what tone the interface should be, and the disagreement shows up as missing shadows and borders, which is the kind of bug that gets reported as a rendering glitch on one device model.
The fix is to hand the library a resolver, and Theme.maybeBrightnessOf is the one to use with a MaterialApp. This is also a decent general lesson: a design system that reads platform state should let you override how it reads it, and this one does.
GlassScaffold is the unit of design, not GlassCard
The README is unusually direct about which widget to reach for, and it is not the one most people would guess.
The package is centred around navigation chrome. GlassScaffold with GlassAppBar and GlassTabBar is the primary pattern, and it is where the iOS 26 liquid glass effect is most impactful. For a custom layout without the standard scaffold structure there is GlassPage as a lower-level building block. GlassCard and GlassContainer are for standalone glass panels in an existing layout, and the README says they are fully supported but opt-in rather than the core pattern. AdaptiveLiquidGlassLayer scopes a glass layer to a region, with grouped cards sharing its settings.
The reason for that ranking is the physical behaviour of glass. Glass effects refract and blur against whatever is behind them, so without a controlled background a glass surface can appear flat, incorrectly tinted, or invisible. GlassScaffold wires up the background source, the glass rendering layer and the bar isolation automatically, which the README describes as one widget instead of five.
A typical screen then looks like this:
GlassScaffold(
background: Image.asset('assets/wallpaper.jpg', fit: BoxFit.cover),
statusBarStyle: GlassStatusBarStyle.auto,
appBar: GlassAppBar(title: const Text('My App')),
body: Center(child: GlassCard(child: Text('Hello, Glass!'))),
)The background is a required part of the design rather than decoration. A glass surface over a flat colour is not going to look like glass, and the person integrating this needs a photograph or gradient behind it to get the effect they saw in the demo.
The Flutter 3.41 floor, and why Impeller matters here
The version requirement is stated as a blockquote right after installation: Flutter 3.41.0 or later is required, which means Dart 3.5.0 or later, and Flutter 3.41 or later is recommended for the best Impeller rendering quality.
Two different things are going on. The hard floor is a technical dependency on shader APIs that did not exist in earlier engine versions, so on an older Flutter the package simply does not work. The recommendation is about the renderer, since Impeller is what the shaders are written against and what produces the intended quality.
The README also makes a claim that is unusual for a widget package, that it uses newer shader APIs that improve significantly with each Flutter release. Read one way that is a warning: your visuals may change when you upgrade the engine, because you are consuming whatever the new renderer does with your shaders. Read another way it is an opportunity: the package is not hiding from the platform, and a maintainer who tracks each Flutter release can improve the effect without you doing anything.
Either way, the practical consequence is a coupling to the engine release cycle that a normal widget package does not have. If your team pins Flutter for a reason, that pin now has a dependency on this package's release cadence. The repository does show the machinery of that upkeep: a CI workflow badge, a codecov badge, a .codecov.yml at the root, a CHANGELOG.md and a CHANGELOG_ARCHIVE.md, an analysis_options.yaml, an example/ application with its own integration_test/ and test/ directories, and a scripts/ directory.
Adaptive quality and app-wide glass defaults
Two optional parameters on wrap() matter for a production app, and both are advertised as optional with sensible defaults if omitted.
adaptiveQuality is described as auto-benchmarks the device and degrades gracefully. That is the right answer to the central problem of this kind of kit, which is that a shader-heavy interface runs beautifully on a recent phone and badly on a cheap one, and a library that offers the same effect everywhere is a library that will stutter somewhere. A benchmark at startup is a cost, and the flag is what lets you decide whether to pay it.
The theme parameter takes a GlassThemeData, and the example uses GlassThemeData.simple with blur set to 10, thickness to 30 and quality set to GlassQuality.standard. The third value is the interesting one, because a quality tier implies that the library can trade fidelity for speed, which is the same trade adaptiveQuality makes automatically. Having both a manual tier and an automatic benchmark means the two mechanisms can be combined, and the documentation does not say which one wins when you set both.
The call site is the same as the basic setup, with the parameters added:
runApp(LiquidGlassWidgets.wrap(
child: const MyApp(),
brightnessResolver: Theme.maybeBrightnessOf, // MaterialApp users: required
adaptiveQuality: true, // auto-benchmarks device, degrades gracefully
theme: GlassThemeData.simple( // optional app-wide glass defaults
blur: 10,
thickness: 30,
quality: GlassQuality.standard,
),
));Accessibility handled by default, and the agent skill distribution
Accessibility is on by default, and the way it is handled is the part most worth knowing about. The library automatically reads the device's Reduce Motion setting and approximates Reduce Transparency through the Increase Contrast signal, with no extra setup required.
Approximating one accessibility signal with another is a compromise, and it is worth being clear-eyed about that. Reduce Transparency is about not drawing a blurred layer over content, and Increase Contrast is a different user preference with a different meaning. Using it as a proxy means a user who has asked for more contrast gets a solid-looking surface, which is a reasonable interpretation of what they asked for, and it is not the same as honouring Reduce Transparency precisely. The README points to an Accessibility section for the details, which is where you would look before shipping.
The other unusual element is the distribution of the skill file for AI coding agents. For Antigravity or Gemini CLI, the README shows an install that downloads a single markdown file into an .agents/skills/liquid-glass-widgets directory:
mkdir -p .agents/skills/liquid-glass-widgets
curl -sSL https://raw.githubusercontent.com/sdegenaar/liquid_glass_widgets/main/skills/liquid-glass-widgets/SKILL.md -o .agents/skills/liquid-glass-widgets/SKILL.mdFor Cursor the file is copied to .cursor/rules/liquid-glass.mdc, for Claude Code into .claude/skills/liquid-glass-widgets/SKILL.md or into the project's CLAUDE.md, and skills/README.md covers other editors. The repository itself carries AGENTS.md and CLAUDE.md at the root, so the maintainer is using the same conventions it asks contributors to follow.
For an adopter this is a feature with a caveat. It saves an agent from hallucinating a stale API or building a BackdropFilter stack, which is a real saving. It also means part of the guidance lives outside the package, in a file fetched from a branch, so it can change without a version bump. The package is published on pub.dev at version 1.14.0 in the installation example, and the repository has no GitHub releases, so the pub.dev version is the release that matters.
Editorial conclusion
Adopt liquid_glass_widgets if you are building a Flutter app that should read as iOS 26 Liquid Glass and you want the blur, the refraction and the accessibility handling already solved rather than approximated with a stack of BackdropFilters. Do not adopt it if you cannot move to Flutter 3.41 or later, because the shader APIs are the point of the library. Verify first that your MaterialApp passes a brightnessResolver to wrap(), since without one the glass can lose its shadows and borders in dark mode, and decide whether GlassScaffold or the lower-level GlassPage fits your layouts.
Frequently asked questions
What Flutter version does liquid_glass_widgets require?
The README states that Flutter 3.41.0 or later is required, which means Dart 3.5.0 or later, and it recommends Flutter 3.41 or later for the best Impeller rendering quality. The package uses newer shader APIs that improve with each Flutter release.
How do I set up liquid_glass_widgets in a Flutter app?
Add liquid_glass_widgets to your dependencies, run flutter pub get, call await LiquidGlassWidgets.initialize() in main() to pre-warm the shaders, and wrap your app with LiquidGlassWidgets.wrap(). The initialization is described as non-blocking disk-to-RAM I/O with no GPU draw calls, so the window presents immediately.
Why do glass shadows disappear in dark mode with a MaterialApp?
Because the widgets read the raw OS brightness by default rather than your app's ThemeMode. The README says to pass brightnessResolver: Theme.maybeBrightnessOf to wrap() so the glass honours your theme, and notes that CupertinoApp users can omit it.
Should I use GlassScaffold or GlassCard in liquid_glass_widgets?
The README treats GlassScaffold with GlassAppBar and GlassTabBar as the primary pattern and says most screens should start there, with GlassPage as the lower-level option for custom layouts. GlassCard and GlassContainer are supported for localised glass UI but are described as opt-in, and GlassScaffold exists because glass surfaces need a controlled background or they can look flat, tinted or invisible.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/sdegenaar-liquid-glass-widgets)Was this useful?