fresnel_ios
The iOS implementation of fresnel.
Apps should not depend on this package directly. Depend on fresnel; the
Flutter tool selects this package automatically on iOS.
What it bridges
| Dart | SwiftUI |
|---|---|
GlassSurface |
.glassEffect(_:in:) |
GlassContainer |
GlassEffectContainer(spacing:) |
GlassElementView(id:) |
.glassEffectID(_:in:) |
GlassElementView(unionId:) |
.glassEffectUnion(id:namespace:) |
GlassShape.concentric() |
ConcentricRectangle |
GlassSymbol |
UIImage(systemName:withConfiguration:) |
Why SwiftUI and not UIKit
UIKit's UIGlassEffect renders a material, but has no equivalent of
glassEffectID morphing or glassEffectUnion. Bridging SwiftUI through a
UIHostingController is the only route to the full material, which is why this
package hosts SwiftUI views rather than wrapping UIVisualEffectView.
How the bridge is shaped
The platform view renders only the material. Your content stays an ordinary
Flutter subtree composited above it, and the hosting view has
isUserInteractionEnabled = false with
hitTestBehavior: PlatformViewHitTestBehavior.transparent.
That means text rendering, gesture handling, navigation and accessibility never
cross the FFI boundary. Interactive glass is driven by an explicit setPressed
call from Flutter's gesture arena rather than by the native view observing
touches. It costs one method channel message per press and buys immunity to the
whole class of failures — unreliable Navigator.pop, lost provider state,
broken hot reload — that comes from letting native views own interaction.
Nothing here replaces Flutter's root view controller.
Two things the container bridge gets right on purpose
Both were found by rendering to a device and looking, not by reading the docs.
Elements are placed with position, never offset. offset translates at
render time and leaves the layout frame where it was. GlassEffectContainer
derives merge geometry from layout frames, so with offset every element
reports the same rect and the whole group fuses into one blob at the origin.
glassEffectUnion is not used. It reconciles members by layout position,
and every element here is absolutely placed from a rect Flutter resolved, so a
union collapses onto a single member's shape. A union means "fuse at any
distance", and distance is what a container's spacing controls — so each
union group is rendered in its own nested GlassEffectContainer whose spacing
is derived from the group's own bounds. Deriving it beats a large constant:
spacing also sizes the shared sampling region, and an arbitrary huge value
makes the OS reserve one far bigger than the group needs.
Deployment target
The podspec and Package.swift declare iOS 13. Every glass call site is gated
with #available(iOS 26.0, *), so one binary serves both and apps on older iOS
still link and run — they simply get the shader path.
Capabilities
getCapabilities answers natively:
ProcessInfo.processInfo.isOperatingSystemAtLeast(
OperatingSystemVersion(majorVersion: 26, minorVersion: 0, patchVersion: 0)
)
Not a regex over a version string. Accessibility flags come from
UIAccessibility; hasImpeller is answered on the Dart side, which is the only
side that can see the renderer.
Verifying Apple's API surface
Published documentation for Liquid Glass is frequently wrong — glassEffect
has no isEnabled: parameter despite what most references claim. Check the SDK:
grep -n "func glassEffect" \
"$(xcrun --sdk iphonesimulator --show-sdk-path)/System/Library/Frameworks/SwiftUICore.framework/Modules/SwiftUICore.swiftmodule/arm64-apple-ios-simulator.swiftinterface"
Known issue
A Flutter BackdropFilter in the same scene suppresses native glass platform
views entirely. fresnel prevents the combination by scoping the render path
per route — see KNOWN_ISSUES.md in the repository.
License
MIT
Libraries
- fresnel_ios
- iOS implementation of
fresnel.