Merge pull request #821 from GetStream/chore/message-input-controller-review

chore(ui, core): docs and linting improvements
This commit is contained in:
Salvatore Giordano
2021-12-13 11:44:47 +01:00
committed by GitHub
6 changed files with 129 additions and 142 deletions
@@ -1,18 +1,13 @@
import 'dart:async';
import 'package:collection/collection.dart';
import 'package:logging/logging.dart';
import 'package:rxdart/rxdart.dart';
import 'package:stream_chat/src/client/channel.dart';
import 'package:stream_chat/src/client/retry_policy.dart';
import 'package:stream_chat/src/core/error/error.dart';
import 'package:stream_chat/src/core/models/message.dart';
import 'package:stream_chat/src/event_type.dart';
import 'package:stream_chat/stream_chat.dart';
/// The retry queue associated to a channel
/// The retry queue associated to a channel.
class RetryQueue {
/// Instantiate a new RetryQueue object
/// Instantiate a new RetryQueue object.
RetryQueue({
required this.channel,
this.logger,
@@ -22,13 +17,13 @@ class RetryQueue {
_listenFailedEvents();
}
/// The channel of this queue
/// The channel of this queue.
final Channel channel;
/// The client associated with this [channel]
/// The client associated with this [channel].
final StreamChatClient client;
/// The logger associated to this queue
/// The logger associated to this queue.
final Logger? logger;
late final RetryPolicy _retryPolicy;
@@ -68,7 +63,7 @@ class RetryQueue {
}).addTo(_compositeSubscription);
}
/// Add a list of messages
/// Add a list of messages.
void add(List<Message> messages) {
if (messages.isEmpty) return;
if (!_messageQueue.containsAllMessage(messages)) {
@@ -180,10 +175,10 @@ class RetryQueue {
}
}
/// Whether our [_messageQueue] has messages or not
/// Whether our [_messageQueue] has messages or not.
bool get hasMessages => _messageQueue.isNotEmpty;
/// Call this method to dispose this object
/// Call this method to dispose this object.
void dispose() {
_messageQueue.clear();
_compositeSubscription.dispose();
@@ -14,7 +14,7 @@ class _PinExpires {
const _pinExpires = _PinExpires();
/// Enum defining the status of a sending message
/// Enum defining the status of a sending message.
enum MessageSendingStatus {
/// Message is being sent
sending,
@@ -40,10 +40,10 @@ enum MessageSendingStatus {
sent,
}
/// The class that contains the information about a message
/// The class that contains the information about a message.
@JsonSerializable()
class Message extends Equatable {
/// Constructor used for json serialization
/// Constructor used for json serialization.
Message({
String? id,
this.text,
@@ -65,13 +65,13 @@ class Message extends Equatable {
this.command,
DateTime? createdAt,
DateTime? updatedAt,
this.deletedAt,
this.user,
this.pinned = false,
this.pinnedAt,
DateTime? pinExpires,
this.pinnedBy,
this.extraData = const {},
this.deletedAt,
this.status = MessageSendingStatus.sending,
this.i18n,
}) : id = id ?? const Uuid().v4(),
@@ -80,7 +80,7 @@ class Message extends Equatable {
_updatedAt = updatedAt,
_quotedMessageId = quotedMessageId;
/// Create a new instance from a json
/// Create a new instance from JSON.
factory Message.fromJson(Map<String, dynamic> json) => _$MessageFromJson(
Serializer.moveToExtraDataFromRoot(json, topLevelFields),
).copyWith(
@@ -91,14 +91,14 @@ class Message extends Equatable {
/// the message is added.
final String id;
/// The text of this message
/// The text of this message.
final String? text;
/// The status of a sending message
/// The status of a sending message.
@JsonKey(ignore: true)
final MessageSendingStatus status;
/// The message type
/// The message type.
@JsonKey(
includeIfNull: false,
toJson: Serializer.readOnly,
@@ -110,15 +110,15 @@ class Message extends Equatable {
@JsonKey(includeIfNull: false)
final List<Attachment> attachments;
/// The list of user mentioned in the message
/// The list of user mentioned in the message.
@JsonKey(toJson: User.toIds)
final List<User> mentionedUsers;
/// A map describing the count of number of every reaction
/// A map describing the count of number of every reaction.
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
final Map<String, int>? reactionCounts;
/// A map describing the count of score of every reaction
/// A map describing the count of score of every reaction.
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
final Map<String, int>? reactionScores;
@@ -133,7 +133,7 @@ class Message extends Equatable {
/// The ID of the parent message, if the message is a thread reply.
final String? parentId;
/// A quoted reply message
/// A quoted reply message.
@JsonKey(toJson: Serializer.readOnly)
final Message? quotedMessage;
@@ -153,10 +153,10 @@ class Message extends Equatable {
/// Check if this message needs to show in the channel.
final bool? showInChannel;
/// If true the message is silent
/// If true the message is silent.
final bool silent;
/// If true the message is shadowed
/// If true the message is shadowed.
@JsonKey(
includeIfNull: false,
toJson: Serializer.readOnly,
@@ -169,6 +169,10 @@ class Message extends Equatable {
final DateTime? _createdAt;
/// Reserved field indicating when the message was deleted.
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
final DateTime? deletedAt;
/// Reserved field indicating when the message was created.
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
DateTime get createdAt => _createdAt ?? DateTime.now();
@@ -179,48 +183,45 @@ class Message extends Equatable {
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
DateTime get updatedAt => _updatedAt ?? DateTime.now();
/// User who sent the message
/// User who sent the message.
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
final User? user;
/// If true the message is pinned
/// If true the message is pinned.
final bool pinned;
/// Reserved field indicating when the message was pinned
/// Reserved field indicating when the message was pinned.
@JsonKey(toJson: Serializer.readOnly)
final DateTime? pinnedAt;
/// Reserved field indicating when the message will expire
/// Reserved field indicating when the message will expire.
///
/// if `null` message has no expiry
/// If `null` message has no expiry.
final DateTime? pinExpires;
/// Reserved field indicating who pinned the message
/// Reserved field indicating who pinned the message.
@JsonKey(toJson: Serializer.readOnly)
final User? pinnedBy;
/// Message custom extraData
/// Message custom extraData.
@JsonKey(includeIfNull: false)
final Map<String, Object?> extraData;
/// True if the message is a system info
/// True if the message is a system info.
bool get isSystem => type == 'system';
/// True if the message has been deleted
/// True if the message has been deleted.
bool get isDeleted => type == 'deleted';
/// True if the message is ephemeral
/// True if the message is ephemeral.
bool get isEphemeral => type == 'ephemeral';
/// Reserved field indicating when the message was deleted.
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly)
final DateTime? deletedAt;
/// A Map of translations.
@JsonKey(includeIfNull: false)
final Map<String, String>? i18n;
/// Known top level fields.
///
/// Useful for [Serializer] methods.
static const topLevelFields = [
'id',
@@ -253,7 +254,7 @@ class Message extends Equatable {
'i18n',
];
/// Serialize to json
/// Serialize to json.
Map<String, dynamic> toJson() => Serializer.moveFromExtraDataToRoot(
_$MessageToJson(this),
);
@@ -265,6 +266,8 @@ class Message extends Equatable {
String? type,
List<Attachment>? attachments,
List<User>? mentionedUsers,
bool? silent,
bool? shadowed,
Map<String, int>? reactionCounts,
Map<String, int>? reactionScores,
List<Reaction>? latestReactions,
@@ -275,8 +278,6 @@ class Message extends Equatable {
int? replyCount,
List<User>? threadParticipants,
bool? showInChannel,
bool? shadowed,
bool? silent,
String? command,
DateTime? createdAt,
DateTime? updatedAt,
@@ -304,6 +305,8 @@ class Message extends Equatable {
type: type ?? this.type,
attachments: attachments ?? this.attachments,
mentionedUsers: mentionedUsers ?? this.mentionedUsers,
silent: silent ?? this.silent,
shadowed: shadowed ?? this.shadowed,
reactionCounts: reactionCounts ?? this.reactionCounts,
reactionScores: reactionScores ?? this.reactionScores,
latestReactions: latestReactions ?? this.latestReactions,
@@ -316,18 +319,16 @@ class Message extends Equatable {
showInChannel: showInChannel ?? this.showInChannel,
command: command ?? this.command,
createdAt: createdAt ?? _createdAt,
silent: silent ?? this.silent,
extraData: extraData ?? this.extraData,
user: user ?? this.user,
shadowed: shadowed ?? this.shadowed,
updatedAt: updatedAt ?? _updatedAt,
deletedAt: deletedAt ?? this.deletedAt,
status: status ?? this.status,
user: user ?? this.user,
pinned: pinned ?? this.pinned,
pinnedAt: pinnedAt ?? this.pinnedAt,
pinnedBy: pinnedBy ?? this.pinnedBy,
pinExpires:
pinExpires == _pinExpires ? this.pinExpires : pinExpires as DateTime?,
pinnedBy: pinnedBy ?? this.pinnedBy,
extraData: extraData ?? this.extraData,
status: status ?? this.status,
i18n: i18n ?? this.i18n,
);
}
@@ -340,6 +341,8 @@ class Message extends Equatable {
type: other.type,
attachments: other.attachments,
mentionedUsers: other.mentionedUsers,
silent: other.silent,
shadowed: other.shadowed,
reactionCounts: other.reactionCounts,
reactionScores: other.reactionScores,
latestReactions: other.latestReactions,
@@ -352,17 +355,15 @@ class Message extends Equatable {
showInChannel: other.showInChannel,
command: other.command,
createdAt: other.createdAt,
silent: other.silent,
extraData: other.extraData,
user: other.user,
shadowed: other.shadowed,
updatedAt: other.updatedAt,
deletedAt: other.deletedAt,
status: other.status,
user: other.user,
pinned: other.pinned,
pinnedAt: other.pinnedAt,
pinExpires: other.pinExpires,
pinnedBy: other.pinnedBy,
extraData: other.extraData,
status: other.status,
i18n: other.i18n,
);
@@ -4,7 +4,6 @@ import 'dart:math';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:collection/collection.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
@@ -14,16 +13,12 @@ import 'package:stream_chat_flutter/src/commands_overlay.dart';
import 'package:stream_chat_flutter/src/emoji/emoji.dart';
import 'package:stream_chat_flutter/src/emoji_overlay.dart';
import 'package:stream_chat_flutter/src/extension.dart';
import 'package:stream_chat_flutter/src/message_list_view.dart';
import 'package:stream_chat_flutter/src/multi_overlay.dart';
import 'package:stream_chat_flutter/src/quoted_message_widget.dart';
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
import 'package:stream_chat_flutter/src/user_mentions_overlay.dart';
import 'package:stream_chat_flutter/src/video_service.dart';
import 'package:stream_chat_flutter/src/video_thumbnail_image.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
import 'package:video_compress/video_compress.dart';
export 'package:video_compress/video_compress.dart' show VideoQuality;
@@ -42,13 +37,14 @@ typedef ErrorListener = void Function(
///
/// This callback should not throw.
///
/// It exists merely for showing custom error, and should not be used otherwise.
/// It exists merely for showing a custom error, and should not be used
/// otherwise.
typedef AttachmentLimitExceedListener = void Function(
int limit,
String error,
);
/// Builder for attachment thumbnails
/// Builder for attachment thumbnails.
typedef AttachmentThumbnailBuilder = Widget Function(
BuildContext,
Attachment,
@@ -77,8 +73,8 @@ typedef ActionButtonBuilder = Widget Function(
IconButton defaultActionButton,
);
/// Widget builder for widgets that require may required data from the
/// [MessageInputController]
/// Widget builder for widgets that may require data from the
/// [MessageInputController].
typedef MessageRelatedBuilder = Widget Function(
BuildContext context,
MessageInputController messageInputController,
@@ -91,7 +87,7 @@ typedef AttachmentsPickerBuilder = Widget Function(
StreamAttachmentPicker defaultPicker,
);
/// Location for actions on the [MessageInput]
/// Location for actions on the [MessageInput].
enum ActionsLocation {
/// Align to left
left,
@@ -106,7 +102,7 @@ enum ActionsLocation {
rightInside,
}
/// Default attachments for widget
/// Default attachments for widget.
enum DefaultAttachmentTypes {
/// Image Attachment
image,
@@ -118,7 +114,7 @@ enum DefaultAttachmentTypes {
file,
}
/// Available locations for the sendMessage button relative to the textField
/// Available locations for the `sendMessage` button relative to the textField.
enum SendButtonLocation {
/// inside the textField
inside,
@@ -131,17 +127,17 @@ const _kMinMediaPickerSize = 360.0;
const _kDefaultMaxAttachmentSize = 20971520; // 20MB in Bytes
/// Inactive state
/// Inactive state:
///
/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/message_input.png)
/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/message_input_paint.png)
///
/// Focused state
/// Focused state:
///
/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/message_input2.png)
/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/message_input2_paint.png)
///
/// Widget used to enter the message and add attachments
/// Widget used to enter a message and add attachments:
///
/// ```dart
/// class ChannelPage extends StatelessWidget {
@@ -176,8 +172,7 @@ const _kDefaultMaxAttachmentSize = 20971520; // 20MB in Bytes
/// as the bottom widget.
///
/// The widget renders the ui based on the first ancestor of
/// type [StreamChatTheme].
/// Modify it to change the widget appearance.
/// type [StreamChatTheme]. Modify it to change the widget appearance.
class MessageInput extends StatefulWidget {
/// Instantiate a new MessageInput
const MessageInput({
@@ -218,61 +213,60 @@ class MessageInput extends StatefulWidget {
this.shouldKeepFocusAfterMessage,
}) : super(key: key);
/// List of options for showing overlays
/// List of options for showing overlays.
final List<OverlayOptions> customOverlays;
/// Video quality to use when compressing the videos
/// Video quality to use when compressing the videos.
final VideoQuality compressedVideoQuality;
/// Frame rate to use when compressing the videos
/// Frame rate to use when compressing the videos.
final int compressedVideoFrameRate;
/// Max attachment size in bytes
/// Defaults to 20 MB
/// do not set it if you're using our default CDN
/// Max attachment size in bytes:
/// - Defaults to 20 MB
/// - Do not set it if you're using our default CDN
final int maxAttachmentSize;
/// Function called after sending the message
/// Function called after sending the message.
final void Function(Message)? onMessageSent;
/// Function called right before sending the message
/// Use this to transform the message
/// Function called right before sending the message.
///
/// Use this to transform the message.
final FutureOr<Message> Function(Message)? preMessageSending;
/// Maximum Height for the TextField to grow before it starts scrolling
/// Maximum Height for the TextField to grow before it starts scrolling.
final double maxHeight;
/// The keyboard type assigned to the TextField
/// The keyboard type assigned to the TextField.
final TextInputType keyboardType;
/// If true the attachments button will not be displayed
/// If true the attachments button will not be displayed.
final bool disableAttachments;
/// Use this property to hide/show the commands button
/// Use this property to hide/show the commands button.
final bool showCommandsButton;
/// Hide send as dm checkbox
/// Hide send as dm checkbox.
final bool hideSendAsDm;
/// The text controller of the TextField
/// The text controller of the TextField.
final MessageInputController? messageInputController;
/// List of action widgets
/// List of action widgets.
final List<Widget> actions;
/// The location of the custom actions
/// The location of the custom actions.
final ActionsLocation actionsLocation;
/// Map that defines a thumbnail builder for an attachment type
/// Map that defines a thumbnail builder for an attachment type.
final Map<String, AttachmentThumbnailBuilder>? attachmentThumbnailBuilders;
/// The focus node associated to the TextField
/// The focus node associated to the TextField.
final FocusNode? focusNode;
///
final Message? quotedMessage;
///
final VoidCallback? onQuotedMessageCleared;
/// The location of the send button
@@ -21,7 +21,6 @@ typedef CustomAttachmentIconBuilder = Widget Function(
bool active,
);
///
class StreamAttachmentPicker extends StatefulWidget {
final bool isOpen;
final double pickerSize;
@@ -32,15 +31,15 @@ class StreamAttachmentPicker extends StatefulWidget {
final ValueChanged<String>? onError;
final FilePickerCallback onFilePicked;
/// Video quality to use when compressing the videos
/// Video quality to use when compressing the videos.
final VideoQuality compressedVideoQuality;
/// Frame rate to use when compressing the videos
/// Frame rate to use when compressing the videos.
final int compressedVideoFrameRate;
/// Max attachment size in bytes
/// Defaults to 20 MB
/// do not set it if you're using our default CDN
/// Max attachment size in bytes:
/// - Defaults to 20 MB
/// - Do not set it if you're using our default CDN
final int maxAttachmentSize;
final List<DefaultAttachmentTypes> allowedAttachmentTypes;
@@ -21,8 +21,8 @@ export 'package:flutter/services.dart'
class StreamMessageTextField extends StatefulWidget {
/// Creates a Material Design text field.
///
/// If [decoration] is non-null (which is the default), the text field requires
/// one of its ancestors to be a [Material] widget.
/// If [decoration] is non-null (which is the default), the text field
/// requires one of its ancestors to be a [Material] widget.
///
/// To remove the decoration entirely (including the extra padding introduced
/// by the decoration to save space for the labels), set the [decoration] to
@@ -115,7 +115,7 @@ class StreamMessageTextField extends StatefulWidget {
this.selectionHeightStyle = ui.BoxHeightStyle.tight,
this.selectionWidthStyle = ui.BoxWidthStyle.tight,
this.keyboardAppearance,
this.scrollPadding = const EdgeInsets.all(20.0),
this.scrollPadding = const EdgeInsets.all(20),
this.dragStartBehavior = DragStartBehavior.start,
this.enableInteractiveSelection = true,
this.selectionControls,
@@ -127,34 +127,24 @@ class StreamMessageTextField extends StatefulWidget {
this.autofillHints,
this.restorationId,
this.enableIMEPersonalizedLearning = true,
}) : assert(textAlign != null),
assert(readOnly != null),
assert(autofocus != null),
assert(obscuringCharacter != null && obscuringCharacter.length == 1),
assert(obscureText != null),
assert(autocorrect != null),
}) : assert(obscuringCharacter.length == 1,
'`obscuringCharacter.length` must be 1'),
smartDashesType = smartDashesType ??
(obscureText ? SmartDashesType.disabled : SmartDashesType.enabled),
smartQuotesType = smartQuotesType ??
(obscureText ? SmartQuotesType.disabled : SmartQuotesType.enabled),
assert(enableSuggestions != null),
assert(enableInteractiveSelection != null),
assert(maxLengthEnforced != null),
assert(
maxLengthEnforced || maxLengthEnforcement == null,
'maxLengthEnforced is deprecated, use only maxLengthEnforcement',
),
assert(scrollPadding != null),
assert(dragStartBehavior != null),
assert(selectionHeightStyle != null),
assert(selectionWidthStyle != null),
assert(maxLines == null || maxLines > 0),
assert(minLines == null || minLines > 0),
assert(maxLines == null || maxLines > 0,
'`maxLines` needs to be left as null or bigger than 0'),
assert(minLines == null || minLines > 0,
'`minLines` needs to be left as null or bigger than 0'),
assert(
(maxLines == null) || (minLines == null) || (maxLines >= minLines),
"minLines can't be greater than maxLines",
),
assert(expands != null),
assert(
!expands || (maxLines == null && minLines == null),
'minLines and maxLines must be null when expands is true.',
@@ -164,14 +154,15 @@ class StreamMessageTextField extends StatefulWidget {
assert(maxLength == null ||
maxLength == TextField.noMaxLength ||
maxLength > 0),
// Assert the following instead of setting it directly to avoid surprising the user by silently changing the value they set.
// Assert the following instead of setting it directly to avoid
// surprising the user by silently changing the value they set.
assert(
!identical(textInputAction, TextInputAction.newline) ||
maxLines == 1 ||
!identical(keyboardType, TextInputType.text),
'Use keyboardType TextInputType.multiline when using TextInputAction.newline on a multiline TextField.',
'''Use keyboardType TextInputType.multiline when using TextInputAction.newline on a multiline TextField.''',
),
assert(enableIMEPersonalizedLearning != null),
keyboardType = keyboardType ??
(maxLines == 1 ? TextInputType.text : TextInputType.multiline),
toolbarOptions = toolbarOptions ??
@@ -230,7 +221,8 @@ class StreamMessageTextField extends StatefulWidget {
/// cause the focus to change, and will not make the keyboard visible.
///
/// This widget builds an [EditableText] and will ensure that the keyboard is
/// showing when it is tapped by calling [EditableTextState.requestKeyboard()].
/// showing when it is tapped by calling
/// [EditableTextState.requestKeyboard()].
final FocusNode? focusNode;
/// The decoration to show around the text field.
@@ -330,16 +322,20 @@ class StreamMessageTextField extends StatefulWidget {
/// If set, a character counter will be displayed below the
/// field showing how many characters have been entered. If set to a number
/// greater than 0, it will also display the maximum number allowed. If set
/// to [TextField.noMaxLength] then only the current character count is displayed.
/// to [TextField.noMaxLength] then only the current character count is
/// displayed.
///
/// After [maxLength] characters have been input, additional input
/// is ignored, unless [maxLengthEnforcement] is set to
/// [MaxLengthEnforcement.none].
///
/// The text field enforces the length with a [LengthLimitingTextInputFormatter],
/// which is evaluated after the supplied [inputFormatters], if any.
/// The text field enforces the length with a
/// [LengthLimitingTextInputFormatter], which is evaluated after the supplied
/// [inputFormatters], if any.
///
/// This value must be either null, [TextField.noMaxLength], or greater than
/// 0.
///
/// This value must be either null, [TextField.noMaxLength], or greater than 0.
/// If null (the default) then there is no limit to the number of characters
/// that can be entered. If set to [TextField.noMaxLength], then no limit will
/// be enforced, but the number of characters entered will still be displayed.
@@ -446,7 +442,8 @@ class StreamMessageTextField extends StatefulWidget {
///
/// This setting is only honored on iOS devices.
///
/// If unset, defaults to the brightness of [ThemeData.primaryColorBrightness].
/// If unset, defaults to the brightness of
/// [ThemeData.primaryColorBrightness].
final Brightness? keyboardAppearance;
/// {@macro flutter.widgets.editableText.scrollPadding}
@@ -490,14 +487,16 @@ class StreamMessageTextField extends StatefulWidget {
/// widget.
///
/// If [mouseCursor] is a [MaterialStateProperty<MouseCursor>],
/// [MaterialStateProperty.resolve] is used for the following [MaterialState]s:
/// [MaterialStateProperty.resolve] is used for the following
/// [MaterialState]s:
///
/// * [MaterialState.error].
/// * [MaterialState.hovered].
/// * [MaterialState.focused].
/// * [MaterialState.disabled].
///
/// If this property is null, [MaterialStateMouseCursor.textable] will be used.
/// If this property is null, [MaterialStateMouseCursor.textable] will be
/// used.
///
/// The [mouseCursor] is the only property of [TextField] that controls the
/// appearance of the mouse pointer. All other properties related to "cursor"
@@ -4,7 +4,9 @@ import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:stream_chat/stream_chat.dart';
/// A value listenable builder related to a [Message]
/// A value listenable builder related to a [Message].
///
/// Pass in a [MessageInputController] as the `valueListenable`.
typedef MessageValueListenableBuilder = ValueListenableBuilder<Message>;
/// A function that returns true if the message is valid and can be sent.
@@ -35,7 +37,7 @@ class MessageInputController extends ValueNotifier<Message> {
validator: validator ?? _defaultValidator,
);
/// Creates a controller for an editable text field from an initial
/// Creates a controller for an editable text field from initial
/// [attachments].
factory MessageInputController.fromAttachments(
List<Attachment> attachments, {
@@ -127,7 +129,7 @@ class MessageInputController extends ValueNotifier<Message> {
/// Returns the start of the selection of the text field.
int get selectionStart => textEditingController.selection.start;
/// Sets the showInChannel flag of the message.
/// Sets the [showInChannel] flag of the message.
set showInChannel(bool newValue) {
value = value.copyWith(showInChannel: newValue);
}
@@ -202,14 +204,14 @@ class MessageInputController extends ValueNotifier<Message> {
mentionedUsers = [];
}
/// Set the [value] to empty.
/// Sets the [message], or [value], to empty.
///
/// After calling this function, [text], [attachments] and [mentionedUsers]
/// all will be empty.
/// will all be empty.
///
/// Calling this will notify all the listeners of this
/// [MessageInputController] that they need to update
/// (it calls [notifyListeners]). For this reason,
/// (calls [notifyListeners]). For this reason,
/// this method should only be called between frames, e.g. in response to user
/// actions, not during the build, layout, or paint phases.
void clear() {
@@ -217,7 +219,7 @@ class MessageInputController extends ValueNotifier<Message> {
_textEditingController.clear();
}
/// Set the [value] to the initial [Message] value.
/// Sets the [value] to the initial [Message] value.
void reset({bool resetId = true}) {
if (resetId) {
_initialMessage = _initialMessage.copyWith(
@@ -246,16 +248,13 @@ class RestorableMessageInputController
extends RestorableChangeNotifier<MessageInputController> {
/// Creates a [RestorableMessageInputController].
///
/// This constructor treats a null `text` argument as if it were the empty
/// string.
/// This constructor creates a default [Message] when no `message` argument
/// is supplied.
RestorableMessageInputController({Message? message})
: _initialValue = message ?? Message();
/// Creates a [RestorableMessageInputController] from an initial
/// [TextEditingValue].
///
/// This constructor treats a null `value` argument as if it were
/// [TextEditingValue.empty].
/// [text] value.
factory RestorableMessageInputController.fromText(String? text) =>
RestorableMessageInputController(message: Message(text: text));