Merge pull request #821 from GetStream/chore/message-input-controller-review
chore(ui, core): docs and linting improvements
This commit is contained in:
@@ -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:
|
||||
///
|
||||
/// 
|
||||
/// 
|
||||
///
|
||||
/// Focused state
|
||||
/// Focused state:
|
||||
///
|
||||
/// 
|
||||
/// 
|
||||
///
|
||||
/// 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));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user