Docs: textfield-centering pitfall + config-source / FCM channel conventions

- mitra_app/CLAUDE.md: pitfall entry for the InputDecorationTheme
  min-height collision that broke chat-input centering. Walks through
  the working recipe (constraints: BoxConstraints(), Material +
  StadiumBorder + Center wrapper). Points at chat_screen.dart::_InputBar
  in both apps as the source of truth.
- backend/CLAUDE.md: two new convention sections.
  - Config-source: when to use DB-stored (operator-tunable via CC) vs
    env-driven (deploy-fixed). Codifies the pattern shipped today for
    MITRA_HEARTBEAT_CADENCE_SECONDS so Xendit credentials / callback
    tokens follow the same shape tomorrow.
  - FCM channel: single shared `halobestie_chat_v1` channel for both
    apps, target via android.notification.channelId. Bump the channel
    ID when introducing a new sound (Android API 26+ binds sound at
    channel-create time).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-21 22:38:50 +08:00
parent 387f0f65de
commit bfb072ddfb
2 changed files with 45 additions and 0 deletions

View File

@@ -63,3 +63,29 @@ void disconnect() {
```
The synchronous side-effects (closing the WS, cancelling timers) still happen immediately. Only the `state =` assignment is deferred, which is a no-op for users — they're navigating away anyway. Regression coverage: `.maestro/flows/ts-mitra-3-08-back_press_after_session_expired_no_red_screen.yaml`.
### Custom-styled TextField must override the theme's min-height constraint
The app-wide `InputDecorationTheme` in `lib/core/theme/halo_theme.dart` sets a 48dp min-height for form fields (auth, profile, etc.). Any pill-style chat-input or compact TextField that has a fixed-height parent (≤ 48dp) will **silently lose vertical centering** — the field refuses to collapse below 48dp, the line-box can't sit on the parent's midline, and `textAlignVertical` becomes a no-op. Text anchors top.
**Rule:** when building a custom-shaped TextField (pill, dense, fixed-height), explicitly null the theme constraint:
```dart
TextField(
textAlignVertical: TextAlignVertical.center,
decoration: const InputDecoration(
isCollapsed: true,
contentPadding: EdgeInsets.symmetric(horizontal: 16),
constraints: BoxConstraints(), // ← REQUIRED — overrides theme min-height
border: InputBorder.none,
enabledBorder: InputBorder.none,
focusedBorder: InputBorder.none,
disabledBorder: InputBorder.none,
errorBorder: InputBorder.none,
focusedErrorBorder: InputBorder.none,
filled: false,
),
)
```
Wrap in `Material(shape: StadiumBorder(...), clipBehavior: antiAlias) + Center(child: TextField)` for proper pill clipping. The chat input bar in [mitra_chat_screen.dart](lib/features/chat/screens/mitra_chat_screen.dart) and [client_app/chat_screen.dart::_InputBar](../client_app/lib/features/chat/screens/chat_screen.dart) both use this pattern; copy from there rather than reinventing.