Skip to content

Commit 48e28e7

Browse files
committed
fix: list rebuilding when keyboard opens/closes
1 parent bd42da9 commit 48e28e7

File tree

2 files changed

+73
-22
lines changed

2 files changed

+73
-22
lines changed

packages/flutter_chat_ui/lib/src/chat_animated_list/chat_animated_list.dart

Lines changed: 10 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,10 @@ import 'package:scrollview_observer/scrollview_observer.dart';
1010

1111
import '../load_more.dart';
1212
import '../scroll_to_bottom.dart';
13-
import '../utils/composer_height_notifier.dart';
14-
import '../utils/keyboard_mixin.dart';
1513
import '../utils/load_more_notifier.dart';
1614
import '../utils/message_list_diff.dart';
1715
import '../utils/typedefs.dart';
16+
import 'sliver_spacing.dart';
1817

1918
/// Enum controlling the initial scroll behavior of the chat list.
2019
enum InitialScrollToEndMode {
@@ -157,7 +156,7 @@ class ChatAnimatedList extends StatefulWidget {
157156

158157
/// State for [ChatAnimatedList].
159158
class _ChatAnimatedListState extends State<ChatAnimatedList>
160-
with TickerProviderStateMixin, WidgetsBindingObserver, KeyboardMixin {
159+
with TickerProviderStateMixin {
161160
final GlobalKey<SliverAnimatedListState> _listKey = GlobalKey();
162161
late final ChatController _chatController;
163162
late final SliverObserverController _observerController;
@@ -245,7 +244,6 @@ class _ChatAnimatedListState extends State<ChatAnimatedList>
245244
}
246245
}
247246

248-
@override
249247
void onKeyboardHeightChanged(double height) {
250248
// Reversed lists handle keyboard automatically
251249
if (widget.reversed) {
@@ -320,8 +318,7 @@ class _ChatAnimatedListState extends State<ChatAnimatedList>
320318

321319
@override
322320
Widget build(BuildContext context) {
323-
final bottomSafeArea = MediaQuery.of(context).padding.bottom;
324-
final builders = context.watch<Builders>();
321+
final builders = context.read<Builders>();
325322

326323
// Define the SliverAnimatedList once as it's used for both
327324
// reversed and non-reversed lists.
@@ -352,7 +349,7 @@ class _ChatAnimatedListState extends State<ChatAnimatedList>
352349
// Order for CustomScrollView(reverse: true) -> Visual Bottom to Top
353350
return <Widget>[
354351
// Visually at the bottom (first in sliver list for reverse: true)
355-
_buildComposerHeightSliver(builders, bottomSafeArea),
352+
_buildComposerHeightSliver(context),
356353
if (widget.bottomSliver != null) widget.bottomSliver!,
357354
sliverAnimatedList,
358355
if (widget.onEndReached != null) _buildLoadMoreSliver(builders),
@@ -371,7 +368,7 @@ class _ChatAnimatedListState extends State<ChatAnimatedList>
371368
if (widget.onEndReached != null) _buildLoadMoreSliver(builders),
372369
sliverAnimatedList,
373370
if (widget.bottomSliver != null) widget.bottomSliver!,
374-
_buildComposerHeightSliver(builders, bottomSafeArea),
371+
_buildComposerHeightSliver(context),
375372
// Visually at the bottom
376373
];
377374
}
@@ -440,20 +437,11 @@ class _ChatAnimatedListState extends State<ChatAnimatedList>
440437
);
441438
}
442439

443-
Widget _buildComposerHeightSliver(Builders builders, double bottomSafeArea) {
444-
return Consumer<ComposerHeightNotifier>(
445-
builder: (context, heightNotifier, child) {
446-
return SliverPadding(
447-
padding: EdgeInsets.only(
448-
bottom:
449-
heightNotifier.height +
450-
(widget.bottomPadding ?? 0) +
451-
(widget.handleSafeArea == true ? bottomSafeArea : 0),
452-
),
453-
);
454-
},
455-
);
456-
}
440+
Widget _buildComposerHeightSliver(BuildContext context) => SliverSpacing(
441+
bottomPadding: widget.bottomPadding,
442+
handleSafeArea: widget.handleSafeArea,
443+
onKeyboardHeightChanged: widget.reversed ? null : onKeyboardHeightChanged,
444+
);
457445

458446
Widget _buildLoadMoreSliver(Builders builders) {
459447
return SliverToBoxAdapter(
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:provider/provider.dart';
3+
4+
import '../../flutter_chat_ui.dart';
5+
import '../utils/keyboard_mixin.dart';
6+
7+
/// A sliver widget that creates vertical spacing at the end of a chat list.
8+
///
9+
/// This widget is primarily used to account for the height of the composer,
10+
/// optional bottom padding, and the keyboard (when visible).
11+
/// It consumes [ComposerHeightNotifier] to dynamically adjust its spacing
12+
/// based on the composer's height and uses [KeyboardMixin] to react to
13+
/// keyboard visibility changes.
14+
class SliverSpacing extends StatefulWidget {
15+
/// Optional padding to add below the composer area.
16+
final double? bottomPadding;
17+
18+
/// Whether to automatically account for the bottom safe area (notch, home indicator).
19+
/// Defaults to true if not specified, but behavior depends on how it's used in parent.
20+
final bool? handleSafeArea;
21+
22+
/// Callback function invoked when the detected keyboard height changes.
23+
///
24+
/// The reported `height` is the adjusted keyboard height, considering the
25+
/// initial safe area.
26+
final void Function(double height)? onKeyboardHeightChanged;
27+
28+
/// Creates a [SliverSpacing] widget.
29+
const SliverSpacing({
30+
super.key,
31+
this.bottomPadding,
32+
this.handleSafeArea,
33+
this.onKeyboardHeightChanged,
34+
});
35+
36+
@override
37+
State<SliverSpacing> createState() => _SliverSpacingState();
38+
}
39+
40+
class _SliverSpacingState extends State<SliverSpacing>
41+
with WidgetsBindingObserver, KeyboardMixin {
42+
@override
43+
Widget build(BuildContext context) {
44+
final safeArea = MediaQuery.of(context).padding.bottom;
45+
return Consumer<ComposerHeightNotifier>(
46+
builder: (context, heightNotifier, child) {
47+
return SliverPadding(
48+
padding: EdgeInsets.only(
49+
bottom:
50+
heightNotifier.height +
51+
(widget.bottomPadding ?? 0) +
52+
(widget.handleSafeArea == true ? safeArea : 0),
53+
),
54+
);
55+
},
56+
);
57+
}
58+
59+
@override
60+
void onKeyboardHeightChanged(double height) {
61+
widget.onKeyboardHeightChanged?.call(height);
62+
}
63+
}

0 commit comments

Comments
 (0)