Skip to content

Repository files navigation

ux

A Flutter toolkit for building fluid, native-feeling UIs.

UxKeyboard

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.

Features

  • Real-time height — reads the keyboard's actual position each frame via FFI (iOS) / JNI (Android)
  • Native animation curves — sampled from CADisplayLink (iOS) and WindowInsetsAnimation (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 freezeisTracking flag lets you freeze scrolling during interactive dismiss

Quick start

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
),
],
);
},
),
);

API

MemberDescription
UxKeyboard.instanceSingleton instance
.heightCurrent keyboard height in logical pixels
.systemHeightLast system-reported keyboard height
.isOpenWhether the keyboard is visible
.isTrackingWhether a dismiss pan gesture is active
.enableInteractiveDismiss({trackingInset})Enable swipe-to-dismiss
.disableInteractiveDismiss()Disable swipe-to-dismiss
addListener / removeListenerStandard ChangeNotifier API

Key points

  • Set resizeToAvoidBottomInset: false on your Scaffold — otherwise Flutter's built-in resize fights with UxKeyboard
  • Use MediaQuery.viewPaddingOf(context).bottom for the safe area (not paddingOf, which is consumed by Scaffold)
  • Use max(keyboardHeight, safeBottom) for bottom padding — the keyboard height includes the safe area when open, and safeBottom covers the home indicator when closed

Other utilities

  • BendBox — a flexible layout widget
  • Bezier — bezier curve utilities

About

UX Kit for Flutter

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages