veneer 0.14.1 copy "veneer: ^0.14.1" to clipboard
veneer: ^0.14.1 copied to clipboard

Real native iOS 26 UI for Flutter: Liquid Glass, UIKit tab and navigation bars, composers and sheets driven by Flutter layout, with matching Flutter UI on Android.

Changelog #

0.14.1 #

  • Navigation bars keep clear of the screen's edge without a status bar: in landscape, or wherever the top safe area is zero, NativeNavigationBar now sits 8 pt below the edge instead of flush against it, natively and in the Flutter replica. Content padding follows.

0.14.0 #

  • Recording bar clip layout: recording: NativeComposerRecording(...) on NativeComposer and NativePromptComposer swaps the Slack-style recording row for a clip layout: an optional title line (e.g. "Recording an audio clip…"), 40 pt cancel and done circles, 2 pt bars 1 pt apart in waveColor, a 00:00 time and, with pausable, a pause button that turns into resume and carries on into the same clip. Icons, colours and VoiceOver labels are configurable; without it the bar is unchanged.

0.13.1 #

  • Native menus inside Flutter-drawn sheets: inside a VeneerFallbackScope (such as a Flutter-drawn sheet, where the overlay can't follow), NativeMenuAnchor now opens UIKit's UIMenu from a platform-view button instead of the Flutter replica.
  • Native navigation bars inside Flutter-drawn sheets: there, NativeNavigationBar is now the real UINavigationBar as a platform view — glass bar buttons, title and menus — instead of the replica. Content padding and the fade under the bar stay as before; UIKit's scroll edge effect itself needs the overlay.
  • Fix: menus opening upward kept their order. UIKit reverses a button's menu when it opens upward (the first item nearest the button); GlassShape and NativeMenuAnchor menus now keep the order they were given.

0.13.0 #

  • Alerts: showNativeAlert(context:, title:, message:, actions:, tint:) presents UIKit's UIAlertController over whatever the app or sheet shows and answers with the picked action's index (NativeAlertAction with normal/cancel/destructive styles and a preferred action); CupertinoAlertDialog off iOS 26.
  • Leaf controls: NativeSwitch, NativeSlider and NativeSegmentedControl (text segments) are UIKit's UISwitch, UISlider and UISegmentedControl as platform views laid out by Flutter, so they also work inside Flutter-drawn sheets. They're controlled like Flutter's: a parent that keeps its value puts the control back. Cupertino controls off iOS 26.

0.12.1 #

  • Menu image size: NativeMenuItem.iconSize sets an image or glyph icon's side in points instead of the menu's 20 — e.g. 24 for avatars, natively and in the Flutter menu.

0.12.0 #

  • Glass behind Flutter content: GlassBackground(child:, style:, tint:, cornerRadius:, onTap:) is Liquid Glass under any Flutter widget — a dock, a search field, a pill holding Flutter text — as a platform view (UIVisualEffectView + UIGlassEffect) composited beneath the child. With onTap the glass is interactive and takes the tap natively. Off iOS 26 it is the replica surface.
  • Menu titles: NativeMenuAnchor(title:) and GlassShape(menuTitle:) head the menu with a title (UIKit's menu title; a header row in the Flutter menu).
  • Round images: NativeIcon.image and NativeIcon.imageFile take circular: true to fill the box and crop to a circle — for avatars in menus.

0.11.0 #

  • Richer menu items: NativeMenuItem takes a subtitle, checked (UIKit's checkmark for the current choice) and enabled (dimmed and unpickable when false), natively and in the Flutter menu. Items that use none of them encode exactly as before.
  • Menus from Flutter triggers: NativeMenuAnchor(items:, child:) opens a native UIMenu from a tap on any Flutter widget, growing out of it; the Flutter menu off iOS 26.
  • Glass-less shapes: GlassStyle.none makes a GlassShape an invisible native hit region (a tap or a menu) with no glass, for content Flutter draws.
  • Fix: native views under platform-view areas stopped taking touches. Flutter adds a platform view's clipping and overlay layers to the FlutterView when its composition changes, often on a frame that moves no glass. The overlay only moved back on top when glass geometry was sent, so until then those layers took every touch over the platform view (a glass button on a platform-view surface did nothing). The overlay now checks its place at a low rate between frames as well.

0.10.0 #

  • Full-screen sheets: showNativeSheet(fullScreen: true) presents a page that fills the screen (UIKit's .fullScreen on iOS 26; a Flutter sheet at full height elsewhere), with no detents and no drag to dismiss. NativeSheet.enterFullScreen(context) turns an open sheet into one while keeping its content running and its state: a native sheet's engine moves to a full-screen presentation behind a cross-fade, a Flutter sheet grows to fill the screen. NativeSheetRoute takes fullScreen and reports isFullScreen. A kept-alive sheet comes back in the presentation it's asked for. Asked while the sheet is still coming up, or while something is presented over it, the move waits until it can.
  • Closing a sheet that's still coming up: NativeSheetController.close (and NativeSheet.close) asked mid-presentation now closes it once it's up, instead of being dropped by UIKit; a sheet still measuring its content just ends.
  • Cleanup before a sheet's engine ends: NativeSheet.setClosingHandler runs inside a native sheet once it has closed, and its engine waits for it (up to 3 s) before ending — so work that has to reach native code, like stopping a camera, isn't cut off.
  • Plugins per sheet kind (iOS): VeneerPlugin.sheetEntrypointPluginRegistrant is told the Dart entrypoint each sheet engine runs, so an app can register heavy plugins (a call's WebRTC, say) only in the engines that use them. It takes precedence over sheetPluginRegistrant.

0.9.3 #

  • Bar button replicas (Android, iOS 15–25): a lone text button in NativeNavigationBar's trailing items is a capsule that fits its label, instead of a 44 pt circle that clipped it. prominent buttons draw as tinted glass (tint, then the bar's tintColor, then the accent) with a white label, as UIKit's .prominent style does.

0.9.2 #

  • Colour emoji in the composer replicas: emoji that ask for colour presentation (U+FE0F), such as ❤️, ☀️, ⚠️, keycaps and ZWJ sequences like ❤️‍🔥, now draw in the platform's colour emoji font. Before, an app text font with its own glyph for the base character (e.g. ❤) drew them in black. Highlights and the keyboard's composing underline still apply.

0.9.1 #

  • Tab bar handoff: when NativeChromeScopes overlap — a route replacing the page holding one with another, or a route with its own scope pushed over one — the newest scope drives the native tab bar. An older scope going away no longer removes the newer one's bar or its tap handlers, and when the newest goes, the one before it takes the bar back with its own tabs and covered state.

0.9.0 #

  • Search field: NativeSearchField is a real UISearchTextField on Liquid Glass, positioned by Flutter layout like a GlassShape. UIKit draws the magnifier, placeholder, clear button and caret and owns the keyboard; text syncs both ways through a TextEditingController, with onChanged, onSubmitted and onFocusChanged, and NativeSearchFieldState.focus() / unfocus(). Off iOS 26 it renders a Flutter field of the same height.
  • Segment bar: GlassSegmentBar is one glass capsule holding a row of icon (or emoji) buttons, with a highlight that springs to the selected segment, reporting taps through onSelected. Off iOS 26 it renders the same capsule in Flutter.
  • Icon fallbacks: leaf, fork.knife, soccerball, airplane and flag now map to Material icons off iOS 26.

0.8.0 #

  • Voice clip attachments: NativeComposerAttachment(audio: NativeComposerAudio(...)) shows the attachment as a full-width row in the prompt composer: a play/pause circle in the tint, a waveform that fills with the tint as progress moves, the duration and a remove button. It sits 8pt from the composer's edges, 56pt tall, in the space the tile strip already takes. The app plays the clip and rebuilds with new values; the play/pause button calls onTap. Drawn natively on iOS 26 and by the Flutter replica elsewhere.
  • Attachments fold away while unfocused: with NativePromptComposer(attachmentSummary: ...), the attachments collapse into a [📎 3] pill beside the buttons when the prompt loses focus (e.g. a scroll dismissed the keyboard), so the composer goes back to one row. Focusing the prompt, or tapping the pill, shows them again.

0.7.3 #

  • Flutter sheet stacking matches UIKit: a Flutter-drawn sheet at its large detent now meets the status bar, as UIKit's does, instead of stopping 10pt lower. A sheet that comes up over another sheet stops 10pt lower, so the one behind shows above it. The sheet behind keeps its top edge and shrinks 9% (was: lifted 10pt and shrank 6%), measured against UIKit sheets on iOS 26.

0.7.2 #

  • Plugin UI over sheets: a presentation aimed at a view controller that already presents a native sheet now goes to the sheet on top. Plugins usually present from the window's root view controller (contact pickers, calendar editors, share sheets), and UIKit refuses that while a sheet is up, so their UI never appeared. Presentations UIKit would accept are untouched; VeneerPlugin.forwardsPresentationsOverSheets = false turns it off.

0.7.1 #

  • Sheet appearance: inside a native sheet, NativeSheet.appeared turns true once the sheet has finished sliding up, and NativeSheet.untilAppeared() waits for it (at once outside native sheets). UIKit won't present over a sheet mid-transition, so wait for it before showing native UI, such as a payment sheet or a system picker, over a sheet as it opens.

0.7.0 #

  • Messages to sheets: showNativeSheet(controller: NativeSheetController()) lets the app send messages to the open sheet, which reads them from NativeSheet.messages(context), and close it with a result. With NativeSheet.request, a sheet can stay a view of state the app owns: the payload is its first state, requests carry actions to the app, and answers and messages bring fresher state back. Works in the Flutter fallback too.
  • Content-sized sheets open about 300 ms sooner: a sheet with a NativeSheetDetent.content() detent now measures its content before it slides up. Flutter lays a view out only once it's in a window, so before, the measurement never came until the sheet was on screen: every such sheet waited out the 350 ms limit and then slid up at the wrong height. Its view now measures attached beside the presenter, off screen, and comes off the moment it's measured. In a benchmark the slide started after about 65 ms instead of about 370 ms, without the dropped frame each opening had.
  • The engine for the next sheet starts once the sheet has slid up, so it never competes with the measuring and the slide.
  • Choose sheet engines' plugins: VeneerPlugin.sheetPluginRegistrant registers only the plugins your sheets use, instead of all of the app's, so plugins that observe the app (calls, push, analytics) don't run twice.

0.6.1 #

  • Fix: a content-sized sheet presented over another sheet no longer hides it. The sheet's engine lays out once before its view has a width, and NativeSheetContent reported that squeezed, screen-tall height; the sheet slid up at full height and then shrank, leaving the sheet under it stacked behind the tall one. Measurements now wait for a real width.

0.6.0 #

  • Sheet requests: a native sheet can ask the app that presented it for something while it's open — NativeSheet.request(name, arguments) inside the sheet, answered by NativeSheet.setRequestHandler in the app — e.g. a fresh credential, since a sheet's engine shares no memory with the app. Answers and errors (PlatformException: no_handler, not_shown) go back to the sheet. In the Flutter fallback the handler is called directly.

0.5.1 #

  • Long capsule titles fit: a NativeBarTitle(capsule: true) that doesn't fill now truncates its title (and subtitle) to the space between the leading and trailing buttons, natively as in the Flutter replica. Before, a title wider than the bar pushed the capsule and the trailing buttons into UIKit's overflow menu. It still hugs its text at the leading edge, now in its own glass.

0.5.0 #

  • Kept-alive sheets: showNativeSheet(keepAlive: true) hides a native sheet on close instead of ending its engine. The sheet's app keeps running (timers, network, audio; no frames while hidden), and the next showNativeSheet at the same entrypoint presents the same app, chrome and all. Inside, NativeSheet.shown says whether it's on screen and NativeSheet.presented brings each presentation's payload; NativeSheet.release frees the engine (for example on sign-out). Ignored with arguments, and on the Flutter fallback.

0.4.6 #

  • Tappable attachments: NativeComposerAttachment.onTap is called when the attachment tile itself is tapped, e.g. to preview a photo or play a video, natively and in the Flutter replica. The remove button stays on top. Without onTap a tap on the tile focuses the prompt, as before.

0.4.5 #

  • Padded bar buttons: NativeBarButton.iconPadding puts its own padding around the icon instead of UIKit's bar-button insets, which widen a large icon's glass into a capsule; a 42 pt avatar with 1 pt padding now fills a 44 pt circle. On iOS 26 it hides the bar's shared glass and carries its own round Liquid Glass, since UIKit pads custom views sideways too. Badges aren't shown on padded buttons.

0.4.4 #

  • Tab action icon size: NativeTabAction.iconSize sets the side of a non-symbol action icon (an SVG or image), natively and in the Flutter replica. Before, it was always 25 pt on iOS and 26 pt in the replica.

0.4.3 #

  • Titled bar buttons: NativeBarButton.showsTitle shows the title beside the icon in a glass capsule, like a labelled toolbar button, instead of using it only for VoiceOver. NativeBarButton.titleStyle sets its font, size, weight and colour, natively too. On iOS 26 it's a UIButton custom view that UIKit puts the bar's glass behind; the Flutter replica draws the same capsule. Badges aren't shown on titled buttons.
  • Explicit scroll state: NativeNavigationBar.scrolled tells a bar that isn't an ancestor of the scrolling content (e.g. one in Scaffold.appBar) whether content is under it, so its scroll edge effect still shows. Null keeps detecting it from the child's scroll notifications.

0.4.2 #

  • Bar button tint: NativeBarButton.tint sets the colour of a prominent button's glass, such as the app's primary colour. Before, prominent buttons always took the bar's tintColor and fell back to the system blue.

0.4.1 #

  • Fixes:
    • Opening a native sheet after another was dismissed no longer aborts the app (Spawning from an engine without a shell). FlutterEngineGroup spawns every engine from its oldest living one, which could be a finished sheet's engine whose context was already destroyed. Sheet engines now spawn from a dedicated idle engine that lives with the app.

0.4.0 #

  • Context menus: NativeContextMenu opens UIKit's own context menu when you long-press any Flutter widget. You get the press shrink, the widget lifting over a blurred backdrop with the menu beside it, haptics, and the settle back. UIKit recognizes the long press on the Flutter view and hit-tests against where Flutter laid the widget out that frame, so it follows scrolling and never interrupts a scroll. The lifted preview is a snapshot clipped to borderRadius. Android and iOS 15–25 get a Flutter menu next to the widget instead.
  • Voice recording in composers: NativeComposerController.startVoiceRecording() turns either composer into a recording bar, as in Slack: cancel, a live waveform, the elapsed time and a done button in the tint colour. Recording is native (AAC .m4a, with microphone permission and haptics). The clip arrives in onVoiceRecorded as a NativeVoiceRecording (path and duration). A refusal arrives in onVoiceRecordingFailed, and so does a Flutter fallback, so apps can record their own way there. Also new: finishVoiceRecording(), cancelVoiceRecording(), isRecording, an optional maxDuration, and VoiceOver labels for the bar's buttons. A recording is cancelled when its composer goes away. Your app needs NSMicrophoneUsageDescription.
  • Mentions in composers: the controller tracks the caret (selection), and gains setText(text, selection:) and replaceRange for completing a mention being typed. The highlights parameter draws given runs of text (such as @names) in the tint colour, natively and in the replicas.
  • Navigation bar: NativeBarTitle(fill: true) stretches a title capsule across the space between the leading and trailing buttons, as a chat header does. iconSize fits an avatar-sized icon, such as a 40 pt group avatar, concentric with the capsule.
  • Fixes:
    • A page taking over the shared native composer now sends its own text. A new page no longer shows the previous page's draft.
    • A page coming back into view now shows its composer again.
    • After a hot restart, a composer the previous isolate left on screen is removed.

0.3.0 #

  • Native sheets can present native sheets. A sheet opened from inside a sheet's engine is a real UIKit sheet over it. Sessions are keyed per presenting engine, so a nested sheet no longer replaces its parent's session (which left the parent unable to close or report its result). Dismissing a sheet finishes the sheets above it too.
  • Content-sized native sheets: NativeSheetDetent.content() now works natively. Wrap the sheet's content in NativeSheetContent; the sheet lays out off screen, reports its height and slides up at it, then follows the content as it changes.
  • Faster sheets: the next engine for an entrypoint starts as soon as one is used, so a sheet opened from a sheet, or the next one, is warm too.
  • Content-sized sheets, sizing: content taller than the sheet is laid out at the sheet's height, so its scroll views end at the sheet's bottom instead of below it; content the sheet is still growing to keeps its height rather than being squeezed for a frame.
  • Stacked sheets: a Flutter sheet under another sheet steps back behind it (slightly smaller, its top peeking above, its grabber hidden) and returns when the sheet above closes.
  • Navigation bar: the scroll edge effect shows only once the page's content scrolls under the bar, natively and in the replica, so content at rest under the bar stays crisp.
  • Fixes: a detent index outside detents no longer crashes; a content-sized Flutter sheet follows content that grows after its first layout; the first drag on a content-sized native sheet scrolls its content instead of being lost.

0.2.2 #

  • Sheets: a Flutter sheet given a backgroundColor stays solid at every detent: content no longer shows the page behind through it, nor a lighter band under the bar's edge fade. A CupertinoDynamicColor follows the theme's brightness while the sheet is open. Sheets without a colour keep iOS 26's translucency below the large detent.
  • Sheets: while the page behind recedes (the large detent), the status bar turns light over it, as in UIKit; adding that never rebuilds the sheet mid-drag.
  • Fallbacks: VeneerFallbackStyle.shadows replaces the default shadow under replica surfaces, so bars, buttons and the tab bar can use a design system's elevation.
  • Fixes: after a hot restart, chrome an earlier isolate left hidden (a Flutter sheet was up) shows again.

0.2.1 #

  • Sheets: NativeSheetDetent.content() sizes a Flutter-drawn sheet to its content and follows it as it changes; sheets shorter than large float inset from the screen edges. Sheet content gets a Material (text fields and list tiles work as-is) and the sheet's surface as its scaffold background.
  • Sheets, safe area and keyboard: a sheet rising for the keyboard grows at least as fast as the keyboard, so its content is never squeezed between the two; the keyboard inset reaching the content no longer counts the sheet's floating inset twice; dragging the sheet puts the keyboard away.
  • Fixes: bars, composers and the tab bar come back when a popup or sheet on an outer navigator closes (a page in a tab's navigator); after a hot restart the native overlay reports its insets again, so pages under a NativeNavigationBar don't slide under the bar.

0.2.0 #

  • Navigation bar: titles can be leading-aligned (NativeBarTitle.alignment) and styled with your app's fonts and colours (style, subtitleStyle), natively too. The title capsule takes an accessory glyph.
  • Bar buttons: badge, prominent tinted glass, text-only buttons, and group to split trailing buttons into separate capsules.
  • Menus: NativeMenuItem.divider() for sections.
  • Prompt composer: stopAction, sendEnabled, sendBusy and editable; attachments take loading.
  • Sheets: payload / NativeSheet.payload() pass data into pre-warmed engines. Sheet engines no longer restyle the app's status bar, and each is torn down when its sheet is dismissed (their overlays no longer outlive them).
  • Fallbacks: VeneerFallbackStyle is a ThemeExtension, so replicas can follow your design system. The replica bar spans its parent and places titles like UIKit, the menu is opaque with section bands, the tab bar sizes to its tabs and clears Android's gesture handle, and the Flutter sheet has a rim at its edge.
  • Fixes: a bar or composer on a page inside a nested navigator (a tab's) now hides when an outer route covers it; a NativeNavigationBar outside a NativeChromeScope gives its content the right top inset; a composer host rebuilt mid-page keeps its height.

0.1.1 #

  • Composer: if the software keyboard goes away while the text view keeps focus (for example at the end of an interactive dismissal), the composer now drops focus and collapses to its capsule, instead of staying expanded with no keyboard and bringing the keyboard back on the next tap.
  • Composer: the collapsed capsule always shows its text from the start.
  • Docs: a demo of the example app in the README.

0.1.0 #

First public release.

  • Tab bar (NativeChromeScope): a real UITabBarController with the split layout's trailing button, badges, selected icons and a bottom scroll edge effect. Slides away while another route covers its page.
  • Navigation bar (NativeNavigationBar): a real UINavigationBar with glass buttons, a title capsule, grouped trailing buttons, native menus, animated item changes and iOS 26's scroll edge effect over Flutter content.
  • Composer (NativeComposer): a messaging composer that morphs from a capsule into a card above the keyboard inside the keyboard's animation, with interactive keyboard dismissal.
  • Prompt composer (NativePromptComposer): an assistant-style composer with inline actions, a primary action that becomes send, multi-line growth, attachments and glass side actions that split off for a live session.
  • Sheets (showNativeSheet): real UIKit sheets hosting Flutter content in a pre-warmed engine, with detents, grabber and Veneer's bars and composers inside; a Flutter sheet with the same behaviour elsewhere.
  • Menus (NativeMenuItem): native UIMenus from bar buttons, composer buttons and glass shapes.
  • Glass (GlassShape, GlassGroup): Liquid Glass positioned by Flutter layout every frame through a synchronous dart:ffi call, merging within groups and clipped by Flutter clips.
  • Icons (NativeIcon): SF Symbols, IconData from any icon font (including variable fonts), a native SVG renderer and raster images.
  • Flutter replicas of every component for Android and iOS 15–25, and VeneerFallbackScope / Veneer.debugForceFallback to force them.
2
likes
160
points
1.94k
downloads
screenshot

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Real native iOS 26 UI for Flutter: Liquid Glass, UIKit tab and navigation bars, composers and sheets driven by Flutter layout, with matching Flutter UI on Android.

Repository (GitHub)
View/report issues

Topics

#ios #liquid-glass #uikit #native #ui

License

MIT (license)

Dependencies

flutter, flutter_svg

More

Packages that depend on veneer

Packages that implement veneer