This commit is contained in:
agra
2026-04-16 18:38:37 +03:00
parent 8ac7b5a5d5
commit 0fff294caf
41 changed files with 1740 additions and 46 deletions

114
README.md
View File

@@ -1,44 +1,90 @@
# ux
UX Kit
A Flutter toolkit for building fluid, native-feeling UIs.
## Includes
Within this kit we should find:
## UxKeyboard
### DATAGRID
- [ ] Table
- [ ] Tree
- [ ] Pivot
- [ ] Cross Tab
Frame-accurate keyboard height tracking for iOS and Android, with interactive dismiss.
### FIELD
- [ ] Text
- [ ] Number
- [ ] Date
- [ ] List
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.
### CHART
- [ ] Bar
- [ ] Area
- [ ] Pie
- [ ] Scatter
### Features
### BUTTON
- [ ] Button
- [ ] Group
- [ ] Split
- [ ] Drop
- **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 freeze** — `isTracking` flag lets you freeze scrolling during interactive dismiss
### ANIMATION
- [ ] Expand
- [ ] Fade
- [ ] Push
- [ ] Slide
### Quick start
### THEME
- [ ] Material
- [ ] Cupertino
- [ ] Fluid
```dart
final keyboard = UxKeyboard.instance;
### OTHER
- [x] BendBox
// 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:
```dart
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);
return Column(
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
| 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 |
### 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