Liquid Glass Easy

المكوّنات

عناصر التحكم الجاهزة: الأزرار والحوارات والأوراق والمزلاج والمفتاح والتحوّل.

صنف

LiquidGlassButton#

يرث من StatelessWidgetالدليل: الزر →

A pill-shaped action button rendered as liquid glass.

This is a plain widget: drop it anywhere in your layout (a Row, a Column, a Stack) and it renders a single LiquidGlassLens around its label/icon. It needs no position and no LiquidGlassView on Impeller; on Skia / Web place it inside a LiquidGlassView so it has a background to refract.

Styling uses one LiquidGlassStyle (style — shape + appearance + refraction), defaulted to a tuned iOS-style glass, so the simplest call is just label + onPressed. For a solid call-to-action (e.g. a blue "Continue"), pass a style with a colored tint — compose from defaultStyle to keep the rest of the tuned look:

Dart
LiquidGlassButton(
  label: 'Continue',
  style: LiquidGlassButton.defaultStyle.copyWith(
    appearance: LiquidGlassAppearance(color: Colors.blue.withAlpha(160)),
  ),
  onPressed: () {},
)

المُنشئات

const LiquidGlassButton({ super.key, this.label, this.icon, this.onPressed, this.width, this.height = 48, this.padding = const EdgeInsets.symmetric(horizontal: 20), this.style, this.visibility = true, this.foregroundColor, this.fontSize = 16, this.fontWeight = FontWeight.w600, this.iconSize = 20, this.touch, this.child, })
المعاملالنوعالافتراضيالوصف
keyKey?
labelString?Button label text. Ignored when child is set.
iconIconData?Optional leading icon. Ignored when child is set.
onPressedVoidCallback?Tap callback.
widthdouble?Explicit width. When null the button hugs its content.
heightdouble48Capsule height; also drives the default pill radius (height / 2).
paddingEdgeInsetsGeometryconst EdgeInsets.symmetric(horizontal: 20)Inner padding around the label/icon.
styleLiquidGlassStyle?The button's glass look as one LiquidGlassStyle (shape + appearance + refraction), taken as the complete look. When null the tuned defaultStyle is used. Its shape may be null, in which case a full pill (LiquidGlassShape with radius height / 2) and a tuned optical border are used. To tweak one facet while keeping the rest of the tuned look, compose with copyWith, e.g. style: LiquidGlassButton.defaultStyle.copyWith(...).
visibilitybooltrueWhether the button is shown; toggling animates the glass in/out.
foregroundColorColor?Color of the label text and icon. Adaptivity outranks this. On an adaptive surface the color comes from the verdict, whatever is named here — a pinned foreground would otherwise defeat the one thing adaptivity exists to guarantee, which is that content stays legible over whatever passes beneath it. To keep a fixed color on an adaptive surface, opt that surface out with adaptivity: LiquidGlassAdaptivity.none. null (the default) is white on a non-adaptive surface.
fontSizedouble16Font size of the label.
fontWeightFontWeightFontWeight.w600Font weight of the label.
iconSizedouble20Size of the leading icon.
touchLiquidGlassTouch?How the button answers a finger — see LiquidGlassTouch. With a LiquidGlassTouch.flex the glass deforms under the press instead of only rippling: it swells under your finger, elongates if you drag, and springs back on release. The button does not move. null (the default) leaves the glass rigid — no gesture listener, no ticker, nothing added to the tree.
childWidget?Custom content, replacing the icon + label row entirely — for anything Icon/Text can't express (SVG, PNG, a badge, a spinner). It is centered inside the button's padding and clipped to the glass shape; it never sizes the button, which stays on width/height so the lens geometry holds. A bare Icon/Text inside it inherits foregroundColor, fontSize, fontWeight and iconSize, so it matches the built-in row by default. Give the widget its own color to paint it yourself.

الأعضاء الساكنة

التوقيعالوصف
LiquidGlassStyle defaultStyleThe tuned default look — a faint white frost over a soft optical refraction. Its shape is null: the button derives a height-tracking full pill with an optical border when style supplies no shape. Compose with copyWith to tweak one facet, e.g. style: LiquidGlassButton.defaultStyle.copyWith(...).

معرَّف في lib/src/widgets/components/liquid_glass_button.dart

صنف

LiquidGlassFab#

يرث من StatelessWidgetالدليل: زر الإجراء العائم →

A floating action button (FAB) rendered as liquid glass.

Supports standard circular FABs, custom child FABs, and extended FABs with an icon and label via LiquidGlassFab.extended.

Drop it anywhere in your layout or pass it to LiquidGlassScaffold's floatingActionButton slot.

Dart
LiquidGlassFab(
  icon: Icons.add,
  onPressed: () {},
)

LiquidGlassFab.extended(
  icon: Icons.edit,
  label: const Text('Compose'),
  onPressed: () {},
)

المُنشئات

const LiquidGlassFab({ super.key, this.icon, this.child, this.onPressed, this.size = 56.0, this.padding = const EdgeInsets.all(16), this.style, this.visibility = true, this.foregroundColor, this.iconSize = 24.0, this.heroTag, this.tooltip, })
المعاملالنوعالافتراضيالوصف
keyKey?
iconIconData?Optional icon for regular or extended FAB.
childWidget?Custom child widget inside standard FAB.
onPressedVoidCallback?Tap callback.
sizedouble56.0Size (diameter) of standard circular FAB.
paddingEdgeInsetsGeometryconst EdgeInsets.all(16)Inner padding around icon/label.
styleLiquidGlassStyle?Glass look descriptor (shape + appearance + refraction).
visibilitybooltrueWhether the FAB is shown; toggling animates glass in/out.
foregroundColorColor?Color of the label text and icon. Adaptivity outranks this. On an adaptive surface the color comes from the verdict, whatever is named here — a pinned foreground would otherwise defeat the one thing adaptivity exists to guarantee, which is that content stays legible over whatever passes beneath it. To keep a fixed color on an adaptive surface, opt that surface out with adaptivity: LiquidGlassAdaptivity.none. null (the default) is white on a non-adaptive surface.
iconSizedouble24.0Icon size.
heroTagObject?Optional Hero tag for route transitions.
tooltipString?Optional tooltip string.
const LiquidGlassFab.extended({ super.key, required this.label, this.icon, this.onPressed, double this.height = 48.0, this.width, this.padding = const EdgeInsets.symmetric(horizontal: 20, vertical: 12), this.style, this.visibility = true, this.foregroundColor, this.iconSize = 20.0, this.heroTag, this.tooltip, })

Extended FAB constructor with label and optional icon.

المعاملالنوعالافتراضيالوصف
keyKey?
labelمطلوبWidget?Label widget for extended FAB.
iconIconData?Optional icon for regular or extended FAB.
onPressedVoidCallback?Tap callback.
widthdouble?Explicit width for extended FAB.
paddingEdgeInsetsGeometryconst EdgeInsets.symmetric(horizontal: 20, vertical: 12)Inner padding around icon/label.
styleLiquidGlassStyle?Glass look descriptor (shape + appearance + refraction).
visibilitybooltrueWhether the FAB is shown; toggling animates glass in/out.
foregroundColorColor?Color of the label text and icon. Adaptivity outranks this. On an adaptive surface the color comes from the verdict, whatever is named here — a pinned foreground would otherwise defeat the one thing adaptivity exists to guarantee, which is that content stays legible over whatever passes beneath it. To keep a fixed color on an adaptive surface, opt that surface out with adaptivity: LiquidGlassAdaptivity.none. null (the default) is white on a non-adaptive surface.
iconSizedouble20.0Icon size.
heroTagObject?Optional Hero tag for route transitions.
tooltipString?Optional tooltip string.

الخصائص

الاسمالنوعالوصف
heightdouble?Explicit height for extended FAB.
isExtendedboolWhether this is an extended FAB layout.

الأعضاء الساكنة

التوقيعالوصف
LiquidGlassStyle defaultStyleThe tuned default look for liquid glass FAB.

معرَّف في lib/src/widgets/components/liquid_glass_fab.dart

نوع مُسمّى

LiquidGlassFloatingActionButton#

Alias for LiquidGlassFab.

Dart
typedef LiquidGlassFloatingActionButton = LiquidGlassFab;

معرَّف في lib/src/widgets/components/liquid_glass_fab.dart

صنف

LiquidGlassDialog#

يرث من StatelessWidgetالدليل: الحوارات →

A generic dialog container rendered as a liquid glass surface.

المُنشئات

const LiquidGlassDialog({ super.key, required this.child, this.width = 340.0, this.padding = const EdgeInsets.all(24.0), this.style, this.visibility = true, this.alignment = Alignment.center, })
المعاملالنوعالافتراضيالوصف
keyKey?
childمطلوبWidgetCustom inner content widget.
widthdouble340.0Maximum width of the dialog card.
paddingEdgeInsetsGeometryconst EdgeInsets.all(24.0)Inner padding surrounding the child.
styleLiquidGlassStyle?Glass style (shape + appearance + refraction).
visibilitybooltrueWhether the dialog lens is visible.
alignmentAlignmentGeometryAlignment.centerAlignment of the dialog on screen.

الأعضاء الساكنة

التوقيعالوصف
LiquidGlassStyle defaultStyleDefault glass look for dialogs.

معرَّف في lib/src/widgets/components/liquid_glass_dialog.dart

صنف

LiquidGlassAlertDialog#

يرث من StatelessWidgetالدليل: الحوارات →

An alert dialog rendered with a liquid glass surface.

Features standard Material 3 dialog slots (icon, title, content, actions) elevated on top of a frosted refractive liquid glass lens.

المُنشئات

const LiquidGlassAlertDialog({ super.key, this.icon, this.iconColor, this.iconPadding = const EdgeInsets.only(bottom: 16.0), this.title, this.titlePadding = const EdgeInsets.only(bottom: 12.0), this.titleTextStyle, this.content, this.contentPadding = const EdgeInsets.only(bottom: 24.0), this.contentTextStyle, this.actions, this.actionsPadding = EdgeInsets.zero, this.actionsAlignment = MainAxisAlignment.end, this.actionsOverflowAlignment = OverflowBarAlignment.end, this.actionsOverflowDirection = VerticalDirection.down, this.actionsOverflowButtonSpacing, this.style, this.width = 340.0, this.scrollable = false, this.visibility = true, })
المعاملالنوعالافتراضيالوصف
keyKey?
iconWidget?Optional leading icon above the title.
iconColorColor?Icon color if a plain Icon is provided.
iconPaddingEdgeInsetsGeometryconst EdgeInsets.only(bottom: 16.0)Padding around the icon.
titleWidget?Title widget (or Text).
titlePaddingEdgeInsetsGeometryconst EdgeInsets.only(bottom: 12.0)Padding around the title.
titleTextStyleTextStyle?Default text style for title.
contentWidget?Content widget (body).
contentPaddingEdgeInsetsGeometryconst EdgeInsets.only(bottom: 24.0)Padding around content widget.
contentTextStyleTextStyle?Default text style for content.
actionsList<Widget>?List of action widgets (e.g. LiquidGlassButton or standard buttons).
actionsPaddingEdgeInsetsGeometryEdgeInsets.zeroPadding around action buttons row/column.
actionsAlignmentMainAxisAlignmentMainAxisAlignment.endHorizontal alignment of actions.
actionsOverflowAlignmentOverflowBarAlignmentOverflowBarAlignment.endOverflow alignment when actions do not fit horizontally.
actionsOverflowDirectionVerticalDirectionVerticalDirection.downDirection of actions when overflowing.
actionsOverflowButtonSpacingdouble?Spacing between action buttons when overflowing.
styleLiquidGlassStyle?Glass style descriptor.
widthdouble340.0Dialog max width.
scrollableboolfalseWhether title and content should be scrollable.
visibilitybooltrueWhether the dialog lens is visible.

معرَّف في lib/src/widgets/components/liquid_glass_dialog.dart

function

showLiquidGlassDialog#

الدليل: الحوارات →

Displays a liquid glass dialog over the current route.

Uses showGeneralDialog with a tuned scale + fade modal animation to present LiquidGlassAlertDialog or custom LiquidGlassDialog widgets.

On Impeller the dialog takes a backdrop read of its own, even inside a LiquidGlassScaffold: the route sits outside the scaffold's tree, where no batch reaches it. Pass batch to have it join the scaffold's chrome batch instead — the read its tab bar's capsule already takes — so the dialog costs no read beyond that one. The batch then covers the whole dialog, its own glass and its buttons alike, and the copy they all sample was taken before the tab bar and the route painted: the dialog refracts the page as it lay under the bar, with no tab bar glass in it and no barrier scrim, and glass inside it — its buttons — reads that page rather than the dialog's surface; wrap it in LiquidGlassBatch.exclude to get that back. Outside a scaffold, or with the scaffold's batch off, the flag does nothing. On the Skia / Web capture path there is no shared key and it does nothing either.

Dart
showLiquidGlassDialog(
  context: context,
  builder: (context) => LiquidGlassAlertDialog(
    title: const Text('Delete Item?'),
    content: const Text('This action cannot be undone.'),
    actions: [
      LiquidGlassButton(
        label: 'Cancel',
        onPressed: () => Navigator.of(context).pop(),
      ),
      LiquidGlassButton(
        label: 'Delete',
        style: LiquidGlassButton.defaultStyle.copyWith(
          appearance: const LiquidGlassAppearance(color: Color(0x60FF3B30)),
        ),
        onPressed: () => Navigator.of(context).pop(true),
      ),
    ],
  ),
);

التوقيع

showLiquidGlassDialog({ required BuildContext context, required WidgetBuilder builder, bool barrierDismissible = true, Color barrierColor = const Color(0x80000000), String? barrierLabel, bool useSafeArea = true, bool useRootNavigator = true, RouteSettings? routeSettings, Offset? anchorPoint, Duration transitionDuration = const Duration(milliseconds: 350), Curve transitionCurve = const Cubic(0.16, 1.0, 0.3, 1.0), Curve reverseTransitionCurve = const Cubic(0.7, 0.0, 0.84, 0.0), bool batch = false, })
المعاملالنوعالافتراضيالوصف
contextمطلوبBuildContext
builderمطلوبWidgetBuilder
barrierDismissiblebooltrue
barrierColorColorconst Color(0x80000000)
barrierLabelString?
useSafeAreabooltrue
useRootNavigatorbooltrue
routeSettingsRouteSettings?
anchorPointOffset?
transitionDurationDurationconst Duration(milliseconds: 350)
transitionCurveCurveconst Cubic(0.16, 1.0, 0.3, 1.0)
reverseTransitionCurveCurveconst Cubic(0.7, 0.0, 0.84, 0.0)
batchboolfalse

معرَّف في lib/src/widgets/components/liquid_glass_dialog.dart

صنف

LiquidGlassSheet#

يرث من StatelessWidgetالدليل: الأوراق →

A sheet of liquid glass — the panel showLiquidGlassSheet presents, and an ordinary widget you can place yourself.

It draws the surface and nothing else: the glass, an optional grabber, an optional header and the padded child. Where it sits and how it is dragged belong to whatever presents it.

The sheet takes the height it is given and hugs its content when it is given none, so all of these work:

On Impeller it works standalone; on Skia / Web it needs an ancestor LiquidGlassView with a background to refract.

Dart
// A panel pinned to the bottom of a page.
Align(
  alignment: Alignment.bottomCenter,
  child: LiquidGlassSheet(child: myControls),
)

// The same surface, presented as a modal.
showLiquidGlassSheet(context: context, builder: (context) => myControls);

// Or inside anything else that owns the motion.
showModalBottomSheet(
  context: context,
  backgroundColor: Colors.transparent,
  isScrollControlled: true,
  builder: (context) => DraggableScrollableSheet(
    expand: false,
    initialChildSize: 0.5,
    builder: (context, scrollController) => LiquidGlassSheet(
      child: ListView(controller: scrollController, children: [...]),
    ),
  ),
);

المُنشئات

const LiquidGlassSheet({ super.key, required this.child, this.header, this.anchor = LiquidGlassSheetAnchor.floating, this.grabber = true, this.grabberColor, this.grabberSize = const Size(38, 5), this.margin, this.padding = const EdgeInsets.fromLTRB(20, 4, 20, 20), this.style, this.visibility = true, this.foregroundColor, this.safeArea = true, this.touch, })
المعاملالنوعالافتراضيالوصف
keyKey?
childمطلوبWidgetThe sheet's content. Give it a scrollable and the sheet fills the height it was handed; give it a Column and it hugs it.
headerWidget?Optional widget between the grabber and child, laid out full width with no padding of its own — a title row, a segmented control, a search field. It is outside padding, so pad it yourself if it should line up with the content.
anchorLiquidGlassSheetAnchorLiquidGlassSheetAnchor.floatingWhether the sheet is inset on all sides or sits on the bottom edge.
grabberbooltrueWhether the small drag handle is drawn at the top of the sheet.
grabberColorColor?Color of the grabber. When null it is the resolved foreground at 35% alpha, so it follows an adaptive flip with the rest.
grabberSizeSizeconst Size(38, 5)Size of the grabber bar. Its height also sets its corner radius.
marginEdgeInsets?Space around the glass. When null it is derived from anchor: 10 on the sides and below for a LiquidGlassSheetAnchor.floating sheet (the bottom growing to clear the home indicator when safeArea is set), and nothing at all for an attached one.
paddingEdgeInsetsGeometryconst EdgeInsets.fromLTRB(20, 4, 20, 20)Padding around child inside the glass.
styleLiquidGlassStyle?The sheet's glass look as one LiquidGlassStyle (shape + appearance + refraction). When null the tuned defaultStyle is used; anything you pass is merged over it, so one facet changes without retyping the rest. A null shape derives a continuous rounded rectangle with an optical rim.
visibilitybooltrueWhether the glass is shown; toggling animates it in/out.
foregroundColorColor?Color of bare Icons and Text inside the sheet. Adaptivity outranks this. On an adaptive surface the color comes from the verdict, whatever is named here. Opt the surface out with adaptivity: LiquidGlassAdaptivity.none to pin a color on it. null (the default) is white on a non-adaptive surface.
safeAreabooltrueWhether the sheet keeps clear of the system insets — the bottom margin of a floating sheet grows to clear the home indicator, and an attached sheet pads its content by the same amount.
touchLiquidGlassTouch?How the surface answers a finger — see LiquidGlassTouch. A sheet is a large body, so it ships rigid; prefer a restrained spec such as LiquidGlassFlex.subtle if you enable it.

الأعضاء الساكنة

التوقيعالوصف
LiquidGlassStyle defaultStyleThe tuned default look — a frost heavier than the button's, because a sheet has to carry content over whatever it covers, over a wide, soft refraction. Its shape is null: the sheet derives a continuous rounded rectangle (radius 28) with an optical rim when style supplies no shape. Compose with copyWith to tweak one facet, e.g. style: LiquidGlassSheet.defaultStyle.copyWith(...).
LiquidGlassShape defaultShape()The shape a sheet uses when its style names none.
EdgeInsets defaultMargin( LiquidGlassSheetAnchor anchor, { double bottomIns…The margin anchor implies when none is given.

معرَّف في lib/src/widgets/components/liquid_glass_sheet.dart

function

showLiquidGlassSheet#

الدليل: الأوراق →

Presents builder's content on a LiquidGlassSheet.

This is Flutter's own showModalBottomSheet with the glass put where its filled Material used to be: the route, the slide-up, the drag, the barrier and the dismissal are all Flutter's, unchanged, and every parameter of theirs is forwarded here. What the sheet looks like is this package's — style is the same LiquidGlassStyle vocabulary every other component takes.

Because it is the Material route underneath, everything you already know about it holds: isScrollControlled for a sheet taller than nine sixteenths of the screen, constraints to bound it, transitionAnimationController to drive it yourself. For a sheet that resizes as you drag it, put a DraggableScrollableSheet in a showModalBottomSheet and place a bare LiquidGlassSheet inside its builder — the glass has to be within what resizes, and this presenter wraps your content from outside it.

On the Skia / Web capture path, a sheet builds in the navigator's overlay rather than inside the LiquidGlassView that captured the page. showModalBottomSheet carries the ambient themes across for its own reasons, and the view's lens scope rides along with them, so a sheet opened from a context inside a view refracts the page behind it. Impeller never needed it.

On Impeller the sheet takes a backdrop read of its own, even inside a LiquidGlassScaffold: the route sits outside the scaffold's tree, where no batch reaches it. Pass batch to have it join the scaffold's chrome batch instead — the read its tab bar's capsule already takes — so the sheet costs no read beyond that one. The batch then covers the whole sheet, its own glass and every lens in builder's subtree alike, and the copy they all sample was taken before the tab bar and the route painted: the sheet refracts the page as it lay under the bar, with no tab bar glass in it and no barrier scrim, and glass inside the sheet reads that page rather than the sheet's surface; wrap it in LiquidGlassBatch.exclude to get that back. Outside a scaffold, or with the scaffold's batch off, the flag does nothing. On the Skia / Web capture path there is no shared key and it does nothing either.

Dart
showLiquidGlassSheet<String>(
  context: context,
  header: const Padding(
    padding: EdgeInsets.fromLTRB(20, 2, 20, 12),
    child: Text('Share', style: TextStyle(fontSize: 22, fontWeight: FontWeight.w700)),
  ),
  builder: (context) => Column(
    mainAxisSize: MainAxisSize.min,
    children: [ ... ],
  ),
);

التوقيع

showLiquidGlassSheet({ required BuildContext context, required WidgetBuilder builder, LiquidGlassStyle? style, LiquidGlassSheetAnchor anchor = LiquidGlassSheetAnchor.floating, Widget? header, bool grabber = true, Color? grabberColor, EdgeInsets? margin, EdgeInsetsGeometry padding = const EdgeInsets.fromLTRB(20, 4, 20, 20), Color? foregroundColor, bool avoidKeyboard = true, LiquidGlassTouch? touch, bool batch = false, Color? barrierColor, String? barrierLabel, bool isScrollControlled = false, double scrollControlDisabledMaxHeightRatio = 9.0 / 16.0, bool useRootNavigator = false, bool isDismissible = true, bool enableDrag = true, bool useSafeArea = false, BoxConstraints? constraints, RouteSettings? routeSettings, AnimationController? transitionAnimationController, Offset? anchorPoint, })
المعاملالنوعالافتراضيالوصف
contextمطلوبBuildContext
builderمطلوبWidgetBuilder
styleLiquidGlassStyle?
anchorLiquidGlassSheetAnchorLiquidGlassSheetAnchor.floating
headerWidget?
grabberbooltrue
grabberColorColor?
marginEdgeInsets?
paddingEdgeInsetsGeometryconst EdgeInsets.fromLTRB(20, 4, 20, 20)
foregroundColorColor?
avoidKeyboardbooltrue
touchLiquidGlassTouch?
batchboolfalse
barrierColorColor?
barrierLabelString?
isScrollControlledboolfalse
scrollControlDisabledMaxHeightRatiodouble9.0 / 16.0
useRootNavigatorboolfalse
isDismissiblebooltrue
enableDragbooltrue
useSafeAreaboolfalse
constraintsBoxConstraints?
routeSettingsRouteSettings?
transitionAnimationControllerAnimationController?
anchorPointOffset?

معرَّف في lib/src/widgets/components/liquid_glass_sheet.dart

تعداد

LiquidGlassSheetAnchor#

الدليل: الأوراق →

How a LiquidGlassSheet meets the bottom of the screen.

القيم

القيمةالوصف
floatingAn inset card: the sheet keeps a margin on all sides and rounds all four corners, so the page stays visible around it. The default.
attachedA full-width panel sitting on the bottom edge. Only the top corners are visible — the glass is built taller than the sheet and the extra hangs off the screen, so the bottom corners are never seen and no sliver of page shows under the sheet.

معرَّف في lib/src/widgets/components/liquid_glass_sheet.dart

صنف

LiquidGlassSlider#

يرث من StatefulWidgetالدليل: المزلاج →

مزلاج زجاجي يرتفع مقبضه ليصير عدسة تحت إصبعك، ويتمدّد بمقدار سرعة دفعك له، ثم يستقر عائداً إلى مساره. يملك زجاجه بنفسه فلا يحتاج عدسة حوله.

المُنشئات

const LiquidGlassSlider({ super.key, required this.value, required this.onChanged, this.onChangeStart, this.onChangeEnd, this.minimumValue = 0, this.maximumValue = 1, this.isContinuous = true, this.activeColor = const Color(0xFF0A84FF), this.inactiveColor = const Color(0x14000000), this.thumbColor = Colors.white, this.width, this.height, this.layout = const LiquidGlassSliderLayout(), this.motion = const LiquidGlassLensMotionSpec(), this.minimumIcon, this.maximumIcon, this.style, this.pixelRatio = 1.0, })
المعاملالنوعالافتراضيالوصف
keyKey?
valueمطلوبdoubleCurrent value, in minimumValue..maximumValue.
onChangedمطلوبValueChanged<double>Called with the new value — continuously while dragging when isContinuous, otherwise once per gesture.
onChangeStartValueChanged<double>?Called when a touch lands on the control.
onChangeEndValueChanged<double>?Called when the gesture ends.
minimumValuedouble0Value range, mirroring minimumValue / maximumValue.
maximumValuedouble1
isContinuousbooltrueWhether value changes are reported continuously while dragging (true, the default) or once at gesture end.
activeColorColorconst Color(0xFF0A84FF)Color of the filled (minimum-side) track portion. Defaults to the iOS system blue.
inactiveColorColorconst Color(0x14000000)Color of the unfilled track. Defaults to black at 8 %, which reads on a light page; over a dark one, pass a translucent white instead.
thumbColorColorColors.whiteColor of the contracted rest thumb.
widthdouble?Width of the control, end to end. The size most callers want, so it sits here rather than only on LiquidGlassSliderLayout.width — it is a shorthand for exactly that field and wins over it when both are given. null (the default) leaves the layout in charge.
heightdouble?Total height of the control. Vertical room rather than thumb size: the lifted thumb overhangs the track and grows further as it squashes, and all of that has to fit inside the glass capture or it is clipped mid-gesture. null (the default) derives it from the thumb sizes and the squash ceiling, which is the safe choice — set it only to reserve more or to pin the footprint, and note it is clamped to at least the lifted thumb. To resize the thumb itself, use layout's thumb pair (LiquidGlassSliderLayout.thumbWidth / liftedThumbWidth and their heights), which are tuned to each other and should move together. A shorthand for LiquidGlassSliderLayout.height, and wins over it.
layoutLiquidGlassSliderLayoutconst LiquidGlassSliderLayout()The control's full geometry: track, both thumb sizes, and the end icons. width and height override the two same-named fields on it; everything else is only settable here.
motionLiquidGlassLensMotionSpecconst LiquidGlassLensMotionSpec()How hard the lifted thumb squashes and stretches as it is carried. LiquidGlassLensMotionSpec(maxDeformation: 0) turns the deformation off entirely and leaves the plain morphing thumb.
minimumIconWidget?Optional icons at the two ends. The track shortens to make room.
maximumIconWidget?
styleLiquidGlassStyle?Glass look of the expanded thumb, its contact shadow included; null keeps defaultStyle. To change one facet and keep the rest, compose from it: LiquidGlassSlider.defaultStyle.copyWith(refraction: …). The shadow lives in appearance.shadow — retune it with defaultStyle.copyWith(appearance: defaultStyle.appearance.copyWith( shadow: …)), or drop it by handing over an appearance that carries none.
pixelRatiodouble1.0Capture resolution for the inner view.

الأعضاء الساكنة

التوقيعالوصف
LiquidGlassStyle defaultStyleThe tuned default thumb glass: a clear capsule — refraction and a soft rim, no tint — so the glass shows what is behind it rather than washing over it. The blur is deliberately near-zero: at thumb size a real blur turns the magnified content to mush, and the refraction reads sharper over a photo without it. The contact shadow is tucked in (inset: 3), so the glass overhangs it — at thumb size a full-width halo reads as a glow rather than as contact. It wraps the thumb's lens rather than living inside it, so the arc that pools below the thumb survives instead of being clipped at the outline, and it tracks the thumb's stretch as it deforms.

معرَّف في lib/src/widgets/components/slider/liquid_glass_slider.dart

صنف

LiquidGlassSliderLayout#

الدليل: المزلاج →

Geometry of a LiquidGlassSlider: the track, the two thumb sizes it morphs between, and the end icons.

The thumb has two sizes, not one. At rest it is a small solid pill lying in the track (thumbWidth × thumbHeight); the moment a finger lands it morphs into a larger glass pill (liftedThumbWidth × liftedThumbHeight) that is carried until the touch ends. The defaults are the tuned pair; scale both together if you resize one, or the morph changes proportion mid-flight.

المُنشئات

const LiquidGlassSliderLayout({ this.width = 280, this.trackHeight = 6, this.thumbWidth = 37, this.thumbHeight = 24, this.liftedThumbWidth = 58, this.liftedThumbHeight = 38.333, this.iconSize = 20, this.iconGap = 8, this.horizontalInset, this.height, })
المعاملالنوعالافتراضيالوصف
widthdouble280Total width of the control, end to end.
trackHeightdouble6Thickness of the track the thumb rides.
thumbWidthdouble37The resting thumb — a solid pill sitting in the track.
thumbHeightdouble24
liftedThumbWidthdouble58The lifted thumb — the glass pill the rest thumb morphs into while a finger is on the control.
liftedThumbHeightdouble38.333
iconSizedouble20Size of the optional end icons, and the gap between an icon and the track. Ignored when the slider has no icons.
iconGapdouble8
horizontalInsetdouble?Horizontal breathing room reserved at each end, and the control's total height. Both default to null, which derives them from the thumb sizes and the slider's squash/stretch ceiling — the lifted thumb overhangs the track's ends, the end rubber-band overshoots, and the deformation grows the pill on both axes, and all of that has to fit inside the glass capture or it is clipped mid-gesture. Set them only to override that reasoning; see derivedHorizontalInset and derivedHeight for what the numbers would otherwise be.
heightdouble?

الدوال

التوقيعالوصف
double derivedHorizontalInset(double maxDeformation)The inset horizontalInset falls back to, for a squash/stretch ceiling of maxDeformation (0 for a slider that does not deform). Three things have to fit: half the lifted thumb's overhang past the track ends, the width the deformation can add, and the rubber-band overshoot past a bound.
double derivedHeight(double maxDeformation)The height height falls back to: the lifted thumb at full squash, plus a little for the expand spring's overshoot.
double resolveHorizontalInset(double maxDeformation)The inset actually used — horizontalInset when set, else derived.
double resolveHeight(double maxDeformation)The height actually used — height when set, else derived.
LiquidGlassSliderLayout copyWith({ double? width, double? trackHeight, doub…

معرَّف في lib/src/widgets/components/slider/liquid_glass_slider_layout.dart

صنف

LiquidGlassSwitch#

يرث من StatefulWidgetالدليل: المفتاح →

A drop-in liquid-glass switch, in the sliding style where the thumb is picked up and carried rather than snapped between two ends.

You give it a value and an onChanged like a regular Switch; it owns its own LiquidGlassView, the colored track, the glass thumb and the whole touch simulation.

The behaviour it carries:

  • A two-state thumb. A contracted solid pill (37×24) morphs into an expanded glass pill (58×38.33) the instant a touch lands (0.4 s, ζ 0.6 — bouncy), and back on release (0.6 s, ζ 0.7). One size + cover-fade morph, since both layers scale in lockstep.
  • The thumb rides the finger 1:1, relative to where it was at touch-down, with a sqrt(overrun) rubber band past the two resting positions, saturating at LiquidGlassSwitchLayout.overshootRoom — the room the capture view actually reserves, which an unbounded sqrt would spend under a mouse. The track never deforms — the give is all in the thumb's overshoot.
  • Dragging into an edge toggles early. Carry the thumb to within 5 px of the far position and the state flips right there — haptic tick, track color cross-fading under your finger (0.25 s) — while the thumb stays held. Dragging back can flip it again.
  • Tap vs drag by distance, then time. A gesture that carried the thumb past kTouchSlop is a drag however brief it was — a mouse crosses the whole control inside any tap window. Anything else under 150 ms is a tap: haptic, color, thumb spring-gliding across (0.5 s, critically damped), contraction 0.2 s later. A drag that never reached an edge always toggles on release, so a long-press-and-release flips the switch.
  • Programmatic changes stay calm. A new value arriving from outside while idle glides the thumb and cross-fades the track without ever expanding the thumb.

Refraction & the overhang The expanded thumb grows larger than the track, so its overhang samples the (transparent) area around the capsule. The shader honors the captured texel's alpha, so that overhang renders as transparent passthrough instead of a black blob. On the Impeller backdrop path the overhang refracts the live backdrop directly.

Sizing The track's two sizes are parameters here:

They size the track, not the thumb. For the whole switch in proportion, scale the layout — layout: const LiquidGlassSwitchLayout() .scaled(1.4) — and for anything finer, state it on layout outright: a LiquidGlassSwitchLayout carries the track's width and height, the thumb's resting size and the size it swells to. The resting positions, the travel, the rubber band's limits and the capture view are all derived from those. width and height are a shorthand for the layout's own two fields and win over them.

The control's layout footprint is the track alone (63×28 by default) while the glass painted around it is larger: the expanded thumb and its bounce overflow the footprint deliberately and are not clipped to it. See reserveSwellRoom if an ancestor clips.

Dart
LiquidGlassSwitch(
  value: _on,
  onChanged: (v) => setState(() => _on = v),
  activeColor: const Color(0xFF34C759),
)
Dart
LiquidGlassSwitch(
  value: _on,
  onChanged: (v) => setState(() => _on = v),
  width: 84,
  height: 38,
)

المُنشئات

const LiquidGlassSwitch({ super.key, required this.value, required this.onChanged, this.activeColor = const Color(0xFF34C759), this.inactiveColor = const Color(0x4C787880), this.thumbColor = Colors.white, this.width, this.height, this.layout = const LiquidGlassSwitchLayout(), this.style, this.reserveSwellRoom = false, this.pixelRatio = 1.0, })
المعاملالنوعالافتراضيالوصف
keyKey?
valueمطلوبboolWhether the switch is on.
onChangedمطلوبValueChanged<bool>Called at every state flip: a tap, a drag reaching an edge, or a released drag that toggled.
activeColorColorconst Color(0xFF34C759)Track color while on. Defaults to iOS system green.
inactiveColorColorconst Color(0x4C787880)Track color while off.
thumbColorColorColors.whiteColor of the contracted rest thumb.
widthdouble?Width of the track — the capsule the thumb rides along, and the control's layout footprint. The size most callers want, so it sits here rather than only on LiquidGlassSwitchLayout.width — it is a shorthand for exactly that field and wins over it when both are given. null (the default) leaves the layout in charge. Widening the track lengthens the travel; it does not resize the thumb.
heightdouble?Height of the track. The track is always a capsule, so its corner radius is half of this. A shorthand for LiquidGlassSwitchLayout.height, and wins over it. It sizes the track, not the thumb — the thumb is a separate tuned pair (rest and held), and a track shortened past it leaves the handle overhanging. To resize the whole switch in proportion, scale the layout instead: layout: const LiquidGlassSwitchLayout() .scaled(1.4).
layoutLiquidGlassSwitchLayoutconst LiquidGlassSwitchLayout()Size of the track and of the thumb that rides it. Defaults to the iOS-26 63×28 capsule; everything else — the resting positions, the travel, the rubber band, the capture view — is derived from it. width and height override the two same-named fields on it; everything else is only settable here.
styleLiquidGlassStyle?Glass look of the expanded thumb, its contact shadow included; null keeps defaultStyle. To change one facet and keep the rest, compose from it: LiquidGlassSwitch.defaultStyle.copyWith(refraction: …). The shadow lives in appearance.shadow — retune it with defaultStyle.copyWith(appearance: defaultStyle.appearance.copyWith( shadow: …)), or drop it by handing over an appearance that carries none.
reserveSwellRoomboolfalseWhether to claim layout space for the thumb's swell. By default the switch measures just its track and the expanded thumb paints outside it. The trade-off is that a clipping ancestor (a ClipRRect, a Card, a scroll view with clipBehavior on) cuts the swell off at the track's edge. Set this to true in that case: the switch then measures LiquidGlassSwitchLayout.viewWidth × viewHeight, large enough to contain the swell and the rubber band's overrun.
pixelRatiodouble1.0Capture resolution for the inner view. 1.0 is a good default; use less for cheaper captures, 0.0 for the device pixel ratio.

الأعضاء الساكنة

التوقيعالوصف
LiquidGlassStyle defaultStyleThe tuned default thumb glass: a clear pill — refraction and rim only, no tint. The glass shows what is behind it rather than washing over it, so the solid body, not a milky fill, is what reads as the thumb. The blur is deliberately near-zero: at thumb size the refraction reads sharper over a photo without one. The contact shadow is tucked in (inset: 3), so the glass overhangs it — at thumb size a full-width halo reads as a glow rather than as contact. It arrives with the glass: its strength is tied to the morph, so the solid rest pill wears nothing and the shadow fades up as the thumb becomes a glass pill under your finger. It is painted beneath the thumb, so the glass sits over its own shadow.

معرَّف في lib/src/widgets/components/switch/liquid_glass_switch.dart

صنف

LiquidGlassSwitchLayout#

الدليل: المفتاح →

Sizing for LiquidGlassSwitch — the track behind the thumb and the two sizes the thumb itself morphs between.

Everything the control positions is derived from these numbers: the two resting positions, the travel between them, the rubber band's limits, and the capture view painted around the whole thing. Change width and height and the rest follows.

The defaults are the iOS-26 sliding switch: a 63×28 track carrying a 37×24 handle that swells to 58×38.33 while it is held — wider AND taller than the track it sits in, which is why the thumb overhangs.

المُنشئات

const LiquidGlassSwitchLayout({ this.width = 63, this.height = 28, this.padding = 2, this.thumbWidth = 37, this.thumbHeight = 24, this.expandedThumbWidth = 58, this.expandedThumbHeight = 38.333, this.pinchedHeight = 22, this.overshootRoom = 20, // ignore: deprecated_member_use_from_same_package this.pressedScale = 1.5, // ignore: deprecated_member_use_from_same_package this.thumbExtraWidth = 28, // ignore: deprecated_member_use_from_same_package this.thumbExtraHeight = 18, })
المعاملالنوعالافتراضيالوصف
widthdouble63Width of the track — the capsule the thumb rides along. This is also the control's layout footprint.
heightdouble28Height of the track. The track is always a capsule, so its corner radius is half of this.
paddingdouble2Inset of the resting thumb inside the track, on every side. With the defaults this is the 2 px gap you see around the white pill.
thumbWidthdouble37Width of the thumb at rest. Should exceed thumbHeight for the pill look.
thumbHeightdouble24Height of the thumb at rest. height - padding * 2 keeps the classic snug fit; anything smaller floats it in the track.
expandedThumbWidthdouble58Width of the thumb while it is held, once it has become glass.
expandedThumbHeightdouble38.333Height of the thumb while it is held. Larger than height is the intended look — the glass pill is meant to overhang the track.
pinchedHeightdouble22Height of the shrunken slice of track drawn behind the glass. This is the separate track — not the one you see at the ends. While the thumb is lifted, a pill-shaped hole is cut where the glass sits and a smaller copy of the whole capsule is dropped into it, so the track reads full size on either side of the thumb and shrunken underneath it. Leave it equal to height and there is no pinch at all; the further below height you take it, the more the slice pulls away from the glass rim. Stated as a height, but it sets a uniform scale: the ratio bodyScale shrinks the width by the same amount, so the slice stays the same shape rather than becoming a squashed one. It does not animate — the slice holds this size for the whole gesture, and only the hole grows with the thumb, so nothing appears to scale as the glass uncovers it.
overshootRoomdouble20How far past a resting position the rubber band is allowed to carry the thumb before the capture view runs out of room. The band advances by sqrt(overrun), so this is generous: 20 px of room absorbs roughly 400 px of finger travel past the end. Drag further than that and the thumb's overhang clips.
pressedScaledouble1.5How much the handle used to swell while it was held. No longer read: the held size is stated outright by expandedThumbWidth / expandedThumbHeight, so the thumb can grow by different amounts on the two axes instead of one uniform scale.
thumbExtraWidthdouble28Extra width the handle used to gain at the peak of the slide. No longer read: the held size is stated outright by expandedThumbWidth.
thumbExtraHeightdouble18Extra height the handle used to gain at the peak of the slide. No longer read: the held size is stated outright by expandedThumbHeight.

الخصائص

الاسمالنوعالوصف
bodyScaledoubleUniform scale of the slice behind the glass, pinchedHeight / height. 1 leaves the track untouched under the thumb.
minThumbCenterXdoubleThumb centre when off — the left resting position, in track coordinates.
maxThumbCenterXdoubleThumb centre when on — the right resting position.
traveldoubleDistance the thumb covers between the two resting positions.
padXdoubleHorizontal room the capture view needs on each side of the track: how far the expanded thumb reaches past the track at a resting position, plus the rubber band's allowance.
padYdoubleVertical room the capture view needs above and below the track.
viewWidthdoubleFull size of the capture view painted around the track.
viewHeightdouble

الدوال

التوقيعالوصف
LiquidGlassSwitchLayout copyWith({ double? width, double? height, double? p…
LiquidGlassSwitchLayout scaled(double factor)A layout scaled uniformly by factor — the quickest way to make the whole switch bigger without re-picking seven numbers.

معرَّف في lib/src/widgets/components/switch/liquid_glass_switch_layout.dart

صنف

LiquidGlassMorph#

يرث من StatefulWidgetالدليل: التحوّل →

A sheet of liquid glass that MORPHS to fit whatever you put in it.

Swap the child and the glass measures the new one and flows to its size. You do not tell it how big to be:

That is the whole API. Add a row to Menu and the glass grows to match, because there is only one truth about how big the menu is and the glass reads it rather than being told it a second time.

width and height are OVERRIDES for the cases where you genuinely own a dimension — a sheet with detents, a fixed-width menu whose height varies. Set one and that axis is pinned while the other is still measured; set both and nothing is measured at all.

How it moves

The morph is two blobs of one liquid, rendered as a single surface by a LiquidGlassBlender. The new shape is born inside the old one and grows around its anchor while that anchor travels to where it will finally sit; the old shape lingers, then drains into it. The two are joined by the blender's smooth union while they overlap, so mid-morph the outline has a waist — which is what a resize can never have and what the eye reads as liquid. At rest the blobs coincide and the union is switched off, so the resting shape is exact.

Which of those you get is the motion: pick one of its presets. One of them, LiquidGlassMorphMotion.plain, is not two blobs at all but a single lens whose outline springs from the old shape to the new: no neck, no drain, one backdrop pass. Any preset becomes that kind with copyWith(blended: false).

Each blob keeps its own corner curve and radius. Nothing is interpolated or swapped: the old shape is drawn as the old shape until it is gone, and the new one as its own from the moment it appears.

Content is not stretched with the glass. The old child blurs, fades and scales out; the new one blurs, fades and scales in around the anchor as the glass arrives, pinned where it will finally sit. Both run on the morph's own clock — its duration is the spring's period — at the fractions LiquidGlassMorphAdvanced names: the old child is gone by contentOutEnd, the new one starts at contentInStart and has landed by contentInEnd. A swap that reverses mid-flight carries each child on from wherever it had got to, so a child that had not yet appeared never does. And the material thickens as the glass grows — more blur, deeper refraction — the way Apple's does when a menu opens from a button.

Layout, and why alignment matters

This widget fills the box it is given and places the glass inside that box according to alignment. It has to: a size on its own does not say which way a surface should grow, and that is the difference between a morph that works anywhere and one that only works in the middle.

Centred, both edges move and every direction looks correct — which is exactly why getting this wrong stays invisible until you move the thing. At Alignment.centerLeft the left edge holds and it opens rightward; at Alignment.bottomRight that corner holds and it opens up and to the left. Grow symmetrically at an edge and the surface walks across the screen or straight off it.

Alignment is continuous, so this is not a nine-position menu: an axis at x sends (1 + x) / 2 of any size change out one side and the rest out the other. Alignment(-0.37, 0.12) is as valid as Alignment.centerLeft. If the surface is placed by something that is not an alignment — a Positioned, a list, a drag — use alignmentFor to recover the alignment its own rect implies.

Under unbounded constraints there is no box to anchor inside, so the widget shrinks to the glass and alignment stops mattering.

Dart
LiquidGlassMorph(
  alignment: Alignment.bottomRight,
  motion: LiquidGlassMorphMotion.fluid,
  child: open
      ? const Menu(key: ValueKey('menu'))
      : const Icon(Icons.more_horiz, key: ValueKey('dots')),
)

المُنشئات

const LiquidGlassMorph({ super.key, this.width, this.height, this.alignment = Alignment.center, this.motion = LiquidGlassMorphMotion.fluid, this.style = const LiquidGlassStyle(), this.smoothness = 40, this.onEnd, this.debugClipBounds = false, this.child, })
المعاملالنوعالافتراضيالوصف
keyKey?
widthdouble?Pins the width instead of measuring it. null — the default — takes the width from child.
heightdouble?Pins the height instead of measuring it. null — the default — takes the height from child.
alignmentAlignmentAlignment.centerWhich edge or corner HOLDS while the surface changes size. See the class docs — this is the parameter people leave at centre and later regret.
motionLiquidGlassMorphMotionLiquidGlassMorphMotion.fluidThe physics. Pick a preset: LiquidGlassMorphMotion.fluid, LiquidGlassMorphMotion.anchoredPop, LiquidGlassMorphMotion.droplet or LiquidGlassMorphMotion.calm.
styleLiquidGlassStyleconst LiquidGlassStyle()The glass material. Its shape is the shape of the DESTINATION: the corner curve and radius the glass takes once it arrives, and the one the new blob is drawn with from the moment it appears. Border, light, tint, blur and refraction are passed through, with blur and refraction thickened as the glass grows. With no shape at all the surface is a capsule: the radius resolves to half the short side.
smoothnessdouble40Peak radius, in logical pixels, of the neck between the two blobs — the same quantity as LiquidGlassBlender.smoothness. It is nothing while the blobs coincide and full once they have parted, so the resting shape is never inflated by it.
onEndVoidCallback?Called once the springs settle, the way AnimatedContainer.onEnd is.
debugClipBoundsboolfalseDebug: outline the blender's backdrop clip region in magenta. Forwarded verbatim to LiquidGlassBlender.debugClipBounds, so it shows the union of the two blobs inflated by the rim/blur/refraction/bridge margin — the region the costly backdrop pass actually runs over. The morph is the widget that moves that region every frame, so this is the way to see it grow and shrink through a swap. Diagnostic only — it costs performance. Leave it false in production.
childWidget?The content, and — unless width and height say otherwise — the thing that decides how big the glass is. Give your children keys: a child that keeps its type is not seen as new — it will neither cross-fade nor be re-measured as a swap. It must be able to size itself: whatever it reports under a loose constraint is what the glass becomes. A Column of rows works; a bare Column with crossAxisAlignment: stretch will report the full width it is offered, which is probably not what you meant.

الأعضاء الساكنة

التوقيعالوصف
Alignment alignmentFor(Rect rect, Rect field)The alignment that would place rect inside field — the exact inverse of Alignment.inscribe. For surfaces positioned by something other than an alignment. A rect inside a field implies the alignment that would have put it there, so anything with a position can be handed the anchor that position wants: Returns 0 on an axis where the box fills the field, since every alignment then places it identically.

معرَّف في lib/src/widgets/components/morph/liquid_glass_morph.dart

صنف

LiquidGlassMorphMotion#

الدليل: التحوّل →

How a LiquidGlassMorph travels between shapes.

Pick a preset and stop there — they are the whole point:

A morph is two blobs of one liquid, not a box being resized. The new shape is born inside the old one and grows around its anchor while that anchor travels to where it will finally sit; the old shape lingers, then drains into it. The two stay joined by a neck until the new shape has swallowed the old. This descriptor is the physics of that: one spring, how far the leading blob runs ahead of its own size, and where it grows from. The quantities that are set once and left alone live in LiquidGlassMorphAdvanced, behind advanced.

blended is the exception: false drops the second blob and the blender with it, and the morph is a single lens whose outline springs from the old shape to the new — no neck, no drain, one backdrop pass. That is what plain is; any other preset becomes the one-lens kind with copyWith(blended: false).

The morph's duration — what the content timing in advanced is a fraction of — is the spring's own period, 2π / √stiffness: the number LiquidGlassMorphMotion.spring takes as duration, and 0.45 s for the presets.

Dart
motion: LiquidGlassMorphMotion.fluid        // the default: a blob that leaps and drags a neck
motion: LiquidGlassMorphMotion.anchoredPop  // grows from its corner, no neck
motion: LiquidGlassMorphMotion.droplet      // born small, long neck
motion: LiquidGlassMorphMotion.calm         // no bounce, for sheets and cards
motion: LiquidGlassMorphMotion.plain        // one lens, one spring, no blending

المُنشئات

const LiquidGlassMorphMotion({ this.stiffness = 195, this.damping = 19.5, this.stretch = 0.6, this.anchor = Alignment.center, this.blended = true, this.advanced = const LiquidGlassMorphAdvanced(), })
المعاملالنوعالافتراضيالوصف
stiffnessdouble195Spring constant, in units of 1/s². Higher arrives sooner.
dampingdouble19.5Damping. Below 2·√stiffness the outline overshoots and comes back; at or above it, it eases in and stops.
stretchdouble0.6How far the leading blob runs ahead of its own size, 01. Growing, the new blob's ANCHOR leaps toward where it is going while its SIZE follows on the plain spring, so it pulls a neck out of the old shape before it fills in. Shrinking the roles swap: the size collapses first and the anchor slides after it, so the surface deflates and then drains. 0 is a plain resize with no neck.
anchorAlignment?Alignment.centerThe point the new shape grows around, and the point of the old shape it is born at — Apple's matched-geometry anchor. Together with the widget's alignment this sets the neck's direction. Alignment.center makes the blob leap from the old centre to the new one and drag a neck behind it. null uses the widget's own alignment, the corner that holds: the anchor then barely travels and the shape simply pops open from that corner, with no neck — which is how Apple's menus grow from a toolbar button.
blendedbooltrueWhether the morph is two blobs joined by a LiquidGlassBlender — the default — or one lens. false is the one-lens morph: a single outline springs from the old shape to the new, its anchor leading its size by stretch the same way, with both children inside it. Nothing is born, nothing drains and there is no neck, so it costs a single backdrop pass — the right kind for a surface that changes size more than it changes place. See plain.
advancedLiquidGlassMorphAdvancedconst LiquidGlassMorphAdvanced()The knobs that are set once and forgotten.
LiquidGlassMorphMotion.spring({ Duration duration = const Duration(milliseconds: 450), double bounce = 0.3, double stretch = 0.6, Alignment? anchor = Alignment.center, bool blended = true, LiquidGlassMorphAdvanced advanced = const LiquidGlassMorphAdvanced(), })

The spring SwiftUI would give you for Spring(duration:bounce:), so a value copied from a design spec lands here unchanged.

المعاملالنوعالافتراضيالوصف
durationDurationconst Duration(milliseconds: 450)The morph's duration, in seconds: the spring's period, 2π / √stiffness. This is the clock the content timing in advanced runs on. It is the duration spring was given, and 0.45 s for the built-in presets.
bouncedouble0.3
stretchdouble0.6How far the leading blob runs ahead of its own size, 01. Growing, the new blob's ANCHOR leaps toward where it is going while its SIZE follows on the plain spring, so it pulls a neck out of the old shape before it fills in. Shrinking the roles swap: the size collapses first and the anchor slides after it, so the surface deflates and then drains. 0 is a plain resize with no neck.
anchorAlignment?Alignment.centerThe point the new shape grows around, and the point of the old shape it is born at — Apple's matched-geometry anchor. Together with the widget's alignment this sets the neck's direction. Alignment.center makes the blob leap from the old centre to the new one and drag a neck behind it. null uses the widget's own alignment, the corner that holds: the anchor then barely travels and the shape simply pops open from that corner, with no neck — which is how Apple's menus grow from a toolbar button.
blendedbooltrueWhether the morph is two blobs joined by a LiquidGlassBlender — the default — or one lens. false is the one-lens morph: a single outline springs from the old shape to the new, its anchor leading its size by stretch the same way, with both children inside it. Nothing is born, nothing drains and there is no neck, so it costs a single backdrop pass — the right kind for a surface that changes size more than it changes place. See plain.
advancedLiquidGlassMorphAdvancedconst LiquidGlassMorphAdvanced()The knobs that are set once and forgotten.

الخصائص

الاسمالنوعالوصف
zetadoubleDamping ratio of the plain spring: 1 is critical, below it bounces.

الدوال

التوقيعالوصف
LiquidGlassMorphMotion copyWith({ double? stiffness, double? damping, doubl…

الأعضاء الساكنة

التوقيعالوصف
LiquidGlassMorphMotion fluidA menu leaping out of a toolbar button: the blob's centre leaves first, the size follows, the old shape lingers and drains after it.
LiquidGlassMorphMotion anchoredPopGrows from the corner that holds, the way Apple's own menus pop open from their button. The anchor barely travels, so there is no neck: the liquid read is the bounce, the thickening, and the content materialising around the corner.
LiquidGlassMorphMotion plainOne lens, one spring: the outline goes from the old shape to the new with a little lead in its anchor and nothing else — no second blob, no neck, no drain, a single backdrop pass. The content still cross-fades on the morph's clock.
LiquidGlassMorphMotion dropletA droplet: born small inside the old shape, leaps hard, drags a long neck, and fills in late.
LiquidGlassMorphMotion calmNo overshoot and a short neck: for sheets and large cards, where a wobble looks wrong.

معرَّف في lib/src/widgets/components/morph/liquid_glass_morph_motion.dart

صنف

LiquidGlassMorphAdvanced#

The knobs of LiquidGlassMorphMotion that are set once and then left alone — grouped so they stay out of the way without being out of reach.

Reach for them when the preset is nearly right but one thing is off: the old shape hangs around too long (linger), the new content lands too early (contentInStart), the neck is too fat too soon (neckRamp).

المُنشئات

const LiquidGlassMorphAdvanced({ this.leadBounce = 0.10, this.followDelay = 0.04, this.seedScale = 1.0, this.linger = 0.12, this.drainSpeed = 1.5, this.drainInward = 0.35, this.sourceFollows = false, this.neckRamp = 24, this.contentOutEnd = 0.40, this.contentInStart = 0.30, this.contentInEnd = 0.80, this.newScaleFrom = 0.90, this.oldScaleTo = 0.92, this.contentBlur = 8, this.contentFollow = 0.0, this.contentSlide = 0, this.thickenBlur = 0.35, this.thickenRefraction = 0.30, })
المعاملالنوعالافتراضيالوصف
leadBouncedouble0.10Extra bounce for the leading pair only. A little makes the leading blob overshoot its mark and snap back — the string-pull of a drop landing.
followDelaydouble0.04Seconds the following pair waits before it starts. Growing, the size holds while the anchor has already left, which lengthens the neck.
seedScaledouble1.0Size of the new blob at birth as a fraction of the old shape, 0.2–1. 1 is Apple's matched geometry: the new shape starts as the old frame. Smaller births a droplet inside the old shape that grows as it leaves.
lingerdouble0.12Seconds the old shape holds still before it starts to drain away.
drainSpeeddouble1.5Stiffness multiplier of the drain. Higher empties the old shape sooner.
drainInwarddouble0.35Where the old shape drains to, 0–1: 0 shrinks in place, 1 shrinks toward the new shape's centre. Some inward pull keeps its last pixels inside the new shape, so no stray rim is left at the edge.
sourceFollowsboolfalsetrue flows the old shape INTO the new one instead of draining it to nothing. Reads as one body of liquid changing shape, with less of a droplet.
neckRampdouble24Over how many px of separation the neck reaches the widget's full smoothness. Coincident blobs get none — a union of two identical outlines would otherwise sit a quarter of the radius outside the true one — and parted blobs get all of it.
contentOutEnddouble0.40Fraction of the morph's duration by which the old child is gone.
contentInStartdouble0.30Fraction of the morph's duration at which the new child starts to appear — faint, blurred and scaled down, inside the glass wherever the glass has reached by then.
contentInEnddouble0.80Fraction of the morph's duration by which the new child is fully there: opaque, sharp and at scale. Below 1 it lands before the glass settles, which is what keeps the blur off the end of the morph.
newScaleFromdouble0.90The new child's scale when it starts to appear; it settles at 1.
oldScaleTodouble0.92The old child's scale as it vanishes. Below 1 it recedes; above 1 it bursts.
contentBlurdouble8Blur, in px, at the faintest point of each child's fade.
contentFollowdouble0.00 pins content where it finally sits and lets the glass travel over it; 1 rides it along with the blob that carries it.
contentSlidedouble0Px the new child slides in from, along the direction of travel.
thickenBlurdouble0.35How much thicker the blur reads at the large size, as a fraction of the style's own sigma. Apple: "a thicker, more substantial material".
thickenRefractiondouble0.30Same, for the refraction band and depth.

الدوال

التوقيعالوصف
LiquidGlassMorphAdvanced copyWith({ double? leadBounce, double? followDelay…

معرَّف في lib/src/widgets/components/morph/liquid_glass_morph_motion.dart

التعليقات

التعليقات هي مناقشات GitHub — يمكنك الرد من أيٍّ من المكانين.