migrate missing files
This commit is contained in:
@@ -44,18 +44,19 @@ class DateDivider extends StatelessWidget {
|
||||
|
||||
if (uppercase) dayInfo = dayInfo.toUpperCase();
|
||||
|
||||
final chatThemeData = StreamChatTheme.of(context);
|
||||
return Center(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 1),
|
||||
decoration: BoxDecoration(
|
||||
color: StreamChatTheme.of(context).colorTheme.overlayDark,
|
||||
color: chatThemeData.colorTheme.overlayDark,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Text(
|
||||
dayInfo,
|
||||
style: StreamChatTheme.of(context).textTheme.footnote.copyWith(
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
),
|
||||
style: chatThemeData.textTheme.footnote.copyWith(
|
||||
color: chatThemeData.colorTheme.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -31,44 +31,41 @@ class DeletedMessage extends StatelessWidget {
|
||||
final bool reverse;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Transform(
|
||||
transform: Matrix4.rotationY(reverse ? pi : 0),
|
||||
alignment: Alignment.center,
|
||||
child: Material(
|
||||
color: messageTheme.messageBackgroundColor,
|
||||
shape: shape ??
|
||||
RoundedRectangleBorder(
|
||||
borderRadius: borderRadiusGeometry ?? BorderRadius.zero,
|
||||
side: borderSide ??
|
||||
BorderSide(
|
||||
color: Theme.of(context).brightness == Brightness.dark
|
||||
? StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.white
|
||||
.withAlpha(24)
|
||||
: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withAlpha(24),
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 8,
|
||||
horizontal: 16,
|
||||
Widget build(BuildContext context) {
|
||||
final chatThemeData = StreamChatTheme.of(context);
|
||||
return Transform(
|
||||
transform: Matrix4.rotationY(reverse ? pi : 0),
|
||||
alignment: Alignment.center,
|
||||
child: Material(
|
||||
color: messageTheme.messageBackgroundColor,
|
||||
shape: shape ??
|
||||
RoundedRectangleBorder(
|
||||
borderRadius: borderRadiusGeometry ?? BorderRadius.zero,
|
||||
side: borderSide ??
|
||||
BorderSide(
|
||||
color: Theme.of(context).brightness == Brightness.dark
|
||||
? chatThemeData.colorTheme.white.withAlpha(24)
|
||||
: chatThemeData.colorTheme.black.withAlpha(24),
|
||||
),
|
||||
),
|
||||
child: Transform(
|
||||
transform: Matrix4.rotationY(reverse ? pi : 0),
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
'Message deleted',
|
||||
style: messageTheme.messageText?.copyWith(
|
||||
fontStyle: FontStyle.italic,
|
||||
color: messageTheme.createdAt?.color,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 8,
|
||||
horizontal: 16,
|
||||
),
|
||||
child: Transform(
|
||||
transform: Matrix4.rotationY(reverse ? pi : 0),
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
'Message deleted',
|
||||
style: messageTheme.messageText?.copyWith(
|
||||
fontStyle: FontStyle.italic,
|
||||
color: messageTheme.createdAt?.color,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,18 +46,12 @@ class GroupImage extends StatelessWidget {
|
||||
onTap: onTap,
|
||||
child: ClipRRect(
|
||||
borderRadius: borderRadius ??
|
||||
StreamChatTheme.of(context)
|
||||
.ownMessageTheme
|
||||
.avatarTheme
|
||||
?.borderRadius,
|
||||
streamChatTheme.ownMessageTheme.avatarTheme?.borderRadius,
|
||||
child: Container(
|
||||
constraints: constraints ??
|
||||
StreamChatTheme.of(context)
|
||||
.ownMessageTheme
|
||||
.avatarTheme
|
||||
?.constraints,
|
||||
streamChatTheme.ownMessageTheme.avatarTheme?.constraints,
|
||||
decoration: BoxDecoration(
|
||||
color: StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||
color: streamChatTheme.colorTheme.accentBlue,
|
||||
),
|
||||
child: Flex(
|
||||
direction: Axis.vertical,
|
||||
@@ -125,8 +119,7 @@ class GroupImage extends StatelessWidget {
|
||||
BorderRadius.zero) +
|
||||
BorderRadius.circular(selectionThickness),
|
||||
child: Container(
|
||||
color: selectionColor ??
|
||||
StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||
color: selectionColor ?? streamChatTheme.colorTheme.accentBlue,
|
||||
height: 64,
|
||||
width: 64,
|
||||
child: Padding(
|
||||
|
||||
@@ -77,16 +77,18 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final showShareButton = !kIsWeb;
|
||||
final mediaQueryData = MediaQuery.of(context);
|
||||
final chatThemeData = StreamChatTheme.of(context);
|
||||
return SizedBox.fromSize(
|
||||
size: Size(
|
||||
MediaQuery.of(context).size.width,
|
||||
MediaQuery.of(context).padding.bottom + widget.preferredSize.height,
|
||||
mediaQueryData.size.width,
|
||||
mediaQueryData.padding.bottom + widget.preferredSize.height,
|
||||
),
|
||||
child: MediaQuery.removePadding(
|
||||
context: context,
|
||||
removeTop: true,
|
||||
child: BottomAppBar(
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
color: chatThemeData.colorTheme.white,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
@@ -98,7 +100,7 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
IconButton(
|
||||
icon: StreamSvgIcon.iconShare(
|
||||
size: 24,
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
color: chatThemeData.colorTheme.black,
|
||||
),
|
||||
onPressed: () async {
|
||||
final attachment =
|
||||
@@ -134,8 +136,7 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
children: <Widget>[
|
||||
Text(
|
||||
'${widget.currentPage + 1} of ${widget.totalPages}',
|
||||
style:
|
||||
StreamChatTheme.of(context).textTheme.headlineBold,
|
||||
style: chatThemeData.textTheme.headlineBold,
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -143,7 +144,7 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
),
|
||||
IconButton(
|
||||
icon: StreamSvgIcon.iconGrid(
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
color: chatThemeData.colorTheme.black,
|
||||
),
|
||||
onPressed: () => _showPhotosModal(context),
|
||||
),
|
||||
@@ -155,10 +156,11 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
}
|
||||
|
||||
void _showPhotosModal(context) {
|
||||
final chatThemeData = StreamChatTheme.of(context);
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
barrierColor: StreamChatTheme.of(context).colorTheme.overlay,
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.white,
|
||||
barrierColor: chatThemeData.colorTheme.overlay,
|
||||
backgroundColor: chatThemeData.colorTheme.white,
|
||||
isScrollControlled: true,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.only(
|
||||
@@ -189,9 +191,7 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Text(
|
||||
'Photos',
|
||||
style: StreamChatTheme.of(context)
|
||||
.textTheme
|
||||
.headlineBold,
|
||||
style: chatThemeData.textTheme.headlineBold,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -199,7 +199,7 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
alignment: Alignment.centerRight,
|
||||
child: IconButton(
|
||||
icon: StreamSvgIcon.close(
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
color: chatThemeData.colorTheme.black,
|
||||
),
|
||||
onPressed: () => Navigator.maybePop(context),
|
||||
),
|
||||
@@ -262,9 +262,7 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
blurRadius: 8,
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
color: chatThemeData.colorTheme.black
|
||||
.withOpacity(0.3),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -50,59 +50,58 @@ class ImageHeader extends StatelessWidget implements PreferredSizeWidget {
|
||||
final int currentIndex;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => AppBar(
|
||||
brightness: Theme.of(context).brightness,
|
||||
elevation: 1,
|
||||
leading: showBackButton
|
||||
? IconButton(
|
||||
icon: StreamSvgIcon.close(
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
size: 24,
|
||||
),
|
||||
onPressed: onBackPressed,
|
||||
)
|
||||
: const SizedBox(),
|
||||
backgroundColor:
|
||||
StreamChatTheme.of(context).channelTheme.channelHeaderTheme.color,
|
||||
actions: <Widget>[
|
||||
if (message.type != 'ephemeral')
|
||||
IconButton(
|
||||
icon: StreamSvgIcon.iconMenuPoint(
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
Widget build(BuildContext context) {
|
||||
final chatThemeData = StreamChatTheme.of(context);
|
||||
return AppBar(
|
||||
brightness: Theme.of(context).brightness,
|
||||
elevation: 1,
|
||||
leading: showBackButton
|
||||
? IconButton(
|
||||
icon: StreamSvgIcon.close(
|
||||
color: chatThemeData.colorTheme.black,
|
||||
size: 24,
|
||||
),
|
||||
onPressed: () {
|
||||
_showMessageActionModalBottomSheet(context);
|
||||
},
|
||||
onPressed: onBackPressed,
|
||||
)
|
||||
: const SizedBox(),
|
||||
backgroundColor: chatThemeData.channelTheme.channelHeaderTheme.color,
|
||||
actions: <Widget>[
|
||||
if (message.type != 'ephemeral')
|
||||
IconButton(
|
||||
icon: StreamSvgIcon.iconMenuPoint(
|
||||
color: chatThemeData.colorTheme.black,
|
||||
),
|
||||
],
|
||||
centerTitle: true,
|
||||
title: message.type != 'ephemeral'
|
||||
? InkWell(
|
||||
onTap: onTitleTap,
|
||||
child: SizedBox(
|
||||
height: preferredSize.height,
|
||||
width: preferredSize.width,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
userName,
|
||||
style:
|
||||
StreamChatTheme.of(context).textTheme.headlineBold,
|
||||
),
|
||||
Text(
|
||||
sentAt,
|
||||
style: StreamChatTheme.of(context)
|
||||
.channelPreviewTheme
|
||||
.subtitle,
|
||||
),
|
||||
],
|
||||
),
|
||||
onPressed: () {
|
||||
_showMessageActionModalBottomSheet(context);
|
||||
},
|
||||
),
|
||||
],
|
||||
centerTitle: true,
|
||||
title: message.type != 'ephemeral'
|
||||
? InkWell(
|
||||
onTap: onTitleTap,
|
||||
child: SizedBox(
|
||||
height: preferredSize.height,
|
||||
width: preferredSize.width,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
userName,
|
||||
style: chatThemeData.textTheme.headlineBold,
|
||||
),
|
||||
Text(
|
||||
sentAt,
|
||||
style: chatThemeData.channelPreviewTheme.subtitle,
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
: const SizedBox(),
|
||||
);
|
||||
),
|
||||
)
|
||||
: const SizedBox(),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
final Size preferredSize;
|
||||
|
||||
@@ -38,26 +38,29 @@ class InfoTile extends StatelessWidget {
|
||||
final Color? backgroundColor;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => PortalEntry(
|
||||
visible: showMessage,
|
||||
portalAnchor: tileAnchor ?? Alignment.topCenter,
|
||||
childAnchor: childAnchor ?? Alignment.bottomCenter,
|
||||
portal: Container(
|
||||
height: 25,
|
||||
color: backgroundColor ??
|
||||
StreamChatTheme.of(context).colorTheme.grey.withOpacity(0.9),
|
||||
child: Center(
|
||||
child: Text(
|
||||
message,
|
||||
style: textStyle ??
|
||||
StreamChatTheme.of(context).textTheme.body.copyWith(
|
||||
color: Colors.white,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
Widget build(BuildContext context) {
|
||||
final chatThemeData = StreamChatTheme.of(context);
|
||||
return PortalEntry(
|
||||
visible: showMessage,
|
||||
portalAnchor: tileAnchor ?? Alignment.topCenter,
|
||||
childAnchor: childAnchor ?? Alignment.bottomCenter,
|
||||
portal: Container(
|
||||
height: 25,
|
||||
color:
|
||||
backgroundColor ?? chatThemeData.colorTheme.grey.withOpacity(0.9),
|
||||
child: Center(
|
||||
child: Text(
|
||||
message,
|
||||
style: textStyle ??
|
||||
chatThemeData.textTheme.body.copyWith(
|
||||
color: Colors.white,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
child: child,
|
||||
);
|
||||
),
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ class _MediaListViewState extends State<MediaListView> {
|
||||
position,
|
||||
) {
|
||||
final media = _media.elementAt(position);
|
||||
final chatThemeData = StreamChatTheme.of(context);
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 1, vertical: 1),
|
||||
child: InkWell(
|
||||
@@ -89,10 +90,8 @@ class _MediaListViewState extends State<MediaListView> {
|
||||
? 1.0
|
||||
: 0.0,
|
||||
child: Container(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.5),
|
||||
color:
|
||||
chatThemeData.colorTheme.black.withOpacity(0.5),
|
||||
alignment: Alignment.topRight,
|
||||
padding: const EdgeInsets.only(
|
||||
top: 8,
|
||||
@@ -100,13 +99,10 @@ class _MediaListViewState extends State<MediaListView> {
|
||||
),
|
||||
child: CircleAvatar(
|
||||
radius: 12,
|
||||
backgroundColor:
|
||||
StreamChatTheme.of(context).colorTheme.white,
|
||||
backgroundColor: chatThemeData.colorTheme.white,
|
||||
child: StreamSvgIcon.check(
|
||||
size: 24,
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black,
|
||||
color: chatThemeData.colorTheme.black,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -128,7 +124,7 @@ class _MediaListViewState extends State<MediaListView> {
|
||||
child: Text(
|
||||
media.videoDuration.format(),
|
||||
style: TextStyle(
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
color: chatThemeData.colorTheme.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
/// This widget is used for showing user tiles for mentions
|
||||
@@ -32,71 +31,70 @@ class MentionTile extends StatelessWidget {
|
||||
final Widget? trailing;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => SizedBox(
|
||||
height: 56,
|
||||
child: Row(
|
||||
children: [
|
||||
const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
leading ??
|
||||
UserAvatar(
|
||||
constraints: BoxConstraints.tight(
|
||||
const Size(
|
||||
40,
|
||||
40,
|
||||
),
|
||||
Widget build(BuildContext context) {
|
||||
final chatThemeData = StreamChatTheme.of(context);
|
||||
return SizedBox(
|
||||
height: 56,
|
||||
child: Row(
|
||||
children: [
|
||||
const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
leading ??
|
||||
UserAvatar(
|
||||
constraints: BoxConstraints.tight(
|
||||
const Size(
|
||||
40,
|
||||
40,
|
||||
),
|
||||
user: member.user!,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 8,
|
||||
),
|
||||
Expanded(
|
||||
child: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
title ??
|
||||
Text(
|
||||
member.user!.name,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: StreamChatTheme.of(context).textTheme.bodyBold,
|
||||
user: member.user!,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 8,
|
||||
),
|
||||
Expanded(
|
||||
child: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
title ??
|
||||
Text(
|
||||
member.user!.name,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: chatThemeData.textTheme.bodyBold,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 2,
|
||||
),
|
||||
subtitle ??
|
||||
Text(
|
||||
'@${member.userId}',
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: chatThemeData.textTheme.footnoteBold.copyWith(
|
||||
color: chatThemeData.colorTheme.grey,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 2,
|
||||
),
|
||||
subtitle ??
|
||||
Text(
|
||||
'@${member.userId}',
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: StreamChatTheme.of(context)
|
||||
.textTheme
|
||||
.footnoteBold
|
||||
.copyWith(
|
||||
color:
|
||||
StreamChatTheme.of(context).colorTheme.grey,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
trailing ??
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
right: 18,
|
||||
left: 8,
|
||||
),
|
||||
child: StreamSvgIcon.mentions(
|
||||
color: StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||
),
|
||||
),
|
||||
trailing ??
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
right: 18,
|
||||
left: 8,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
child: StreamSvgIcon.mentions(
|
||||
color: chatThemeData.colorTheme.accentBlue,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,12 +3,12 @@ import 'dart:ui';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stream_chat_flutter/src/extension.dart';
|
||||
import 'package:stream_chat_flutter/src/message_action.dart';
|
||||
import 'package:stream_chat_flutter/src/reaction_picker.dart';
|
||||
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
||||
import 'package:stream_chat_flutter/src/utils.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
import 'package:stream_chat_flutter/src/extension.dart';
|
||||
|
||||
/// Constructs a modal with actions for a message
|
||||
class MessageActionsModal extends StatefulWidget {
|
||||
@@ -108,7 +108,8 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
||||
Widget build(BuildContext context) => _showMessageOptionsModal();
|
||||
|
||||
Widget _showMessageOptionsModal() {
|
||||
final size = MediaQuery.of(context).size;
|
||||
final mediaQueryData = MediaQuery.of(context);
|
||||
final size = mediaQueryData.size;
|
||||
final user = StreamChat.of(context).user;
|
||||
|
||||
final roughMaxSize = 2 * size.width / 3;
|
||||
@@ -133,6 +134,7 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
||||
final hasFileAttachment =
|
||||
widget.message.attachments.any((it) => it.type == 'file') == true;
|
||||
|
||||
final streamChatThemeData = StreamChatTheme.of(context);
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.translucent,
|
||||
onTap: () => Navigator.maybePop(context),
|
||||
@@ -145,7 +147,7 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
||||
sigmaY: 10,
|
||||
),
|
||||
child: Container(
|
||||
color: StreamChatTheme.of(context).colorTheme.overlay,
|
||||
color: streamChatThemeData.colorTheme.overlay,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -226,11 +228,9 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
||||
left: widget.reverse ? 0 : 40,
|
||||
),
|
||||
child: SizedBox(
|
||||
width: MediaQuery.of(context).size.width * 0.75,
|
||||
width: mediaQueryData.size.width * 0.75,
|
||||
child: Material(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.whiteSnow,
|
||||
color: streamChatThemeData.colorTheme.whiteSnow,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
@@ -266,9 +266,8 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
||||
].insertBetween(
|
||||
Container(
|
||||
height: 1,
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.greyWhisper,
|
||||
color: streamChatThemeData
|
||||
.colorTheme.greyWhisper,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -310,11 +309,12 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
||||
void _showFlagDialog() async {
|
||||
final client = StreamChat.of(context).client;
|
||||
|
||||
final streamChatThemeData = StreamChatTheme.of(context);
|
||||
final answer = await showConfirmationDialog(
|
||||
context,
|
||||
title: 'Flag Message',
|
||||
icon: StreamSvgIcon.flag(
|
||||
color: StreamChatTheme.of(context).colorTheme.accentRed,
|
||||
color: streamChatThemeData.colorTheme.accentRed,
|
||||
size: 24,
|
||||
),
|
||||
question:
|
||||
@@ -324,7 +324,7 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
||||
cancelText: 'CANCEL',
|
||||
);
|
||||
|
||||
final theme = StreamChatTheme.of(context);
|
||||
final theme = streamChatThemeData;
|
||||
if (answer == true) {
|
||||
try {
|
||||
await client.flagMessage(widget.message.id);
|
||||
@@ -400,48 +400,54 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildReplyButton(BuildContext context) => InkWell(
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
if (widget.onReplyTap != null) {
|
||||
widget.onReplyTap!(widget.message);
|
||||
}
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 11, horizontal: 16),
|
||||
child: Row(
|
||||
children: [
|
||||
StreamSvgIcon.reply(
|
||||
color: StreamChatTheme.of(context).primaryIconTheme.color,
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Text(
|
||||
'Reply',
|
||||
style: StreamChatTheme.of(context).textTheme.body,
|
||||
),
|
||||
],
|
||||
),
|
||||
Widget _buildReplyButton(BuildContext context) {
|
||||
final streamChatThemeData = StreamChatTheme.of(context);
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
if (widget.onReplyTap != null) {
|
||||
widget.onReplyTap!(widget.message);
|
||||
}
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 11, horizontal: 16),
|
||||
child: Row(
|
||||
children: [
|
||||
StreamSvgIcon.reply(
|
||||
color: streamChatThemeData.primaryIconTheme.color,
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Text(
|
||||
'Reply',
|
||||
style: streamChatThemeData.textTheme.body,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildFlagButton(BuildContext context) => InkWell(
|
||||
onTap: _showFlagDialog,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 11, horizontal: 16),
|
||||
child: Row(
|
||||
children: [
|
||||
StreamSvgIcon.iconFlag(
|
||||
color: StreamChatTheme.of(context).primaryIconTheme.color,
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Text(
|
||||
'Flag Message',
|
||||
style: StreamChatTheme.of(context).textTheme.body,
|
||||
),
|
||||
],
|
||||
),
|
||||
Widget _buildFlagButton(BuildContext context) {
|
||||
final streamChatThemeData = StreamChatTheme.of(context);
|
||||
return InkWell(
|
||||
onTap: _showFlagDialog,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 11, horizontal: 16),
|
||||
child: Row(
|
||||
children: [
|
||||
StreamSvgIcon.iconFlag(
|
||||
color: streamChatThemeData.primaryIconTheme.color,
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Text(
|
||||
'Flag Message',
|
||||
style: streamChatThemeData.textTheme.body,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDeleteButton(BuildContext context) {
|
||||
final isDeleteFailed =
|
||||
@@ -469,54 +475,61 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCopyButton(BuildContext context) => InkWell(
|
||||
onTap: () async {
|
||||
widget.onCopyTap?.call(widget.message);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 11, horizontal: 16),
|
||||
child: Row(
|
||||
children: [
|
||||
StreamSvgIcon.copy(
|
||||
size: 24,
|
||||
color: StreamChatTheme.of(context).primaryIconTheme.color,
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Text(
|
||||
'Copy Message',
|
||||
style: StreamChatTheme.of(context).textTheme.body,
|
||||
),
|
||||
],
|
||||
),
|
||||
Widget _buildCopyButton(BuildContext context) {
|
||||
final streamChatThemeData = StreamChatTheme.of(context);
|
||||
return InkWell(
|
||||
onTap: () async {
|
||||
widget.onCopyTap?.call(widget.message);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 11, horizontal: 16),
|
||||
child: Row(
|
||||
children: [
|
||||
StreamSvgIcon.copy(
|
||||
size: 24,
|
||||
color: streamChatThemeData.primaryIconTheme.color,
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Text(
|
||||
'Copy Message',
|
||||
style: streamChatThemeData.textTheme.body,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildEditMessage(BuildContext context) => InkWell(
|
||||
onTap: () async {
|
||||
Navigator.pop(context);
|
||||
_showEditBottomSheet(context);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 11, horizontal: 16),
|
||||
child: Row(
|
||||
children: [
|
||||
StreamSvgIcon.edit(
|
||||
color: StreamChatTheme.of(context).primaryIconTheme.color,
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Text(
|
||||
'Edit Message',
|
||||
style: StreamChatTheme.of(context).textTheme.body,
|
||||
),
|
||||
],
|
||||
),
|
||||
Widget _buildEditMessage(BuildContext context) {
|
||||
final streamChatThemeData = StreamChatTheme.of(context);
|
||||
return InkWell(
|
||||
onTap: () async {
|
||||
Navigator.pop(context);
|
||||
_showEditBottomSheet(context);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 11, horizontal: 16),
|
||||
child: Row(
|
||||
children: [
|
||||
StreamSvgIcon.edit(
|
||||
color: streamChatThemeData.primaryIconTheme.color,
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Text(
|
||||
'Edit Message',
|
||||
style: streamChatThemeData.textTheme.body,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildResendMessage(BuildContext context) {
|
||||
final isUpdateFailed =
|
||||
widget.message.status == MessageSendingStatus.failed_update;
|
||||
final streamChatThemeData = StreamChatTheme.of(context);
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
@@ -532,12 +545,12 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
||||
child: Row(
|
||||
children: [
|
||||
StreamSvgIcon.circleUp(
|
||||
color: StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||
color: streamChatThemeData.colorTheme.accentBlue,
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Text(
|
||||
isUpdateFailed ? 'Resend Edited Message' : 'Resend',
|
||||
style: StreamChatTheme.of(context).textTheme.body,
|
||||
style: streamChatThemeData.textTheme.body,
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -547,13 +560,13 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
||||
|
||||
void _showEditBottomSheet(BuildContext context) {
|
||||
final channel = StreamChannel.of(context).channel;
|
||||
final streamChatThemeData = StreamChatTheme.of(context);
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
elevation: 2,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
isScrollControlled: true,
|
||||
backgroundColor:
|
||||
StreamChatTheme.of(context).messageInputTheme.inputBackground,
|
||||
backgroundColor: streamChatThemeData.messageInputTheme.inputBackground,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(16),
|
||||
@@ -575,8 +588,7 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: StreamSvgIcon.edit(
|
||||
color:
|
||||
StreamChatTheme.of(context).colorTheme.greyGainsboro,
|
||||
color: streamChatThemeData.colorTheme.greyGainsboro,
|
||||
),
|
||||
),
|
||||
const Text(
|
||||
@@ -608,27 +620,30 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildThreadReplyButton(BuildContext context) => InkWell(
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
if (widget.onThreadReplyTap != null) {
|
||||
widget.onThreadReplyTap!(widget.message);
|
||||
}
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 11, horizontal: 16),
|
||||
child: Row(
|
||||
children: [
|
||||
StreamSvgIcon.thread(
|
||||
color: StreamChatTheme.of(context).primaryIconTheme.color,
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Text(
|
||||
'Thread Reply',
|
||||
style: StreamChatTheme.of(context).textTheme.body,
|
||||
),
|
||||
],
|
||||
),
|
||||
Widget _buildThreadReplyButton(BuildContext context) {
|
||||
final streamChatThemeData = StreamChatTheme.of(context);
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
if (widget.onThreadReplyTap != null) {
|
||||
widget.onThreadReplyTap!(widget.message);
|
||||
}
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 11, horizontal: 16),
|
||||
child: Row(
|
||||
children: [
|
||||
StreamSvgIcon.thread(
|
||||
color: streamChatThemeData.primaryIconTheme.color,
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Text(
|
||||
'Thread Reply',
|
||||
style: streamChatThemeData.textTheme.body,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,15 +6,15 @@ import 'package:flutter/material.dart';
|
||||
import 'package:jiffy/jiffy.dart';
|
||||
import 'package:rxdart/rxdart.dart';
|
||||
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
||||
import 'package:stream_chat_flutter/src/extension.dart';
|
||||
import 'package:stream_chat_flutter/src/info_tile.dart';
|
||||
import 'package:stream_chat_flutter/src/message_widget.dart';
|
||||
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
||||
import 'package:stream_chat_flutter/src/swipeable.dart';
|
||||
import 'package:stream_chat_flutter/src/system_message.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||
import 'package:visibility_detector/visibility_detector.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
import 'package:stream_chat_flutter/src/extension.dart';
|
||||
import 'package:stream_chat_flutter/src/swipeable.dart';
|
||||
|
||||
/// Widget builder for message
|
||||
typedef MessageBuilder = Widget Function(
|
||||
@@ -274,15 +274,15 @@ class _MessageListViewState extends State<MessageListView> {
|
||||
bool _showScrollToBottom = false;
|
||||
late final ItemPositionsListener _itemPositionListener;
|
||||
int? _messageListLength;
|
||||
StreamChannelState? streamChannel;
|
||||
late StreamChannelState streamChannel;
|
||||
|
||||
int? get _initialIndex {
|
||||
if (widget.initialScrollIndex != null) return widget.initialScrollIndex;
|
||||
if (streamChannel!.initialMessageId != null) {
|
||||
final messages = streamChannel!.channel.state!.messages;
|
||||
if (streamChannel.initialMessageId != null) {
|
||||
final messages = streamChannel.channel.state!.messages;
|
||||
final totalMessages = messages.length;
|
||||
final messageIndex =
|
||||
messages.indexWhere((e) => e.id == streamChannel!.initialMessageId);
|
||||
messages.indexWhere((e) => e.id == streamChannel.initialMessageId);
|
||||
final index = totalMessages - messageIndex;
|
||||
if (index != 0) return index - 1;
|
||||
return index;
|
||||
@@ -295,9 +295,9 @@ class _MessageListViewState extends State<MessageListView> {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool _isInitialMessage(String id) => streamChannel!.initialMessageId == id;
|
||||
bool _isInitialMessage(String id) => streamChannel.initialMessageId == id;
|
||||
|
||||
bool get _upToDate => streamChannel!.channel.state!.isUpToDate;
|
||||
bool get _upToDate => streamChannel.channel.state!.isUpToDate;
|
||||
|
||||
bool get _isThreadConversation => widget.parentMessage != null;
|
||||
|
||||
@@ -316,46 +316,37 @@ class _MessageListViewState extends State<MessageListView> {
|
||||
final MessageListController _messageListController = MessageListController();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => MessageListCore(
|
||||
messageFilter: widget.messageFilter,
|
||||
loadingBuilder: widget.loadingBuilder ??
|
||||
(context) => const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
Widget build(BuildContext context) {
|
||||
final chatThemeData = StreamChatTheme.of(context);
|
||||
return MessageListCore(
|
||||
messageFilter: widget.messageFilter,
|
||||
loadingBuilder: widget.loadingBuilder ??
|
||||
(context) => const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
emptyBuilder: widget.emptyBuilder ??
|
||||
(context) => Center(
|
||||
child: Text(
|
||||
'No chats here yet...',
|
||||
style: chatThemeData.textTheme.footnote.copyWith(
|
||||
color: chatThemeData.colorTheme.black.withOpacity(.5)),
|
||||
),
|
||||
emptyBuilder: widget.emptyBuilder ??
|
||||
(context) => Center(
|
||||
child: Text(
|
||||
'No chats here yet...',
|
||||
style: StreamChatTheme.of(context)
|
||||
.textTheme
|
||||
.footnote
|
||||
.copyWith(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(.5)),
|
||||
),
|
||||
),
|
||||
messageListBuilder:
|
||||
widget.messageListBuilder ?? (context, list) => _buildListView(list),
|
||||
messageListController: _messageListController,
|
||||
parentMessage: widget.parentMessage,
|
||||
showScrollToBottom: widget.showScrollToBottom,
|
||||
errorWidgetBuilder: widget.errorWidgetBuilder ??
|
||||
(BuildContext context, Object error) => Center(
|
||||
child: Text(
|
||||
'Something went wrong',
|
||||
style: chatThemeData.textTheme.footnote.copyWith(
|
||||
color: chatThemeData.colorTheme.black.withOpacity(.5)),
|
||||
),
|
||||
messageListBuilder: widget.messageListBuilder ??
|
||||
(context, list) => _buildListView(list),
|
||||
messageListController: _messageListController,
|
||||
parentMessage: widget.parentMessage,
|
||||
showScrollToBottom: widget.showScrollToBottom,
|
||||
errorWidgetBuilder: widget.errorWidgetBuilder ??
|
||||
(BuildContext context, Object error) => Center(
|
||||
child: Text(
|
||||
'Something went wrong',
|
||||
style: StreamChatTheme.of(context)
|
||||
.textTheme
|
||||
.footnote
|
||||
.copyWith(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(.5)),
|
||||
),
|
||||
),
|
||||
);
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildListView(List<Message> data) {
|
||||
messages = data;
|
||||
@@ -448,10 +439,10 @@ class _MessageListViewState extends State<MessageListView> {
|
||||
if (i == 0) return const SizedBox(height: 30);
|
||||
if (i == messages.length + 1) {
|
||||
final replyCount = widget.parentMessage!.replyCount;
|
||||
final chatThemeData = StreamChatTheme.of(context);
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient:
|
||||
StreamChatTheme.of(context).colorTheme.bgGradient,
|
||||
gradient: chatThemeData.colorTheme.bgGradient,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
@@ -459,10 +450,8 @@ class _MessageListViewState extends State<MessageListView> {
|
||||
// ignore: lines_longer_than_80_chars
|
||||
'$replyCount ${replyCount == 1 ? 'Reply' : 'Replies'}',
|
||||
textAlign: TextAlign.center,
|
||||
style: StreamChatTheme.of(context)
|
||||
.channelTheme
|
||||
.channelHeaderTheme
|
||||
.subtitle,
|
||||
style: chatThemeData
|
||||
.channelTheme.channelHeaderTheme.subtitle,
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -616,8 +605,8 @@ class _MessageListViewState extends State<MessageListView> {
|
||||
|
||||
Widget _buildScrollToBottom() => StreamBuilder<Tuple2<bool, int>>(
|
||||
stream: Rx.combineLatest2(
|
||||
streamChannel!.channel.state!.isUpToDateStream,
|
||||
streamChannel!.channel.state!.unreadCountStream,
|
||||
streamChannel.channel.state!.isUpToDateStream,
|
||||
streamChannel.channel.state!.unreadCountStream,
|
||||
(bool isUpToDate, int unreadCount) => Tuple2(isUpToDate, unreadCount),
|
||||
),
|
||||
builder: (_, snapshot) {
|
||||
@@ -633,8 +622,9 @@ class _MessageListViewState extends State<MessageListView> {
|
||||
}
|
||||
final unreadCount = snapshot.data!.item2;
|
||||
final showUnreadCount = unreadCount > 0 &&
|
||||
streamChannel!.channel.state!.members.any((e) =>
|
||||
e.userId == streamChannel!.channel.client.state.user!.id);
|
||||
streamChannel.channel.state!.members.any((e) =>
|
||||
e.userId == streamChannel.channel.client.state.user!.id);
|
||||
final chatThemeData = StreamChatTheme.of(context);
|
||||
return Positioned(
|
||||
bottom: 8,
|
||||
right: 8,
|
||||
@@ -644,15 +634,15 @@ class _MessageListViewState extends State<MessageListView> {
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
FloatingActionButton(
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.white,
|
||||
backgroundColor: chatThemeData.colorTheme.white,
|
||||
onPressed: () {
|
||||
if (unreadCount > 0) {
|
||||
streamChannel!.channel.markRead();
|
||||
streamChannel.channel.markRead();
|
||||
}
|
||||
if (!_upToDate) {
|
||||
_bottomPaginationActive = false;
|
||||
_topPaginationActive = false;
|
||||
streamChannel!.reloadChannel();
|
||||
streamChannel.reloadChannel();
|
||||
} else {
|
||||
setState(() => _showScrollToBottom = false);
|
||||
_scrollController!.scrollTo(
|
||||
@@ -663,7 +653,7 @@ class _MessageListViewState extends State<MessageListView> {
|
||||
}
|
||||
},
|
||||
child: StreamSvgIcon.down(
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
color: chatThemeData.colorTheme.black,
|
||||
),
|
||||
),
|
||||
if (showUnreadCount)
|
||||
@@ -692,12 +682,12 @@ class _MessageListViewState extends State<MessageListView> {
|
||||
);
|
||||
|
||||
Widget _buildLoadingIndicator(
|
||||
StreamChannelState? streamChannel,
|
||||
StreamChannelState streamChannel,
|
||||
QueryDirection direction,
|
||||
) {
|
||||
final stream = direction == QueryDirection.top
|
||||
? streamChannel!.queryTopMessages
|
||||
: streamChannel!.queryBottomMessages;
|
||||
? streamChannel.queryTopMessages
|
||||
: streamChannel.queryBottomMessages;
|
||||
return StreamBuilder<bool>(
|
||||
key: const Key('LOADING-INDICATOR'),
|
||||
stream: stream,
|
||||
@@ -764,7 +754,7 @@ class _MessageListViewState extends State<MessageListView> {
|
||||
BuildContext context,
|
||||
Message message,
|
||||
List<Message> messages,
|
||||
StreamChannelState? streamChannel,
|
||||
StreamChannelState streamChannel,
|
||||
) {
|
||||
Widget messageWidget;
|
||||
if (widget.messageBuilder != null) {
|
||||
@@ -790,7 +780,7 @@ class _MessageListViewState extends State<MessageListView> {
|
||||
onVisibilityChanged: (visibility) {
|
||||
final isVisible = visibility.visibleBounds != Rect.zero;
|
||||
if (isVisible) {
|
||||
final channel = streamChannel!.channel;
|
||||
final channel = streamChannel.channel;
|
||||
if (_upToDate &&
|
||||
channel.config?.readEvents == true &&
|
||||
channel.state!.unreadCount! > 0) {
|
||||
@@ -811,6 +801,7 @@ class _MessageListViewState extends State<MessageListView> {
|
||||
final isMyMessage = message.user!.id == StreamChat.of(context).user!.id;
|
||||
final isOnlyEmoji = message.text!.isOnlyEmoji;
|
||||
|
||||
final chatThemeData = StreamChatTheme.of(context);
|
||||
return MessageWidget(
|
||||
showReplyMessage: false,
|
||||
showResendMessage: false,
|
||||
@@ -837,8 +828,8 @@ class _MessageListViewState extends State<MessageListView> {
|
||||
borderSide: isMyMessage || isOnlyEmoji ? BorderSide.none : null,
|
||||
showUserAvatar: isMyMessage ? DisplayWidget.gone : DisplayWidget.show,
|
||||
messageTheme: isMyMessage
|
||||
? StreamChatTheme.of(context).ownMessageTheme
|
||||
: StreamChatTheme.of(context).otherMessageTheme,
|
||||
? chatThemeData.ownMessageTheme
|
||||
: chatThemeData.otherMessageTheme,
|
||||
onShowMessage: widget.onShowMessage,
|
||||
onReturnAction: (action) {
|
||||
switch (action) {
|
||||
@@ -897,7 +888,7 @@ class _MessageListViewState extends State<MessageListView> {
|
||||
);
|
||||
}
|
||||
|
||||
final channel = streamChannel!.channel;
|
||||
final channel = streamChannel.channel;
|
||||
final readList = channel.state?.read?.where((read) {
|
||||
if (read.user.id == userId) return false;
|
||||
return read.lastRead.isAfter(message.createdAt) ||
|
||||
@@ -946,6 +937,7 @@ class _MessageListViewState extends State<MessageListView> {
|
||||
? BorderSide.none
|
||||
: null;
|
||||
|
||||
final chatThemeData = StreamChatTheme.of(context);
|
||||
Widget child = MessageWidget(
|
||||
key: ValueKey<String>('MESSAGE-${message.id}'),
|
||||
message: message,
|
||||
@@ -970,7 +962,7 @@ class _MessageListViewState extends State<MessageListView> {
|
||||
if (messages.map((e) => e.id).contains(quotedMessageId)) {
|
||||
scrollToIndex();
|
||||
} else {
|
||||
await streamChannel!.loadChannelAtMessage(quotedMessageId).then((_) {
|
||||
await streamChannel.loadChannelAtMessage(quotedMessageId).then((_) {
|
||||
WidgetsBinding.instance!.addPostFrameCallback((_) {
|
||||
if (messages.map((e) => e.id).contains(quotedMessageId)) {
|
||||
scrollToIndex();
|
||||
@@ -1013,8 +1005,8 @@ class _MessageListViewState extends State<MessageListView> {
|
||||
horizontal: isOnlyEmoji ? 0 : 16.0,
|
||||
),
|
||||
messageTheme: isMyMessage
|
||||
? StreamChatTheme.of(context).ownMessageTheme
|
||||
: StreamChatTheme.of(context).otherMessageTheme,
|
||||
? chatThemeData.ownMessageTheme
|
||||
: chatThemeData.otherMessageTheme,
|
||||
readList: readList,
|
||||
allRead: allRead,
|
||||
onShowMessage: widget.onShowMessage,
|
||||
@@ -1054,7 +1046,7 @@ class _MessageListViewState extends State<MessageListView> {
|
||||
widget.onMessageSwiped?.call(message);
|
||||
},
|
||||
backgroundIcon: StreamSvgIcon.reply(
|
||||
color: StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||
color: chatThemeData.colorTheme.accentBlue,
|
||||
),
|
||||
child: child,
|
||||
),
|
||||
@@ -1064,7 +1056,7 @@ class _MessageListViewState extends State<MessageListView> {
|
||||
if (!initialMessageHighlightComplete &&
|
||||
widget.highlightInitialMessage &&
|
||||
_isInitialMessage(message.id)) {
|
||||
final colorTheme = StreamChatTheme.of(context).colorTheme;
|
||||
final colorTheme = chatThemeData.colorTheme;
|
||||
final highlightColor =
|
||||
widget.messageHighlightColor ?? colorTheme.highlight;
|
||||
child = TweenAnimationBuilder<Color?>(
|
||||
@@ -1100,14 +1092,23 @@ class _MessageListViewState extends State<MessageListView> {
|
||||
initialIndex = _initialIndex;
|
||||
initialAlignment = _initialAlignment;
|
||||
|
||||
_getOnThreadTap();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
streamChannel = StreamChannel.of(context);
|
||||
|
||||
_messageNewListener?.cancel();
|
||||
_messageNewListener =
|
||||
streamChannel!.channel.on(EventType.messageNew).listen((event) {
|
||||
streamChannel.channel.on(EventType.messageNew).listen((event) {
|
||||
if (_upToDate) {
|
||||
_bottomPaginationActive = false;
|
||||
_topPaginationActive = false;
|
||||
}
|
||||
if (event.message!.user!.id ==
|
||||
streamChannel!.channel.client.state.user!.id) {
|
||||
streamChannel.channel.client.state.user!.id) {
|
||||
WidgetsBinding.instance!.addPostFrameCallback((_) {
|
||||
_scrollController?.jumpTo(
|
||||
index: 0,
|
||||
@@ -1117,11 +1118,9 @@ class _MessageListViewState extends State<MessageListView> {
|
||||
});
|
||||
|
||||
if (_isThreadConversation) {
|
||||
streamChannel!.getReplies(widget.parentMessage!.id);
|
||||
streamChannel.getReplies(widget.parentMessage!.id);
|
||||
}
|
||||
|
||||
_getOnThreadTap();
|
||||
super.initState();
|
||||
super.didChangeDependencies();
|
||||
}
|
||||
|
||||
void _getOnThreadTap() {
|
||||
@@ -1139,12 +1138,12 @@ class _MessageListViewState extends State<MessageListView> {
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (_) => StreamBuilder<Message>(
|
||||
stream: streamChannel!.channel.state!.messagesStream.map(
|
||||
stream: streamChannel.channel.state!.messagesStream.map(
|
||||
(messages) =>
|
||||
messages!.firstWhere((m) => m.id == message.id)),
|
||||
initialData: message,
|
||||
builder: (_, snapshot) => StreamChannel(
|
||||
channel: streamChannel!.channel,
|
||||
channel: streamChannel.channel,
|
||||
child: widget.threadBuilder!(context, snapshot.data),
|
||||
),
|
||||
),
|
||||
@@ -1157,7 +1156,7 @@ class _MessageListViewState extends State<MessageListView> {
|
||||
@override
|
||||
void dispose() {
|
||||
if (!_upToDate) {
|
||||
streamChannel!.reloadChannel();
|
||||
streamChannel.reloadChannel();
|
||||
}
|
||||
_messageNewListener?.cancel();
|
||||
super.dispose();
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stream_chat_flutter/src/extension.dart';
|
||||
import 'package:stream_chat_flutter/src/reaction_bubble.dart';
|
||||
import 'package:stream_chat_flutter/src/reaction_picker.dart';
|
||||
import 'package:stream_chat_flutter/src/stream_chat.dart';
|
||||
import 'package:stream_chat_flutter/src/user_avatar.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||
import 'package:stream_chat_flutter/src/extension.dart';
|
||||
|
||||
/// Modal widget for displaying message reactions
|
||||
class MessageReactionsModal extends StatelessWidget {
|
||||
@@ -176,8 +176,9 @@ class MessageReactionsModal extends StatelessWidget {
|
||||
|
||||
Widget _buildReactionCard(BuildContext context) {
|
||||
final currentUser = StreamChat.of(context).user;
|
||||
final chatThemeData = StreamChatTheme.of(context);
|
||||
return Card(
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
color: chatThemeData.colorTheme.white,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
@@ -190,7 +191,7 @@ class MessageReactionsModal extends StatelessWidget {
|
||||
children: [
|
||||
Text(
|
||||
'Message Reactions',
|
||||
style: StreamChatTheme.of(context).textTheme.headlineBold,
|
||||
style: chatThemeData.textTheme.headlineBold,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Flexible(
|
||||
@@ -220,6 +221,7 @@ class MessageReactionsModal extends StatelessWidget {
|
||||
BuildContext context,
|
||||
) {
|
||||
final isCurrentUser = reaction.user?.id == currentUser.id;
|
||||
final chatThemeData = StreamChatTheme.of(context);
|
||||
return ConstrainedBox(
|
||||
constraints: BoxConstraints.loose(const Size(
|
||||
64,
|
||||
@@ -258,7 +260,7 @@ class MessageReactionsModal extends StatelessWidget {
|
||||
messageTheme.reactionsBorderColor ?? Colors.transparent,
|
||||
backgroundColor: messageTheme.reactionsBackgroundColor ??
|
||||
Colors.transparent,
|
||||
maskColor: StreamChatTheme.of(context).colorTheme.white,
|
||||
maskColor: chatThemeData.colorTheme.white,
|
||||
tailCirclesSpacing: 1,
|
||||
highlightOwnReactions: false,
|
||||
),
|
||||
@@ -269,7 +271,7 @@ class MessageReactionsModal extends StatelessWidget {
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
reaction.user!.name.split(' ')[0],
|
||||
style: StreamChatTheme.of(context).textTheme.footnoteBold,
|
||||
style: chatThemeData.textTheme.footnoteBold,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
|
||||
@@ -35,6 +35,7 @@ class MessageSearchItem extends StatelessWidget {
|
||||
final channel = getMessageResponse.channel;
|
||||
final channelName = channel?.extraData['name'];
|
||||
final user = message.user!;
|
||||
final chatThemeData = StreamChatTheme.of(context);
|
||||
return ListTile(
|
||||
onTap: onTap,
|
||||
leading: UserAvatar(
|
||||
@@ -49,21 +50,18 @@ class MessageSearchItem extends StatelessWidget {
|
||||
children: [
|
||||
Text(
|
||||
user.id == StreamChat.of(context).user?.id ? 'You' : user.name,
|
||||
style: StreamChatTheme.of(context).channelPreviewTheme.title,
|
||||
style: chatThemeData.channelPreviewTheme.title,
|
||||
),
|
||||
if (channelName != null) ...[
|
||||
Text(
|
||||
' in ',
|
||||
style: StreamChatTheme.of(context)
|
||||
.channelPreviewTheme
|
||||
.title
|
||||
?.copyWith(
|
||||
fontWeight: FontWeight.normal,
|
||||
),
|
||||
style: chatThemeData.channelPreviewTheme.title?.copyWith(
|
||||
fontWeight: FontWeight.normal,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
channelName as String,
|
||||
style: StreamChatTheme.of(context).channelPreviewTheme.title,
|
||||
style: chatThemeData.channelPreviewTheme.title,
|
||||
),
|
||||
],
|
||||
],
|
||||
@@ -121,22 +119,23 @@ class MessageSearchItem extends StatelessWidget {
|
||||
text = parts.join(' ');
|
||||
}
|
||||
|
||||
final chatThemeData = StreamChatTheme.of(context);
|
||||
return Text.rich(
|
||||
_getDisplayText(
|
||||
text!,
|
||||
message.mentionedUsers,
|
||||
message.attachments,
|
||||
StreamChatTheme.of(context).channelPreviewTheme.subtitle?.copyWith(
|
||||
fontStyle: (message.isSystem || message.isDeleted)
|
||||
? FontStyle.italic
|
||||
: FontStyle.normal,
|
||||
),
|
||||
StreamChatTheme.of(context).channelPreviewTheme.subtitle?.copyWith(
|
||||
fontStyle: (message.isSystem || message.isDeleted)
|
||||
? FontStyle.italic
|
||||
: FontStyle.normal,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
chatThemeData.channelPreviewTheme.subtitle?.copyWith(
|
||||
fontStyle: (message.isSystem || message.isDeleted)
|
||||
? FontStyle.italic
|
||||
: FontStyle.normal,
|
||||
),
|
||||
chatThemeData.channelPreviewTheme.subtitle?.copyWith(
|
||||
fontStyle: (message.isSystem || message.isDeleted)
|
||||
? FontStyle.italic
|
||||
: FontStyle.normal,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stream_chat_flutter/src/info_tile.dart';
|
||||
import 'package:stream_chat_flutter/src/message_search_item.dart';
|
||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||
|
||||
/// Callback called when tapping on a user
|
||||
typedef MessageSearchItemTapCallback = void Function(GetMessageResponse);
|
||||
@@ -270,12 +270,13 @@ class _MessageSearchListViewState extends State<MessageSearchListView> {
|
||||
);
|
||||
|
||||
if (widget.showResultCount) {
|
||||
final chatThemeData = StreamChatTheme.of(context);
|
||||
child = Column(
|
||||
children: [
|
||||
Container(
|
||||
width: double.maxFinite,
|
||||
decoration: BoxDecoration(
|
||||
gradient: StreamChatTheme.of(context).colorTheme.bgGradient,
|
||||
gradient: chatThemeData.colorTheme.bgGradient,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
@@ -285,7 +286,7 @@ class _MessageSearchListViewState extends State<MessageSearchListView> {
|
||||
child: Text(
|
||||
'${items.length} results',
|
||||
style: TextStyle(
|
||||
color: StreamChatTheme.of(context).colorTheme.grey,
|
||||
color: chatThemeData.colorTheme.grey,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'package:collection/collection.dart' show IterableExtension;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||
|
||||
/// Text widget to display in message
|
||||
class MessageText extends StatelessWidget {
|
||||
@@ -31,6 +31,7 @@ class MessageText extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
final text = _replaceMentions(message.text ?? '').replaceAll('\n', '\\\n');
|
||||
|
||||
final themeData = Theme.of(context);
|
||||
return MarkdownBody(
|
||||
data: text,
|
||||
onTapLink: (
|
||||
@@ -60,14 +61,14 @@ class MessageText extends StatelessWidget {
|
||||
}
|
||||
},
|
||||
styleSheet: MarkdownStyleSheet.fromTheme(
|
||||
Theme.of(context).copyWith(
|
||||
textTheme: Theme.of(context).textTheme.apply(
|
||||
bodyColor: messageTheme.messageText?.color,
|
||||
decoration: messageTheme.messageText?.decoration,
|
||||
decorationColor: messageTheme.messageText?.decorationColor,
|
||||
decorationStyle: messageTheme.messageText?.decorationStyle,
|
||||
fontFamily: messageTheme.messageText?.fontFamily,
|
||||
),
|
||||
themeData.copyWith(
|
||||
textTheme: themeData.textTheme.apply(
|
||||
bodyColor: messageTheme.messageText?.color,
|
||||
decoration: messageTheme.messageText?.decoration,
|
||||
decorationColor: messageTheme.messageText?.decorationColor,
|
||||
decorationStyle: messageTheme.messageText?.decorationStyle,
|
||||
fontFamily: messageTheme.messageText?.fontFamily,
|
||||
),
|
||||
),
|
||||
).copyWith(
|
||||
a: messageTheme.messageLinks,
|
||||
|
||||
@@ -8,6 +8,8 @@ import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_portal/flutter_portal.dart';
|
||||
import 'package:jiffy/jiffy.dart';
|
||||
import 'package:stream_chat_flutter/src/extension.dart';
|
||||
import 'package:stream_chat_flutter/src/image_group.dart';
|
||||
import 'package:stream_chat_flutter/src/message_action.dart';
|
||||
import 'package:stream_chat_flutter/src/message_actions_modal.dart';
|
||||
import 'package:stream_chat_flutter/src/message_reactions_modal.dart';
|
||||
@@ -15,8 +17,6 @@ import 'package:stream_chat_flutter/src/quoted_message_widget.dart';
|
||||
import 'package:stream_chat_flutter/src/reaction_bubble.dart';
|
||||
import 'package:stream_chat_flutter/src/url_attachment.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
import 'package:stream_chat_flutter/src/image_group.dart';
|
||||
import 'package:stream_chat_flutter/src/extension.dart';
|
||||
|
||||
/// Widget builder for building attachments
|
||||
typedef AttachmentBuilder = Widget Function(
|
||||
@@ -109,6 +109,7 @@ class MessageWidget extends StatefulWidget {
|
||||
borderRadius: attachmentBorderRadiusGeometry ?? BorderRadius.zero,
|
||||
);
|
||||
|
||||
final mediaQueryData = MediaQuery.of(context);
|
||||
if (attachments.length > 1) {
|
||||
return Padding(
|
||||
padding: attachmentPadding,
|
||||
@@ -118,8 +119,8 @@ class MessageWidget extends StatefulWidget {
|
||||
color: messageTheme.messageBackgroundColor,
|
||||
child: ImageGroup(
|
||||
size: Size(
|
||||
MediaQuery.of(context).size.width * 0.8,
|
||||
MediaQuery.of(context).size.height * 0.3,
|
||||
mediaQueryData.size.width * 0.8,
|
||||
mediaQueryData.size.height * 0.3,
|
||||
),
|
||||
images: attachments,
|
||||
message: message,
|
||||
@@ -142,8 +143,8 @@ class MessageWidget extends StatefulWidget {
|
||||
message: message,
|
||||
messageTheme: messageTheme,
|
||||
size: Size(
|
||||
MediaQuery.of(context).size.width * 0.8,
|
||||
MediaQuery.of(context).size.height * 0.3,
|
||||
mediaQueryData.size.width * 0.8,
|
||||
mediaQueryData.size.height * 0.3,
|
||||
),
|
||||
onShowMessage: onShowMessage,
|
||||
onReturnAction: onReturnAction,
|
||||
@@ -167,24 +168,25 @@ class MessageWidget extends StatefulWidget {
|
||||
return wrapAttachmentWidget(
|
||||
context,
|
||||
Column(
|
||||
children: attachments
|
||||
.map((attachment) => VideoAttachment(
|
||||
attachment: attachment,
|
||||
messageTheme: messageTheme,
|
||||
size: Size(
|
||||
MediaQuery.of(context).size.width * 0.8,
|
||||
MediaQuery.of(context).size.height * 0.3,
|
||||
),
|
||||
message: message,
|
||||
onShowMessage: onShowMessage,
|
||||
onReturnAction: onReturnAction,
|
||||
onAttachmentTap: onAttachmentTap != null
|
||||
? () {
|
||||
onAttachmentTap(message, attachment);
|
||||
}
|
||||
: null,
|
||||
))
|
||||
.toList(),
|
||||
children: attachments.map((attachment) {
|
||||
final mediaQueryData = MediaQuery.of(context);
|
||||
return VideoAttachment(
|
||||
attachment: attachment,
|
||||
messageTheme: messageTheme,
|
||||
size: Size(
|
||||
mediaQueryData.size.width * 0.8,
|
||||
mediaQueryData.size.height * 0.3,
|
||||
),
|
||||
message: message,
|
||||
onShowMessage: onShowMessage,
|
||||
onReturnAction: onReturnAction,
|
||||
onAttachmentTap: onAttachmentTap != null
|
||||
? () {
|
||||
onAttachmentTap(message, attachment);
|
||||
}
|
||||
: null,
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
border,
|
||||
reverse,
|
||||
@@ -200,18 +202,19 @@ class MessageWidget extends StatefulWidget {
|
||||
return wrapAttachmentWidget(
|
||||
context,
|
||||
Column(
|
||||
children: attachments
|
||||
.map((attachment) => GiphyAttachment(
|
||||
attachment: attachment,
|
||||
message: message,
|
||||
size: Size(
|
||||
MediaQuery.of(context).size.width * 0.8,
|
||||
MediaQuery.of(context).size.height * 0.3,
|
||||
),
|
||||
onShowMessage: onShowMessage,
|
||||
onReturnAction: onReturnAction,
|
||||
))
|
||||
.toList(),
|
||||
children: attachments.map((attachment) {
|
||||
final mediaQueryData = MediaQuery.of(context);
|
||||
return GiphyAttachment(
|
||||
attachment: attachment,
|
||||
message: message,
|
||||
size: Size(
|
||||
mediaQueryData.size.width * 0.8,
|
||||
mediaQueryData.size.height * 0.3,
|
||||
),
|
||||
onShowMessage: onShowMessage,
|
||||
onReturnAction: onReturnAction,
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
border,
|
||||
reverse,
|
||||
@@ -230,21 +233,24 @@ class MessageWidget extends StatefulWidget {
|
||||
|
||||
return Column(
|
||||
children: attachments
|
||||
.map<Widget>((attachment) => wrapAttachmentWidget(
|
||||
context,
|
||||
FileAttachment(
|
||||
message: message,
|
||||
attachment: attachment,
|
||||
size: Size(
|
||||
MediaQuery.of(context).size.width * 0.8,
|
||||
MediaQuery.of(context).size.height * 0.3,
|
||||
),
|
||||
.map<Widget>((attachment) {
|
||||
final mediaQueryData = MediaQuery.of(context);
|
||||
return wrapAttachmentWidget(
|
||||
context,
|
||||
FileAttachment(
|
||||
message: message,
|
||||
attachment: attachment,
|
||||
size: Size(
|
||||
mediaQueryData.size.width * 0.8,
|
||||
mediaQueryData.size.height * 0.3,
|
||||
),
|
||||
border,
|
||||
reverse,
|
||||
attachmentBorderRadiusGeometry as BorderRadius? ??
|
||||
BorderRadius.zero,
|
||||
))
|
||||
),
|
||||
border,
|
||||
reverse,
|
||||
attachmentBorderRadiusGeometry as BorderRadius? ??
|
||||
BorderRadius.zero,
|
||||
);
|
||||
})
|
||||
.insertBetween(SizedBox(
|
||||
height: attachmentPadding.vertical / 2,
|
||||
))
|
||||
@@ -658,12 +664,13 @@ class _MessageWidgetState extends State<MessageWidget>
|
||||
widget.onQuotedMessageTap != null
|
||||
? () => widget.onQuotedMessageTap!(widget.message.quotedMessageId)
|
||||
: null;
|
||||
final chatThemeData = StreamChatTheme.of(context);
|
||||
return QuotedMessageWidget(
|
||||
onTap: onTap,
|
||||
message: widget.message.quotedMessage!,
|
||||
messageTheme: isMyMessage
|
||||
? StreamChatTheme.of(context).otherMessageTheme
|
||||
: StreamChatTheme.of(context).ownMessageTheme,
|
||||
? chatThemeData.otherMessageTheme
|
||||
: chatThemeData.ownMessageTheme,
|
||||
reverse: widget.reverse,
|
||||
padding: EdgeInsets.only(
|
||||
right: 8, left: 8, top: 8, bottom: hasNonUrlAttachments ? 8 : 0),
|
||||
@@ -672,6 +679,7 @@ class _MessageWidgetState extends State<MessageWidget>
|
||||
|
||||
Widget get _bottomRow {
|
||||
if (isDeleted) {
|
||||
final chatThemeData = StreamChatTheme.of(context);
|
||||
return Transform(
|
||||
transform: Matrix4.rotationY(widget.reverse ? pi : 0),
|
||||
alignment: Alignment.center,
|
||||
@@ -679,16 +687,14 @@ class _MessageWidgetState extends State<MessageWidget>
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
StreamSvgIcon.eye(
|
||||
color: StreamChatTheme.of(context).colorTheme.grey,
|
||||
color: chatThemeData.colorTheme.grey,
|
||||
size: 16,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'Only visible to you',
|
||||
style: StreamChatTheme.of(context)
|
||||
.textTheme
|
||||
.footnote
|
||||
.copyWith(color: StreamChatTheme.of(context).colorTheme.grey),
|
||||
style: chatThemeData.textTheme.footnote
|
||||
.copyWith(color: chatThemeData.colorTheme.grey),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -41,56 +41,55 @@ class OptionListTile extends StatelessWidget {
|
||||
final TextStyle? titleTextStyle;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Column(
|
||||
children: [
|
||||
Container(
|
||||
color: separatorColor ??
|
||||
StreamChatTheme.of(context).colorTheme.greyGainsboro,
|
||||
height: 1,
|
||||
),
|
||||
Material(
|
||||
color: tileColor ?? StreamChatTheme.of(context).colorTheme.white,
|
||||
child: SizedBox(
|
||||
height: 63,
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
child: Row(
|
||||
children: [
|
||||
if (leading != null) Center(child: leading),
|
||||
if (leading == null)
|
||||
const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
Expanded(
|
||||
flex: 4,
|
||||
child: Text(
|
||||
title!,
|
||||
style: titleTextStyle ??
|
||||
(titleColor == null
|
||||
? StreamChatTheme.of(context).textTheme.bodyBold
|
||||
: StreamChatTheme.of(context)
|
||||
.textTheme
|
||||
.bodyBold
|
||||
.copyWith(
|
||||
color: titleColor,
|
||||
)),
|
||||
Widget build(BuildContext context) {
|
||||
final chatThemeData = StreamChatTheme.of(context);
|
||||
return Column(
|
||||
children: [
|
||||
Container(
|
||||
color: separatorColor ?? chatThemeData.colorTheme.greyGainsboro,
|
||||
height: 1,
|
||||
),
|
||||
Material(
|
||||
color: tileColor ?? chatThemeData.colorTheme.white,
|
||||
child: SizedBox(
|
||||
height: 63,
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
child: Row(
|
||||
children: [
|
||||
if (leading != null) Center(child: leading),
|
||||
if (leading == null)
|
||||
const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
Expanded(
|
||||
flex: 4,
|
||||
child: Text(
|
||||
title!,
|
||||
style: titleTextStyle ??
|
||||
(titleColor == null
|
||||
? chatThemeData.textTheme.bodyBold
|
||||
: chatThemeData.textTheme.bodyBold.copyWith(
|
||||
color: titleColor,
|
||||
)),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(right: 16),
|
||||
child: Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: trailing ?? Container(),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(right: 16),
|
||||
child: Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: trailing ?? Container(),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,6 +126,8 @@ class ReactionBubble extends StatelessWidget {
|
||||
(r) => r.type == reaction.type,
|
||||
);
|
||||
|
||||
final chatThemeData = StreamChatTheme.of(context);
|
||||
final userId = StreamChat.of(context).user?.id;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 4,
|
||||
@@ -135,24 +137,16 @@ class ReactionBubble extends StatelessWidget {
|
||||
assetName: reactionIcon.assetName,
|
||||
width: 16,
|
||||
height: 16,
|
||||
color: (!highlightOwnReactions ||
|
||||
reaction.user?.id == StreamChat.of(context).user?.id)
|
||||
? StreamChatTheme.of(context).colorTheme.accentBlue
|
||||
: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(.5),
|
||||
color: (!highlightOwnReactions || reaction.user?.id == userId)
|
||||
? chatThemeData.colorTheme.accentBlue
|
||||
: chatThemeData.colorTheme.black.withOpacity(.5),
|
||||
)
|
||||
: Icon(
|
||||
Icons.help_outline_rounded,
|
||||
size: 16,
|
||||
color: (!highlightOwnReactions ||
|
||||
reaction.user?.id == StreamChat.of(context).user?.id)
|
||||
? StreamChatTheme.of(context).colorTheme.accentBlue
|
||||
: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(.5),
|
||||
color: (!highlightOwnReactions || reaction.user?.id == userId)
|
||||
? chatThemeData.colorTheme.accentBlue
|
||||
: chatThemeData.colorTheme.black.withOpacity(.5),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@ import 'dart:math';
|
||||
|
||||
import 'package:ezanimation/ezanimation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
||||
import 'package:stream_chat_flutter/src/extension.dart';
|
||||
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
/// 
|
||||
@@ -33,7 +33,8 @@ class _ReactionPickerState extends State<ReactionPicker>
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final reactionIcons = StreamChatTheme.of(context).reactionIcons;
|
||||
final chatThemeData = StreamChatTheme.of(context);
|
||||
final reactionIcons = chatThemeData.reactionIcons;
|
||||
|
||||
if (animations.isEmpty && reactionIcons.isNotEmpty) {
|
||||
reactionIcons.forEach((element) {
|
||||
@@ -57,7 +58,7 @@ class _ReactionPickerState extends State<ReactionPicker>
|
||||
scale: val,
|
||||
child: Material(
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
color: chatThemeData.colorTheme.white,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
@@ -119,9 +120,8 @@ class _ReactionPickerState extends State<ReactionPicker>
|
||||
animations[index].value * 24.0,
|
||||
),
|
||||
color: ownReactionIndex != -1
|
||||
? StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.accentBlue
|
||||
? chatThemeData
|
||||
.colorTheme.accentBlue
|
||||
: Theme.of(context)
|
||||
.iconTheme
|
||||
.color!
|
||||
|
||||
@@ -97,65 +97,60 @@ class ThreadHeader extends StatelessWidget implements PreferredSizeWidget {
|
||||
final List<Widget>? actions;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => AppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
brightness: Theme.of(context).brightness,
|
||||
elevation: 1,
|
||||
leading: leading ??
|
||||
(showBackButton
|
||||
? StreamBackButton(
|
||||
cid: StreamChannel.of(context).channel.cid,
|
||||
onPressed: onBackPressed,
|
||||
showUnreads: true,
|
||||
)
|
||||
: const SizedBox()),
|
||||
backgroundColor:
|
||||
StreamChatTheme.of(context).channelTheme.channelHeaderTheme.color,
|
||||
centerTitle: true,
|
||||
actions: actions,
|
||||
title: InkWell(
|
||||
onTap: onTitleTap,
|
||||
child: SizedBox(
|
||||
height: preferredSize.height,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
title ??
|
||||
Text(
|
||||
'Thread Reply',
|
||||
style: StreamChatTheme.of(context)
|
||||
.channelTheme
|
||||
.channelHeaderTheme
|
||||
.title,
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
subtitle ??
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'with ',
|
||||
style: StreamChatTheme.of(context)
|
||||
.channelTheme
|
||||
.channelHeaderTheme
|
||||
.subtitle,
|
||||
Widget build(BuildContext context) {
|
||||
final chatThemeData = StreamChatTheme.of(context);
|
||||
return AppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
brightness: Theme.of(context).brightness,
|
||||
elevation: 1,
|
||||
leading: leading ??
|
||||
(showBackButton
|
||||
? StreamBackButton(
|
||||
cid: StreamChannel.of(context).channel.cid,
|
||||
onPressed: onBackPressed,
|
||||
showUnreads: true,
|
||||
)
|
||||
: const SizedBox()),
|
||||
backgroundColor: chatThemeData.channelTheme.channelHeaderTheme.color,
|
||||
centerTitle: true,
|
||||
actions: actions,
|
||||
title: InkWell(
|
||||
onTap: onTitleTap,
|
||||
child: SizedBox(
|
||||
height: preferredSize.height,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
title ??
|
||||
Text(
|
||||
'Thread Reply',
|
||||
style: chatThemeData.channelTheme.channelHeaderTheme.title,
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
subtitle ??
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'with ',
|
||||
style: chatThemeData
|
||||
.channelTheme.channelHeaderTheme.subtitle,
|
||||
),
|
||||
Flexible(
|
||||
child: ChannelName(
|
||||
textStyle: chatThemeData
|
||||
.channelTheme.channelHeaderTheme.subtitle,
|
||||
),
|
||||
Flexible(
|
||||
child: ChannelName(
|
||||
textStyle: StreamChatTheme.of(context)
|
||||
.channelTheme
|
||||
.channelHeaderTheme
|
||||
.subtitle,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
final Size preferredSize;
|
||||
|
||||
@@ -26,91 +26,84 @@ class UrlAttachment extends StatelessWidget {
|
||||
final EdgeInsets textPadding;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => GestureDetector(
|
||||
onTap: () {
|
||||
launchURL(
|
||||
context,
|
||||
urlAttachment.ogScrapeUrl,
|
||||
);
|
||||
},
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
if (urlAttachment.imageUrl != null)
|
||||
Container(
|
||||
clipBehavior: Clip.antiAliasWithSaveLayer,
|
||||
margin: const EdgeInsets.symmetric(horizontal: 8),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Stack(
|
||||
children: [
|
||||
CachedNetworkImage(
|
||||
width: double.infinity,
|
||||
imageUrl: urlAttachment.imageUrl!,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
Positioned(
|
||||
left: 0,
|
||||
bottom: -1,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.only(
|
||||
topRight: Radius.circular(16),
|
||||
),
|
||||
color:
|
||||
StreamChatTheme.of(context).colorTheme.blueAlice,
|
||||
Widget build(BuildContext context) {
|
||||
final chatThemeData = StreamChatTheme.of(context);
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
launchURL(
|
||||
context,
|
||||
urlAttachment.ogScrapeUrl,
|
||||
);
|
||||
},
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
if (urlAttachment.imageUrl != null)
|
||||
Container(
|
||||
clipBehavior: Clip.antiAliasWithSaveLayer,
|
||||
margin: const EdgeInsets.symmetric(horizontal: 8),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Stack(
|
||||
children: [
|
||||
CachedNetworkImage(
|
||||
width: double.infinity,
|
||||
imageUrl: urlAttachment.imageUrl!,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
Positioned(
|
||||
left: 0,
|
||||
bottom: -1,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.only(
|
||||
topRight: Radius.circular(16),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 8,
|
||||
left: 8,
|
||||
right: 8,
|
||||
),
|
||||
child: Text(
|
||||
hostDisplayName,
|
||||
style: StreamChatTheme.of(context)
|
||||
.textTheme
|
||||
.bodyBold
|
||||
.copyWith(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.accentBlue,
|
||||
),
|
||||
color: chatThemeData.colorTheme.blueAlice,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 8,
|
||||
left: 8,
|
||||
right: 8,
|
||||
),
|
||||
child: Text(
|
||||
hostDisplayName,
|
||||
style: chatThemeData.textTheme.bodyBold.copyWith(
|
||||
color: chatThemeData.colorTheme.accentBlue,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: textPadding,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (urlAttachment.title != null)
|
||||
Text(
|
||||
urlAttachment.title!.trim(),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: StreamChatTheme.of(context)
|
||||
.textTheme
|
||||
.body
|
||||
.copyWith(fontWeight: FontWeight.w700),
|
||||
),
|
||||
if (urlAttachment.text != null)
|
||||
Text(
|
||||
urlAttachment.text!,
|
||||
style: StreamChatTheme.of(context)
|
||||
.textTheme
|
||||
.body
|
||||
.copyWith(fontWeight: FontWeight.w400),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
Padding(
|
||||
padding: textPadding,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (urlAttachment.title != null)
|
||||
Text(
|
||||
urlAttachment.title!.trim(),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: chatThemeData.textTheme.body
|
||||
.copyWith(fontWeight: FontWeight.w700),
|
||||
),
|
||||
if (urlAttachment.text != null)
|
||||
Text(
|
||||
urlAttachment.text!,
|
||||
style: chatThemeData.textTheme.body
|
||||
.copyWith(fontWeight: FontWeight.w400),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||
|
||||
/// Widget that displays a user avatar
|
||||
class UserAvatar extends StatelessWidget {
|
||||
@@ -95,8 +95,7 @@ class UserAvatar extends StatelessWidget {
|
||||
child: Container(
|
||||
constraints: constraints ??
|
||||
streamChatTheme.ownMessageTheme.avatarTheme?.constraints,
|
||||
color: selectionColor ??
|
||||
StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||
color: selectionColor ?? streamChatTheme.colorTheme.accentBlue,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(selectionThickness),
|
||||
child: avatar,
|
||||
|
||||
@@ -48,47 +48,52 @@ class UserItem extends StatelessWidget {
|
||||
final bool showLastOnline;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => ListTile(
|
||||
onTap: () {
|
||||
if (onTap != null) {
|
||||
onTap!(user);
|
||||
Widget build(BuildContext context) {
|
||||
final chatThemeData = StreamChatTheme.of(context);
|
||||
return ListTile(
|
||||
onTap: () {
|
||||
if (onTap != null) {
|
||||
onTap!(user);
|
||||
}
|
||||
},
|
||||
onLongPress: () {
|
||||
if (onLongPress != null) {
|
||||
onLongPress!(user);
|
||||
}
|
||||
},
|
||||
leading: UserAvatar(
|
||||
user: user,
|
||||
onTap: (user) {
|
||||
if (onImageTap != null) {
|
||||
onImageTap!(user);
|
||||
}
|
||||
},
|
||||
onLongPress: () {
|
||||
if (onLongPress != null) {
|
||||
onLongPress!(user);
|
||||
}
|
||||
},
|
||||
leading: UserAvatar(
|
||||
user: user,
|
||||
onTap: (user) {
|
||||
if (onImageTap != null) {
|
||||
onImageTap!(user);
|
||||
}
|
||||
},
|
||||
constraints: const BoxConstraints.tightFor(
|
||||
height: 40,
|
||||
width: 40,
|
||||
),
|
||||
constraints: const BoxConstraints.tightFor(
|
||||
height: 40,
|
||||
width: 40,
|
||||
),
|
||||
trailing: selected
|
||||
? StreamSvgIcon.checkSend(
|
||||
color: StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||
)
|
||||
: null,
|
||||
title: Text(
|
||||
user.name,
|
||||
style: StreamChatTheme.of(context).textTheme.bodyBold,
|
||||
),
|
||||
subtitle: showLastOnline ? _buildLastActive(context) : null,
|
||||
);
|
||||
),
|
||||
trailing: selected
|
||||
? StreamSvgIcon.checkSend(
|
||||
color: chatThemeData.colorTheme.accentBlue,
|
||||
)
|
||||
: null,
|
||||
title: Text(
|
||||
user.name,
|
||||
style: chatThemeData.textTheme.bodyBold,
|
||||
),
|
||||
subtitle: showLastOnline ? _buildLastActive(context) : null,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLastActive(context) => Text(
|
||||
user.online == true
|
||||
? 'Online'
|
||||
: 'Last online ${Jiffy(user.lastActive).fromNow()}',
|
||||
style: StreamChatTheme.of(context).textTheme.footnote.copyWith(
|
||||
color:
|
||||
StreamChatTheme.of(context).colorTheme.black.withOpacity(.5)),
|
||||
);
|
||||
Widget _buildLastActive(context) {
|
||||
final chatTheme = StreamChatTheme.of(context);
|
||||
return Text(
|
||||
user.online == true
|
||||
? 'Online'
|
||||
: 'Last online ${Jiffy(user.lastActive).fromNow()}',
|
||||
style: chatTheme.textTheme.footnote
|
||||
.copyWith(color: chatTheme.colorTheme.black.withOpacity(.5)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,21 +295,24 @@ class _UserListViewState extends State<UserListView>
|
||||
if (i < items.length) {
|
||||
final item = items[i];
|
||||
return item.when(
|
||||
headerItem: (header) => Container(
|
||||
key: ValueKey<String>('HEADER-$header'),
|
||||
color: StreamChatTheme.of(context).colorTheme.black.withOpacity(0.05),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
|
||||
child: Text(
|
||||
header,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 14.5,
|
||||
color: StreamChatTheme.of(context).colorTheme.grey,
|
||||
headerItem: (header) {
|
||||
final chatThemeData = StreamChatTheme.of(context);
|
||||
return Container(
|
||||
key: ValueKey<String>('HEADER-$header'),
|
||||
color: chatThemeData.colorTheme.black.withOpacity(0.05),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
|
||||
child: Text(
|
||||
header,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 14.5,
|
||||
color: chatThemeData.colorTheme.grey,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
userItem: (user) {
|
||||
final selected = widget.selectedUsers?.contains(user) ?? false;
|
||||
return Container(
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
/// Launch URL
|
||||
Future<void> launchURL(BuildContext context, String? url) async {
|
||||
@@ -27,89 +27,81 @@ Future<bool?> showConfirmationDialog(
|
||||
Widget? icon,
|
||||
String? question,
|
||||
String? cancelText,
|
||||
}) =>
|
||||
showModalBottomSheet(
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.white,
|
||||
context: context,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(16),
|
||||
topRight: Radius.circular(16),
|
||||
)),
|
||||
builder: (context) {
|
||||
final effect = StreamChatTheme.of(context).colorTheme.borderTop;
|
||||
return SafeArea(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const SizedBox(height: 26),
|
||||
if (icon != null) icon,
|
||||
const SizedBox(height: 26),
|
||||
}) {
|
||||
final chatThemeData = StreamChatTheme.of(context);
|
||||
return showModalBottomSheet(
|
||||
backgroundColor: chatThemeData.colorTheme.white,
|
||||
context: context,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(16),
|
||||
topRight: Radius.circular(16),
|
||||
)),
|
||||
builder: (context) {
|
||||
final effect = chatThemeData.colorTheme.borderTop;
|
||||
return SafeArea(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const SizedBox(height: 26),
|
||||
if (icon != null) icon,
|
||||
const SizedBox(height: 26),
|
||||
Text(
|
||||
title,
|
||||
style: chatThemeData.textTheme.headlineBold,
|
||||
),
|
||||
const SizedBox(height: 7),
|
||||
if (question != null)
|
||||
Text(
|
||||
title,
|
||||
style: StreamChatTheme.of(context).textTheme.headlineBold,
|
||||
question,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 7),
|
||||
if (question != null)
|
||||
Text(
|
||||
question,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 36),
|
||||
Container(
|
||||
color: effect.color!.withOpacity(effect.alpha ?? 1),
|
||||
height: 1,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
if (cancelText != null)
|
||||
Flexible(
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
child: TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(false);
|
||||
},
|
||||
child: Text(
|
||||
cancelText,
|
||||
style: StreamChatTheme.of(context)
|
||||
.textTheme
|
||||
.bodyBold
|
||||
.copyWith(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.5)),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 36),
|
||||
Container(
|
||||
color: effect.color!.withOpacity(effect.alpha ?? 1),
|
||||
height: 1,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
if (cancelText != null)
|
||||
Flexible(
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
child: TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context, true);
|
||||
Navigator.of(context).pop(false);
|
||||
},
|
||||
child: Text(
|
||||
okText,
|
||||
style: StreamChatTheme.of(context)
|
||||
.textTheme
|
||||
.bodyBold
|
||||
.copyWith(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.accentRed),
|
||||
cancelText,
|
||||
style: chatThemeData.textTheme.bodyBold.copyWith(
|
||||
color: chatThemeData.colorTheme.black
|
||||
.withOpacity(0.5)),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
});
|
||||
Flexible(
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
child: TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context, true);
|
||||
},
|
||||
child: Text(
|
||||
okText,
|
||||
style: chatThemeData.textTheme.bodyBold.copyWith(
|
||||
color: chatThemeData.colorTheme.accentRed),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/// Shows info dialog
|
||||
Future<bool?> showInfoDialog(
|
||||
@@ -119,63 +111,64 @@ Future<bool?> showInfoDialog(
|
||||
Widget? icon,
|
||||
String? details,
|
||||
StreamChatThemeData? theme,
|
||||
}) =>
|
||||
showModalBottomSheet(
|
||||
backgroundColor: theme?.colorTheme.white ??
|
||||
StreamChatTheme.of(context).colorTheme.white,
|
||||
context: context,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(16),
|
||||
topRight: Radius.circular(16),
|
||||
)),
|
||||
builder: (context) => SafeArea(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 26,
|
||||
),
|
||||
if (icon != null) icon,
|
||||
const SizedBox(
|
||||
height: 26,
|
||||
),
|
||||
Text(
|
||||
title,
|
||||
style: theme?.textTheme.headlineBold ??
|
||||
StreamChatTheme.of(context).textTheme.headlineBold,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 7,
|
||||
),
|
||||
if (details != null) Text(details),
|
||||
const SizedBox(
|
||||
height: 36,
|
||||
),
|
||||
Container(
|
||||
color: theme?.colorTheme.black.withOpacity(.08) ??
|
||||
StreamChatTheme.of(context).colorTheme.black.withOpacity(.08),
|
||||
height: 1,
|
||||
),
|
||||
Center(
|
||||
child: TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: Text(
|
||||
okText,
|
||||
style: TextStyle(
|
||||
color: theme?.colorTheme.black.withOpacity(0.5) ??
|
||||
StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
}) {
|
||||
final chatThemeData = StreamChatTheme.of(context);
|
||||
return showModalBottomSheet(
|
||||
backgroundColor: theme?.colorTheme.white ?? chatThemeData.colorTheme.white,
|
||||
context: context,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(16),
|
||||
topRight: Radius.circular(16),
|
||||
)),
|
||||
builder: (context) => SafeArea(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 26,
|
||||
),
|
||||
if (icon != null) icon,
|
||||
const SizedBox(
|
||||
height: 26,
|
||||
),
|
||||
Text(
|
||||
title,
|
||||
style: theme?.textTheme.headlineBold ??
|
||||
chatThemeData.textTheme.headlineBold,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 7,
|
||||
),
|
||||
if (details != null) Text(details),
|
||||
const SizedBox(
|
||||
height: 36,
|
||||
),
|
||||
Container(
|
||||
color: theme?.colorTheme.black.withOpacity(.08) ??
|
||||
chatThemeData.colorTheme.black.withOpacity(.08),
|
||||
height: 1,
|
||||
),
|
||||
Center(
|
||||
child: TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: Text(
|
||||
okText,
|
||||
style: TextStyle(
|
||||
color: theme?.colorTheme.black.withOpacity(0.5) ??
|
||||
chatThemeData.colorTheme.accentBlue,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Get random png with initials
|
||||
String getRandomPicUrl(User user) =>
|
||||
|
||||
Reference in New Issue
Block a user