Skip to content

Commit c67a249

Browse files
authored
feat(ui): allow further customization of StreamMessageInput padding (#2328)
1 parent ab6554c commit c67a249

File tree

3 files changed

+135
-63
lines changed

3 files changed

+135
-63
lines changed

packages/stream_chat_flutter/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## Upcoming
2+
3+
✅ Added
4+
5+
- Added `padding` and `textInputMargin` to `StreamMessageInput` to allow fine-tuning the layout.
6+
17
## 9.15.0
28

39
✅ Added

packages/stream_chat_flutter/lib/src/message_input/stream_message_input.dart

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,8 @@ class StreamMessageInput extends StatefulWidget {
165165
)
166166
bool useNativeAttachmentPickerOnMobile = false,
167167
this.pollConfig,
168+
this.padding = const EdgeInsets.all(8),
169+
this.textInputMargin,
168170
}) : assert(
169171
idleSendIcon == null || idleSendButton == null,
170172
'idleSendIcon and idleSendButton cannot be used together',
@@ -424,6 +426,18 @@ class StreamMessageInput extends StatefulWidget {
424426
/// If not provided, the default configuration is used.
425427
final PollConfig? pollConfig;
426428

429+
/// Padding for the message input.
430+
///
431+
/// Defaults to `EdgeInsets.all(8)`.
432+
final EdgeInsets padding;
433+
434+
/// Margin for the message input. Allows overriding the default computed
435+
/// margin.
436+
///
437+
/// Defaults to null, and margin is applied based on action and send button
438+
/// locations.
439+
final EdgeInsets? textInputMargin;
440+
427441
static String? _defaultHintGetter(
428442
BuildContext context,
429443
HintType type,
@@ -733,7 +747,7 @@ class StreamMessageInputState extends State<StreamMessageInput>
733747
return StreamMessageValueListenableBuilder(
734748
valueListenable: controller,
735749
builder: (context, value, _) => Padding(
736-
padding: const EdgeInsets.all(8),
750+
padding: widget.padding,
737751
child: Column(
738752
spacing: 8,
739753
mainAxisSize: MainAxisSize.min,
@@ -1049,7 +1063,7 @@ class StreamMessageInputState extends State<StreamMessageInput>
10491063
},
10501064
onDragExited: (details) {},
10511065
child: Container(
1052-
margin: margin,
1066+
margin: widget.textInputMargin ?? margin,
10531067
clipBehavior: Clip.hardEdge,
10541068
decoration: BoxDecoration(
10551069
borderRadius: _messageInputTheme.borderRadius,

packages/stream_chat_flutter/test/src/message_input/message_input_test.dart

Lines changed: 113 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// ignore_for_file: lines_longer_than_80_chars
22

3+
import 'package:desktop_drop/desktop_drop.dart';
34
import 'package:flutter/material.dart';
45
import 'package:flutter/services.dart';
56
import 'package:flutter_test/flutter_test.dart';
@@ -13,67 +14,8 @@ void main() {
1314
testWidgets(
1415
'checks message input features',
1516
(WidgetTester tester) async {
16-
final client = MockClient();
17-
final clientState = MockClientState();
18-
final channel = MockChannel();
19-
final channelState = MockChannelState();
20-
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
21-
22-
when(() => client.state).thenReturn(clientState);
23-
when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id'));
24-
when(() => channel.lastMessageAt).thenReturn(lastMessageAt);
25-
when(() => channel.state).thenReturn(channelState);
26-
when(() => channel.client).thenReturn(client);
27-
when(channel.getRemainingCooldown).thenReturn(0);
28-
when(() => channel.isMuted).thenReturn(false);
29-
when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false));
30-
when(() => channel.extraDataStream).thenAnswer(
31-
(i) => Stream.value({
32-
'name': 'test',
33-
}),
34-
);
35-
when(() => channel.extraData).thenReturn({
36-
'name': 'test',
37-
});
38-
when(() => channelState.membersStream).thenAnswer(
39-
(i) => Stream.value([
40-
Member(
41-
userId: 'user-id',
42-
user: User(id: 'user-id'),
43-
)
44-
]),
45-
);
46-
when(() => channelState.members).thenReturn([
47-
Member(
48-
userId: 'user-id',
49-
user: User(id: 'user-id'),
50-
),
51-
]);
52-
when(() => channelState.messages).thenReturn([
53-
Message(
54-
text: 'hello',
55-
user: User(id: 'other-user'),
56-
)
57-
]);
58-
when(() => channelState.messagesStream).thenAnswer(
59-
(i) => Stream.value([
60-
Message(
61-
text: 'hello',
62-
user: User(id: 'other-user'),
63-
)
64-
]),
65-
);
66-
67-
await tester.pumpWidget(MaterialApp(
68-
home: StreamChat(
69-
client: client,
70-
child: StreamChannel(
71-
channel: channel,
72-
child: const Scaffold(
73-
body: StreamMessageInput(),
74-
),
75-
),
76-
),
17+
await tester.pumpWidget(buildWidget(
18+
const StreamMessageInput(),
7719
));
7820

7921
// wait for the initial state to be rendered.
@@ -159,6 +101,53 @@ void main() {
159101
},
160102
);
161103

104+
testWidgets(
105+
'allows setting padding on message input',
106+
(WidgetTester tester) async {
107+
await tester.pumpWidget(
108+
buildWidget(
109+
const StreamMessageInput(
110+
padding: EdgeInsets.only(left: 50),
111+
),
112+
),
113+
);
114+
115+
// wait for the initial state to be rendered.
116+
await tester.pumpAndSettle();
117+
118+
expect(
119+
find.descendant(
120+
of: find.byType(StreamMessageValueListenableBuilder),
121+
matching: find.byWidgetPredicate((w) =>
122+
w is Padding &&
123+
w.padding == const EdgeInsets.only(left: 50))),
124+
findsOneWidget);
125+
},
126+
);
127+
128+
testWidgets(
129+
'allows setting explicit margin on text field',
130+
(WidgetTester tester) async {
131+
await tester.pumpWidget(
132+
buildWidget(
133+
const StreamMessageInput(
134+
textInputMargin: EdgeInsets.only(left: 50),
135+
),
136+
),
137+
);
138+
// wait for the initial state to be rendered.
139+
await tester.pumpAndSettle();
140+
141+
expect(
142+
find.descendant(
143+
of: find.byType(DropTarget),
144+
matching: find.byWidgetPredicate((w) =>
145+
w is Container &&
146+
w.margin == const EdgeInsets.only(left: 50))),
147+
findsOneWidget);
148+
},
149+
);
150+
162151
group('MessageInput keyboard interactions', () {
163152
final client = MockClient();
164153
final clientState = MockClientState();
@@ -525,6 +514,69 @@ void main() {
525514
});
526515
}
527516

517+
MaterialApp buildWidget(StreamMessageInput input) {
518+
final client = MockClient();
519+
final clientState = MockClientState();
520+
final channel = MockChannel();
521+
final channelState = MockChannelState();
522+
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
523+
524+
when(() => client.state).thenReturn(clientState);
525+
when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id'));
526+
when(() => channel.lastMessageAt).thenReturn(lastMessageAt);
527+
when(() => channel.state).thenReturn(channelState);
528+
when(() => channel.client).thenReturn(client);
529+
when(channel.getRemainingCooldown).thenReturn(0);
530+
when(() => channel.isMuted).thenReturn(false);
531+
when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false));
532+
when(() => channel.extraDataStream).thenAnswer(
533+
(i) => Stream.value({
534+
'name': 'test',
535+
}),
536+
);
537+
when(() => channel.extraData).thenReturn({
538+
'name': 'test',
539+
});
540+
when(() => channelState.membersStream).thenAnswer(
541+
(i) => Stream.value([
542+
Member(
543+
userId: 'user-id',
544+
user: User(id: 'user-id'),
545+
)
546+
]),
547+
);
548+
when(() => channelState.members).thenReturn([
549+
Member(
550+
userId: 'user-id',
551+
user: User(id: 'user-id'),
552+
),
553+
]);
554+
when(() => channelState.messages).thenReturn([
555+
Message(
556+
text: 'hello',
557+
user: User(id: 'other-user'),
558+
)
559+
]);
560+
when(() => channelState.messagesStream).thenAnswer(
561+
(i) => Stream.value([
562+
Message(
563+
text: 'hello',
564+
user: User(id: 'other-user'),
565+
)
566+
]),
567+
);
568+
569+
return MaterialApp(
570+
home: StreamChat(
571+
client: client,
572+
child: StreamChannel(
573+
channel: channel,
574+
child: Scaffold(body: input),
575+
),
576+
),
577+
);
578+
}
579+
528580
// Helper function to simulate key press events
529581
Future<void> simulateKeyDownEvent(
530582
LogicalKeyboardKey key, {

0 commit comments

Comments
 (0)