A Flutter toolkit for building fluid, native-feeling UIs.
Frame-accurate keyboard height tracking for iOS and Android, with interactive dismiss.
Flutter's built-in MediaQuery.viewInsets.bottom lags behind the actual keyboard position
and doesn't support interactive dismiss. UxKeyboard reads the keyboard height directly
from the native layer via FFI — zero channel latency, every frame.
- Real-time height — reads the keyboard's actual position each frame via FFI (iOS) / JNI (Android)
- Native animation curves — sampled from
CADisplayLink(iOS) andWindowInsetsAnimation(Android), with adaptive learning that refines the curve from observations - Interactive dismiss — swipe the keyboard down like iMessage/Telegram, with snap-back or dismiss
- Scroll freeze —
isTrackingflag lets you freeze scrolling during interactive dismiss
final keyboard =UxKeyboard.instance;
// Enable swipe-to-dismiss. trackingInset is the height of your input bar.
keyboard.enableInteractiveDismiss(trackingInset:56);Use ListenableBuilder to rebuild when the keyboard height changes:
Scaffold(
resizeToAvoidBottomInset:false, // we handle it ourselves
body:ListenableBuilder(
listenable: keyboard,
builder: (context, _) {
final keyboardHeight = keyboard.height;
final safeBottom =MediaQuery.viewPaddingOf(context).bottom;
final bottom =max(keyboardHeight, safeBottom);
returnColumn(
children: [
Expanded(
child:ListView.builder(
reverse:true,
// Freeze scrolling during interactive dismiss
physics: keyboard.isTracking
?NeverScrollableScrollPhysics()
:null,
// ...
),
),
Container(
padding:EdgeInsets.only(bottom:8+ bottom),
// your input bar
),
],
);
},
),
);| Member | Description |
|---|---|
UxKeyboard.instance | Singleton instance |
.height | Current keyboard height in logical pixels |
.systemHeight | Last system-reported keyboard height |
.isOpen | Whether the keyboard is visible |
.isTracking | Whether a dismiss pan gesture is active |
.enableInteractiveDismiss({trackingInset}) | Enable swipe-to-dismiss |
.disableInteractiveDismiss() | Disable swipe-to-dismiss |
addListener / removeListener | Standard ChangeNotifier API |
- Set
resizeToAvoidBottomInset: falseon yourScaffold— otherwise Flutter's built-in resize fights withUxKeyboard - Use
MediaQuery.viewPaddingOf(context).bottomfor the safe area (notpaddingOf, which is consumed byScaffold) - Use
max(keyboardHeight, safeBottom)for bottom padding — the keyboard height includes the safe area when open, andsafeBottomcovers the home indicator when closed
- BendBox — a flexible layout widget
- Bezier — bezier curve utilities