flutter_liquid_glass_kit 2.0.0
flutter_liquid_glass_kit: ^2.0.0 copied to clipboard
Platform-adaptive Liquid Glass UI kit with native SwiftUI rendering on Apple platforms and optimized Flutter fallbacks on Android, Windows, and web.
flutter_liquid_glass_kit #
Platform-adaptive Liquid Glass components for Flutter. The package uses native SwiftUI glass on iOS and macOS, with an optimized Flutter renderer elsewhere.
| Platform | Renderer |
|---|---|
| iOS 26+ | Native SwiftUI .glassEffect() |
| iOS 16-25 | Native system-material fallback |
| Android | Optimized Flutter blur, tint, highlight, border, and shadow |
| macOS 26+ | Native SwiftUI .glassEffect() |
| macOS 12-15 | Native SwiftUI system-material fallback |
| Windows | Flutter desktop blur, tint, highlight, border, and shadow |
| Web | Flutter blur, tint, highlight, border, and shadow |
macOS uses a native AppKit/SwiftUI plugin with CocoaPods and Swift Package Manager support. Windows remains a Dart-only plugin implementation and needs no native plugin binary or extra platform setup.
Installation #
flutter pub add flutter_liquid_glass_kit
import 'package:flutter_liquid_glass_kit/flutter_liquid_glass_kit.dart';
Components #
Card #
const LiquidGlassCard(
padding: EdgeInsets.all(20),
child: Text('Hello, Glass!'),
)
width and height are optional fixed dimensions. When they are omitted, the
card follows its parent constraints and child size. margin is outside the
glass surface; padding is inside it.
Button #
LiquidGlassButton(
onPressed: () {},
isLoading: false,
child: const Text('Continue'),
)
A null onPressed disables the button. isLoading: true also disables taps and
replaces the child with a progress indicator. Use pressScaleFactor and
animationDuration to customize press feedback.
Text field #
LiquidGlassTextField(
controller: nameController,
decoration: const InputDecoration(
hintText: 'Name',
prefixIcon: Icon(Icons.person_outline),
),
textInputAction: TextInputAction.next,
autofillHints: const [AutofillHints.name],
onSubmitted: saveName,
)
The editable field remains a Flutter TextField, while its background uses
native SwiftUI glass on Apple platforms and the optimized fallback elsewhere.
This keeps Flutter controllers, focus nodes, formatters, selection, autofill,
and keyboard behavior available without a second native text-input bridge.
Provide borders through decoration when needed; otherwise the glass outline
is used.
Navigation bar #
LiquidGlassNavBar is a normal layout widget. It is controlled: update
currentIndex from onTap.
Scaffold(
body: pages[currentIndex],
bottomNavigationBar: SafeArea(
minimum: const EdgeInsets.all(16),
child: LiquidGlassNavBar(
currentIndex: currentIndex,
onTap: (index) => setState(() => currentIndex = index),
items: const [
LiquidGlassNavItem(
icon: Icon(Icons.home_outlined),
activeIcon: Icon(Icons.home),
label: 'Home',
iosSystemImage: 'house',
iosSelectedSystemImage: 'house.fill',
),
LiquidGlassNavItem(
icon: Text('S'),
label: 'Search',
iosSystemImage: 'magnifyingglass',
),
],
),
),
)
Use LiquidGlassFloatingNavBar inside a Stack when content should continue
behind the bar:
Stack(
children: [
pages[currentIndex],
LiquidGlassFloatingNavBar(
child: LiquidGlassNavBar(
currentIndex: currentIndex,
onTap: (index) => setState(() => currentIndex = index),
items: items,
),
),
],
)
The floating wrapper applies horizontal padding and includes the bottom safe
area by default. Set respectSafeArea: false only when an ancestor already
handles it.
On the Flutter fallback, users can hold and drag the selection indicator. Icons and labels preview the item under the indicator, but navigation occurs only after release. Cancelling restores the current item. During horizontal dragging, the indicator stretches vertically in proportion to finger speed and relaxes when movement pauses.
Choose a built-in Android indicator animation:
LiquidGlassNavBar(
currentIndex: currentIndex,
onTap: onTap,
androidAnimationStyle: LiquidGlassNavBarAnimationStyle.elastic,
items: items,
)
Available styles are liquid, elastic, pulse, and smooth. These fallback
dock animations also apply on macOS and Windows. The value can be changed at
runtime like any other widget property. For complete control, provide a
geometry resolver:
LiquidGlassNavBar(
currentIndex: currentIndex,
onTap: onTap,
androidAnimationResolver: (state) {
final speed = (state.dragSpeed / 1800).clamp(0.0, 1.0);
return Rect.fromCenter(
center: Offset(state.center, state.dockHeight / 2),
width: state.restingRect.width + 12 * state.holdProgress,
height: state.restingRect.height + 24 * speed,
);
},
items: items,
)
The resolver receives transition progress, hold progress, signed drag velocity, item dimensions, travel distance, and resting geometry. Keep it fast and free of side effects because it runs every animation frame. These options affect the Flutter fallback indicator used by Android, macOS, Windows, and web; native iOS continues using its system animation.
iOS, Android, macOS, and Windows can optionally shrink the bar while a vertical scrollable moves down, then restore it after enough upward movement or an idle delay. One configuration applies across these platforms:
LiquidGlassNavBar(
currentIndex: currentIndex,
onTap: onTap,
scrollConfiguration: const LiquidGlassNavBarScrollConfiguration(
collapsedScale: 0.82,
collapseThreshold: 12,
expandThreshold: 12,
animationDuration: Duration(milliseconds: 280),
idleExpandDuration: Duration(seconds: 5),
),
items: items,
)
Use iosScrollConfiguration or androidScrollConfiguration only when one
platform needs different values. macOS and Windows use the shared
scrollConfiguration. The former
LiquidGlassIOSNavBarScrollConfiguration and
LiquidGlassAndroidNavBarScrollConfiguration names remain as deprecated type
aliases.
The behavior also expands at the top and when an item is selected. It listens
through the nearest ScrollNotificationObserver; a Scaffold supplies one
automatically. Wrap custom layouts in ScrollNotificationObserver when no
Scaffold ancestor is present. Platform overrides are ignored outside their
target platform. Scroll resizing is disabled on web.
icon and activeIcon accept arbitrary widgets and inherit the active
IconTheme and DefaultTextStyle. Do not set an explicit colour when the
widget should follow selected/unselected styling. Native iOS uses SF Symbol
names from iosSystemImage and iosSelectedSystemImage; when omitted, it uses
the neutral circle symbol.
Native Apple fidelity #
Native surfaces preserve all four Flutter corner radii. On iOS 26 and macOS 26
each surface uses SwiftUI glassEffect directly; Reduce Transparency replaces
blur with a strong opaque tint, Increase Contrast strengthens tint and borders,
and Reduce Motion disables interactive glass. Earlier supported Apple systems
use the same per-corner shape and accessibility adaptations with system
material.
Wrap a page's surfaces in LiquidGlassBackdropGroup to use one native
platform view and one SwiftUI hosting controller for the page: UiKitView on
iOS or AppKitView on macOS. Descendant cards, buttons, text fields, and direct
PlatformGlass widgets register their current geometry and style with that
host. On Apple OS 26+, the host renders them together inside one SwiftUI
GlassEffectContainer; off-screen surfaces are omitted from native updates.
LiquidGlassBackdropGroup(
child: ListView(
padding: const EdgeInsets.all(16),
children: const [
LiquidGlassCard(child: Text('Account')),
SizedBox(height: 12),
LiquidGlassCard(child: Text('Privacy')),
],
),
)
Use one group per visible page. Do not place a single group around multiple
PageView pages or overlapping route transitions. A PlatformGlass outside a
group remains supported and creates its own native view for compatibility.
Cards are passive glass; buttons and enabled text fields opt into native
interactive response.
Shared settings #
Define a baseline once with LiquidGlassSettingsScope:
LiquidGlassSettingsScope(
settings: LiquidGlassSettings.matteDark.copyWith(
tintColor: const Color(0xFF9333EA),
tintOpacity: 0.30,
),
child: const MyAppContent(),
)
Or define settings while grouping non-overlapping fallback surfaces:
LiquidGlassBackdropGroup(
settings: const LiquidGlassSettings.matteDark,
child: ListView(children: glassCards),
)
Page-specific settings use the same boundary. Every descendant component inherits the page color unless it supplies local settings:
LiquidGlassBackdropGroup(
settings: const LiquidGlassSettings(
androidColor: Color(0xFF355C68),
),
child: const SavedPage(),
)
Settings resolve in this order:
- Settings passed directly to a card, button, nav bar, or
PlatformGlass. - The nearest
LiquidGlassSettingsScopeor configured backdrop group. LiquidGlassSettings.matteLight.
A component can override the shared baseline:
const LiquidGlassCard(
settings: LiquidGlassSettings(
tintColor: Color(0xFF16A34A),
tintOpacity: 0.32,
),
child: Text('Local green tint'),
)
Settings reference #
| Parameter | Default | Behavior |
|---|---|---|
tintColor |
null |
Custom surface color. Null selects an adaptive matte tint on the fallback. |
androidColor |
null |
Exact solid Android color. When set, skips blur, gradient, border, and shadow. Ignored on other platforms. |
iosGlassStyle |
LiquidGlassIOSStyle.system |
Native Apple custom-surface style. system follows the user's Clear/Opaque preference; clear always requests clear glass. Native iOS navigation remains system-managed. |
tintOpacity |
0.15 |
Tint strength from 0.0 to 1.0. Saturated colors usually work well at 0.20-0.40. |
blurSigma |
20 |
Requested backdrop blur. Forwarded to native Apple surfaces and capped on Flutter fallback platforms. |
androidBlurSigma |
8 |
Maximum fallback blur. Named for compatibility; set to 0 for a fast matte-only surface. |
borderOpacity |
0.25 |
Fallback border-highlight opacity. |
borderWidth |
1 |
Fallback border width in logical pixels. |
shadowOpacity |
0.12 |
Fallback drop-shadow opacity. Set to 0 on dense screens to reduce paint cost. |
shadowBlurRadius |
16 |
Fallback shadow blur radius. |
shadowOffset |
(0, 4) |
Fallback shadow offset. |
Built-in presets:
LiquidGlassSettings.matteLight: bright neutral glass.LiquidGlassSettings.matteDark: strong charcoal glass with a lower Android blur cap.
For a visible custom tint, provide both color and opacity:
const LiquidGlassSettings(
tintColor: Color(0xFF9333EA),
tintOpacity: 0.30,
)
To follow the user's Apple Liquid Glass appearance preference, keep the default system style. Force clear glass only when the design explicitly requires it:
const LiquidGlassCard(
settings: LiquidGlassSettings(
iosGlassStyle: LiquidGlassIOSStyle.system,
),
child: Text('Follows Clear or Opaque'),
)
const LiquidGlassCard(
settings: LiquidGlassSettings(
iosGlassStyle: LiquidGlassIOSStyle.clear,
),
child: Text('Always clear'),
)
Android performance #
For maximum performance, provide androidColor. Android then paints one solid
color and skips all glass effects. Cards and buttons also expose this as a
direct per-component shortcut:
LiquidGlassBackdropGroup(
settings: const LiquidGlassSettings(
androidColor: Color(0xFF263238),
),
child: ListView(
children: const [
LiquidGlassCard(child: Text('Inherited page color')),
LiquidGlassCard(
androidColor: Color(0xFF00695C),
child: Text('Card override'),
),
LiquidGlassButton(
androidColor: Color(0xFF3949AB),
onPressed: null,
child: Text('Button override'),
),
],
),
)
On iOS and macOS these components continue using native glass. To opt one Android
component back into glass inside a solid page scope, pass local settings with
androidColor omitted.
Wrap scrollable sections containing several glass surfaces in
LiquidGlassBackdropGroup. It coordinates inherited settings and, by default,
pauses blur and blurred shadows while scrolling while preserving tint, border,
and content. Backdrop filters remain independent for consistent Android color:
LiquidGlassBackdropGroup(
shareBackdropFilters: false,
disableBlurWhileScrolling: true,
disableShadowsWhileScrolling: true,
effectRestoreDelay: Duration(milliseconds: 80),
child: ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) => LiquidGlassCard(
child: ItemRow(items[index]),
),
),
)
The short restore delay prevents expensive effects from being recreated
between closely spaced scroll notifications. Set either disable flag to
false to keep that effect active during motion, or set effectRestoreDelay
to Duration.zero for immediate restoration. Other useful optimizations:
-
Set
shareBackdropFilters: trueonly for non-overlapping surfaces after checking target Android devices. It reduces blur passes, but grouped filters can render the first visible surface with different brightness on some Android renderers while scroll clipping changes. -
Lower
androidBlurSigmato6-8, or0for matte-only rendering. -
Reduce
shadowBlurRadiusor setshadowOpacityto0in long lists. -
Use lazy lists such as
ListView.builder. -
Avoid continuously animated backgrounds behind large blurred surfaces.
-
Use
LiquidGlassScrollBehaviorto remove Android overscroll effects that can alter backdrop sampling.
Do not put overlapping PageView pages, route transitions, or floating surfaces
in one backdrop group. Grouped filters share input and overlapping surfaces can
sample the wrong backdrop. LiquidGlassNavBar already disables shared backdrop
filtering for its floating fallback surface.
Rendering mode #
if (isNativeLiquidGlassSupported) {
print('Native Apple surface');
} else {
print('Flutter fallback surface');
}
This value is true on iOS and macOS even before OS 26 because supported older systems still use a native material fallback. Android, Windows, web, and other platforms return false.
Requirements #
- Flutter 3.19 or newer
- Dart 3.0 or newer
- iOS 16+; iOS 26+ uses native Liquid Glass
- macOS 12+; macOS 26+ uses native Liquid Glass
- Android API 21+
- Windows desktop targets supported by Flutter 3.19+