NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
pub.dev · #3229 most downloaded on pub.dev
Native iOS Liquid Glass widgets for Flutter with pixel-perfect fidelity. Includes Button, Icon, TabBar, Slider, Switch, and more with reliable version detection.
Last release 1 months ago
27 Aug 2026
Ships fairly regularly
a new release about every 2 weeks
Nearly every release is documented
notes for 41 of 41 stable releases
Nothing withdrawn
no release was ever pulled
10 months old
43 releases · first in 2025
v1.6.0 — PR #66 glass imageAsset tint, PR #67 icon supersampling, PR …
Minor rather than patch because
CNGlassEffectgains a value (clear). That is source-breaking for anyone with an exhaustiveswitchover the enum. 1.5.5 was tagged during development and never published; everything it contained is here.
CNGlassEffect.clear, and LiquidGlassConfig.effect is now honouredLiquidGlassConfig.effect was serialised over the channel as 'effect': config.effect.name, parsed natively, stored, handed to the SwiftUI view and even used as an .animation(_:value:) key — and then glassEffectForConfig() ignored it and hardcoded Glass.regular on both iOS and macOS. CNGlassEffect.regular and CNGlassEffect.prominent therefore rendered identically, and SwiftUI's third variant, Glass.clear, was unreachable.
clear matters for glass over imagery: regular frosts what is behind it, clear lets it through — the treatment Apple's own media controls use over video and artwork.
Fix: CNGlassEffect.clear is added and both containers honour the value they were already being given.
prominent is unchanged and still falls back to regular — SwiftUI's Glass has no prominent counterpart to map onto. The enum value is kept so existing code compiles and so the mapping can be filled in later; it is now documented as a fallback rather than left to be discovered by screenshot. Note also that clear barely blurs, so content layered on top of it needs its own contrast handling (a shadow or scrim) in a way regular does not.
Reported and fixed by @johndavid92 (PR #69). Verified on an iPhone 16 Pro / iOS 26 simulator with panels of identical size and position over vertical stripes that never vary along y — so every panel sees a pixel-identical backdrop and any difference is the glass alone. RMSE between panel interiors:
| comparison | RMSE |
|---|---|
before — regular vs prominent |
0.0010 |
| noise floor (same panel shifted 1px) | 0.0027 |
after — regular vs prominent |
0.0010 (unchanged) |
after — regular vs clear |
0.337 |
Before the fix the two effects differed by less than a one-pixel shift of the same image: peak difference 1/255, zero differing pixels at 2% fuzz. macOS could not be built in the verification environment (pre-existing Swift Package Manager scaffolding issue, unrelated to the change), so the macOS glassEffectForConfig body was type-checked against the MacOSX26.0 SDK directly instead.
CNGlassButtonGroup still hardcodes Glass.regular in GlassButtonSwiftUI.swift on both platforms — a separate surface that does not take CNGlassEffect, left for a follow-up.
imageAsset icons don't adapt to Liquid GlassAn imageAsset supplied without an explicit color was handed to UIKit as a non-template UIImage, so it kept its baked pixels. On an iOS 26 glass button the material resolves its light/dark appearance from the backdrop, so a black-baked glyph over a dark backdrop went invisible — and a white-baked one did the same over a light backdrop. customIcon and SF Symbols already receive .alwaysTemplate and were unaffected, which is what made the inconsistency easy to miss until an app mixed the two.
Fix: uncoloured image assets are rendered as templates in CupertinoButtonPlatformView and CupertinoPopupMenuButtonPlatformView, so the control's tint — and Liquid Glass's automatic foreground adaptation — applies. Explicit imageAsset.color values keep their existing baked-tint behaviour.
Templating is gated on the artwork actually being single-colour. .alwaysTemplate keeps only the alpha channel: lossless for a black glyph or a grey stroked SVG, but it flattens anything multi-colour to a silhouette. ImageUtils.isEffectivelyMonochrome(_:) samples the image on a capped 32×32 grid, compares only near-opaque pixels (so antialiased edges don't read as a second colour) and returns on the first distinct colour — so multi-colour assets are left exactly as they were.
Also in this change:
GlassButtonSwiftUI.effectiveIconColor was tint ?? iconColor, so a control-level tint overrode an explicit per-icon colour. Now iconColor ?? tint.setButtonIcon template pass is scoped to image assets. Applied to every icon source it would also template SF Symbols, so a multicolor/palette symbol without an explicit colour rendered correctly on creation and then flattened after any icon update.Behaviour change: the plain glass style no longer sends a forced theme-accent tint from Dart, so the native side can resolve its own foreground. On iOS 26 this is mostly invisible for icons — UIButton.Configuration.glass() already overrode the image foreground, so a glass SF Symbol rendered as the adaptive system colour rather than the accent even before this release. It is visible on glass button labels, which now use the system label colour instead of CupertinoThemeData.primaryColor. Set CNButton.tint explicitly to keep an accent-coloured glass label. prominentGlass keeps its accent fallback, and the pre-iOS-26 fallback still reads primaryColor from the Cupertino theme.
Reported and fixed by @azharul-islam (PR #66). Reproduced and verified on an iPhone 16 Pro / iOS 26 simulator: over a black backdrop in light mode, a black-baked imageAsset was invisible on the glass before the fix and adapts to white after, while a deliberately four-colour asset keeps all four colours.
customIcon glyphs pixelated on thin strokesiconDataToImageBytes painted the glyph at 1× fontSize, then the "scale to fill" re-blit upscaled the cropped ink — icon fonts pad their glyphs inside the em box, so the ink is smaller than the requested size. At 1× there is no sub-pixel detail to upscale from, so thin strokes (chevrons, checkmarks, plus signs, outline glyphs) came out with visible staircase edges. Every customIcon path in the package routes through this function: CNButton, CNPopupMenuButton, CNTabBar, CNIcon, CNGlassButtonGroup.
Fix: the glyph is rasterised onto a canvas scaled to 2× the device pixel ratio, and the re-blit downscales with FilterQuality.high instead of the Paint() default of FilterQuality.none. Output dimensions are unchanged.
Measured on an iPhone 16 Pro / iOS 26 simulator against a ground-truth render — mean absolute alpha error, chevron at 20 pt, 3× screen:
| intermediate buffer | MAE | |
|---|---|---|
| before | 27k px | 7.00 |
FilterQuality change alone |
27k px | 6.82 |
2× canvas + FilterQuality.high |
108k px | 2.98 |
| 4× linear (font and canvas) | 434k px | 1.78 |
The FilterQuality change on its own is close to a no-op — the re-blit is only a ~1.1× upscale, so the filter barely matters and it measured worse on 5 of 12 glyph/size combinations. The intermediate resolution was the actual problem. The last row is what PR #67 originally proposed; it measures better but is not distinguishable at real icon sizes, and it costs 16× the pixels in a buffer that is walked pixel-by-pixel to find the ink bounds.
Reported and fixed by @azharul-islam (PR #67), refined to 2× on merge.
iconDataToImageBytes is now memoisedThere was no cache on icon rasterisation, and CNButton and CNIcon build FutureBuilder(future: iconDataToImageBytes(...)) inside build() — so every rebuild re-ran three canvas passes plus a pixel-by-pixel scan of the intermediate buffer.
Results are memoised per (glyph, font, size, colour, device pixel ratio). Futures rather than results are cached, so concurrent callers share one rasterisation; and because FutureBuilder only re-subscribes when its future's identity changes, those widgets now keep their snapshot across rebuilds instead of dropping back to a blank placeholder. The map is capped at 256 entries with oldest-first eviction, since size is caller-supplied and can be animated. clearIconImageCache() is exported for tests and for apps that swap icon fonts at runtime.
Testing → PR #66: glass imageAsset tint — puts the same artwork through SF Symbol, customIcon, uncoloured imageAsset and explicitly coloured imageAsset on a glass button, over a selectable backdrop (black / white / photo / split) and either brightness. Includes a deliberately four-colour row as the regression check for template flattening, plus popup-menu and button-group rows.Testing → PR #67: icon supersampling — renders the same glyph through the renderer with the supersample factor and FilterQuality exposed as separate knobs, blown up nearest-neighbour so real pixels are visible, with mean-alpha-error and intermediate-buffer cost per variant.Testing → PR #69: LiquidGlass config.effect — one panel per CNGlassEffect.values over vertical stripes that never vary along y, so every panel sees a pixel-identical backdrop and two panels that render differently can only differ because of the glass. Backdrop switches to a photo for the over-imagery case.example/lib/pr_probe_entry.dart — an alternate entrypoint that boots straight into one test page in one configuration via --dart-define, for capturing before/after screenshots without tapping through the demo list.One column per month.
LiquidGlassContainer's native glass rendered short of its frame whenever that frame reached into the bottom safe area (floating bottom panel on home-i
LiquidGlassContainer's native glass rendered short of its frame whenever
that frame reached into the bottom safe area (floating bottom panel on
home-indicator devices). UIHostingController propagates the screen's
safe area into its SwiftUI content, so the hosted GeometryReader/shape
was inset by the overlap.
Fixed by @fbernack in PR #64 via hostingController.safeAreaRegions = [].
Reproduced and verified on a physical iPhone (iOS 26.5): a 140pt panel
flush to the bottom of a 34pt inset rendered ~21pt short before the fix
and filled its frame completely after. Reporter measured up to 22pt on
an iPhone 17 Pro / iOS 26.4 simulator.
macOS unaffected — NSHostingController, and macOS windows have no
bottom safe-area inset in practice.
Adds Testing -> "PR #64: LiquidGlass safe-area clip" demo with live
offset/height/shape controls and Flutter-drawn frame + safe-area
reference markers so the defect is directly visible.
Co-Authored-By: Claude Opus 5 (1M context) noreply@anthropic.com
LiquidGlassContainer glass renders short in the bottom safe areaWhen a LiquidGlassContainer's frame reached into the bottom safe area — the common "floating bottom panel" layout on home-indicator devices — the native glass stopped short of the frame it was given. Flutter-drawn children stayed in the right place, so they visually spilled outside the glass.
Cause: LiquidGlassContainerPlatformView hosts the SwiftUI glass in a UIHostingController, which propagates the screen's safe area into its SwiftUI content. Where the platform view's frame overlapped the home-indicator inset, the hosted GeometryReader/shape was inset by that overlap, so the glass filled only the reduced size.
Fix: self.hostingController.safeAreaRegions = [], matching the intent of the existing safeAreaInsets override in CupertinoSwitchPlatformView.swift. safeAreaRegions is iOS 16.4+ and the class is already @available(iOS 26.0, *), so no extra availability guard is needed.
Reported and fixed by @fbernack (PR #64), who measured up to 22 pt short on an iPhone 17 Pro (iOS 26.4 simulator). Independently reproduced and verified on a physical iPhone running iOS 26.5: with a 140 pt panel pinned flush to the bottom of a 34 pt inset, the glass fell ~21 pt short of its frame before the fix and filled it completely after.
macOS is unaffected — LiquidGlassContainerNSView uses NSHostingController, and macOS windows have no bottom safe-area inset in practice.
Testing → PR #64: LiquidGlass safe-area clip — a glass panel whose bottom offset, height, and shape (rect / capsule / circle) are adjustable live. A solid red strip fills exactly the safe-area inset (the zone the bug leaves uncovered), a Flutter-drawn magenta outline marks the panel's true frame, and a cyan dashed line marks the safe-area boundary — so "glass short of frame" is directly visible rather than a judgement call. Sliding the bottom offset past the boundary shows whether the glass stays attached to its frame.CNTabBar taps were silently swallowed whenever an ancestor widget held an active gesture recognizer over the same region (e.g. requests_inspector wrap
CNTabBar taps were silently swallowed whenever an ancestor widget held
an active gesture recognizer over the same region (e.g. requests_inspector
wrapping the app in GestureDetector(onLongPress:)). The platform views
were constructed without a gestureRecognizers set, making them passive
arena participants.
Both the iOS UiKitView and macOS AppKitView branches now pass an
EagerGestureRecognizer, which resolves accepted on pointer-down so
touches forward to the native UITabBar regardless of competition.
Merged from PR #63 by @MohamedAboElM3aTy, amended before merge: the
submitted bare TapGestureRecognizer() is a no-op because Flutter's
isPointerAllowed returns false when no callbacks are set.
Also formats tab_bar.dart (contributor indentation broke dart format
and cost 10 pana points).
Follow-up sweep for the remaining components tracked in #65.
Co-Authored-By: Claude Opus 5 (1M context) noreply@anthropic.com
CNTabBar taps swallowed by ancestor gesture recognizersCNTabBar stopped responding to taps whenever any ancestor widget held an active gesture recognizer over the same region. The reporter's minimal repro:
GestureDetector(
onLongPress: () {}, // any active recognizer is enough
child: CNTabBar(items: [...], currentIndex: i, onTap: ...),
)
Tapping a tab did nothing. In the wild this surfaced via requests_inspector, which wraps the whole app in a long-press GestureDetector — installing it silently killed the tab bar.
Cause: _buildNativeTabBarPlatformView constructed the iOS UiKitView / macOS AppKitView without a gestureRecognizers set. With none supplied, the platform view is a passive arena participant and only receives touches if nothing else claims them, so the ancestor won uncontested.
Fix: both platform-view branches now pass Factory<EagerGestureRecognizer>(() => EagerGestureRecognizer()). EagerGestureRecognizer.addAllowedPointer immediately calls resolve(GestureDisposition.accepted), so the platform view claims the pointer on pointer-down and touches forward to the native UITabBar regardless of what is competing.
Note the trade-off: gestures made directly over the tab bar (long-press, drag) now go to the native bar instead of bubbling to ancestor recognizers. This is the intended behaviour for a tab bar, which is only ever tapped.
Reported and originally patched by @MohamedAboElM3aTy (#62, PR #63). The submitted patch used a bare TapGestureRecognizer(), which is a no-op — Flutter's TapGestureRecognizer.isPointerAllowed returns false when none of onTapDown/onTap/onTapUp/onTapCancel/onTapMove are set, so the recognizer never enters the arena. Amended to EagerGestureRecognizer before merge and verified on-device (iPhone, iOS 26.5).
The identical issue affects other platform-view widgets and is tracked in #65. It is not a uniform fix, so it was deliberately kept out of this release:
button.dart, switch.dart, slider.dart, popup_menu_button.dart pass a bare TapGestureRecognizer() and therefore carry the same latent no-op.segmented_control.dart, glass_button_group.dart, icon.dart, liquid_glass_container.dart, floating_island.dart, search_bar.dart, search_scaffold.dart pass no gestureRecognizers at all.icon.dart is decorative (it would steal taps from parent widgets), liquid_glass_container.dart / floating_island.dart / search_bar.dart deliberately wrap the platform view in IgnorePointer and handle interaction Flutter-side, and search_scaffold.dart's view is Positioned.fill behind all Flutter content.TapGestureRecognizer()..onTap = () {}) rather than an eager one, so drags still fall through to a parent scrollable — button.dart:654 documents that intent explicitly.Testing → #62 / PR #63: CNTabBar tap gesture arena — three switchable scenarios verifying the fix on-device: an ancestor GestureDetector(onLongPress:) (the reporter's minimal repro), an ancestor PageView (drag-vs-tap competition), and a no-ancestor baseline for regression coverage. Each scenario avoids nesting a Scrollable in the gesture region so ancestor gestures resolve deterministically.New Testing demo screen reproducing both #46 bugs.
Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com
CNBottomSheet.showCupertino compile error on Flutter < 3.44CNBottomSheet.showCupertino in v1.5.1 called Flutter's showCupertinoSheet() with a scrollableBuilder: named parameter. That parameter was only added to showCupertinoSheet in Flutter 3.44.0 (via flutter/flutter#177337, 2026-01-23). Anyone consuming the package on stable 3.35 – 3.41.x — including a substantial share of production Flutter users, FlutterFlow environments, and CI setups — hit a hard compile error:
No named parameter with the name 'scrollableBuilder'.
Fix: switched to builder: (the current API on 3.35 – 3.41; deprecated-but-still-accepted on 3.44+). The lambda didn't use its ScrollController anyway, so the swap is behaviour-preserving. Compatible from Flutter 3.35 through today's master.
Thanks @ArkayGit for the clean, precisely-scoped bug report.
CNToast queue deadlock after route popCNToast stored the caller's BuildContext on each queued _ToastEntry and re-resolved Overlay.of(entry.context) inside _showNext() — which fires from a Timer after each toast's duration. If the caller's widget was disposed while toasts were pending (typical: user shows a toast then taps back), the framework crashed with:
Looking up a deactivated widget's ancestor is unsafe.
Worse, after the throw, _isShowing was left true and the queue was permanently stuck — every subsequent CNToast.show/success/error/warning/info call would silently _queue.add() without displaying, until app restart. Only CNToast.loading() still worked (it bypasses the queue and inserts into the overlay directly).
Fix (in lib/components/toast.dart):
_ToastEntry now stores an OverlayState (resolved eagerly at CNToast.show() time), not a BuildContext. _showNext() no longer touches the caller's context and cannot throw on a deactivated widget.entry.overlay.mounted short-circuit and a try/catch around overlay.insert(...) — if the OverlayState disappears between enqueue and display (extremely rare), the entry is dropped and the queue keeps draining. _isShowing is guaranteed to reset.CNToast renders with yellow double-underline under MaterialApp (reporter's screenshot)The same issue also carried a second, unrelated defect surfaced by @macedondev's screenshot: text rendered as dim red monospace with a yellow double-underline. That's MaterialApp._errorTextStyle (flutter/lib/src/material/app.dart:45), Flutter's built-in "your Text has no Material ancestor" debug fallback. MaterialApp installs it as the ambient DefaultTextStyle at the app root; _ToastOverlay renders its Text inside an OverlayEntry with no Material between them, so the fallback style wins. CupertinoApp doesn't install _errorTextStyle, which is why the maintainer couldn't reproduce the bug locally.
Fix: wrap the toast content in Material(type: MaterialType.transparency, ...) inside _ToastOverlay.build(), positioned between IgnorePointer and Align so Positioned still sees the Overlay's Stack as its direct parent (a Material between them would break Positioned's parent-data contract and throw Incorrect use of ParentDataWidget). Transparent Material paints nothing, so CupertinoApp users see no visual change; MaterialApp users get proper text-style resolution and the yellow underlines are gone. Applies to both the queued (_showNext()) and direct-insert (loading()) toast paths.
Thanks @macedondev for the report.
Testing → #46: CNToast use_build_context_synchronously — two labeled scenarios. Bug A (queue crash on dispose): spam-and-pop button that queues 5 toasts then pops the route ~250ms in, reproducing the "Looking up a deactivated widget's ancestor" crash pre-fix and demonstrating the queue-drains-cleanly behaviour post-fix. Bug B (yellow underlines): a route whose body is a nested MaterialApp so CNToast fires into an overlay with the ambient _errorTextStyle — reproduces the reporter's exact rendering. Plus a quick-trigger row for all six CNToast.* variants for regression coverage.v1.5.1 — modal-hide mixin, CNBottomSheet, destructive popup items
v1.5.1 — modal-hide mixin, CNBottomSheet, destructive popup items
iOS hybrid composition was reusing the same PlatformViewContainer for both a host-page CN-widget and a CN-widget inside a presented sheet, causing the host-page widget's pixels to leak through the sheet's scrim (and vice-versa).
Fix: new ModalHideMixin (in lib/utils/modal_hide_mixin.dart) applied to all 9 CN widgets that use a PlatformView — CNButton, CNGlassButtonGroup, CNSwitch, CNSegmentedControl, CNPopupMenuButton, CNSearchBar, CNLiquidGlassContainer, CNFloatingIsland, CNSlider. Each widget now destroys its PlatformView (with a same-size placeholder reserving the layout slot) while a sheet covers it, and recreates it when the sheet dismisses.
Each affected widget gained an autoHideOnModal: bool = true constructor parameter so users can opt out per-instance.
CNBottomSheet + CNSheetGeometryProbeFor the modal-hide to be position-aware (only widgets actually behind the sheet hide, not the entire host route), the sheet has to publish its rect each frame. Two new public APIs cover this:
CNBottomSheet (in lib/components/bottom_sheet.dart) — drop-in wrappers that inject the probe automatically:
CNBottomSheet.show(context: context, builder: (ctx) => MySheet());
CNBottomSheet.showCupertino(context: context, builder: (ctx) => MySheet());
CNBottomSheet.showModalPopup(context: context, builder: (ctx) => MySheet());
CNSheetGeometryProbe — wrap your own sheet builder manually if you need to keep using the framework APIs directly:
showModalBottomSheet(
context: context,
builder: (ctx) => CNSheetGeometryProbe(child: MySheet()),
);
Without one of these the package falls back to a conservative "hide every CN-widget on this route while any modal is up" behavior — safe, but coarser than needed (an app-bar CN-button could disappear behind a 30%-height sheet).
CNTabBarRouteObserver also gained topModalRect: ValueNotifier<Rect?> and publishTopModalRect(Rect?), used by the probe.
CNPopupMenuItem.isDestructiveCNPopupMenuItem gained isDestructive: bool = false. When true:
UIMenuElement.Attributes.destructive → the label renders in the system destructive red (previously only the icon could be red via iconColor).UIAlertAction.Style.destructive.CupertinoActionSheetAction(isDestructiveAction: true).Thanks @ashellz for the report.
CNGlassButtonGroup's FutureBuilder returned SizedBox.shrink() for one frame between the modal-hide placeholder removal and the platform view actually mounting — text below jumped up then back down on every sheet dismiss. The pending branch now mirrors the placeholder's axis-aware dimensions so the layout slot is held across the swap.
CNSwitch rendered as a checkboxThe macOS Toggle defaulted to a checkbox under recent SDKs. Applied .toggleStyle(.switch) to force the switch appearance. iOS unaffected. Thanks @jonathanfristedt.
MissingPluginException storm during transitionsch.invokeMethod('setTransitioning', …) calls now use .catchError((_) {}) to swallow async rejections, and fallback platform-view classes (iOS < 26) register no-op MethodChannel handlers so Dart-side calls from ModalHideMixin and route-transition containment don't throw.
pubspec.yaml documentation: now points to https://gunumdogdu.com/docs.CNBottomSheet / CNSheetGeometryProbe usage and the navigatorObserver requirement for modal-hide to work.Testing → #53: CNButton under bottom sheet — four sheet-opener variants over a host page full of CN widgets.Testing → #55: PopupMenu isDestructive — text, icon, and mixed menus with destructive items.Testing → Glass widgets modal halo test — all 9 CN widgets behind sheets.Testing → CNButton modal halo test — modal route push/pop animations.Testing → #37: CNAppBar button halo test.CNGlassButtonGroup glass merging at default spacing still shows a "dumbbell" between buttons. The single-uniform-pill rewrite hit a SwiftUI hit-testing limitation (.glassEffect() intercepts touches at a layer below .allowsHitTesting(false)); a UIKit UIVisualEffectView rewrite is queued for the next release.Building on the existing CNTabBarNative (the native iOS 26 Liquid Glass tab bar), this release adds:
Building on the existing CNTabBarNative (the native iOS 26 Liquid Glass tab bar), this release adds:
minimizeBehavior: with CNTabMinimizeBehavior.{automatic, never, onScrollDown, onScrollUp}, changeable at runtime via setMinimizeBehavior(...). Requires a tab backed by a CNNativeList, since iOS drives the minimize from a real native scroll view.CNTab(nativeList: CNNativeList(items: [CNListItem(...)])) renders a native scrollable list; onListItemTap(tabIndex, itemIndex) reports taps. New CNNativeList / CNListItem models (now exported from the package).bottomAccessory: CNTabAccessory(...) floats above the bar and slides inline when it minimizes; onAccessoryTap reports taps; show/update/hide at runtime with setBottomAccessory(...) (pass null to hide).asRoot: selects modal presentation (default) or root presentation; in root mode a tab with no nativeList hosts your real Flutter UI.nativeSearchFilter (default true) filters the search tab's own list locally; set false to drive results yourself via onSearchChanged + setItems.setItems(...) for dynamic/paginated data, plus onDismissed (fires when the bar is closed natively, e.g. via the ✕ button).The native manager was rewritten (CNNativeTabBar.swift replaces CNNativeTabBarManager.swift) on a stable SwiftUI TabView view tree.
CNTabBar vs CNTabBarNative comparison table, presentation modes, and a per-method API reference. Added a preview GIF (minimize + accessory + search).CNTabBarNative is a native takeover, not a Flutter bottom-nav — for Flutter screens per tab, use CNTabBar (clarifies #7).CNTabBarNative.enable parameters onSearchSubmitted, onSearchCancelled, onSearchActiveChanged — the search tab now reports through onSearchChanged; these are no longer fired and will be removed in a future major release.CNTabBarNative.checkIsEnabled() — use the synchronous isEnabled getter instead.CNTabBar. iOS 26's Liquid Glass tab bar enforces the system color for unselected items and ignores customization — verified on-device with both the legacy UITabBar.unselectedItemTintColor and the modern UITabBarAppearance per-state iconColor / title foregroundColor. The selected tint is honored; the unselected state is system-owned, so a public knob would silently no-op on the package's target OS.Fixed #39 / merged PR #42 — CNTabBar.iconSize is now honored for customIcon items (any IconData like CupertinoIcons.house or Icons.home). Previously t
CNTabBar.iconSize is now honored for customIcon items (any IconData like CupertinoIcons.house or Icons.home). Previously the custom-icon rasterizer was hardcoded to 25pt regardless of the bar-level iconSize or per-item icon.size. Bug originally surfaced via #39 (SVG imageAsset layout glitch on v1.4.3) and addressed by @Azzeccagarbugli's PR #42, with an extra refinement on top:
lib/components/tab_bar.dart: _prepareCreationParams now passes widget.iconSize ?? item.icon?.size ?? 25.0 to iconDataToImageBytes; same precedence on activeCustomIcon; _buildTabIcon (Flutter fallback) mirrors the precedence across all icon kinds.lib/utils/icon_renderer.dart: full rewrite of iconDataToImageBytes. Drops the heavy RenderRepaintBoundary + BuildOwner setup in favor of a TextPainter + canvas + alpha-channel crop approach. Final step (refinement on top of the PR): re-blit the cropped glyph into a square size × size canvas with the glyph scaled to fill, so custom icons at a given pointSize match SF Symbol's visible ink at the same pointSize (matches SF Symbol's "pointSize is ink size" convention — previously, font em-box padding made customIcon visually smaller than equivalent SF Symbols at the same size).test/widget_test.dart: 6 new widget tests verifying the iconSize > icon.size > 25pt precedence across SF Symbol / customIcon / imageAsset in the Flutter fallback path.Swift Package Manager support — fixes #44. The plugin now ships a Package.swift manifest for both iOS and macOS targets and is recognized by pana as "Swift PM-ready".
Flutter's SPM rollout is well underway: SPM support landed in Flutter 3.24 (Aug 2024) as opt-in, became the default for new apps in Flutter 3.44, and CocoaPods is being phased out — Firebase stops publishing to CocoaPods in October 2026 and the CocoaPods registry becomes read-only on December 2, 2026. Plugins without SPM support already lose pana points and trigger build warnings on modern Flutter versions.
Compatibility (no users locked out): the plugin's own pubspec constraints stay at sdk: ^3.9.0 / flutter: '>=3.3.0'. The SPM Package.swift sits alongside the existing podspec and is only consulted by Flutter 3.24+. Older Flutter consumers continue to resolve the package via CocoaPods exactly as before — the podspec's source_files path was updated to point at the new SPM-shaped source directory (ios/cupertino_native_better/Sources/cupertino_native_better/**, mirrored on macOS) so both build paths produce the same result.
| Consumer Flutter | Build path |
|---|---|
| 3.3 – 3.23 (pre-SPM) | CocoaPods via podspec. Package.swift ignored. Unchanged from v1.4.5. |
| 3.24 – 3.43 (SPM opt-in) | CocoaPods by default; SPM if flutter config --enable-swift-package-manager is set. Both paths work. |
| 3.44+ (SPM default) | SPM via Package.swift. |
Thanks to @josec-ecw for the PR.
Testing → PR #42: CNTabBar iconSize (customIcon) — three side-by-side CNTabBars (SF Symbol / customIcon / SVG imageAsset) with an iconSize slider so all three icon kinds can be verified to scale identically. Four Stratis UI Figma SVGs bundled (camera-01, card-add, chromecast, home-03) — same source as #39 reporter — for SVG verification.Fixed #35 / #41 (recreate animation on launch + navigation) — CNTabBar no longer plays a visible "morph through every tab" animation on app launch or
Fixed #35 / #41 (recreate animation on launch + navigation) — CNTabBar no longer plays a visible "morph through every tab" animation on app launch or after navigating away and back.
refresh cycles bar.selectedItem through every tab to force UITabBar's label layout (workaround for an old "5 items, sporadic missing labels" bug — Issue #6). On iOS 26 with Liquid Glass, that cycling was visible as the selection pill morphing through every tab. The cycle is now wrapped in UIView.setAnimationsEnabled(false) … true — labels still render correctly, but the pill no longer animates between items. Tab bar appears instantly with the configured currentIndex.autoHideOnPageTransition previously swapped the platform view to a SizedBox during route slides, destroying the native UITabBar. On return, a fresh view was created and _onCreated re-ran setSelectedIndex + refresh → visible animate-to-index. Fix: when autoHideOnPageTransition is on, CNTabBar.build now always returns an IndexedStack (children: [SizedBox, UiKitView]); only the painted index toggles between 0 and 1 across the transition. The UiKitView's element stays mounted in both states, so the native UITabBar is preserved across navigation. Bar just appears with the correct index, no animation.Fixed #41 (PlatformViewGuard 500 ms fallback flash on first build) — non-iOS26 fallback widgets briefly visible during cold start.
PlatformViewGuard.ensureScheduled always delayed platform-view creation by 500 ms to give Flutter's FlutterPlatformViewsController time to purge stale registrations from a previous Dart isolate after a hot restart. That race only exists in debug; release builds (cold-start, no isolate recycling) were paying the same flash-of-fallback cost for nothing.PlatformViewGuard is now ready immediately in kReleaseMode. Native iOS 26 widgets render from the first frame in production; debug-mode hot-restart safety is preserved unchanged.Fixed #36 follow-up — LiquidGlassContainer's rectangular layer drop shadow leaking past its rounded glass corners while a modal/sheet was presented above (was already targeted in v1.4.4 but only clipped the rectangular layer bounds; rounded clip wasn't applied across all shape configurations correctly). The corner-radius clip in applyTransitionContainment now consistently matches rect (configured cornerRadius), capsule (min(width, height)/2), and circle (min(width, height)/2) — no more square shadow nubs.
Fixed #40 (CNButton label customization) — CNButtonConfig now accepts:
labelFontFamily — custom font family (must be registered in Info.plist or as a Flutter font asset).labelFontSize — point size override.labelColor — explicit foreground color (overrides the tint-derived default for non-filled styles, and the system default for filled / borderedProminent / prominentGlass).labelFontWeight — Flutter FontWeight override.Implementation: Swift side applies them via UIButton.Configuration.titleTextAttributesTransformer, so the overrides take effect on the native label without losing the iOS 26 Liquid Glass / prominent / tinted button background. Both creation-time (creationParams) and runtime (setLabelStyle channel call from _syncPropsToNativeIfNeeded) updates are supported. Flutter FontWeight.value (0-8) is mapped to UIFont.Weight.
Example:
CNButton(
label: '1',
tint: CupertinoColors.systemOrange,
onPressed: () {},
config: const CNButtonConfig(
style: CNButtonStyle.prominentGlass,
width: 80, minHeight: 80,
labelFontSize: 36,
labelFontWeight: FontWeight.w600,
labelColor: CupertinoColors.white,
),
)
CNTabBar.autoHideOnPageTransition keeps its default true. With the IndexedStack-based hide it's now zero-cost: state is preserved across navigation while still preventing the original page-wide PlatformViewLayer occlusion artifact during route slides.CNButton (e.g. dial-pad style: large number + small letters underneath) is intentionally not added to CNButton's API. The same effect composes cleanly via LiquidGlassContainer wrapping a Column of two Flutter Texts — full TextStyle freedom on both labels with the native iOS 26 Liquid Glass background. See the closing comment on Issue #40 for a snippet.Testing → #40: CNButton label style — interactive screen for verifying the new label-style params (font-size slider, color swatches, font-family segmented control, style picker).Fixed #36 — LiquidGlassContainer's rectangular layer drop shadow leaking past its rounded glass corners while a modal/sheet was presented above. Visib
LiquidGlassContainer's rectangular layer drop shadow leaking past its rounded glass corners while a modal/sheet was presented above. Visible as four square shadow nubs at the card's corners through the modal's scrim, even though the visible glass was rounded.
applyTransitionContainment(true) (added in v1.4.3 for the dynamic halo containment) only set clipsToBounds = true on the container's CALayer, which clips to the rectangular layer bounds — leaving the four corners outside the rounded glass shape unclipped. The layer's drop shadow rendered into those corners and bled through the modal scrim.LiquidGlassContainerView.swift, applyTransitionContainment(true) now also sets container.layer.cornerRadius (and the hosting view's) to match the configured glass shape:
rect shape → uses the configured cornerRadiuscapsule / circle → min(width, height) / 2CNTabBarRouteObserver" section directly under Quick Start, documenting:
CupertinoApp / MaterialApp / GoRouter snippets.markAnyModalActive / markAnyModalInactive API for non-route overlays (Scaffold.showBottomSheet).Testing → #36: LiquidGlassContainer behind modal — focused reproduction page for Issue #36. Card-shaped LiquidGlassContainer matching the issue reporter's _AdaptiveGlassContainer widget exactly (cornerRadius: 15, rect, EdgeInsets.all(13), no tint), four sheet variants including the reporter's exact showModalBottomSheet invocation (rounded top, scaffold-bg, Clip.antiAlias, bounce animation, useRootNavigator: true), and a CalendarDatePicker inside each sheet matching the modal content from the issue's screenshots. White scaffold + black/white pill buttons mirror the reporter's app styling.Fixed #34 — CNButton glass capsule not stretching with its parent frame; icon overflowing a small pill when wrapped in SizedBox / Expanded. Regression
Fixed #34 — CNButton glass capsule not stretching with its parent frame; icon overflowing a small pill when wrapped in SizedBox / Expanded. Regression introduced in v1.4.0.
container.clipsToBounds = true + uiButton.clipsToBounds = true (plus layer shadow/background clearing) across every iOS 26 glass widget as part of the #29 halo containment. UIButton.Configuration.glass() renders its capsule via an internal background subview whose visual size includes a soft-edge glow extending slightly beyond the button's layer bounds — the always-on clipping was cropping that glow AND preventing the capsule from growing with a stretched frame.CNButton, CNPopupMenuButton, CNFloatingIsland, CNGlassButtonGroup, LiquidGlassContainer, CNSearchBar. Containers are unclipped at rest, so the glass capsule renders its full soft-edge glow and stretches properly with SizedBox / Expanded / Container(width: ...).Fixed #29 (fully) — the original halo-during-route-transition artifact is now resolved via a dynamic containment pattern instead of always-on clipping. This also catches the popup/sheet bleed cases that the v1.4.0 fix didn't cover (popup routes, persistent bottom sheets).
setTransitioning(active:) on CNButton, CNPopupMenuButton, CNFloatingIsland, CNGlassButtonGroup, LiquidGlassContainer, CNSearchBar, and the regular CNTabBar variant. When active, it applies the halo-containment clipping + shadow/background clearing; when inactive, it reverts.setTransitioning(true) when either fires:
ModalRoute.secondaryAnimation — catches CupertinoPageRoute / MaterialPageRoute forward/reverse transitions.CNTabBarRouteObserver.anyModalDepth counter that tracks any PopupRoute / Sheet / Popup / Dialog-named route (showCupertinoSheet, showCupertinoModalPopup, showModalBottomSheet, DialogRoute, etc.).CNTabBar (whose native container is intentionally unclipped so the floating search orb can render above the bar), the auto-hide trigger is broadened to anyModalDepth so popups over a search-enabled tab bar no longer leak shadow through the sheet's top edge.CNTabBarRouteObserver.anyModalDepth (read-only ValueListenable<int>) — broader counter than the existing modalDepth. Tracks every modal-like route (all PopupRoutes plus any route whose runtime type contains Sheet / Popup / Dialog). Used internally by the glass widgets for halo-containment activation.
CNTabBarRouteObserver.markAnyModalActive() / markAnyModalInactive() — public manual API for non-route overlays that NavigatorObserver can't see, notably Scaffold.showBottomSheet (persistent bottom sheet anchored to ScaffoldState, not the Navigator):
final controller = Scaffold.of(context).showBottomSheet(...);
CNTabBarRouteObserver.markAnyModalActive();
controller.closed.whenComplete(CNTabBarRouteObserver.markAnyModalInactive);
Testing → CNButton modal halo test — stretched CNButton variants + 4 sheet types (showCupertinoSheet, showCupertinoModalPopup, showModalBottomSheet, showBottomSheet) for verifying halo containment across every overlay variant.Testing → Glass widgets modal halo test — same 4-sheet matrix against CNPopupMenuButton, CNGlassButtonGroup, LiquidGlassContainer, CNSearchBar, CNFloatingIsland.Stack+Positioned tab bar and Split-search clip repro screens now include all 4 sheet types for regression coverage.DefaultMaterialLocalizations.delegate to the root CupertinoApp so demos can mix showModalBottomSheet (Material) with Cupertino routes without adding flutter_localizations.CNTabBar without searchItem still uses the narrow Sheet-only modalDepth heuristic for its auto-hide (avoids the recreate-and-restore flash on quick action-sheet popups). CNTabBar with searchItem uses the broader anyModalDepth because its container can't be clipped (the floating search orb needs to render above the bar's top edge).CNTabBar auto-hide while full-screen sheet is presented (Issue #31 )
CNTabBar auto-hide while full-screen sheet is presented (Issue #31)
Adds CNTabBarRouteObserver (NavigatorObserver) and CNTabBar.autoHideOnModal.
With one line of setup the tab bar disappears while a full-screen sheet
(CupertinoSheetRoute, ModalBottomSheetRoute) is on top and reappears
when dismissed — matching iOS UITabBarController's native behaviour and
fixing the z-order bug where Flutter-rendered TextFields inside the
sheet were invisible behind the tab bar's native UIView.
Auto-hide is intentionally narrow: only routes whose runtime type name
contains Sheet. Action-sheet popups, dialogs, and regular page pushes
do NOT trigger auto-hide — those don't fully cover the bar and the
platform-view recreate animation would look ugly on quick popups.
Setup:
CupertinoApp(
navigatorObservers: [CNTabBarRouteObserver()],
...
)
Opt-out per-bar: CNTabBar(autoHideOnModal: false, ...)
Also fixes a related bug in _onCreated: the order of the post-create
calls was refresh -> setSelectedIndex, but native refresh captures
bar.selectedItem at start and asynchronously cycles + restores. That
restored the stale creationParams selectedIndex (0) over the
setSelectedIndex we'd just sent. Swapped the order to setSelectedIndex
-> refresh; refresh now captures the correct index.
Pana: 160/160.
Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
CNTabBarRouteObserver — a NavigatorObserver that lets CNTabBar auto-hide while a full-screen sheet is presented over its route. Resolves Issue #31 (Material TextField invisible inside showCupertinoSheet when CNTabBar is in the bottom nav slot).
The native UITabBar is rendered inside a Flutter UiKitView. When a Flutter-rendered sheet route is presented over the same navigator, hybrid composition can leave the tab bar's UIView at a higher z-index than the modal's Flutter content — making TextFields inside the sheet invisible and letting the bar bleed through during sheet drags. Auto-hide swaps the platform view for an empty placeholder while the sheet is up, mirroring what iOS does natively when a UITabBarController presents a full-screen modal.
Setup (one line per app):
CupertinoApp(
navigatorObservers: [CNTabBarRouteObserver()],
// ...
)
Or MaterialApp(navigatorObservers: [CNTabBarRouteObserver()], ...). Without this observer registered, CNTabBar still renders correctly — it just won't auto-hide on top of sheets and you may hit the Issue #31 z-order glitch.
CNTabBar(autoHideOnModal: bool = true) — opt-out for the auto-hide behaviour. Default true. Set false to keep the tab bar visible behind sheets (rare; typically requires a native sheet that won't trigger the z-order issue).
_onCreated we called refresh before setSelectedIndex. The native refresh method (a workaround for the 5-item-label-rendering bug Issue #6) captures bar.selectedItem at start, cycles through items asynchronously, then "restores" the captured value — overriding the setSelectedIndex(currentIndex) we sent right after, leaving the bar stuck at the stale creationParams.selectedIndex = 0.setSelectedIndex now runs BEFORE refresh. Refresh then captures the correct index and restores to it. Applied to both the 50ms and 200ms recreation passes.Sheet (CupertinoSheetRoute, ModalBottomSheetRoute). Action-sheet popups (CupertinoModalPopupRoute), dialogs, and regular page pushes do NOT trigger auto-hide. This avoids a visible "platform view recreate + index restore" jump animation on quick popups, while still fixing the z-order issue for full-screen sheets.Testing → #31: TextField — NO search variant (hypothesis test) — same flow as the Issue #31 reproduction but with CNTabBar configured without searchItem and autoHideOnModal: false, used to verify the bug isn't search-specific.CNTabBarRouteObserver() on the example app's CupertinoApp so all demo screens benefit from auto-hide.iOS 26 Liquid Glass selection pill no longer cropped at top
iOS 26 Liquid Glass selection pill no longer cropped at top
The v1.4.0 Issue #2 fix clipped the platform-view container to stop
the Liquid Glass drop shadow from bleeding over modal bottom sheets.
The same clip was also cutting off the iOS 26 tab bar's selection
pill during its morph animation between tabs.
Fix: container still clips (shadow containment preserved), but the
UITabBar is positioned 14pt below container.topAnchor in all 5
layout sites, and getIntrinsicSize reports barHeight + 14pt. The
selection pill — including rapid morph between tabs — now renders
fully inside the clipped container. Bar's visible position on screen
is unchanged.
Example app:
Known issue documented in CHANGELOG and inline Swift comment:
simulator Liquid Glass rendering differs from real device; verify on
hardware before treating visual artifacts as bugs.
Pana: 160/160.
Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
CNTabBar iOS 26 Liquid Glass selection pill was being cropped at its top edge after the v1.4.0 Issue #2 fix.
Testing → #33: SVG in CNTabBar — reproduction screen mirroring the SVG-icons-in-CNTabBar pattern reported in Issue #33, including the reporter's NavBarItem wrapper, iconSize: 24, and tint configuration. Local iOS 26 verification shows SVGs render correctly; the screen is published so the reporter (and future users hitting the same symptom) can confirm on their own setup.Testing → Stack+Positioned tab bar — demonstrates the Stack + Positioned(bottom: 0) layout pattern as an alternative to Scaffold.bottomNavigationBar for users who need custom z-order control.Testing → CNTabBar split-search clip — bright teal background and a modal-bottom-sheet trigger button so both the search-orb top-edge clip and the Issue #2 shadow-bleed scenarios can be verified side-by-side on one screen.s.platform = :ios, '15.0').Fixed: CNTabBar top-edge shadow bleeding over modals / bottom sheets — the regression of Issue #2 that landed between v1.3.0 and v1.3.8 (Issue #2)
Fixed: CNTabBar top-edge shadow bleeding over modals / bottom sheets — the regression of Issue #2 that landed between v1.3.0 and v1.3.8 (Issue #2)
clipsToBounds = true containment from the original v1.3.0 fix was made conditional and disabled on iOS 26+. That removed the only thing keeping the UITabBar's top-edge hairline inside the platform view's bounds.container.clipsToBounds = true restored unconditionally on the regular tab-bar platform view (5 sites — single-bar and split-bar in both init and setLayout)bar.shadowImage = UIImage() on every UITabBar instance as belt-and-suspenders against iOS 26 ignoring the appearance-level shadow overrideFixed: Liquid Glass halo rendering outside platform-view bounds during iOS route transitions — the "placeholder square" reported across CNButton, CNTabBar, and other widgets (Issue #29)
UIButton.Configuration.glass(), UITabBar glass material, .glassEffect() SwiftUI modifier) render a translucent halo that extends slightly outside the view's frame. Without containment, that halo became visible during route transitions as a square outline around the widget on the outgoing page.CNButton, CNPopupMenuButton, CNSearchBar, CNFloatingIsland, CNLiquidGlassContainercontainer.clipsToBounds = true, container.layer.shadowOpacity = 0, container.layer.backgroundColor = clear, container.isOpaque = false plus the same on the inner subview where applicableFixed: CNTabBar with searchItem — the floating Liquid Glass search orb's top edge was being cropped (commented in Issue #31 by @el2zay)
CupertinoTabBarSearchPlatformView) now leaves its container un-clipped so the search orb can render its top edge correctly. Top-edge hairline is still suppressed via bar.shadowImage = UIImage() and bar.layer.shadowOpacity = 0, so the original Issue #2 shadow bleed does not return.Fixed: CNGlassButtonGroup badge X-position — the last badge floated near the screen edge instead of sitting on its button when buttons were centered as a tight pill inside a wider container
updateBadgePositions() now estimates each button's rendered width from icon size + padding + minHeight (capsule width = max(intrinsic, minHeight)), computes the centered-HStack starting offset, and places each badge at the actual button's top-right corner instead of dividing container width evenly across button count.Fixed: CNGlassButtonGroup Auto Layout _UITemporaryLayoutWidth = 0 warning on initial mount — silenced by lowering hosting-view constraint priorities to .defaultHigh so UIKit can break them silently during the brief temp-width=0 phase without logging.
Testing → #2: Modal bottom sheet shadow — opens a CupertinoModalPopup over a CNTabBar so the top-edge shadow bleed (or its absence) is easy to inspectTesting → #29: Per-widget halo test — one slow-transition push per widget so each can be verified in isolationTesting → CNTabBar split-search clip — CNTabBar with a searchItem so the floating orb is easy to inspect at the bottom-rightTesting → #31: TextField disappear in modal — Material Scaffold + CNTabBar (with searchItem) in bottomNavigationBar, opens a modal sheet with a Material TextField and a CupertinoTextField for comparisonAdded: checked property on CNPopupMenuItem for checkmark/selected state (Issue #28)
checked property on CNPopupMenuItem for checkmark/selected state (Issue #28)
UIAction.state = .on for native checkmark displayPlatformException(recreating_view) on iOS hot restart (PR #30 by @lucakramberger)
PlatformViewGuard utility delays platform view creation during startupCNTabBar refactored from nested FutureBuilders to state-managed async pipelinedeinit cleanup added to tab bar platform viewsFixed: macOS build — resolved 5 compilation errors in native Swift code
badgeCount parameter missing from setupSwiftUIButton method.clear color inference on CALayer.backgroundColor (now uses NSColor.clear.cgColor)FlutterPlatformView protocol replaced with NSView for LiquidGlassContainernamespace argument from GlassButtonSwiftUI callNSButton.title non-optional handlingAdded: Popup menu button support in CNGlassButtonGroup via CNButtonData.popup() (PR #23 by @byackee)
CNGlassButtonGroup via CNButtonData.popup() (PR #23 by @byackee)
UIMenu supportCNButtonDataPopupItem model for popup menu itemslabelFontFamily and labelFontSize properties for CNTabBar (PR #26, Issue #16 by @byackee)
buttonCustomIconColor now works on iOS 26 with Liquid Glass rendering (PR #24, Issue #21 by @byackee)
capturedButtonIconColor.withTintColor(.alwaysOriginal) to preserve custom icon colorsiconColor also supported for custom IconData iconsCNSwitch no longer pushed upward by keyboard (PR #25, Issue #4 by @byackee)
safeAreaRegions.remove(.keyboard) official APIFixed: Horizontal glass button group "waist" effect — reduced toolbar shrinkage between adjacent buttons by enforcing minimum 80pt glass spacing for h
CNTabBar now shows a Flutter fallback tab bar while the native view initializes, instead of blank space for ~2 seconds (Issue #5)CNTabBar with 5 items no longer has sporadic missing labels — added a second refresh pass for slow-to-initialize native views (Issue #6)Fixed: CNTabBar iconSize now correctly applies to CNImageAsset (SVG/image) icons (Issue #19)
CNTabBar iconSize now correctly applies to CNImageAsset (SVG/image) icons (Issue #19)
iconSize propertyloadFlutterAsset and createImageFromData now receive the size parameterAdded: interaction property for CNButtonConfig and CNButtonDataConfig (PR #15 by @anirudhrao-github)
interaction property for CNButtonConfig and CNButtonDataConfig (PR #15 by @anirudhrao-github)
interaction: false, button maintains normal look but doesn't respond to touchesLiquidGlassContainer layout simplified for better parent alignment controlAdded: customIconSize property for CNButtonConfig and CNButtonDataConfig (PR #12 by @anirudhrao-github)
Added: customIconSize property for CNButtonConfig and CNButtonDataConfig (PR #12 by @anirudhrao-github)
Added: iconSize property for CNTabBar to control SF Symbol icon sizes
Fixed: CNGlassButtonGroup no longer forces equal width on all buttons (PR #12 by @anirudhrao-github)
.fixedSize(horizontal: true, vertical: false) for proper sizingFixed: CNTabBar.onTap now fires for reselects (Issue #13)
onTap, allowing scroll-to-top or navigation reset on reselectFixed: CNTabBar icon clipping on iOS 26+ Liquid Glass
clipsToBounds on iOS 26+ to allow proper Liquid Glass pill overflowCNGlassButtonGroup
Added: Badge support for CNGlassButtonGroup icon buttons (PR #11 by @anirudhrao-github)
CNGlassButtonGroup icon buttons (PR #11 by @anirudhrao-github)
badgeCount property on CNButtonData.icon() for displaying notification badgesImproved: Added library-level documentation for better API discoverability
button, button_data, button_style, and cupertino_native librariesFixed: Dart formatting issues for pub.dev compliance
button.dart and glass_button_group.dartFixed: Tint color now works correctly when buttons are inside CNGlassButtonGroup (PR #8 by @anirudhrao-github)
CNGlassButtonGroup (PR #8 by @anirudhrao-github)
Old initialize() method kept for backwards compatibility (marked deprecated)
Added: CNTabBarNative - Native iOS 26 Tab Bar with full UITabBarController integration
UITabBarController + UISearchController for authentic iOS 26 liquid glass effectsCNTabBarNative.enable() / CNTabBarNative.disable() for app-level tab bar managementCNTab class for tab configuration with SF Symbols and search tab supportonTabSelected, onSearchChanged, onSearchSubmitted, onSearchCancelled, onSearchActiveChangedAdded: CNSearchScaffold - Native search scaffold controller for standalone search UI
Added: CNToast - Toast notification widget with Liquid Glass effects
show(), success(), error(), warning(), info(), loading()CNLoadingToastHandle for dismissing loading toastsAdded: label property to CNTabBarSearchItem for customizing the search tab label
Added: preserveTopToBottomOrder property to CNPopupMenuButton (Issue #3)
true, menu items maintain top-to-bottom order (1,2,3,4) regardless of menu directionfalse preserves native iOS behavior where item 1 stays closest to the buttonUIDeferredMenuElement.uncached for dynamic position detectionEnhanced: PlatformVersion now auto-initializes on first access
await PlatformVersion.initialize() in main()PlatformVersion.isIOS26OrLater directlyinitialize() method kept for backwards compatibility (marked deprecated)Added: New helper properties to PlatformVersion:
isIOS, isMacOS, isAndroid, isAppleisIOSVersionInRange(min, max), isMacOSVersionInRange(min, max)Fixed: CNPopupMenuButton.icon now respects the order defined in items (Issue #3)
preserveTopToBottomOrder parameter to control item ordering behaviorpreserveTopToBottomOrder: true for consistent top-to-bottom orderFixed: Tab bar shadow artifact appearing over modals and bottom sheets (Issue #2)
configureWithDefaultBackground() to configureWithTransparentBackground()shadowColor = .clear, shadowImage = UIImage()container.clipsToBounds = true and layer.shadowOpacity = 0Fixed: Search bar keyboard auto-opening behavior (Issue #1)
automaticallyActivatesSearch: false now properly prevents keyboard from auto-openingAdded: iOS 26 Search Tab Feature for CNTabBar with animated Liquid Glass expansion
Added: iOS 26 Search Tab Feature for CNTabBar with animated Liquid Glass expansion
UISearchTab-style search integration that follows Apple's iOS 26 designAdded: CNTabBarSearchItem configuration class for search tab customization
placeholder: Custom placeholder text for the search fieldonSearchChanged: Callback for live filtering as user typesonSearchSubmit: Callback when user submits searchonSearchActiveChanged: Callback for expand/collapse state changesautomaticallyActivatesSearch: Control keyboard auto-activation behaviorAdded: CNTabBarSearchStyle for visual customization
Added: CNTabBarSearchController for programmatic search control
activateSearch() / deactivateSearch(): Expand/collapse search programmaticallytext property: Get/set search textclear(): Clear search text with optional deactivationautomaticallyActivatesSearch now properly controls keyboard behavior
false: Search bar expands but keyboard only opens when user taps the text fieldtrue (default): Keyboard opens automatically when search expandsUISearchTab.automaticallyActivatesSearch from UIKitMissingPluginException errors during hot reload for setItems and refresh methods
Added: Lightweight setBadges method for CNTabBar to update badge values without rebuilding the entire tab bar
setBadges method for CNTabBar to update badge values without rebuilding the entire tab bar
badgeValue on existing UITabBarItems for smooth, instant badge changes_syncPropsToNativeIfNeeded() and calls lightweight native setBadges method instead of full setItems rebuildFixed: Visual update - minor bug fixes and improvements
Fixed: Split mode tab selection bug where the wrong tab appeared selected on first load
Fixed: Split mode tab selection bug where the wrong tab appeared selected on first load
split: true in CNTabBar, the right bar (e.g., Rewards tab) would incorrectly appear selected even when the left bar tab (e.g., Discover) was actually selectedrefresh method, when restoring selection after cycling through tabs for label rendering, the code was incorrectly setting right.selectedItem = rightItems.first when rightOriginal was nilright.selectedItem = rightOriginal), which correctly keeps the right bar unselected when a left bar tab is activeFixed: Added setSelectedIndex call after refresh in Flutter widget to ensure correct selection state after view initialization
Fixed: Attempted fix for split mode tab selection (superseded by 1.1.7)
iOS Minimum Version: Raised iOS deployment target from 13.0 to 15.0
@FocusState and other iOS 15+ SwiftUI features'FocusState' is only available in iOS 15.0 or newer'self' used before 'super.init' call in CNSearchBarFixed: Minor internal improvements
Fixed: Full 50/50 pub.dev static analysis score (160/160 pana points)
Fixed: Dart formatter compliance
Fixed: Resolved use_build_context_synchronously lint warnings
use_build_context_synchronously lint warningsAdded: Complete documentation for all widgets with real iOS 26 screenshots
Nothing published for this version
Nothing published for this version
Fixed: Dart formatting issues to achieve full 50/50 static analysis score on pub.dev
Fixed: All use_build_context_synchronously warnings by capturing context-derived values before async gaps
use_build_context_synchronously warnings by capturing context-derived values before async gapsdangling_library_doc_comments warningunnecessary_library_name and unnecessary_import warningsFixed: Unreliable tap detection in CupertinoButton fallback mode
onPressed didn't fire consistentlyminSize: 0 to prevent CupertinoButton's internal minimum size from conflicting with SizedBox constraintsborderRadius and pressedOpacity for better hit testing and visual feedbackFixed: Reverted GestureDetector overlay that caused crash on iOS 18
unrecognized selector sent to instance 'onTap:'BROKEN: Added GestureDetector overlay that crashed on iOS 18
Fix: Replaced deprecated Color.value and withOpacity usages with modern alternatives.
use_build_context_synchronously warnings across multiple components.Color.value and withOpacity usages with modern alternatives.Major Release - Complete iOS Fallback Fixes
Major Release - Complete iOS Fallback Fixes
This release addresses critical issues that caused components to malfunction on iOS versions below 26.
cupertino_native_plus to cupertino_native_betterpackage:cupertino_native_better/cupertino_native_better.dartwidget.isIcon was returning true for any button with an icon, even if it also had a labelwidget.isIcon && widget.label == null to only treat truly icon-only buttons as icon-onlycustomIcon, ignoring SF Symbols (icon/activeIcon)_buildTabIcon() helper that properly handles all icon types with correct priorityshouldUseNativeGlass (iOS 26+) for SF Symbol support, but SF Symbols work on iOS 13+supportsSFSymbols getter that always returns true on iOS/macOSPlatformVersion.supportsSFSymbols for checking SF Symbol availability (iOS 13+, macOS 11+)Your coding agent can read these notes before it upgrades. Set up the MCP server →