您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
455 行
18 KiB
455 行
18 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using ChatComponents;
|
|
using uiwidgets;
|
|
using Unity.UIWidgets.async;
|
|
using Unity.UIWidgets.foundation;
|
|
using Unity.UIWidgets.material;
|
|
using Unity.UIWidgets.painting;
|
|
using Unity.UIWidgets.ui;
|
|
using Unity.UIWidgets.widgets;
|
|
using UnityEngine;
|
|
using Color = Unity.UIWidgets.ui.Color;
|
|
|
|
namespace UIWidgetsSample
|
|
{
|
|
public delegate string CustomDateHeaderText(DateTime dateTime);
|
|
|
|
public delegate void OnAttachmentPressed();
|
|
|
|
public delegate Future OnEndReached();
|
|
|
|
public delegate void OnMessageLongPress(ChatComponents.Message message);
|
|
|
|
public delegate void OnMessageTap(ChatComponents.Message message);
|
|
|
|
public delegate void OnPreviewDataFetched( PreviewData previewData,ChatComponents.TextMessage textMessage = null);
|
|
|
|
public delegate void OnSendPressed(PartialText partialText);
|
|
|
|
public delegate void OnTextChanged(string _str);
|
|
|
|
public delegate Widget BuildCustomMessage(ChatComponents.Message message);
|
|
|
|
public class Chat : StatefulWidget
|
|
{
|
|
/// See [Message.buildCustomMessage]
|
|
/// If [dateFormat], [dateLocale] and/or [timeFormat] is not enough to
|
|
/// customize date headers in your case, use this to return an arbitrary
|
|
/// string based on a [DateTime] of a particular message. Can be helpful to
|
|
/// return "Today" if [DateTime] is today. IMPORTANT: this will replace
|
|
/// all default date headers, so you must handle all cases yourself, like
|
|
/// for example today, yesterday and before. Or you can just return the same
|
|
/// date header for any message.
|
|
public readonly BuildCustomMessage buildCustomMessage;
|
|
|
|
public readonly CustomDateHeaderText customDateHeaderText;
|
|
|
|
/// Allows you to customize the date format. IMPORTANT: only for the date,
|
|
/// do not return time here. See [timeFormat] to customize the time format.
|
|
/// [dateLocale] will be ignored if you use this, so if you want a localized date
|
|
/// make sure you initialize your [DateFormat] with a locale. See [customDateHeaderText]
|
|
/// for more customization.
|
|
public readonly DateTime? dateFormat;
|
|
|
|
/// Locale will be passed to the `Intl` package. Make sure you initialized
|
|
/// date formatting in your app before passing any locale here, otherwise
|
|
/// an error will be thrown. Also see [customDateHeaderText], [dateFormat], [timeFormat].
|
|
public readonly string dateLocale;
|
|
|
|
/// Disable automatic image preview on tap.
|
|
public readonly bool? disableImageGallery;
|
|
|
|
/// See [Input.isAttachmentUploading]
|
|
public readonly bool? isAttachmentUploading;
|
|
|
|
/// See [ChatList.isLastPage]
|
|
public readonly bool? isLastPage;
|
|
|
|
/// Localized copy. Extend [ChatL10n] class to create your own copy or use
|
|
/// existing one, like the default [ChatL10nEn]. You can customize only
|
|
/// certain variables, see more here [ChatL10nEn].
|
|
public readonly ChatL10n l10n;
|
|
|
|
/// List of [types.Message] to render in the chat widget
|
|
public readonly List<ChatComponents.Message> messages;
|
|
|
|
/// See [Input.onAttachmentPressed]
|
|
public readonly OnAttachmentPressed onAttachmentPressed;
|
|
|
|
/// See [ChatList.onEndReached]
|
|
public readonly OnEndReached onEndReached;
|
|
|
|
/// See [ChatList.onEndReachedThreshold]
|
|
public readonly float? onEndReachedThreshold;
|
|
|
|
/// See [Message.onMessageLongPress]
|
|
public readonly OnMessageLongPress onMessageLongPress;
|
|
|
|
/// See [Message.onMessageTap]
|
|
public readonly OnMessageTap onMessageTap;
|
|
|
|
/// See [Message.onPreviewDataFetched]
|
|
public readonly OnPreviewDataFetched onPreviewDataFetched;
|
|
|
|
/// See [Input.onSendPressed]
|
|
public readonly OnSendPressed onSendPressed;
|
|
|
|
/// See [Input.onTextChanged]
|
|
public readonly OnTextChanged onTextChanged;
|
|
|
|
/// See [Message.showUserAvatars]
|
|
public readonly bool showUserAvatars;
|
|
|
|
/// Show user names for received messages. Useful for a group chat. Will be
|
|
/// shown only on text messages.
|
|
public readonly bool showUserNames;
|
|
|
|
/// Chat theme. Extend [ChatTheme] class to create your own theme or use
|
|
/// existing one, like the [DefaultChatTheme]. You can customize only certain
|
|
/// variables, see more here [DefaultChatTheme].
|
|
public readonly ChatTheme theme;
|
|
|
|
/// Allows you to customize the time format. IMPORTANT: only for the time,
|
|
/// do not return date here. See [dateFormat] to customize the date format.
|
|
/// [dateLocale] will be ignored if you use this, so if you want a localized time
|
|
/// make sure you initialize your [DateFormat] with a locale. See [customDateHeaderText]
|
|
/// for more customization.
|
|
public readonly DateTime? timeFormat;
|
|
|
|
/// See [Message.usePreviewData]
|
|
public readonly bool usePreviewData;
|
|
|
|
/// See [InheritedUser.user]
|
|
public readonly User user;
|
|
|
|
/// Creates a chat widget
|
|
public Chat(
|
|
List<ChatComponents.Message> messages,
|
|
OnSendPressed onSendPressed,
|
|
User user,
|
|
Key key = null,
|
|
BuildCustomMessage buildCustomMessage = null,
|
|
CustomDateHeaderText customDateHeaderText = null,
|
|
DateTime? dateFormat = null,
|
|
string dateLocale = null,
|
|
bool? disableImageGallery = null,
|
|
bool? isAttachmentUploading = null,
|
|
bool? isLastPage = null,
|
|
ChatL10nEn l10n = null,
|
|
OnAttachmentPressed onAttachmentPressed = null,
|
|
OnEndReached onEndReached = null,
|
|
float? onEndReachedThreshold = null,
|
|
OnMessageLongPress onMessageLongPress = null,
|
|
OnMessageTap onMessageTap = null,
|
|
OnPreviewDataFetched onPreviewDataFetched = null,
|
|
OnTextChanged onTextChanged = null,
|
|
bool showUserAvatars = false,
|
|
bool showUserNames = false,
|
|
ChatTheme theme = null,
|
|
DateTime? timeFormat = null,
|
|
bool usePreviewData = true
|
|
) : base(key)
|
|
{
|
|
this.buildCustomMessage = buildCustomMessage;
|
|
this.customDateHeaderText = customDateHeaderText;
|
|
this.dateFormat = dateFormat;
|
|
this.dateLocale = dateLocale;
|
|
this.disableImageGallery = disableImageGallery;
|
|
this.isAttachmentUploading = isAttachmentUploading;
|
|
this.isLastPage = isLastPage;
|
|
this.l10n = l10n == null ? new ChatL10nEn() : l10n;
|
|
this.messages = messages;
|
|
this.onAttachmentPressed = onAttachmentPressed;
|
|
this.onEndReached = onEndReached;
|
|
this.onEndReachedThreshold = onEndReachedThreshold;
|
|
this.onMessageLongPress = onMessageLongPress;
|
|
this.onMessageTap = onMessageTap;
|
|
this.onPreviewDataFetched = onPreviewDataFetched;
|
|
this.onSendPressed = onSendPressed;
|
|
this.onTextChanged = onTextChanged;
|
|
this.showUserAvatars = showUserAvatars;
|
|
this.showUserNames = showUserNames;
|
|
this.theme = theme == null ? new DefaultChatTheme() : theme;
|
|
this.timeFormat = timeFormat;
|
|
this.usePreviewData = usePreviewData;
|
|
this.user = user;
|
|
}
|
|
|
|
public override State createState()
|
|
{
|
|
return new _ChatState();
|
|
}
|
|
}
|
|
|
|
/// [Chat] widget state
|
|
public class _ChatState : State<Chat>
|
|
{
|
|
private List<object> _chatMessages = new List<object>();
|
|
private List<PreviewImage> _gallery = new List<PreviewImage>();
|
|
private int _imageViewIndex;
|
|
private bool _isImageViewVisible;
|
|
|
|
public override void initState()
|
|
{
|
|
base.initState();
|
|
|
|
didUpdateWidget(widget);
|
|
}
|
|
|
|
public override void didUpdateWidget(StatefulWidget oldWidget)
|
|
{
|
|
oldWidget = (Chat) oldWidget;
|
|
base.didUpdateWidget((Chat)oldWidget);
|
|
|
|
if (widget.messages.isNotEmpty())
|
|
{
|
|
var result = ChatUtils.calculateChatMessages(
|
|
widget.messages,
|
|
widget.user,
|
|
customDateHeaderText: widget.customDateHeaderText,
|
|
dateFormat: widget.dateFormat,
|
|
dateLocale: widget.dateLocale,
|
|
showUserNames: widget.showUserNames,
|
|
timeFormat: widget.timeFormat
|
|
);
|
|
|
|
_chatMessages = result[0] as List<object>;
|
|
_gallery = result[1] as List<PreviewImage>;
|
|
}
|
|
}
|
|
|
|
/*private Widget _buildImageGallery()
|
|
{
|
|
return new Dismissible(
|
|
GlobalKey.key("photo_view_gallery"),
|
|
direction:
|
|
DismissDirection.down,
|
|
onDismissed:
|
|
direction => _onCloseGalleryPressed(),
|
|
child:
|
|
new Stack(
|
|
children: new List<Widget>
|
|
{
|
|
PhotoViewGallery.builder(
|
|
builder: (BuildContext context, int index) =>
|
|
PhotoViewGalleryPageOptions(
|
|
imageProvider: new BrowserConditional().getProvider(_gallery[index].uri)
|
|
),
|
|
itemCount: _gallery.Count,
|
|
loadingBuilder: (_context, _event) =>
|
|
_imageGalleryLoadingBuilder(_context, _event),
|
|
onPageChanged:
|
|
_onPageChanged,
|
|
pageController:
|
|
new PageController(_imageViewIndex),
|
|
scrollPhysics:
|
|
new ClampingScrollPhysics()
|
|
),
|
|
new Positioned(
|
|
right: 16,
|
|
top: 56,
|
|
child: new CloseButton(
|
|
color: Colors.white,
|
|
onPressed: _onCloseGalleryPressed
|
|
)
|
|
)
|
|
}
|
|
)
|
|
);
|
|
}*/
|
|
|
|
private Widget _buildMessage(object _object)
|
|
{
|
|
if (_object is DateHeader)
|
|
{
|
|
return new Container(
|
|
alignment: Alignment.center,
|
|
margin: EdgeInsets.only(
|
|
bottom: 32,
|
|
top: 16
|
|
),
|
|
child:
|
|
new Text(
|
|
((DateHeader) _object).text,
|
|
style: widget.theme.dateDividerTextStyle
|
|
)
|
|
);
|
|
|
|
}
|
|
|
|
else if (_object is MessageSpacer)
|
|
{
|
|
var height = ((MessageSpacer) _object).height;
|
|
return new SizedBox(
|
|
height: height
|
|
);
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
var map = _object as Dictionary<string, object>;
|
|
var message = map["message"] as ChatComponents.Message;
|
|
var _messageWidth =
|
|
widget.showUserAvatars && message.author.id != widget.user.id
|
|
? Mathf.Min(MediaQuery.of(context).size.width * 0.72f, 440).floor()
|
|
: Mathf.Min(MediaQuery.of(context).size.width * 0.78f, 440).floor();
|
|
|
|
return new Message(
|
|
key: new ValueKey<string>(message.id),
|
|
buildCustomMessage: widget.buildCustomMessage,
|
|
message: message,
|
|
messageWidth: _messageWidth,
|
|
onMessageLongPress: widget.onMessageLongPress,
|
|
onMessageTap: tappedMessage =>
|
|
{
|
|
if (tappedMessage is ImageMessage && widget.disableImageGallery != true)
|
|
_onImagePressed((ImageMessage) tappedMessage);
|
|
|
|
widget.onMessageTap?.Invoke(tappedMessage);
|
|
},
|
|
onPreviewDataFetched:
|
|
(previewData,textMessage)=>
|
|
{
|
|
_onPreviewDataFetched( textMessage,previewData);
|
|
},
|
|
roundBorder: true, //(map["nextMessageInGroup"] is bool ? (bool) map["nextMessageInGroup"] : false),
|
|
showAvatar:
|
|
widget.showUserAvatars && false,
|
|
//(map["nextMessageInGroup"] is bool ? (bool) map["nextMessageInGroup"] : false) == false,
|
|
showName:
|
|
false,
|
|
//(map["showName"] is bool ? (bool) map["showName"] : false),
|
|
showStatus:
|
|
false,
|
|
//(map["showStatus"] is bool ? (bool) map["showStatus"] : false),
|
|
showUserAvatars:
|
|
widget.showUserAvatars,
|
|
usePreviewData:
|
|
widget.usePreviewData
|
|
);
|
|
}
|
|
|
|
}
|
|
|
|
private Widget _imageGalleryLoadingBuilder(
|
|
BuildContext context,
|
|
ImageChunkEvent _event = null
|
|
)
|
|
{
|
|
return new Center(
|
|
child: new SizedBox(
|
|
width: 20.0f,
|
|
height: 20.0f,
|
|
child: new CircularProgressIndicator(
|
|
value: _event == null || _event.expectedTotalBytes == null
|
|
? 0
|
|
: _event.cumulativeBytesLoaded / _event.expectedTotalBytes
|
|
)
|
|
)
|
|
);
|
|
}
|
|
|
|
private void _onCloseGalleryPressed()
|
|
{
|
|
setState(() => { _isImageViewVisible = false; });
|
|
}
|
|
|
|
private void _onImagePressed(ImageMessage message)
|
|
{
|
|
setState(() =>
|
|
{
|
|
/*_imageViewIndex = _gallery.Where(
|
|
element => element.id == message.id && element.uri == message.uri
|
|
);*/
|
|
foreach (var element in _gallery)
|
|
if (element.id == message.id && element.uri == message.uri)
|
|
{
|
|
_imageViewIndex = _gallery.IndexOf(element);
|
|
break;
|
|
}
|
|
|
|
_isImageViewVisible = true;
|
|
});
|
|
}
|
|
|
|
private void _onPageChanged(int index)
|
|
{
|
|
setState(() => { _imageViewIndex = index; });
|
|
}
|
|
|
|
private void _onPreviewDataFetched(
|
|
ChatComponents.TextMessage message,
|
|
PreviewData previewData
|
|
)
|
|
{
|
|
widget.onPreviewDataFetched?.Invoke(previewData,message);
|
|
}
|
|
|
|
public override Widget build(BuildContext context)
|
|
{
|
|
var results = new List<Widget>();
|
|
results.Add(new Container(
|
|
color: Color.fromARGB(0, 0, 0, 0),
|
|
child: new SafeArea(
|
|
bottom: false,
|
|
child: new Column(
|
|
children: new List<Widget>
|
|
{
|
|
new Flexible(
|
|
child: widget.messages.isEmpty()
|
|
? (Widget) SizedBox.expand(
|
|
child: new Container(
|
|
//color: Colors.yellow,
|
|
alignment: Alignment.center,
|
|
margin: EdgeInsets.symmetric(
|
|
horizontal: 24
|
|
),
|
|
child: new Text(
|
|
widget.l10n.emptyChatPlaceholder,
|
|
style: widget.theme.emptyChatPlaceholderTextStyle,
|
|
textAlign: TextAlign.center
|
|
)
|
|
)
|
|
)
|
|
: new GestureDetector(
|
|
onTap: () => FocusManager.instance.primaryFocus?.unfocus(),
|
|
child: new ChatList(
|
|
isLastPage: widget.isLastPage,
|
|
itemBuilder: (item, index) => _buildMessage(item),
|
|
items: _chatMessages,
|
|
onEndReached: widget.onEndReached,
|
|
onEndReachedThreshold: widget.onEndReachedThreshold
|
|
)
|
|
)
|
|
),
|
|
new Input(
|
|
isAttachmentUploading: widget.isAttachmentUploading,
|
|
onAttachmentPressed: widget.onAttachmentPressed,
|
|
onSendPressed: widget.onSendPressed,
|
|
onTextChanged: widget.onTextChanged
|
|
)
|
|
}
|
|
)
|
|
)
|
|
));
|
|
// if (_isImageViewVisible)
|
|
// results.Add(_buildImageGallery());
|
|
var test = widget.l10n;
|
|
|
|
return new InheritedUser(
|
|
widget.user,
|
|
new InheritedChatTheme(
|
|
widget.theme,
|
|
new InheritedL10n(
|
|
widget.l10n,
|
|
new Stack(
|
|
children: results
|
|
)
|
|
)
|
|
)
|
|
);
|
|
}
|
|
}
|
|
}
|