fix(reply): cache FlutterView to prevent crash on screen re-open

View.of(context) was being called in didChangeMetrics() during widget
deactivation, causing "Looking up a deactivated widget's ancestor"
errors. The context becomes invalid before mounted becomes false.

Fixed by caching the FlutterView reference in didChangeDependencies()
for both _ReplyScreenState and _ReplyToolbarState, then using the
cached reference in didChangeMetrics().

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

Changed files
+21 -5
lib
screens
+21 -5
lib/screens/compose/reply_screen.dart
···
import 'dart:async';
import 'dart:math' as math;
+
import 'dart:ui' show FlutterView;
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
···
bool _authInvalidated = false;
double _lastKeyboardHeight = 0;
Timer? _bannerDismissTimer;
+
FlutterView? _cachedView;
@override
void initState() {
···
});
}
+
@override
+
void didChangeDependencies() {
+
super.didChangeDependencies();
+
// Cache the view reference so we can safely use it in didChangeMetrics
+
// even when the widget is being deactivated
+
_cachedView = View.of(context);
+
}
+
void _setupAuthListener() {
try {
context.read<AuthProvider>().addListener(_onAuthChanged);
···
super.didChangeMetrics();
// Guard against being called after widget is deactivated
// (can happen during keyboard animation while navigating away)
-
if (!mounted) return;
+
if (!mounted || _cachedView == null) return;
-
final keyboardHeight = View.of(context).viewInsets.bottom;
+
final keyboardHeight = _cachedView!.viewInsets.bottom;
// Detect keyboard closing and unfocus text field
if (_lastKeyboardHeight > 0 && keyboardHeight == 0) {
···
with WidgetsBindingObserver {
final ValueNotifier<double> _keyboardMarginNotifier = ValueNotifier(0);
final ValueNotifier<double> _safeAreaBottomNotifier = ValueNotifier(0);
+
FlutterView? _cachedView;
@override
void initState() {
···
@override
void didChangeDependencies() {
super.didChangeDependencies();
+
// Cache view reference for safe access in didChangeMetrics
+
_cachedView = View.of(context);
_updateMargins();
}
···
@override
void didChangeMetrics() {
-
_updateMargins();
+
// Schedule update after frame to ensure context is valid
+
WidgetsBinding.instance.addPostFrameCallback((_) {
+
_updateMargins();
+
});
}
void _updateMargins() {
-
if (!mounted) {
+
if (!mounted || _cachedView == null) {
return;
}
-
final view = View.of(context);
+
final view = _cachedView!;
final devicePixelRatio = view.devicePixelRatio;
final keyboardInset = view.viewInsets.bottom / devicePixelRatio;
final viewPaddingBottom = view.viewPadding.bottom / devicePixelRatio;