Skip to content

Foundation

This chapter covers flutter_miuix's low-level infrastructure: popup registration and transitions (MiuixPopupController / MiuixPopupHost / MiuixPopupScope / MiuixDialogLayout / MiuixPopupLayout), squircle rounded corners (MiuixSquircleBorder / addSquircleRect), press feedback (MiuixPressable), content color propagation (MiuixContentColor), spring & damping utilities (MiuixSpringEngine / obtainDampingDistance etc.), runtime shader wrapper (MiuixRuntimeShader), scroll-end haptic feedback (MiuixScrollEndHaptic), and vector icons (MiuixVectorIcon / miuixParsePath).

MiuixPopupTransitionBuilder

Type alias for the popup content transition builder. MiuixPopupTransition.builder is of this type; it receives 0..1 progress (0 = fully hidden, 1 = fully shown), the child, and returns the transitioned widget.

Signature:

dart
typedef MiuixPopupTransitionBuilder = Widget Function(
  BuildContext context,
  Animation<double> animation,
  Widget child,
);
ParameterTypeDescription
contextBuildContextBuild context
animationAnimation<double>Transition progress, always clamped to 0..1; 0=fully hidden, 1=fully shown
childWidgetThe child to be transitioned

MiuixPopupController

Controls the visibility of a dialog or plain popup. Extends ChangeNotifier and implements ValueListenable<bool>.

Field / MethodTypeDescription
MiuixPopupController({visible = false})constructorInitial visibility, default false
visibleboolCurrent visibility; setter notifies listeners on change
valueboolCurrent ValueListenable value, equivalent to visible
visibleListenableValueListenable<bool>Returns this, convenient for animation listening
show()voidEquivalent to visible = true
dismiss()voidEquivalent to visible = false
toggle()voidEquivalent to visible = !visible

The controller can be retained across layout rebuilds or directly listened to via visibleListenable.

MiuixPopupTransition

Describes an enter or exit transition.

ParameterTypeDefaultDescription
builderMiuixPopupTransitionBuilderrequiredReceives 0..1 progress, child; returns the transition widget
durationDurationrequiredAnimation duration in non-spring mode
curveCurveCurves.linearCurve in non-spring mode
springSpringDescription?nullSpring mode; when non-null, spring simulation takes precedence
visibilityThresholddouble0.0001Tolerance for spring simulation

The progress received by builder is always clamped to 0..1; 0 = fully hidden, 1 = fully shown.

Factory MiuixPopupTransition.fade: fade-only transition.

dart
MiuixPopupTransition.fade(
  duration: const Duration(milliseconds: 200),
  curve: Curves.easeOut,
)

MiuixPopupDefaults

Default popup transition definitions (MiuixPopupDefaults._() private constructor; all fields are static final).

FieldDuration / CurveUsage
dialogDimEnter300ms / decelerateDialog dim enter
dialogDimExit250ms / decelerateDialog dim exit
popupDimEnter300ms / sinOutPlain popup dim enter
popupDimExit150ms / sinOutPlain popup dim exit
popupEnter200ms / linearPlain popup content enter (fade)
popupExit150ms / linearPlain popup content exit (fade)
largeDialogEnter300ms / spring(stiffness=438.6, ratio=0.9)Large-screen dialog enter: fade + 0.8→1 scale
largeDialogExit200ms / decelerateLarge-screen dialog exit: fade + scale to 0.8
smallDialogEnter300ms / spring(stiffness=450, ratio=0.88)Small-screen dialog enter: slide up from bottom
smallDialogExit200ms / decelerateSmall-screen dialog exit: slide down

MiuixPopupEntry

Unified popup entry in the registry, extends ChangeNotifier. Usually not created manually; register via MiuixDialogLayout or MiuixPopupLayout.

FieldTypeDefaultDescription
controllerMiuixPopupControllerrequiredController
contentWidgetBuilderrequiredContent builder
enterTransition / exitTransitionMiuixPopupTransition?nullCustom enter/exit transitions; null uses defaults
enableWindowDimbooltrueWhether window dim is enabled
dimEnterTransition / dimExitTransitionMiuixPopupTransition?nullCustom dim transitions; null uses defaults
zIndexdoubleassigned by registryStack order
orphanedboolfalseWhether the host has relinquished ownership (see below)
isDialogbool(overridden by subclass)Whether this is a dialog entry

orphaned mechanism: when true, the _MiuixHostedEntry is responsible for disposing of this entry after the exit animation finishes and the entry is removed from the registry; when false, the entry is still owned by the host, and the HostedEntry must not dispose of it — otherwise, when the host shows the dialog again, addListener would be called on an already-disposed ChangeNotifier, throwing use-after-dispose.

MiuixDialogEntry

Dialog entry, extends MiuixPopupEntry.

Extra parameterTypeDefaultDescription
enableAutoLargeScreenbooltrueAuto-switch enter/exit transitions by large/small screen
dimAlphaValueListenable<double>?nullDim alpha linkage (e.g., following scroll opacity)
onDismissFinishedVoidCallback?nullCallback when exit animation finishes

isDialog is always true.

MiuixPlainPopupEntry

Plain popup entry, extends MiuixPopupEntry.

Extra parameterTypeDefaultDescription
enableBackHandlerbooltrueWhether to intercept the back button (only the topmost takes effect)

isDialog is always false.

MiuixPopupRegistry

Holds the dialogs and plain popups in a mount layer and assigns z-order by registration order. Extends ChangeNotifier.

Field / MethodTypeDescription
MiuixPopupRegistry.fallbackstaticProcess-level fallback registry used when no MiuixPopupScope is installed
dialogsList<MiuixDialogEntry>Dialog entries (unmodifiable view)
popupsList<MiuixPlainPopupEntry>Plain popup entries (unmodifiable view)
isEmptyboolWhether empty
entriesIterable<MiuixPopupEntry>All entries (dialogs first, then popups)
contains(entry)boolWhether the entry is present
add(entry)voidAdds and assigns zIndex; auto-listens to controller/entry changes
remove(entry)voidRemoves and unlistens; resets zIndex when empty

MiuixPopupScope

An InheritedWidget that provides local/root popup registries to a subtree. Each Scope has its own local registry; nested Scopes inherit the outermost root by default, and establishRoot establishes a new root boundary.

ParameterTypeDefaultDescription
childWidgetrequiredSubtree
registryMiuixPopupRegistry?nullCustom local registry; null uses an internally created one
establishRootboolfalseWhether to establish a new root boundary
Static methodReturnsDescription
of(context, {root = false})MiuixPopupRegistryReturns local or root of the current Scope; returns fallback if not wrapped
maybeOf(context, {root = false})MiuixPopupRegistry?Same as above, but returns null if not wrapped

MiuixDialogLayout

Registers a dialog in the current Scope; does not paint anything itself (build returns SizedBox.shrink()).

ParameterTypeDefaultDescription
controllerMiuixPopupControllerrequiredController
contentWidgetBuilder?requiredContent builder; null hides the dialog and auto-dismisses if visible
enterTransition / exitTransitionMiuixPopupTransition?nullCustom enter/exit transitions
enableWindowDimbooltrueWhether dim is enabled
enableAutoLargeScreenbooltrueSwitch transitions by large/small screen
dimEnterTransition / dimExitTransitionMiuixPopupTransition?nullCustom dim transitions
dimAlphaValueListenable<double>?nullDim alpha linkage
onDismissFinishedVoidCallback?nullExit animation finished callback
renderInRootbooltrueWhether to register in the root registry (true) or local (false)

MiuixPopupLayout

Registers a plain popup in the current Scope; does not paint anything itself.

ParameterTypeDefaultDescription
controllerMiuixPopupControllerrequiredController
contentWidgetBuilder?requiredContent builder; null hides the popup
enterTransition / exitTransitionMiuixPopupTransition?nullCustom enter/exit transitions
enableWindowDimbooltrueWhether dim is enabled
enableBackHandlerbooltrueWhether to intercept the back button
dimEnterTransition / dimExitTransitionMiuixPopupTransition?nullCustom dim transitions
renderInRootbooltrueWhether to register in the root registry

MiuixPopupHost

Paints all entries in the registry, intercepts lower-layer pointers, and handles the back button for the topmost plain popup.

ParameterTypeDefaultDescription
childWidget?nullLower-layer content; when non-null, the Host acts directly as a Stack wrapper
registryMiuixPopupRegistry?nullCustom registry; null uses MiuixPopupScope.of(context)
windowDimmingColorColorColor(0x4D000000)Default dim color

Back-button handling: system back is intercepted via PopScope. canPop is false only when there is a visible popup with enableBackHandler=true; pressing back calls controller.dismiss() on the topmost popup.

Typical usage (app root):

dart
MiuixPopupHost(
  child: MaterialApp(home: MyApp()),
)

MiuixPopupUtils

Convenience static utility entry point (MiuixPopupUtils._() private constructor; only static methods are exposed).

MethodEquivalent to
MiuixPopupUtils.dialogLayout({...})MiuixDialogLayout(...)
MiuixPopupUtils.popupLayout({...})MiuixPopupLayout(...)

isMiuixLargeScreen(context)bool

Whether the current logical window meets the Miuix large-screen threshold: width ≥ 840 and height ≥ 480.

Squircle rounded corners

The signature HyperOS smooth corner, approximating a superellipse with cubic Béziers (control ratio 0.643).

SquircleDefaults

ConstantValueDescription
extension1.1Tile size multiplier relative to cornerRadius; 1.0=arc, 1.1=continuous corner
extensionMin1.0Lower bound of extension
extensionMax2.0Upper bound of extension

addSquircleRect(path, width, height, cornerRadius, {extension, enabled})

Appends a squircle rounded rectangle to path.

ParameterTypeDefaultDescription
pathPathrequiredTarget Path
width / heightdoublerequiredPixel dimensions; non-positive values are skipped
cornerRadiusdoublerequiredCorner radius; clamped to half the shorter side
extensiondoubleSquircleDefaults.extensionTile multiplier; clamped to [1.0, 2.0]
enabledbooltrueWhen false, falls back to a regular rounded rectangle

MiuixSquircleBorder

A ShapeBorder whose outline is a squircle. Can be used directly with ShapeDecoration, PhysicalShape, Material, etc.

ParameterTypeDefaultDescription
cornerRadiusdouble0.0Corner radius (logical pixels)
extensiondoubleSquircleDefaults.extension (1.1)Tile multiplier
enabledbooltrueWhether squircle is enabled; false falls back to a regular corner
sideBorderSideBorderSide.noneBorder

Implements dimensions, getInnerPath, getOuterPath, paint, scale, ==, hashCode, so it can be used directly as ShapeDecoration.shape.

Example:

dart
Container(
  width: 80, height: 80,
  decoration: ShapeDecoration(
    color: Colors.blue,
    shape: MiuixSquircleBorder(cornerRadius: 24),
  ),
)

MiuixPressable

Miuix-style pressable container. Overlays a translucent mask on the child; on press/hover/focus, the alpha is driven by springs, with optional sink (scale-down) or tilt (3D rotation) feedback.

ParameterTypeDefaultDescription
onPressedVoidCallback?requiredClick callback; null forces enabled=false
childWidgetrequiredChild
enabledbooltrueWhether enabled
feedbackTypeMiuixPressFeedbackTypenoneExtra feedback type
sinkAmountdouble0.94Sink feedback scale target
tiltAmountdouble8.0Tilt feedback max angle (degrees)
overlayColorColor?nullMask color; null uses MiuixTheme.colors.onBackground
borderRadiusBorderRadius?nullMask corner radius; mutually exclusive with shape
shapeShapeBorder?nullMask shape (e.g., squircle/stadium); takes precedence over borderRadius
heldDownboolfalseExternally forced "held-down" state (used by Preference, menu items)
autofocusboolfalseWhether to autofocus
focusNodeFocusNode?nullExternal focus node
semanticLabelString?nullAccessibility label
buttonbooltrueWhether to mark as button semantics (set false for Checkbox/Switch)
behaviorHitTestBehavioropaqueHit-test behavior
onLongPressVoidCallback?nullLong-press callback

MiuixPressFeedbackType

Press visual feedback type.

ValueDescription
noneNo feedback (only the press mask)
sinkSlight scale-down on press
tilt3D tilt based on touch position on press

Mask alpha increments: hover +0.06, focus +0.08, press +0.10; they stack.

Example:

dart
MiuixPressable(
  onPressed: () {},
  feedbackType: MiuixPressFeedbackType.sink,
  shape: MiuixSquircleBorder(cornerRadius: 16),
  child: const Padding(
    padding: EdgeInsets.all(16),
    child: Text('Press me'),
  ),
)

MiuixContentColor

Propagates a default "content color" (text/icon color) to the subtree. Pushed by containers like MiuixSurface / MiuixCard / MiuixButton so children like MiuixText / MiuixIcon can pick it up by default.

Parameter / MethodTypeDefaultDescription
colorColorrequiredContent color
childWidgetrequiredSubtree
MiuixContentColor.of(context)ColorReturns the nearest ancestor's content color; black if not wrapped

Spring & damping utilities

The underlying math and per-frame engine for Folme spring motion.

MiuixSpringDefaults

ConstantValueDescription
maxFrameDeltaSeconds0.016Max per-frame step (seconds)
minFrameDeltaSeconds0.001Min per-frame step (seconds)
highVelocityThreshold5000.0High-velocity threshold; above it, a slower natural period is used
criticalDampingRatio1.0Critical damping ratio
standardSpringPeriod0.4Standard natural period (seconds)
slowerSpringPeriodForHighVelocity0.55Natural period used at high velocity (seconds)

obtainDampingDistance(normalizedInput, range)double

Damping formula is x - x² + x³/3; normalizedInput is clamped to 0..1 and multiplied by range.

obtainTouchDistance(currentPixelOffset, range)double

Inverts damped displacement back to touch displacement. Formula: range - range^(2/3) * (range - 3*offset)^(1/3).

MiuixSpringOperator

Computes the next-frame velocity via explicit Euler integration.

ParameterDescription
dampingRatioDamping ratio
naturalPeriodNatural period (seconds, >0)

updateVelocity({currentVelocity, deltaTime, currentPosition, targetPosition}) returns the new velocity.

MiuixSpringEngine

Critically damped per-frame engine. You can manually start/step, or drive it with a Flutter Ticker via runSettleAnimation.

MethodDescription
start({startValue, targetValue, initialVelocity})Initializes a spring motion from startValue to targetValue; automatically picks standard/slower period by initial velocity
step(deltaTime)boolAdvances one frame; returns true when equilibrium is reached
runSettleAnimation({vsync, startValue, targetValue = 0, initialVelocity, onFrame, onSettle})Future<void>Drives to equilibrium via Ticker, calling onFrame(currentPosition) each frame; onSettle is called on both normal completion and cancellation

Fields velocity and currentPosition expose the current state.

Runtime shader wrapper

isRenderEffectSupported()bool

Always true. Flutter's ImageFilter / BackdropFilter is available on all target platforms.

isRuntimeShaderSupported()bool

Always true. Flutter's FragmentProgram is available on both Impeller and Skia.

MiuixRuntimeShader

Cross-platform wrapper for runtime shaders.

Flutter's FragmentShader is produced only from precompiled .frag assets (via impellerc), with uniforms set by index (setFloat(index, value)). This wrapper translates names to indices via uniformLayout (uniform name → starting float index), enabling a "set uniform by name" call style.

Parameter / FieldTypeDescription
MiuixRuntimeShader.fromProgram(program, {uniformLayout, samplerLayout})constructorConstructs from a loaded FragmentProgram
shaderui.FragmentShaderUnderlying shader; can be used directly as a ui.Shader for Paint..shader
uniformLayoutMap<String, int>uniform name → starting float index
samplerLayoutMap<String, int>sampler name → sampler index
MethodDescription
setFloatUniform(name, value)Sets a single float uniform
setFloat2Uniform(name, v1, v2)Sets a vec2 uniform
setFloat3Uniform(name, v1, v2, v3)Sets a vec3 uniform
setFloat4Uniform(name, v1, v2, v3, v4)Sets a vec4 uniform
setFloatArrayUniform(name, values)Sets a float-array uniform
setColorUniform(name, color)Sets a color uniform (RGBA 0..1)
setInputShader(name, image)Sets a sampler (takes a ui.Image)
dispose()Releases the underlying FragmentShader

Names not registered in uniformLayout / samplerLayout throw ArgumentError.

MiuixScrollEndHaptic

Triggers a haptic feedback when scrollable content is flung to the start/end boundary.

ParameterTypeDefaultDescription
hapticFeedbackTypeMiuixHapticFeedbackTypetextHandleMoveHaptic type
childWidgetrequiredSubtree containing scrollable children

MiuixHapticFeedbackType

ValueDescriptionFlutter mapping
textHandleMoveLight selection feedback (default; Android TextHandleMove)HapticFeedback.selectionClick
lightImpactLight impactHapticFeedback.lightImpact
mediumImpactMedium impactHapticFeedback.mediumImpact
heavyImpactHeavy impactHapticFeedback.heavyImpact

State machine: scrolling from the boundary back into content (when scrollDelta exceeds 1.0) resets the state; only inertial overscroll (OverscrollNotification with dragDetails == null) is handled — drag overscroll does not trigger; each boundary hit fires only once to avoid jitter.

Example:

dart
MiuixScrollEndHaptic(
  child: ListView(children: [...]),
)

Vector icons

MiuixVectorPath

Description of a single vector path.

ParameterTypeDefaultDescription
buildPath Function()requiredBuilds the path (viewport coordinates); a callback avoids sharing mutable Path
stylePaintingStylefillFill or stroke
colorColorColor(0xFF000000)Original vector color (SolidColor); used only when not tinted
alphadouble1.0Opacity (corresponds to fillAlpha / strokeAlpha)
strokeWidthdouble0.0Stroke width (0 = 1px hairline)
strokeCapStrokeCapbuttStroke cap
groupTransformMatrix4?nullGroup transform in viewport coordinates (e.g., vertical flip)

MiuixVectorIcon

Vector icon.

ParameterTypeDescription
nameStringIcon name (used for debugging and semantic fallback)
viewportSizeViewport size for path coordinates (viewportWidth/Height)
intrinsicSizeSizeDefault render size (defaultWidth/Height, logical pixels); used when MiuixIcon does not specify a size
pathsList<MiuixVectorPath>All paths of the icon (in paint order)

MiuixVectorIconPainter

A CustomPainter that draws MiuixVectorIcon onto a viewport-sized canvas; outer scaling is handled by FittedBox.

ParameterTypeDescription
iconMiuixVectorIconVector icon
tintColor?Tint color; when non-null, applies ColorFilter.mode(tint, BlendMode.srcIn) to the whole vector; null draws with original vector colors (multi-color / untinted scenarios)

miuixEvenOddPath()Path

Constructs an empty Path with the even-odd fill rule, convenient for chaining ..moveTo(...) in icon definitions.

miuixParsePath(data, {fillType})Path

Parses an SVG-style path data string into a Path. Used by extended icons (miuix-icons, 156×5 variants).

Supported commands (absolute coordinates only):

CommandMeaningArgs
M x yMove to2
L x yLine to2
Q x1 y1 x yQuadratic Bézier4
C x1 y1 x2 y2 x yCubic Bézier6
ZClose0

Numbers are whitespace-separated; command letters are individual tokens. fillType defaults to nonZero.

HorizontalTo / VerticalTo are expanded to full L x y at generation time, so H/V do not need to be handled here.

Released under the Apache-2.0 License.