Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow setting the visibility of the SendButton #76

Merged
merged 2 commits into from
Aug 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/flutter_chat_ui.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ library flutter_chat_ui;

export 'src/chat_l10n.dart';
export 'src/chat_theme.dart';
export 'src/models/send_button_visibility_mode.dart';
export 'src/util.dart' show formatBytes;
export 'src/widgets/attachment_button.dart';
export 'src/widgets/chat.dart';
Expand Down
9 changes: 9 additions & 0 deletions lib/src/models/send_button_visibility_mode.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/// Used to toggle the visibility behavior of the [SendButton] based on the
/// [TextField] state inside the [Input] widget.
enum SendButtonVisibilityMode {
/// Always show the [SendButton] regardless of the [TextField] state.
always,

/// The [SendButton] will only appear when the [TextField] is not empty.
editing,
}
12 changes: 12 additions & 0 deletions lib/src/widgets/chat.dart
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import 'dart:math';

import 'package:flutter/material.dart';
import 'package:flutter_chat_types/flutter_chat_types.dart' as types;
import 'package:flutter_chat_ui/src/widgets/inherited_l10n.dart';
import 'package:intl/intl.dart';
import 'package:photo_view/photo_view_gallery.dart';

import '../chat_l10n.dart';
import '../chat_theme.dart';
import '../conditional/conditional.dart';
import '../models/date_header.dart';
import '../models/message_spacer.dart';
import '../models/preview_image.dart';
import '../models/send_button_visibility_mode.dart';
import '../util.dart';
import 'chat_list.dart';
import 'inherited_chat_theme.dart';
Expand Down Expand Up @@ -40,6 +43,7 @@ class Chat extends StatefulWidget {
this.onPreviewDataFetched,
required this.onSendPressed,
this.onTextChanged,
this.sendButtonVisibilityMode = SendButtonVisibilityMode.editing,
this.showUserAvatars = false,
this.showUserNames = false,
this.theme = const DefaultChatTheme(),
Expand Down Expand Up @@ -119,6 +123,12 @@ class Chat extends StatefulWidget {
/// See [Input.onTextChanged]
final void Function(String)? onTextChanged;

/// Controls the visibility behavior of the [SendButton] based on the
/// [TextField] state inside the [Input] widget.
///
/// Defaults to [SendButtonVisibilityMode.editing].
final SendButtonVisibilityMode sendButtonVisibilityMode;

/// See [Message.showUserAvatars]
final bool showUserAvatars;

Expand Down Expand Up @@ -364,6 +374,8 @@ class _ChatState extends State<Chat> {
onAttachmentPressed: widget.onAttachmentPressed,
onSendPressed: widget.onSendPressed,
onTextChanged: widget.onTextChanged,
sendButtonVisibilityMode:
widget.sendButtonVisibilityMode,
),
],
),
Expand Down
16 changes: 14 additions & 2 deletions lib/src/widgets/input.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_chat_types/flutter_chat_types.dart' as types;

import '../models/send_button_visibility_mode.dart';
import 'attachment_button.dart';
import 'inherited_chat_theme.dart';
import 'inherited_l10n.dart';
Expand All @@ -24,6 +26,7 @@ class Input extends StatefulWidget {
this.onAttachmentPressed,
required this.onSendPressed,
this.onTextChanged,
required this.sendButtonVisibilityMode,
}) : super(key: key);

/// See [AttachmentButton.onPressed]
Expand All @@ -42,6 +45,9 @@ class Input extends StatefulWidget {
/// Will be called whenever the text inside [TextField] changes
final void Function(String)? onTextChanged;

/// See [Chat.sendButtonVisibilityMode]
final SendButtonVisibilityMode sendButtonVisibilityMode;

@override
_InputState createState() => _InputState();
}
Expand All @@ -55,7 +61,11 @@ class _InputState extends State<Input> {
@override
void initState() {
super.initState();
_textController.addListener(_handleTextControllerChange);
// When sendButtonVisibilityMode is set to SendButtonVisibilityMode.editing,
// we will need to add a listener to check if _textController is empty.
if (widget.sendButtonVisibilityMode == SendButtonVisibilityMode.editing) {
_textController.addListener(_handleTextControllerChange);
}
}

@override
Expand Down Expand Up @@ -179,7 +189,9 @@ class _InputState extends State<Input> {
),
),
Visibility(
visible: _sendButtonVisible,
visible: widget.sendButtonVisibilityMode ==
SendButtonVisibilityMode.always ||
_sendButtonVisible,
child: SendButton(
onPressed: _handleSendPressed,
),
Expand Down
2 changes: 2 additions & 0 deletions test/flutter_chat_ui_test.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import 'input.test.dart' as input_test;
import 'util.test.dart' as util_test;

void main() {
input_test.main();
util_test.main();
}
82 changes: 82 additions & 0 deletions test/input.test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import 'package:flutter/material.dart';
import 'package:flutter_chat_types/flutter_chat_types.dart' as types;
import 'package:flutter_chat_ui/flutter_chat_ui.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
group('sendButtonVisibilityMode', () {
testWidgets(
'The SendButton should always be visible when sendButtonVisibilityMode is set to SendButtonVisibilityMode.always',
(WidgetTester tester) async {
// Build the Chat widget.
await tester.pumpWidget(
MaterialApp(
home: Material(
child: Chat(
messages: const [],
onSendPressed: (types.PartialText message) {},
sendButtonVisibilityMode: SendButtonVisibilityMode.always,
user:
const types.User(id: '06c33e8b-e835-4736-80f4-63f44b66666c'),
),
),
),
);
// Trigger a frame.
await tester.pump();

// The SendButton should exist in the widget tree.
expect(find.byType(SendButton), findsOneWidget);
});

testWidgets(
'The SendButton should be invisible only when sendButtonVisibilityMode is not specified and the TextField is empty',
(WidgetTester tester) async {
// Build the Chat widget.
await tester.pumpWidget(
MaterialApp(
home: Material(
child: Chat(
messages: const [],
onSendPressed: (types.PartialText message) {},
user:
const types.User(id: '06c33e8b-e835-4736-80f4-63f44b66666c'),
),
),
),
);
// Trigger a frame.
await tester.pump();

// The SendButton should not exist in the widget tree.
expect(find.byType(SendButton), findsNothing);
});

testWidgets(
'The SendButton should be visible only when sendButtonVisibilityMode is not specified and the TextField is not empty',
(WidgetTester tester) async {
// Build the Chat widget.
await tester.pumpWidget(
MaterialApp(
home: Material(
child: Chat(
messages: const [],
onSendPressed: (types.PartialText message) {},
user:
const types.User(id: '06c33e8b-e835-4736-80f4-63f44b66666c'),
),
),
),
);
// Trigger a frame.
await tester.pump();
// Enter 'hi' into the TextField.
await tester.enterText(find.byType(TextField), 'hi');
// Trigger a frame.
await tester.pump();

// The SendButton should exist in the widget tree.
expect(find.byType(SendButton), findsOneWidget);
});
});
}