Keyboard Accessory
A bar docked to the top edge of the soft keyboard while a TextEntry has focus. It belongs to the
keyboard, not to the entry: it appears when the keyboard comes up, moves with it, and goes away with it.
Nothing is rendered inside the text field.
The reason it exists: the iOS numeric keypad has no return key. Give a user a numeric field and no Done button and there is genuinely no way for them to put the keyboard away.
Presets
Section titled “Presets”The quickest version — no bar markup at all:
<shiny:TextEntry Placeholder="Amount" Keyboard="Numeric" Text="{Binding Amount, Mode=TwoWay}" AccessoryPreset="NavigationAndDone" />| Preset | Bar |
|---|---|
None |
No bar. The default |
Done |
A trailing “Done” that dismisses the keyboard |
Navigation |
Leading previous/next field arrows |
NavigationAndDone |
Arrows on the left, “Done” on the right |
Your own bar
Section titled “Your own bar”Accessory takes a KeyboardAccessoryView. Items is its content property, so items can be listed
directly inside it:
<shiny:TextEntry Placeholder="Notes" Text="{Binding Notes, Mode=TwoWay}"> <shiny:TextEntry.Accessory> <shiny:KeyboardAccessoryView> <shiny:KeyboardNavigationItem Direction="Previous" /> <shiny:KeyboardNavigationItem Direction="Next" /> <shiny:KeyboardAccessorySpacer /> <shiny:KeyboardAccessoryItem Text="#tag" Command="{Binding InsertTagCommand}" /> <shiny:KeyboardDismissItem /> </shiny:KeyboardAccessoryView> </shiny:TextEntry.Accessory></shiny:TextEntry>A KeyboardAccessorySpacer takes whatever width is left over, which is how items are pushed apart —
everything before it stays left, everything after it goes right.
For something that is not a row of buttons at all, set BarContent to any View and it replaces the
item row wholesale. Items lays out as one auto-width column per item, so a long row gets squeezed
rather than scrolled — when there are more items than fit the bar, BarContent with a horizontal
ScrollView (and a KeyboardDismissItem pinned beside it) is the shape you want. Any
KeyboardAccessoryItem nested inside BarContent is wired to the focused field exactly like one
listed in Items, however deep it sits.
| Type | Purpose |
|---|---|
KeyboardAccessoryView |
The bar itself |
KeyboardAccessoryItem |
Icon/label button — Icon, Text, ToolColor, FontSize, IconSize, Command, CommandParameter, Clicked |
KeyboardNavigationItem |
Direction="Previous" / "Next". Moves focus and dims itself to 0.35 opacity at the ends of the run |
KeyboardDismissItem |
“Done” — dismisses the keyboard. Set Text for a non-English bar; the default no longer overwrites a text already assigned |
KeyboardAccessorySpacer |
Flexible gap |
KeyboardAccessoryView properties
Section titled “KeyboardAccessoryView properties”| Property | Type | Default | Description |
|---|---|---|---|
Items |
IList<View> | empty | Bar contents, left to right (content property) |
BarContent |
View? | null | Replaces the item row with your own layout |
BarHeight |
double | 44 |
Bar height |
BarBackgroundColor |
Color? | surface-container-high token | Bar fill |
BarBorderColor |
Color? | outline-variant token | The hairline along the top edge |
ItemSpacing |
double | 4 |
Gap between items |
CurrentOwner |
TextEntry? | — | Read-only. The field the bar is serving right now |
Field navigation
Section titled “Field navigation”KeyboardNavigationItem walks the fields on the page through KeyboardFieldNavigator, which collects
every enabled, visible, non-read-only TextEntry / InputView under the field’s page in depth-first
visual-tree order — for a form laid out top to bottom, the order it reads in. MAUI has no TabIndex
to honour (that was a Xamarin.Forms concept), so declaration order is the order.
Set FieldGroup on the entries to navigate within a subset rather than the whole page:
<shiny:TextEntry Placeholder="Card number" FieldGroup="payment" AccessoryPreset="NavigationAndDone" /><shiny:TextEntry Placeholder="Expiry" FieldGroup="payment" AccessoryPreset="NavigationAndDone" /><shiny:TextEntry Placeholder="CVV" FieldGroup="payment" AccessoryPreset="NavigationAndDone" />A disabled arrow stays disabled-looking when it is tapped. That sounds like nothing, but MAUI’s TapGestureRecognizer disregards IsEnabled on several platforms, so the tap does reach the item — the move is refused, and the press-flash that used to run alongside it ended by restoring full opacity, undoing the dim. The flash is skipped while the item is disabled.
KeyboardFieldNavigator is public and platform-free, so it can be driven from your own items:
KeyboardFieldNavigator.CanMove(entry, KeyboardNavigationDirection.Next);KeyboardFieldNavigator.Move(entry, KeyboardNavigationDirection.Next);KeyboardFieldNavigator.Collect(entry); // the ordered runLimitation: virtualized containers (CollectionView, TableView, DataGrid) only realize the rows
currently on screen, so navigation cannot reach a field that has not been realized yet.
Platform behaviour
Section titled “Platform behaviour”This is a deliberately platform-only feature, in the same category as Desktop being MAUI-only.
| Platform | Behaviour |
|---|---|
| iOS / Mac Catalyst | The real UIResponder.InputAccessoryView. The OS owns the bar, so it rides the keyboard’s own animation exactly. On Catalyst proper, where there is no soft keyboard, it generally will not appear at all |
| Android | Android has no accessory API — the IME runs in a different process and only an IME app can draw inside it. The same bar is rendered in the activity’s content view and pushed up by however much the IME actually overlaps it, measured rather than assumed so adjustResize, adjustPan and forced edge-to-edge (API 35+) are all correct. On API 30+ it is frame-synced to the IME animation via WindowInsetsAnimation; below that it snaps into place when the insets land |
| Windows / macOS / Linux | No soft keyboard. Accessory compiles and does nothing |
| Blazor | No equivalent, on purpose — see below |
Because the Android bar tracks the IME insets rather than focus, a device with a hardware keyboard attached (which raises no IME) correctly shows no bar.
Why there is no Blazor version
Section titled “Why there is no Blazor version”iOS Safari already draws its own bar above the keyboard and will not let a page remove it, so ours would stack on top of it and the user would see two. Chrome on Android draws none, so the behaviour would diverge across the only two browsers where it matters. Building one would be parity theatre.
Multi-line inputs
Section titled “Multi-line inputs”The bar serves any InputView, not only single-line entries — this is how
MarkdownEditor puts its formatting toolbar on the keyboard, and it is the
supported way to build anything similar. On a multi-line editor a KeyboardDismissItem is close to
mandatory: the return key inserts a newline, so nothing else puts the keyboard away.
- One bar instance serves one field.
AccessoryPresetmaterializes a bar per entry automatically; if you share a singleKeyboardAccessoryViewacross fields, remember that on iOS aUIViewcan only have one superview. - The bar lives outside the control’s visual tree, so it is torn down explicitly when the field is unloaded — navigating away does not leave a bar behind.
- Swapping the bar while the field is already focused reloads the responder’s input views, so the change is visible immediately.


