Files
buzz/mobile/lib/features/channels/android_ime_lift.dart
klopez4212 69107dc3bf Polish mobile message threads and composer (#5645)
## Summary

- refine mobile message metadata, search spacing, and Activity filter
semantics
- add channel-parity Latest navigation and stable tail following to
threads
- synchronize Android composer/keyboard geometry and keep Latest spacing
stable across IME transitions

## Validation

- `bin/just mobile-check`
- `bin/just mobile-test` (1,276 tests)
- Pixel 10 install/launch and channel/thread keyboard, Latest, tail, and
back-navigation review
- signed iPhone install/launch workflow

## Snapshots

See the review snapshots below.

---------

Signed-off-by: kenny lopez <klopez4212@gmail.com>
Signed-off-by: Kenny Lopez <klopez4212@gmail.com>
Signed-off-by: Princess Donut <b238ea756dee4d98afa5883fc7f1de61eeabe65bf700e3a5a5a80db5e42e2c2b@buzz.block.builderlab.xyz>
Co-authored-by: Fast Fizz <2df81cb51f05a9d5387ef24d7b9ecb8fcdfcd1c70ffabc67061c9596e1b5b1c4@buzz.block.builderlab.xyz>
Co-authored-by: Princess Donut <b238ea756dee4d98afa5883fc7f1de61eeabe65bf700e3a5a5a80db5e42e2c2b@buzz.block.builderlab.xyz>
2026-08-15 09:14:14 +01:00

36 lines
1.4 KiB
Dart

import 'dart:math' show max;
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
/// Android keeps the message viewport fixed while the IME animates. Only this
/// small wrapper follows the frame-by-frame inset; timelines apply the final
/// inset once metrics settle.
class AndroidImeLift extends StatelessWidget {
/// The composer subtree that follows the animated Android IME inset.
final Widget child;
/// Creates a wrapper that lifts [child] without resizing its surrounding
/// message viewport on Android.
const AndroidImeLift({super.key, required this.child});
@override
Widget build(BuildContext context) {
if (!usesFixedAndroidImeViewport) return child;
final imeBottom = MediaQuery.viewInsetsOf(context).bottom;
final systemBottom = MediaQuery.viewPaddingOf(context).bottom;
return Padding(
// The composer already reserves [systemBottom]. Android's IME inset
// includes that navigation area, so lifting by the full value leaves a
// second safe-area gap above the keyboard.
padding: EdgeInsets.only(bottom: max(0, imeBottom - systemBottom)),
child: child,
);
}
}
/// Whether channel and thread scaffolds should keep a fixed viewport while
/// Android IME insets animate, with their composer lifted independently.
bool get usesFixedAndroidImeViewport =>
defaultTargetPlatform == TargetPlatform.android;