Skip to content

Commit 3e68000

Browse files
Mostronatormostronator
andauthored
refactor: clean up chat room screen — extract side effects and deduplicate (#481)
- Move keyboard-triggered scroll logic from build() to didChangeDependencies() to keep build() purely declarative - Extract duplicate onInfoTypeChanged callback into shared _handleInfoTypeChanged method (DRY) - Bump message input top border opacity from 0.03 to 0.15 and width from 0.3 to 0.5 so the separator is actually visible Addresses CodeRabbit nitpicks from PR #466. Co-authored-by: mostronator <mostronator@users.noreply.github.com>
1 parent 9a8ac50 commit 3e68000

File tree

1 file changed

+39
-45
lines changed

1 file changed

+39
-45
lines changed

lib/features/chat/screens/chat_room_screen.dart

Lines changed: 39 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,41 @@ class _ChatRoomScreenState extends ConsumerState<ChatRoomScreen> {
3838
super.dispose();
3939
}
4040

41+
@override
42+
void didChangeDependencies() {
43+
super.didChangeDependencies();
44+
final isKeyboardVisible = MediaQuery.of(context).viewInsets.bottom > 0;
45+
46+
if (isKeyboardVisible && !_wasKeyboardVisible) {
47+
Future.delayed(const Duration(milliseconds: 100), () {
48+
if (_scrollController.hasClients &&
49+
_scrollController.position.maxScrollExtent > 0) {
50+
try {
51+
_scrollController.animateTo(
52+
_scrollController.position.maxScrollExtent,
53+
duration: const Duration(milliseconds: 300),
54+
curve: Curves.easeOut,
55+
);
56+
} catch (e) {
57+
// Silently handle any scroll errors
58+
}
59+
}
60+
});
61+
}
62+
63+
_wasKeyboardVisible = isKeyboardVisible;
64+
}
65+
66+
/// Shared callback for dismissing keyboard and updating info type selection.
67+
void _handleInfoTypeChanged(String? type) {
68+
if (type != null) {
69+
FocusScope.of(context).unfocus();
70+
}
71+
setState(() {
72+
_selectedInfoType = type;
73+
});
74+
}
75+
4176
@override
4277
Widget build(BuildContext context) {
4378
final chatDetailState = ref.watch(chatRoomsProvider(widget.orderId));
@@ -63,31 +98,6 @@ class _ChatRoomScreenState extends ConsumerState<ChatRoomScreen> {
6398
// Check if keyboard is visible
6499
final isKeyboardVisible = MediaQuery.of(context).viewInsets.bottom > 0;
65100

66-
// If keyboard just became visible, scroll to bottom
67-
if (isKeyboardVisible && !_wasKeyboardVisible) {
68-
// Use Future.delayed instead of microtask to ensure the list is built
69-
Future.delayed(const Duration(milliseconds: 100), () {
70-
// Verify controller is attached and list has content
71-
if (_scrollController.hasClients &&
72-
chatDetailState.messages.isNotEmpty &&
73-
_scrollController.position.maxScrollExtent > 0) {
74-
try {
75-
_scrollController.animateTo(
76-
_scrollController.position.maxScrollExtent,
77-
duration: const Duration(milliseconds: 300),
78-
curve: Curves.easeOut,
79-
);
80-
} catch (e) {
81-
// Silently handle any scroll errors
82-
// This prevents exceptions from breaking the UI
83-
}
84-
}
85-
});
86-
}
87-
88-
// Update keyboard visibility tracking
89-
_wasKeyboardVisible = isKeyboardVisible;
90-
91101
return Scaffold(
92102
backgroundColor: AppTheme.backgroundDark,
93103
appBar: AppBar(
@@ -118,15 +128,7 @@ class _ChatRoomScreenState extends ConsumerState<ChatRoomScreen> {
118128
// Info buttons
119129
InfoButtons(
120130
selectedInfoType: _selectedInfoType,
121-
onInfoTypeChanged: (type) {
122-
// Dismiss keyboard when selecting info tabs to prevent overlap
123-
if (type != null) {
124-
FocusScope.of(context).unfocus();
125-
}
126-
setState(() {
127-
_selectedInfoType = type;
128-
});
129-
},
131+
onInfoTypeChanged: _handleInfoTypeChanged,
130132
),
131133

132134
// Selected info content
@@ -156,23 +158,15 @@ class _ChatRoomScreenState extends ConsumerState<ChatRoomScreen> {
156158
color: AppTheme.backgroundDark,
157159
border: Border(
158160
top: BorderSide(
159-
color: Colors.grey.withValues(alpha: 0.03),
160-
width: 0.3,
161+
color: Colors.grey.withValues(alpha: 0.15),
162+
width: 0.5,
161163
),
162164
),
163165
),
164166
child: MessageInput(
165167
orderId: widget.orderId,
166168
selectedInfoType: _selectedInfoType,
167-
onInfoTypeChanged: (type) {
168-
// Dismiss keyboard when selecting info tabs to prevent overlap
169-
if (type != null) {
170-
FocusScope.of(context).unfocus();
171-
}
172-
setState(() {
173-
_selectedInfoType = type;
174-
});
175-
},
169+
onInfoTypeChanged: _handleInfoTypeChanged,
176170
),
177171
),
178172

0 commit comments

Comments
 (0)