migrate missing files

This commit is contained in:
Salvatore Giordano
2021-05-10 16:01:20 +02:00
parent 43d5e9063e
commit 435a88cd1b
25 changed files with 1292 additions and 1336 deletions
@@ -44,17 +44,18 @@ class DateDivider extends StatelessWidget {
if (uppercase) dayInfo = dayInfo.toUpperCase(); if (uppercase) dayInfo = dayInfo.toUpperCase();
final chatThemeData = StreamChatTheme.of(context);
return Center( return Center(
child: Container( child: Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 1), padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 1),
decoration: BoxDecoration( decoration: BoxDecoration(
color: StreamChatTheme.of(context).colorTheme.overlayDark, color: chatThemeData.colorTheme.overlayDark,
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
), ),
child: Text( child: Text(
dayInfo, dayInfo,
style: StreamChatTheme.of(context).textTheme.footnote.copyWith( style: chatThemeData.textTheme.footnote.copyWith(
color: StreamChatTheme.of(context).colorTheme.white, color: chatThemeData.colorTheme.white,
), ),
), ),
), ),
@@ -31,7 +31,9 @@ class DeletedMessage extends StatelessWidget {
final bool reverse; final bool reverse;
@override @override
Widget build(BuildContext context) => Transform( Widget build(BuildContext context) {
final chatThemeData = StreamChatTheme.of(context);
return Transform(
transform: Matrix4.rotationY(reverse ? pi : 0), transform: Matrix4.rotationY(reverse ? pi : 0),
alignment: Alignment.center, alignment: Alignment.center,
child: Material( child: Material(
@@ -42,14 +44,8 @@ class DeletedMessage extends StatelessWidget {
side: borderSide ?? side: borderSide ??
BorderSide( BorderSide(
color: Theme.of(context).brightness == Brightness.dark color: Theme.of(context).brightness == Brightness.dark
? StreamChatTheme.of(context) ? chatThemeData.colorTheme.white.withAlpha(24)
.colorTheme : chatThemeData.colorTheme.black.withAlpha(24),
.white
.withAlpha(24)
: StreamChatTheme.of(context)
.colorTheme
.black
.withAlpha(24),
), ),
), ),
child: Padding( child: Padding(
@@ -72,3 +68,4 @@ class DeletedMessage extends StatelessWidget {
), ),
); );
} }
}
@@ -46,18 +46,12 @@ class GroupImage extends StatelessWidget {
onTap: onTap, onTap: onTap,
child: ClipRRect( child: ClipRRect(
borderRadius: borderRadius ?? borderRadius: borderRadius ??
StreamChatTheme.of(context) streamChatTheme.ownMessageTheme.avatarTheme?.borderRadius,
.ownMessageTheme
.avatarTheme
?.borderRadius,
child: Container( child: Container(
constraints: constraints ?? constraints: constraints ??
StreamChatTheme.of(context) streamChatTheme.ownMessageTheme.avatarTheme?.constraints,
.ownMessageTheme
.avatarTheme
?.constraints,
decoration: BoxDecoration( decoration: BoxDecoration(
color: StreamChatTheme.of(context).colorTheme.accentBlue, color: streamChatTheme.colorTheme.accentBlue,
), ),
child: Flex( child: Flex(
direction: Axis.vertical, direction: Axis.vertical,
@@ -125,8 +119,7 @@ class GroupImage extends StatelessWidget {
BorderRadius.zero) + BorderRadius.zero) +
BorderRadius.circular(selectionThickness), BorderRadius.circular(selectionThickness),
child: Container( child: Container(
color: selectionColor ?? color: selectionColor ?? streamChatTheme.colorTheme.accentBlue,
StreamChatTheme.of(context).colorTheme.accentBlue,
height: 64, height: 64,
width: 64, width: 64,
child: Padding( child: Padding(
@@ -77,16 +77,18 @@ class _ImageFooterState extends State<ImageFooter> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final showShareButton = !kIsWeb; final showShareButton = !kIsWeb;
final mediaQueryData = MediaQuery.of(context);
final chatThemeData = StreamChatTheme.of(context);
return SizedBox.fromSize( return SizedBox.fromSize(
size: Size( size: Size(
MediaQuery.of(context).size.width, mediaQueryData.size.width,
MediaQuery.of(context).padding.bottom + widget.preferredSize.height, mediaQueryData.padding.bottom + widget.preferredSize.height,
), ),
child: MediaQuery.removePadding( child: MediaQuery.removePadding(
context: context, context: context,
removeTop: true, removeTop: true,
child: BottomAppBar( child: BottomAppBar(
color: StreamChatTheme.of(context).colorTheme.white, color: chatThemeData.colorTheme.white,
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
@@ -98,7 +100,7 @@ class _ImageFooterState extends State<ImageFooter> {
IconButton( IconButton(
icon: StreamSvgIcon.iconShare( icon: StreamSvgIcon.iconShare(
size: 24, size: 24,
color: StreamChatTheme.of(context).colorTheme.black, color: chatThemeData.colorTheme.black,
), ),
onPressed: () async { onPressed: () async {
final attachment = final attachment =
@@ -134,8 +136,7 @@ class _ImageFooterState extends State<ImageFooter> {
children: <Widget>[ children: <Widget>[
Text( Text(
'${widget.currentPage + 1} of ${widget.totalPages}', '${widget.currentPage + 1} of ${widget.totalPages}',
style: style: chatThemeData.textTheme.headlineBold,
StreamChatTheme.of(context).textTheme.headlineBold,
), ),
], ],
), ),
@@ -143,7 +144,7 @@ class _ImageFooterState extends State<ImageFooter> {
), ),
IconButton( IconButton(
icon: StreamSvgIcon.iconGrid( icon: StreamSvgIcon.iconGrid(
color: StreamChatTheme.of(context).colorTheme.black, color: chatThemeData.colorTheme.black,
), ),
onPressed: () => _showPhotosModal(context), onPressed: () => _showPhotosModal(context),
), ),
@@ -155,10 +156,11 @@ class _ImageFooterState extends State<ImageFooter> {
} }
void _showPhotosModal(context) { void _showPhotosModal(context) {
final chatThemeData = StreamChatTheme.of(context);
showModalBottomSheet( showModalBottomSheet(
context: context, context: context,
barrierColor: StreamChatTheme.of(context).colorTheme.overlay, barrierColor: chatThemeData.colorTheme.overlay,
backgroundColor: StreamChatTheme.of(context).colorTheme.white, backgroundColor: chatThemeData.colorTheme.white,
isScrollControlled: true, isScrollControlled: true,
shape: const RoundedRectangleBorder( shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only( borderRadius: BorderRadius.only(
@@ -189,9 +191,7 @@ class _ImageFooterState extends State<ImageFooter> {
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
child: Text( child: Text(
'Photos', 'Photos',
style: StreamChatTheme.of(context) style: chatThemeData.textTheme.headlineBold,
.textTheme
.headlineBold,
), ),
), ),
), ),
@@ -199,7 +199,7 @@ class _ImageFooterState extends State<ImageFooter> {
alignment: Alignment.centerRight, alignment: Alignment.centerRight,
child: IconButton( child: IconButton(
icon: StreamSvgIcon.close( icon: StreamSvgIcon.close(
color: StreamChatTheme.of(context).colorTheme.black, color: chatThemeData.colorTheme.black,
), ),
onPressed: () => Navigator.maybePop(context), onPressed: () => Navigator.maybePop(context),
), ),
@@ -262,9 +262,7 @@ class _ImageFooterState extends State<ImageFooter> {
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
blurRadius: 8, blurRadius: 8,
color: StreamChatTheme.of(context) color: chatThemeData.colorTheme.black
.colorTheme
.black
.withOpacity(0.3), .withOpacity(0.3),
), ),
], ],
@@ -50,25 +50,26 @@ class ImageHeader extends StatelessWidget implements PreferredSizeWidget {
final int currentIndex; final int currentIndex;
@override @override
Widget build(BuildContext context) => AppBar( Widget build(BuildContext context) {
final chatThemeData = StreamChatTheme.of(context);
return AppBar(
brightness: Theme.of(context).brightness, brightness: Theme.of(context).brightness,
elevation: 1, elevation: 1,
leading: showBackButton leading: showBackButton
? IconButton( ? IconButton(
icon: StreamSvgIcon.close( icon: StreamSvgIcon.close(
color: StreamChatTheme.of(context).colorTheme.black, color: chatThemeData.colorTheme.black,
size: 24, size: 24,
), ),
onPressed: onBackPressed, onPressed: onBackPressed,
) )
: const SizedBox(), : const SizedBox(),
backgroundColor: backgroundColor: chatThemeData.channelTheme.channelHeaderTheme.color,
StreamChatTheme.of(context).channelTheme.channelHeaderTheme.color,
actions: <Widget>[ actions: <Widget>[
if (message.type != 'ephemeral') if (message.type != 'ephemeral')
IconButton( IconButton(
icon: StreamSvgIcon.iconMenuPoint( icon: StreamSvgIcon.iconMenuPoint(
color: StreamChatTheme.of(context).colorTheme.black, color: chatThemeData.colorTheme.black,
), ),
onPressed: () { onPressed: () {
_showMessageActionModalBottomSheet(context); _showMessageActionModalBottomSheet(context);
@@ -88,14 +89,11 @@ class ImageHeader extends StatelessWidget implements PreferredSizeWidget {
children: <Widget>[ children: <Widget>[
Text( Text(
userName, userName,
style: style: chatThemeData.textTheme.headlineBold,
StreamChatTheme.of(context).textTheme.headlineBold,
), ),
Text( Text(
sentAt, sentAt,
style: StreamChatTheme.of(context) style: chatThemeData.channelPreviewTheme.subtitle,
.channelPreviewTheme
.subtitle,
), ),
], ],
), ),
@@ -103,6 +101,7 @@ class ImageHeader extends StatelessWidget implements PreferredSizeWidget {
) )
: const SizedBox(), : const SizedBox(),
); );
}
@override @override
final Size preferredSize; final Size preferredSize;
@@ -38,19 +38,21 @@ class InfoTile extends StatelessWidget {
final Color? backgroundColor; final Color? backgroundColor;
@override @override
Widget build(BuildContext context) => PortalEntry( Widget build(BuildContext context) {
final chatThemeData = StreamChatTheme.of(context);
return PortalEntry(
visible: showMessage, visible: showMessage,
portalAnchor: tileAnchor ?? Alignment.topCenter, portalAnchor: tileAnchor ?? Alignment.topCenter,
childAnchor: childAnchor ?? Alignment.bottomCenter, childAnchor: childAnchor ?? Alignment.bottomCenter,
portal: Container( portal: Container(
height: 25, height: 25,
color: backgroundColor ?? color:
StreamChatTheme.of(context).colorTheme.grey.withOpacity(0.9), backgroundColor ?? chatThemeData.colorTheme.grey.withOpacity(0.9),
child: Center( child: Center(
child: Text( child: Text(
message, message,
style: textStyle ?? style: textStyle ??
StreamChatTheme.of(context).textTheme.body.copyWith( chatThemeData.textTheme.body.copyWith(
color: Colors.white, color: Colors.white,
), ),
maxLines: 1, maxLines: 1,
@@ -61,3 +63,4 @@ class InfoTile extends StatelessWidget {
child: child, child: child,
); );
} }
}
@@ -56,6 +56,7 @@ class _MediaListViewState extends State<MediaListView> {
position, position,
) { ) {
final media = _media.elementAt(position); final media = _media.elementAt(position);
final chatThemeData = StreamChatTheme.of(context);
return Padding( return Padding(
padding: const EdgeInsets.symmetric(horizontal: 1, vertical: 1), padding: const EdgeInsets.symmetric(horizontal: 1, vertical: 1),
child: InkWell( child: InkWell(
@@ -89,10 +90,8 @@ class _MediaListViewState extends State<MediaListView> {
? 1.0 ? 1.0
: 0.0, : 0.0,
child: Container( child: Container(
color: StreamChatTheme.of(context) color:
.colorTheme chatThemeData.colorTheme.black.withOpacity(0.5),
.black
.withOpacity(0.5),
alignment: Alignment.topRight, alignment: Alignment.topRight,
padding: const EdgeInsets.only( padding: const EdgeInsets.only(
top: 8, top: 8,
@@ -100,13 +99,10 @@ class _MediaListViewState extends State<MediaListView> {
), ),
child: CircleAvatar( child: CircleAvatar(
radius: 12, radius: 12,
backgroundColor: backgroundColor: chatThemeData.colorTheme.white,
StreamChatTheme.of(context).colorTheme.white,
child: StreamSvgIcon.check( child: StreamSvgIcon.check(
size: 24, size: 24,
color: StreamChatTheme.of(context) color: chatThemeData.colorTheme.black,
.colorTheme
.black,
), ),
), ),
), ),
@@ -128,7 +124,7 @@ class _MediaListViewState extends State<MediaListView> {
child: Text( child: Text(
media.videoDuration.format(), media.videoDuration.format(),
style: TextStyle( style: TextStyle(
color: StreamChatTheme.of(context).colorTheme.white, color: chatThemeData.colorTheme.white,
), ),
), ),
), ),
@@ -1,5 +1,4 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
/// This widget is used for showing user tiles for mentions /// This widget is used for showing user tiles for mentions
@@ -32,7 +31,9 @@ class MentionTile extends StatelessWidget {
final Widget? trailing; final Widget? trailing;
@override @override
Widget build(BuildContext context) => SizedBox( Widget build(BuildContext context) {
final chatThemeData = StreamChatTheme.of(context);
return SizedBox(
height: 56, height: 56,
child: Row( child: Row(
children: [ children: [
@@ -64,7 +65,7 @@ class MentionTile extends StatelessWidget {
member.user!.name, member.user!.name,
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: StreamChatTheme.of(context).textTheme.bodyBold, style: chatThemeData.textTheme.bodyBold,
), ),
const SizedBox( const SizedBox(
height: 2, height: 2,
@@ -74,12 +75,8 @@ class MentionTile extends StatelessWidget {
'@${member.userId}', '@${member.userId}',
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: StreamChatTheme.of(context) style: chatThemeData.textTheme.footnoteBold.copyWith(
.textTheme color: chatThemeData.colorTheme.grey,
.footnoteBold
.copyWith(
color:
StreamChatTheme.of(context).colorTheme.grey,
), ),
), ),
], ],
@@ -93,10 +90,11 @@ class MentionTile extends StatelessWidget {
left: 8, left: 8,
), ),
child: StreamSvgIcon.mentions( child: StreamSvgIcon.mentions(
color: StreamChatTheme.of(context).colorTheme.accentBlue, color: chatThemeData.colorTheme.accentBlue,
), ),
), ),
], ],
), ),
); );
} }
}
@@ -3,12 +3,12 @@ import 'dart:ui';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.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/message_action.dart';
import 'package:stream_chat_flutter/src/reaction_picker.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/stream_svg_icon.dart';
import 'package:stream_chat_flutter/src/utils.dart'; import 'package:stream_chat_flutter/src/utils.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.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 /// Constructs a modal with actions for a message
class MessageActionsModal extends StatefulWidget { class MessageActionsModal extends StatefulWidget {
@@ -108,7 +108,8 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
Widget build(BuildContext context) => _showMessageOptionsModal(); Widget build(BuildContext context) => _showMessageOptionsModal();
Widget _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 user = StreamChat.of(context).user;
final roughMaxSize = 2 * size.width / 3; final roughMaxSize = 2 * size.width / 3;
@@ -133,6 +134,7 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
final hasFileAttachment = final hasFileAttachment =
widget.message.attachments.any((it) => it.type == 'file') == true; widget.message.attachments.any((it) => it.type == 'file') == true;
final streamChatThemeData = StreamChatTheme.of(context);
return GestureDetector( return GestureDetector(
behavior: HitTestBehavior.translucent, behavior: HitTestBehavior.translucent,
onTap: () => Navigator.maybePop(context), onTap: () => Navigator.maybePop(context),
@@ -145,7 +147,7 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
sigmaY: 10, sigmaY: 10,
), ),
child: Container( 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, left: widget.reverse ? 0 : 40,
), ),
child: SizedBox( child: SizedBox(
width: MediaQuery.of(context).size.width * 0.75, width: mediaQueryData.size.width * 0.75,
child: Material( child: Material(
color: StreamChatTheme.of(context) color: streamChatThemeData.colorTheme.whiteSnow,
.colorTheme
.whiteSnow,
clipBehavior: Clip.hardEdge, clipBehavior: Clip.hardEdge,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
@@ -266,9 +266,8 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
].insertBetween( ].insertBetween(
Container( Container(
height: 1, height: 1,
color: StreamChatTheme.of(context) color: streamChatThemeData
.colorTheme .colorTheme.greyWhisper,
.greyWhisper,
), ),
), ),
), ),
@@ -310,11 +309,12 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
void _showFlagDialog() async { void _showFlagDialog() async {
final client = StreamChat.of(context).client; final client = StreamChat.of(context).client;
final streamChatThemeData = StreamChatTheme.of(context);
final answer = await showConfirmationDialog( final answer = await showConfirmationDialog(
context, context,
title: 'Flag Message', title: 'Flag Message',
icon: StreamSvgIcon.flag( icon: StreamSvgIcon.flag(
color: StreamChatTheme.of(context).colorTheme.accentRed, color: streamChatThemeData.colorTheme.accentRed,
size: 24, size: 24,
), ),
question: question:
@@ -324,7 +324,7 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
cancelText: 'CANCEL', cancelText: 'CANCEL',
); );
final theme = StreamChatTheme.of(context); final theme = streamChatThemeData;
if (answer == true) { if (answer == true) {
try { try {
await client.flagMessage(widget.message.id); await client.flagMessage(widget.message.id);
@@ -400,7 +400,9 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
); );
} }
Widget _buildReplyButton(BuildContext context) => InkWell( Widget _buildReplyButton(BuildContext context) {
final streamChatThemeData = StreamChatTheme.of(context);
return InkWell(
onTap: () { onTap: () {
Navigator.pop(context); Navigator.pop(context);
if (widget.onReplyTap != null) { if (widget.onReplyTap != null) {
@@ -412,36 +414,40 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
child: Row( child: Row(
children: [ children: [
StreamSvgIcon.reply( StreamSvgIcon.reply(
color: StreamChatTheme.of(context).primaryIconTheme.color, color: streamChatThemeData.primaryIconTheme.color,
), ),
const SizedBox(width: 16), const SizedBox(width: 16),
Text( Text(
'Reply', 'Reply',
style: StreamChatTheme.of(context).textTheme.body, style: streamChatThemeData.textTheme.body,
), ),
], ],
), ),
), ),
); );
}
Widget _buildFlagButton(BuildContext context) => InkWell( Widget _buildFlagButton(BuildContext context) {
final streamChatThemeData = StreamChatTheme.of(context);
return InkWell(
onTap: _showFlagDialog, onTap: _showFlagDialog,
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric(vertical: 11, horizontal: 16), padding: const EdgeInsets.symmetric(vertical: 11, horizontal: 16),
child: Row( child: Row(
children: [ children: [
StreamSvgIcon.iconFlag( StreamSvgIcon.iconFlag(
color: StreamChatTheme.of(context).primaryIconTheme.color, color: streamChatThemeData.primaryIconTheme.color,
), ),
const SizedBox(width: 16), const SizedBox(width: 16),
Text( Text(
'Flag Message', 'Flag Message',
style: StreamChatTheme.of(context).textTheme.body, style: streamChatThemeData.textTheme.body,
), ),
], ],
), ),
), ),
); );
}
Widget _buildDeleteButton(BuildContext context) { Widget _buildDeleteButton(BuildContext context) {
final isDeleteFailed = final isDeleteFailed =
@@ -469,7 +475,9 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
); );
} }
Widget _buildCopyButton(BuildContext context) => InkWell( Widget _buildCopyButton(BuildContext context) {
final streamChatThemeData = StreamChatTheme.of(context);
return InkWell(
onTap: () async { onTap: () async {
widget.onCopyTap?.call(widget.message); widget.onCopyTap?.call(widget.message);
Navigator.pop(context); Navigator.pop(context);
@@ -480,19 +488,22 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
children: [ children: [
StreamSvgIcon.copy( StreamSvgIcon.copy(
size: 24, size: 24,
color: StreamChatTheme.of(context).primaryIconTheme.color, color: streamChatThemeData.primaryIconTheme.color,
), ),
const SizedBox(width: 16), const SizedBox(width: 16),
Text( Text(
'Copy Message', 'Copy Message',
style: StreamChatTheme.of(context).textTheme.body, style: streamChatThemeData.textTheme.body,
), ),
], ],
), ),
), ),
); );
}
Widget _buildEditMessage(BuildContext context) => InkWell( Widget _buildEditMessage(BuildContext context) {
final streamChatThemeData = StreamChatTheme.of(context);
return InkWell(
onTap: () async { onTap: () async {
Navigator.pop(context); Navigator.pop(context);
_showEditBottomSheet(context); _showEditBottomSheet(context);
@@ -502,21 +513,23 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
child: Row( child: Row(
children: [ children: [
StreamSvgIcon.edit( StreamSvgIcon.edit(
color: StreamChatTheme.of(context).primaryIconTheme.color, color: streamChatThemeData.primaryIconTheme.color,
), ),
const SizedBox(width: 16), const SizedBox(width: 16),
Text( Text(
'Edit Message', 'Edit Message',
style: StreamChatTheme.of(context).textTheme.body, style: streamChatThemeData.textTheme.body,
), ),
], ],
), ),
), ),
); );
}
Widget _buildResendMessage(BuildContext context) { Widget _buildResendMessage(BuildContext context) {
final isUpdateFailed = final isUpdateFailed =
widget.message.status == MessageSendingStatus.failed_update; widget.message.status == MessageSendingStatus.failed_update;
final streamChatThemeData = StreamChatTheme.of(context);
return InkWell( return InkWell(
onTap: () { onTap: () {
Navigator.pop(context); Navigator.pop(context);
@@ -532,12 +545,12 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
child: Row( child: Row(
children: [ children: [
StreamSvgIcon.circleUp( StreamSvgIcon.circleUp(
color: StreamChatTheme.of(context).colorTheme.accentBlue, color: streamChatThemeData.colorTheme.accentBlue,
), ),
const SizedBox(width: 16), const SizedBox(width: 16),
Text( Text(
isUpdateFailed ? 'Resend Edited Message' : 'Resend', 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) { void _showEditBottomSheet(BuildContext context) {
final channel = StreamChannel.of(context).channel; final channel = StreamChannel.of(context).channel;
final streamChatThemeData = StreamChatTheme.of(context);
showModalBottomSheet( showModalBottomSheet(
context: context, context: context,
elevation: 2, elevation: 2,
clipBehavior: Clip.hardEdge, clipBehavior: Clip.hardEdge,
isScrollControlled: true, isScrollControlled: true,
backgroundColor: backgroundColor: streamChatThemeData.messageInputTheme.inputBackground,
StreamChatTheme.of(context).messageInputTheme.inputBackground,
shape: const RoundedRectangleBorder( shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only( borderRadius: BorderRadius.only(
topLeft: Radius.circular(16), topLeft: Radius.circular(16),
@@ -575,8 +588,7 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
Padding( Padding(
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
child: StreamSvgIcon.edit( child: StreamSvgIcon.edit(
color: color: streamChatThemeData.colorTheme.greyGainsboro,
StreamChatTheme.of(context).colorTheme.greyGainsboro,
), ),
), ),
const Text( const Text(
@@ -608,7 +620,9 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
); );
} }
Widget _buildThreadReplyButton(BuildContext context) => InkWell( Widget _buildThreadReplyButton(BuildContext context) {
final streamChatThemeData = StreamChatTheme.of(context);
return InkWell(
onTap: () { onTap: () {
Navigator.pop(context); Navigator.pop(context);
if (widget.onThreadReplyTap != null) { if (widget.onThreadReplyTap != null) {
@@ -620,15 +634,16 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
child: Row( child: Row(
children: [ children: [
StreamSvgIcon.thread( StreamSvgIcon.thread(
color: StreamChatTheme.of(context).primaryIconTheme.color, color: streamChatThemeData.primaryIconTheme.color,
), ),
const SizedBox(width: 16), const SizedBox(width: 16),
Text( Text(
'Thread Reply', 'Thread Reply',
style: StreamChatTheme.of(context).textTheme.body, style: streamChatThemeData.textTheme.body,
), ),
], ],
), ),
), ),
); );
} }
}
@@ -12,6 +12,7 @@ import 'package:image_picker/image_picker.dart';
import 'package:photo_manager/photo_manager.dart'; import 'package:photo_manager/photo_manager.dart';
import 'package:shimmer/shimmer.dart'; import 'package:shimmer/shimmer.dart';
import 'package:stream_chat_flutter/src/emoji/emoji.dart'; import 'package:stream_chat_flutter/src/emoji/emoji.dart';
import 'package:stream_chat_flutter/src/extension.dart';
import 'package:stream_chat_flutter/src/media_list_view.dart'; import 'package:stream_chat_flutter/src/media_list_view.dart';
import 'package:stream_chat_flutter/src/message_list_view.dart'; import 'package:stream_chat_flutter/src/message_list_view.dart';
import 'package:stream_chat_flutter/src/quoted_message_widget.dart'; import 'package:stream_chat_flutter/src/quoted_message_widget.dart';
@@ -19,11 +20,10 @@ import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
import 'package:stream_chat_flutter/src/stream_svg_icon.dart'; import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
import 'package:stream_chat_flutter/src/video_service.dart'; import 'package:stream_chat_flutter/src/video_service.dart';
import 'package:stream_chat_flutter/src/video_thumbnail_image.dart'; import 'package:stream_chat_flutter/src/video_thumbnail_image.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
import 'package:substring_highlight/substring_highlight.dart'; import 'package:substring_highlight/substring_highlight.dart';
import 'package:video_compress/video_compress.dart'; import 'package:video_compress/video_compress.dart';
import 'package:stream_chat_flutter/src/extension.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
/// Builder for attachment thumbnails /// Builder for attachment thumbnails
typedef AttachmentThumbnailBuilder = Widget Function( typedef AttachmentThumbnailBuilder = Widget Function(
@@ -299,8 +299,9 @@ class MessageInputState extends State<MessageInput> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final streamChatThemeData = StreamChatTheme.of(context);
Widget child = Container( Widget child = Container(
color: StreamChatTheme.of(context).messageInputTheme.inputBackground, color: streamChatThemeData.messageInputTheme.inputBackground,
child: SafeArea( child: SafeArea(
child: GestureDetector( child: GestureDetector(
onPanUpdate: (details) { onPanUpdate: (details) {
@@ -325,9 +326,7 @@ class MessageInputState extends State<MessageInput> {
Padding( Padding(
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
child: StreamSvgIcon.reply( child: StreamSvgIcon.reply(
color: StreamChatTheme.of(context) color: streamChatThemeData.colorTheme.greyGainsboro,
.colorTheme
.greyGainsboro,
), ),
), ),
const Text( const Text(
@@ -385,7 +384,9 @@ class MessageInputState extends State<MessageInput> {
], ],
); );
Widget _buildDmCheckbox() => Row( Widget _buildDmCheckbox() {
final streamChatThemeData = StreamChatTheme.of(context);
return Row(
children: [ children: [
Container( Container(
height: 16, height: 16,
@@ -394,10 +395,7 @@ class MessageInputState extends State<MessageInput> {
border: _sendAsDm border: _sendAsDm
? null ? null
: Border.all( : Border.all(
color: StreamChatTheme.of(context) color: streamChatThemeData.colorTheme.black.withOpacity(.5),
.colorTheme
.black
.withOpacity(.5),
width: 2, width: 2,
), ),
borderRadius: BorderRadius.circular(3), borderRadius: BorderRadius.circular(3),
@@ -406,8 +404,8 @@ class MessageInputState extends State<MessageInput> {
child: Material( child: Material(
borderRadius: BorderRadius.circular(3), borderRadius: BorderRadius.circular(3),
color: _sendAsDm color: _sendAsDm
? StreamChatTheme.of(context).colorTheme.accentBlue ? streamChatThemeData.colorTheme.accentBlue
: StreamChatTheme.of(context).colorTheme.white, : streamChatThemeData.colorTheme.white,
child: InkWell( child: InkWell(
onTap: () { onTap: () {
setState(() { setState(() {
@@ -422,7 +420,7 @@ class MessageInputState extends State<MessageInput> {
: CrossFadeState.showSecond, : CrossFadeState.showSecond,
firstChild: StreamSvgIcon.check( firstChild: StreamSvgIcon.check(
size: 16, size: 16,
color: StreamChatTheme.of(context).colorTheme.white, color: streamChatThemeData.colorTheme.white,
), ),
secondChild: const SizedBox( secondChild: const SizedBox(
height: 16, height: 16,
@@ -437,16 +435,14 @@ class MessageInputState extends State<MessageInput> {
padding: const EdgeInsets.symmetric(horizontal: 12), padding: const EdgeInsets.symmetric(horizontal: 12),
child: Text( child: Text(
'Also send as direct message', 'Also send as direct message',
style: StreamChatTheme.of(context).textTheme.footnote.copyWith( style: streamChatThemeData.textTheme.footnote.copyWith(
color: StreamChatTheme.of(context) color: streamChatThemeData.colorTheme.black.withOpacity(0.5),
.colorTheme
.black
.withOpacity(0.5),
), ),
), ),
), ),
], ],
); );
}
Widget _animateSendButton(BuildContext context) { Widget _animateSendButton(BuildContext context) {
final sendButton = widget.activeSendButton != null final sendButton = widget.activeSendButton != null
@@ -636,10 +632,7 @@ class MessageInputState extends State<MessageInput> {
), ),
Text( Text(
_chosenCommand?.name.toUpperCase() ?? '', _chosenCommand?.name.toUpperCase() ?? '',
style: StreamChatTheme.of(context) style: theme.textTheme.footnoteBold.copyWith(
.textTheme
.footnoteBold
.copyWith(
color: Colors.white, color: Colors.white,
), ),
), ),
@@ -839,7 +832,9 @@ class MessageInputState extends State<MessageInput> {
tween: Tween(begin: 0, end: 1), tween: Tween(begin: 0, end: 1),
duration: const Duration(milliseconds: 300), duration: const Duration(milliseconds: 300),
curve: Curves.easeInOutExpo, curve: Curves.easeInOutExpo,
builder: (context, val, wid) => Transform.scale( builder: (context, val, wid) {
final streamChatThemeData = StreamChatTheme.of(context);
return Transform.scale(
scale: val, scale: val,
child: Padding( child: Padding(
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
@@ -848,15 +843,13 @@ class MessageInputState extends State<MessageInput> {
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
), ),
color: StreamChatTheme.of(context).colorTheme.white, color: streamChatThemeData.colorTheme.white,
clipBehavior: Clip.antiAlias, clipBehavior: Clip.antiAlias,
child: Container( child: Container(
constraints: BoxConstraints.loose( constraints: BoxConstraints.loose(
const Size.fromHeight(400)), const Size.fromHeight(400)),
decoration: BoxDecoration( decoration: BoxDecoration(
color: StreamChatTheme.of(context) color: streamChatThemeData.colorTheme.white,
.colorTheme
.white,
borderRadius: BorderRadius.circular(8)), borderRadius: BorderRadius.circular(8)),
child: ListView( child: ListView(
padding: const EdgeInsets.all(0), padding: const EdgeInsets.all(0),
@@ -872,17 +865,15 @@ class MessageInputState extends State<MessageInput> {
horizontal: 8, horizontal: 8,
), ),
child: StreamSvgIcon.lightning( child: StreamSvgIcon.lightning(
color: StreamChatTheme.of(context) color: streamChatThemeData
.colorTheme .colorTheme.accentBlue,
.accentBlue,
), ),
), ),
Text( Text(
'Instant Commands', 'Instant Commands',
style: TextStyle( style: TextStyle(
color: StreamChatTheme.of(context) color: streamChatThemeData
.colorTheme .colorTheme.black
.black
.withOpacity(.5), .withOpacity(.5),
), ),
) )
@@ -919,16 +910,11 @@ class MessageInputState extends State<MessageInput> {
TextSpan( TextSpan(
text: text:
' /${c.name} ${c.args}', ' /${c.name} ${c.args}',
style: style: streamChatThemeData
StreamChatTheme.of( .textTheme.body
context)
.textTheme
.body
.copyWith( .copyWith(
// ignore: lines_longer_than_80_chars // ignore: lines_longer_than_80_chars
color: StreamChatTheme.of( color: streamChatThemeData
// ignore: lines_longer_than_80_chars
context)
// ignore: lines_longer_than_80_chars // ignore: lines_longer_than_80_chars
.colorTheme .colorTheme
.grey, .grey,
@@ -948,7 +934,8 @@ class MessageInputState extends State<MessageInput> {
), ),
), ),
), ),
)), );
}),
)); ));
} }
@@ -956,37 +943,30 @@ class MessageInputState extends State<MessageInput> {
final _attachmentContainsFile = final _attachmentContainsFile =
_attachments.values.any((it) => it.type == 'file'); _attachments.values.any((it) => it.type == 'file');
final chatThemeData = StreamChatTheme.of(context);
Color _getIconColor(int index) { Color _getIconColor(int index) {
final streamChatThemeData = chatThemeData;
switch (index) { switch (index) {
case 0: case 0:
return _attachments.isEmpty return _attachments.isEmpty
? StreamChatTheme.of(context).colorTheme.accentBlue ? streamChatThemeData.colorTheme.accentBlue
: (!_attachmentContainsFile : (!_attachmentContainsFile
? StreamChatTheme.of(context).colorTheme.accentBlue ? streamChatThemeData.colorTheme.accentBlue
: StreamChatTheme.of(context) : streamChatThemeData.colorTheme.black.withOpacity(0.2));
.colorTheme
.black
.withOpacity(0.2));
case 1: case 1:
return _attachmentContainsFile return _attachmentContainsFile
? StreamChatTheme.of(context).colorTheme.accentBlue ? streamChatThemeData.colorTheme.accentBlue
: (_attachments.isEmpty : (_attachments.isEmpty
? StreamChatTheme.of(context) ? streamChatThemeData.colorTheme.black.withOpacity(0.5)
.colorTheme : streamChatThemeData.colorTheme.black.withOpacity(0.2));
.black
.withOpacity(0.5)
: StreamChatTheme.of(context)
.colorTheme
.black
.withOpacity(0.2));
case 2: case 2:
return _attachmentContainsFile && _attachments.isNotEmpty return _attachmentContainsFile && _attachments.isNotEmpty
? StreamChatTheme.of(context).colorTheme.black.withOpacity(0.2) ? streamChatThemeData.colorTheme.black.withOpacity(0.2)
: StreamChatTheme.of(context).colorTheme.black.withOpacity(0.5); : streamChatThemeData.colorTheme.black.withOpacity(0.5);
case 3: case 3:
return _attachmentContainsFile && _attachments.isNotEmpty return _attachmentContainsFile && _attachments.isNotEmpty
? StreamChatTheme.of(context).colorTheme.black.withOpacity(0.2) ? streamChatThemeData.colorTheme.black.withOpacity(0.2)
: StreamChatTheme.of(context).colorTheme.black.withOpacity(0.5); : streamChatThemeData.colorTheme.black.withOpacity(0.5);
default: default:
return Colors.black; return Colors.black;
} }
@@ -997,7 +977,7 @@ class MessageInputState extends State<MessageInput> {
_animateContainer ? const Duration(milliseconds: 300) : Duration.zero, _animateContainer ? const Duration(milliseconds: 300) : Duration.zero,
height: _openFilePickerSection ? _filePickerSize : 0, height: _openFilePickerSection ? _filePickerSize : 0,
child: Material( child: Material(
color: StreamChatTheme.of(context).colorTheme.whiteSmoke, color: chatThemeData.colorTheme.whiteSmoke,
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
@@ -1061,7 +1041,7 @@ class MessageInputState extends State<MessageInput> {
}, },
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: StreamChatTheme.of(context).colorTheme.white, color: chatThemeData.colorTheme.white,
borderRadius: const BorderRadius.only( borderRadius: const BorderRadius.only(
topLeft: Radius.circular(16), topLeft: Radius.circular(16),
topRight: Radius.circular(16), topRight: Radius.circular(16),
@@ -1076,8 +1056,7 @@ class MessageInputState extends State<MessageInput> {
width: 40, width: 40,
height: 4, height: 4,
decoration: BoxDecoration( decoration: BoxDecoration(
color: color: chatThemeData.colorTheme.whiteSmoke,
StreamChatTheme.of(context).colorTheme.whiteSmoke,
borderRadius: BorderRadius.circular(4), borderRadius: BorderRadius.circular(4),
), ),
), ),
@@ -1090,7 +1069,7 @@ class MessageInputState extends State<MessageInput> {
Expanded( Expanded(
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: StreamChatTheme.of(context).colorTheme.white, color: chatThemeData.colorTheme.white,
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
), ),
child: _PickerWidget( child: _PickerWidget(
@@ -1166,6 +1145,7 @@ class MessageInputState extends State<MessageInput> {
} }
Widget _buildCommandIcon(String iconType) { Widget _buildCommandIcon(String iconType) {
final chatThemeData = StreamChatTheme.of(context);
switch (iconType) { switch (iconType) {
case 'giphy': case 'giphy':
return CircleAvatar( return CircleAvatar(
@@ -1176,7 +1156,7 @@ class MessageInputState extends State<MessageInput> {
); );
case 'ban': case 'ban':
return CircleAvatar( return CircleAvatar(
backgroundColor: StreamChatTheme.of(context).colorTheme.accentBlue, backgroundColor: chatThemeData.colorTheme.accentBlue,
radius: 12, radius: 12,
child: StreamSvgIcon.iconUserDelete( child: StreamSvgIcon.iconUserDelete(
size: 16, size: 16,
@@ -1185,7 +1165,7 @@ class MessageInputState extends State<MessageInput> {
); );
case 'flag': case 'flag':
return CircleAvatar( return CircleAvatar(
backgroundColor: StreamChatTheme.of(context).colorTheme.accentBlue, backgroundColor: chatThemeData.colorTheme.accentBlue,
radius: 12, radius: 12,
child: StreamSvgIcon.flag( child: StreamSvgIcon.flag(
size: 14, size: 14,
@@ -1194,7 +1174,7 @@ class MessageInputState extends State<MessageInput> {
); );
case 'imgur': case 'imgur':
return CircleAvatar( return CircleAvatar(
backgroundColor: StreamChatTheme.of(context).colorTheme.accentBlue, backgroundColor: chatThemeData.colorTheme.accentBlue,
radius: 12, radius: 12,
child: ClipOval( child: ClipOval(
child: StreamSvgIcon.imgur( child: StreamSvgIcon.imgur(
@@ -1204,7 +1184,7 @@ class MessageInputState extends State<MessageInput> {
); );
case 'mute': case 'mute':
return CircleAvatar( return CircleAvatar(
backgroundColor: StreamChatTheme.of(context).colorTheme.accentBlue, backgroundColor: chatThemeData.colorTheme.accentBlue,
radius: 12, radius: 12,
child: StreamSvgIcon.mute( child: StreamSvgIcon.mute(
size: 16, size: 16,
@@ -1213,7 +1193,7 @@ class MessageInputState extends State<MessageInput> {
); );
case 'unban': case 'unban':
return CircleAvatar( return CircleAvatar(
backgroundColor: StreamChatTheme.of(context).colorTheme.accentBlue, backgroundColor: chatThemeData.colorTheme.accentBlue,
radius: 12, radius: 12,
child: StreamSvgIcon.userAdd( child: StreamSvgIcon.userAdd(
size: 16, size: 16,
@@ -1222,7 +1202,7 @@ class MessageInputState extends State<MessageInput> {
); );
case 'unmute': case 'unmute':
return CircleAvatar( return CircleAvatar(
backgroundColor: StreamChatTheme.of(context).colorTheme.accentBlue, backgroundColor: chatThemeData.colorTheme.accentBlue,
radius: 12, radius: 12,
child: StreamSvgIcon.volumeUp( child: StreamSvgIcon.volumeUp(
size: 16, size: 16,
@@ -1231,7 +1211,7 @@ class MessageInputState extends State<MessageInput> {
); );
default: default:
return CircleAvatar( return CircleAvatar(
backgroundColor: StreamChatTheme.of(context).colorTheme.accentBlue, backgroundColor: chatThemeData.colorTheme.accentBlue,
radius: 12, radius: 12,
child: StreamSvgIcon.lightning( child: StreamSvgIcon.lightning(
size: 16, size: 16,
@@ -1249,17 +1229,14 @@ class MessageInputState extends State<MessageInput> {
Future<List<Member>>? queryMembers; Future<List<Member>>? queryMembers;
final channelState = StreamChannel.of(context);
if (query.isNotEmpty) { if (query.isNotEmpty) {
queryMembers = StreamChannel.of(context) queryMembers = channelState.channel
.channel
.queryMembers(filter: Filter.autoComplete('name', query)) .queryMembers(filter: Filter.autoComplete('name', query))
.then((res) => res.members); .then((res) => res.members);
} }
final members = StreamChannel.of(context) final members = channelState.channel.state?.members
.channel
.state
?.members
.where((m) => m.user?.name.toLowerCase().contains(query) == true) .where((m) => m.user?.name.toLowerCase().contains(query) == true)
.toList() ?? .toList() ??
[]; [];
@@ -1281,12 +1258,14 @@ class MessageInputState extends State<MessageInput> {
tween: Tween(begin: 0, end: 1), tween: Tween(begin: 0, end: 1),
duration: const Duration(milliseconds: 300), duration: const Duration(milliseconds: 300),
curve: Curves.easeInOutExpo, curve: Curves.easeInOutExpo,
builder: (context, val, wid) => Transform.scale( builder: (context, val, wid) {
final chatThemeData = StreamChatTheme.of(context);
return Transform.scale(
scale: val, scale: val,
child: Card( child: Card(
margin: const EdgeInsets.all(8), margin: const EdgeInsets.all(8),
elevation: 2, elevation: 2,
color: StreamChatTheme.of(context).colorTheme.white, color: chatThemeData.colorTheme.white,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
), ),
@@ -1294,7 +1273,7 @@ class MessageInputState extends State<MessageInput> {
child: Container( child: Container(
constraints: BoxConstraints.loose(const Size.fromHeight(240)), constraints: BoxConstraints.loose(const Size.fromHeight(240)),
decoration: BoxDecoration( decoration: BoxDecoration(
color: StreamChatTheme.of(context).colorTheme.white, color: chatThemeData.colorTheme.white,
), ),
child: FutureBuilder<List<Member>>( child: FutureBuilder<List<Member>>(
future: queryMembers ?? Future.value(members), future: queryMembers ?? Future.value(members),
@@ -1310,8 +1289,7 @@ class MessageInputState extends State<MessageInput> {
.where((it) => it.user != null) .where((it) => it.user != null)
.map( .map(
(m) => Material( (m) => Material(
color: color: chatThemeData.colorTheme.white,
StreamChatTheme.of(context).colorTheme.white,
child: InkWell( child: InkWell(
onTap: () { onTap: () {
if (m.user != null) { if (m.user != null) {
@@ -1350,7 +1328,8 @@ class MessageInputState extends State<MessageInput> {
), ),
), ),
), ),
), );
},
), ),
), ),
); );
@@ -1379,15 +1358,16 @@ class MessageInputState extends State<MessageInput> {
final renderBox = context.findRenderObject() as RenderBox; final renderBox = context.findRenderObject() as RenderBox;
final size = renderBox.size; final size = renderBox.size;
return OverlayEntry( return OverlayEntry(builder: (context) {
builder: (context) => Positioned( final chatThemeData = StreamChatTheme.of(context);
return Positioned(
bottom: size.height + MediaQuery.of(context).viewInsets.bottom, bottom: size.height + MediaQuery.of(context).viewInsets.bottom,
left: 0, left: 0,
right: 0, right: 0,
child: Card( child: Card(
margin: const EdgeInsets.all(8), margin: const EdgeInsets.all(8),
elevation: 2, elevation: 2,
color: StreamChatTheme.of(context).colorTheme.white, color: chatThemeData.colorTheme.white,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
), ),
@@ -1402,7 +1382,7 @@ class MessageInputState extends State<MessageInput> {
offset: Offset(0, -4), offset: Offset(0, -4),
), ),
], ],
color: StreamChatTheme.of(context).colorTheme.white, color: chatThemeData.colorTheme.white,
), ),
child: ListView.builder( child: ListView.builder(
padding: const EdgeInsets.all(0), padding: const EdgeInsets.all(0),
@@ -1415,21 +1395,16 @@ class MessageInputState extends State<MessageInput> {
child: Row( child: Row(
children: [ children: [
Padding( Padding(
padding: padding: const EdgeInsets.symmetric(horizontal: 8),
const EdgeInsets.symmetric(horizontal: 8),
child: StreamSvgIcon.smile( child: StreamSvgIcon.smile(
color: StreamChatTheme.of(context) color: chatThemeData.colorTheme.accentBlue,
.colorTheme
.accentBlue,
), ),
), ),
Flexible( Flexible(
child: Text( child: Text(
'Emoji matching "$query"', 'Emoji matching "$query"',
style: TextStyle( style: TextStyle(
color: StreamChatTheme.of(context) color: chatThemeData.colorTheme.black
.colorTheme
.black
.withOpacity(.5), .withOpacity(.5),
), ),
), ),
@@ -1440,6 +1415,7 @@ class MessageInputState extends State<MessageInput> {
} }
final emoji = emojis.elementAt(i - 1)!; final emoji = emojis.elementAt(i - 1)!;
final themeData = Theme.of(context);
return ListTile( return ListTile(
title: SubstringHighlight( title: SubstringHighlight(
text: text:
@@ -1447,12 +1423,11 @@ class MessageInputState extends State<MessageInput> {
"${emoji.char} ${emoji.name!.replaceAll('_', ' ')}", "${emoji.char} ${emoji.name!.replaceAll('_', ' ')}",
term: query, term: query,
textStyleHighlight: textStyleHighlight:
Theme.of(context).textTheme.headline6!.copyWith( themeData.textTheme.headline6!.copyWith(
fontSize: 14.5, fontSize: 14.5,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
textStyle: textStyle: themeData.textTheme.headline6!.copyWith(
Theme.of(context).textTheme.headline6!.copyWith(
fontSize: 14.5, fontSize: 14.5,
), ),
), ),
@@ -1463,7 +1438,8 @@ class MessageInputState extends State<MessageInput> {
}), }),
), ),
), ),
)); );
});
} }
void _chooseEmoji(List<String> splits, Emoji emoji) { void _chooseEmoji(List<String> splits, Emoji emoji) {
@@ -1589,7 +1565,9 @@ class MessageInputState extends State<MessageInput> {
); );
} }
Widget _buildRemoveButton(Attachment attachment) => SizedBox( Widget _buildRemoveButton(Attachment attachment) {
final chatThemeData = StreamChatTheme.of(context);
return SizedBox(
height: 24, height: 24,
width: 24, width: 24,
child: RawMaterialButton( child: RawMaterialButton(
@@ -1603,16 +1581,16 @@ class MessageInputState extends State<MessageInput> {
onPressed: () { onPressed: () {
setState(() => _attachments.remove(attachment.id)); setState(() => _attachments.remove(attachment.id));
}, },
fillColor: fillColor: chatThemeData.colorTheme.black.withOpacity(.5),
StreamChatTheme.of(context).colorTheme.black.withOpacity(.5),
child: Center( child: Center(
child: StreamSvgIcon.close( child: StreamSvgIcon.close(
size: 24, size: 24,
color: StreamChatTheme.of(context).colorTheme.white, color: chatThemeData.colorTheme.white,
), ),
), ),
), ),
); );
}
Widget _buildAttachment(Attachment attachment) { Widget _buildAttachment(Attachment attachment) {
if (widget.attachmentThumbnailBuilders?.containsKey(attachment.type) == if (widget.attachmentThumbnailBuilders?.containsKey(attachment.type) ==
@@ -1642,18 +1620,18 @@ class MessageInputState extends State<MessageInput> {
fit: BoxFit.cover, fit: BoxFit.cover,
errorWidget: (_, obj, trace) => errorWidget: (_, obj, trace) =>
getFileTypeImage(attachment.extraData['other'] as String?), getFileTypeImage(attachment.extraData['other'] as String?),
progressIndicatorBuilder: (context, _, progress) => progressIndicatorBuilder: (context, _, progress) {
Shimmer.fromColors( final chatThemeData = StreamChatTheme.of(context);
baseColor: return Shimmer.fromColors(
StreamChatTheme.of(context).colorTheme.greyGainsboro, baseColor: chatThemeData.colorTheme.greyGainsboro,
highlightColor: highlightColor: chatThemeData.colorTheme.whiteSmoke,
StreamChatTheme.of(context).colorTheme.whiteSmoke,
child: Image.asset( child: Image.asset(
'images/placeholder.png', 'images/placeholder.png',
fit: BoxFit.cover, fit: BoxFit.cover,
package: 'stream_chat_flutter', package: 'stream_chat_flutter',
), ),
), );
},
); );
case 'video': case 'video':
return Stack( return Stack(
@@ -1685,17 +1663,14 @@ class MessageInputState extends State<MessageInput> {
Widget _buildCommandButton() { Widget _buildCommandButton() {
final s = textEditingController.text.trim(); final s = textEditingController.text.trim();
final chatThemeData = StreamChatTheme.of(context);
return IconButton( return IconButton(
icon: StreamSvgIcon.lightning( icon: StreamSvgIcon.lightning(
color: s.isNotEmpty color: s.isNotEmpty
? StreamChatTheme.of(context).colorTheme.greyGainsboro ? chatThemeData.colorTheme.greyGainsboro
: (_commandsOverlay != null : (_commandsOverlay != null
? StreamChatTheme.of(context) ? chatThemeData.messageInputTheme.actionButtonColor
.messageInputTheme : chatThemeData.messageInputTheme.actionButtonIdleColor),
.actionButtonColor
: StreamChatTheme.of(context)
.messageInputTheme
.actionButtonIdleColor),
), ),
padding: const EdgeInsets.all(0), padding: const EdgeInsets.all(0),
constraints: const BoxConstraints.tightFor( constraints: const BoxConstraints.tightFor(
@@ -1730,13 +1705,13 @@ class MessageInputState extends State<MessageInput> {
); );
} }
Widget _buildAttachmentButton() => IconButton( Widget _buildAttachmentButton() {
final chatThemeData = StreamChatTheme.of(context);
return IconButton(
icon: StreamSvgIcon.attach( icon: StreamSvgIcon.attach(
color: _openFilePickerSection color: _openFilePickerSection
? StreamChatTheme.of(context).messageInputTheme.actionButtonColor ? chatThemeData.messageInputTheme.actionButtonColor
: StreamChatTheme.of(context) : chatThemeData.messageInputTheme.actionButtonIdleColor,
.messageInputTheme
.actionButtonIdleColor,
), ),
padding: const EdgeInsets.all(0), padding: const EdgeInsets.all(0),
constraints: const BoxConstraints.tightFor( constraints: const BoxConstraints.tightFor(
@@ -1763,6 +1738,7 @@ class MessageInputState extends State<MessageInput> {
} }
}, },
); );
}
/// Show the attachment modal, making the user choose where to /// Show the attachment modal, making the user choose where to
/// pick a media from /// pick a media from
@@ -2103,8 +2079,9 @@ class MessageInputState extends State<MessageInput> {
StreamSubscription? _keyboardListener; StreamSubscription? _keyboardListener;
void _showErrorAlert(String description) { void _showErrorAlert(String description) {
final chatThemeData = StreamChatTheme.of(context);
showModalBottomSheet( showModalBottomSheet(
backgroundColor: StreamChatTheme.of(context).colorTheme.white, backgroundColor: chatThemeData.colorTheme.white,
context: context, context: context,
shape: const RoundedRectangleBorder( shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only( borderRadius: BorderRadius.only(
@@ -2118,7 +2095,7 @@ class MessageInputState extends State<MessageInput> {
height: 26, height: 26,
), ),
StreamSvgIcon.error( StreamSvgIcon.error(
color: StreamChatTheme.of(context).colorTheme.accentRed, color: chatThemeData.colorTheme.accentRed,
size: 24, size: 24,
), ),
const SizedBox( const SizedBox(
@@ -2126,7 +2103,7 @@ class MessageInputState extends State<MessageInput> {
), ),
Text( Text(
'Something went wrong', 'Something went wrong',
style: StreamChatTheme.of(context).textTheme.headlineBold, style: chatThemeData.textTheme.headlineBold,
), ),
const SizedBox( const SizedBox(
height: 7, height: 7,
@@ -2142,8 +2119,7 @@ class MessageInputState extends State<MessageInput> {
height: 36, height: 36,
), ),
Container( Container(
color: color: chatThemeData.colorTheme.black.withOpacity(.08),
StreamChatTheme.of(context).colorTheme.black.withOpacity(.08),
height: 1, height: 1,
), ),
Row( Row(
@@ -2155,13 +2131,8 @@ class MessageInputState extends State<MessageInput> {
}, },
child: Text( child: Text(
'OK', 'OK',
style: StreamChatTheme.of(context) style: chatThemeData.textTheme.bodyBold
.textTheme .copyWith(color: chatThemeData.colorTheme.accentBlue),
.bodyBold
.copyWith(
color: StreamChatTheme.of(context)
.colorTheme
.accentBlue),
), ),
), ),
], ],
@@ -2291,6 +2262,7 @@ class __PickerWidgetState extends State<_PickerWidget> {
return const Center(child: CircularProgressIndicator()); return const Center(child: CircularProgressIndicator());
} }
final chatThemeData = StreamChatTheme.of(context);
if (snapshot.data!) { if (snapshot.data!) {
if (widget.containsFile) { if (widget.containsFile) {
return GestureDetector( return GestureDetector(
@@ -2299,12 +2271,12 @@ class __PickerWidgetState extends State<_PickerWidget> {
}, },
child: Container( child: Container(
constraints: const BoxConstraints.expand(), constraints: const BoxConstraints.expand(),
color: StreamChatTheme.of(context).colorTheme.whiteSmoke, color: chatThemeData.colorTheme.whiteSmoke,
alignment: Alignment.center, alignment: Alignment.center,
child: Text( child: Text(
'Add more files', 'Add more files',
style: TextStyle( style: TextStyle(
color: StreamChatTheme.of(context).colorTheme.accentBlue, color: chatThemeData.colorTheme.accentBlue,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),
@@ -2322,7 +2294,7 @@ class __PickerWidgetState extends State<_PickerWidget> {
PhotoManager.openSetting(); PhotoManager.openSetting();
}, },
child: Container( child: Container(
color: StreamChatTheme.of(context).colorTheme.whiteSmoke, color: chatThemeData.colorTheme.whiteSmoke,
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
@@ -2331,26 +2303,21 @@ class __PickerWidgetState extends State<_PickerWidget> {
'svgs/icon_picture_empty_state.svg', 'svgs/icon_picture_empty_state.svg',
package: 'stream_chat_flutter', package: 'stream_chat_flutter',
height: 140, height: 140,
color: StreamChatTheme.of(context).colorTheme.greyGainsboro, color: chatThemeData.colorTheme.greyGainsboro,
), ),
Text( Text(
// ignore: lines_longer_than_80_chars // ignore: lines_longer_than_80_chars
'Please enable access to your photos \nand videos so you can share them with friends.', 'Please enable access to your photos \nand videos so you can share them with friends.',
style: StreamChatTheme.of(context).textTheme.body.copyWith( style: chatThemeData.textTheme.body
color: StreamChatTheme.of(context).colorTheme.grey), .copyWith(color: chatThemeData.colorTheme.grey),
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
const SizedBox(height: 6), const SizedBox(height: 6),
Center( Center(
child: Text( child: Text(
'Allow access to your gallery', 'Allow access to your gallery',
style: StreamChatTheme.of(context) style: chatThemeData.textTheme.bodyBold.copyWith(
.textTheme color: chatThemeData.colorTheme.accentBlue,
.bodyBold
.copyWith(
color: StreamChatTheme.of(context)
.colorTheme
.accentBlue,
), ),
), ),
), ),
@@ -6,15 +6,15 @@ import 'package:flutter/material.dart';
import 'package:jiffy/jiffy.dart'; import 'package:jiffy/jiffy.dart';
import 'package:rxdart/rxdart.dart'; import 'package:rxdart/rxdart.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.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/info_tile.dart';
import 'package:stream_chat_flutter/src/message_widget.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/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/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:stream_chat_flutter_core/stream_chat_flutter_core.dart';
import 'package:visibility_detector/visibility_detector.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 /// Widget builder for message
typedef MessageBuilder = Widget Function( typedef MessageBuilder = Widget Function(
@@ -274,15 +274,15 @@ class _MessageListViewState extends State<MessageListView> {
bool _showScrollToBottom = false; bool _showScrollToBottom = false;
late final ItemPositionsListener _itemPositionListener; late final ItemPositionsListener _itemPositionListener;
int? _messageListLength; int? _messageListLength;
StreamChannelState? streamChannel; late StreamChannelState streamChannel;
int? get _initialIndex { int? get _initialIndex {
if (widget.initialScrollIndex != null) return widget.initialScrollIndex; if (widget.initialScrollIndex != null) return widget.initialScrollIndex;
if (streamChannel!.initialMessageId != null) { if (streamChannel.initialMessageId != null) {
final messages = streamChannel!.channel.state!.messages; final messages = streamChannel.channel.state!.messages;
final totalMessages = messages.length; final totalMessages = messages.length;
final messageIndex = final messageIndex =
messages.indexWhere((e) => e.id == streamChannel!.initialMessageId); messages.indexWhere((e) => e.id == streamChannel.initialMessageId);
final index = totalMessages - messageIndex; final index = totalMessages - messageIndex;
if (index != 0) return index - 1; if (index != 0) return index - 1;
return index; return index;
@@ -295,9 +295,9 @@ class _MessageListViewState extends State<MessageListView> {
return 0; 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; bool get _isThreadConversation => widget.parentMessage != null;
@@ -316,7 +316,9 @@ class _MessageListViewState extends State<MessageListView> {
final MessageListController _messageListController = MessageListController(); final MessageListController _messageListController = MessageListController();
@override @override
Widget build(BuildContext context) => MessageListCore( Widget build(BuildContext context) {
final chatThemeData = StreamChatTheme.of(context);
return MessageListCore(
messageFilter: widget.messageFilter, messageFilter: widget.messageFilter,
loadingBuilder: widget.loadingBuilder ?? loadingBuilder: widget.loadingBuilder ??
(context) => const Center( (context) => const Center(
@@ -326,18 +328,12 @@ class _MessageListViewState extends State<MessageListView> {
(context) => Center( (context) => Center(
child: Text( child: Text(
'No chats here yet...', 'No chats here yet...',
style: StreamChatTheme.of(context) style: chatThemeData.textTheme.footnote.copyWith(
.textTheme color: chatThemeData.colorTheme.black.withOpacity(.5)),
.footnote
.copyWith(
color: StreamChatTheme.of(context)
.colorTheme
.black
.withOpacity(.5)),
), ),
), ),
messageListBuilder: widget.messageListBuilder ?? messageListBuilder:
(context, list) => _buildListView(list), widget.messageListBuilder ?? (context, list) => _buildListView(list),
messageListController: _messageListController, messageListController: _messageListController,
parentMessage: widget.parentMessage, parentMessage: widget.parentMessage,
showScrollToBottom: widget.showScrollToBottom, showScrollToBottom: widget.showScrollToBottom,
@@ -345,17 +341,12 @@ class _MessageListViewState extends State<MessageListView> {
(BuildContext context, Object error) => Center( (BuildContext context, Object error) => Center(
child: Text( child: Text(
'Something went wrong', 'Something went wrong',
style: StreamChatTheme.of(context) style: chatThemeData.textTheme.footnote.copyWith(
.textTheme color: chatThemeData.colorTheme.black.withOpacity(.5)),
.footnote
.copyWith(
color: StreamChatTheme.of(context)
.colorTheme
.black
.withOpacity(.5)),
), ),
), ),
); );
}
Widget _buildListView(List<Message> data) { Widget _buildListView(List<Message> data) {
messages = data; messages = data;
@@ -448,10 +439,10 @@ class _MessageListViewState extends State<MessageListView> {
if (i == 0) return const SizedBox(height: 30); if (i == 0) return const SizedBox(height: 30);
if (i == messages.length + 1) { if (i == messages.length + 1) {
final replyCount = widget.parentMessage!.replyCount; final replyCount = widget.parentMessage!.replyCount;
final chatThemeData = StreamChatTheme.of(context);
return Container( return Container(
decoration: BoxDecoration( decoration: BoxDecoration(
gradient: gradient: chatThemeData.colorTheme.bgGradient,
StreamChatTheme.of(context).colorTheme.bgGradient,
), ),
child: Padding( child: Padding(
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
@@ -459,10 +450,8 @@ class _MessageListViewState extends State<MessageListView> {
// ignore: lines_longer_than_80_chars // ignore: lines_longer_than_80_chars
'$replyCount ${replyCount == 1 ? 'Reply' : 'Replies'}', '$replyCount ${replyCount == 1 ? 'Reply' : 'Replies'}',
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: StreamChatTheme.of(context) style: chatThemeData
.channelTheme .channelTheme.channelHeaderTheme.subtitle,
.channelHeaderTheme
.subtitle,
), ),
), ),
); );
@@ -616,8 +605,8 @@ class _MessageListViewState extends State<MessageListView> {
Widget _buildScrollToBottom() => StreamBuilder<Tuple2<bool, int>>( Widget _buildScrollToBottom() => StreamBuilder<Tuple2<bool, int>>(
stream: Rx.combineLatest2( stream: Rx.combineLatest2(
streamChannel!.channel.state!.isUpToDateStream, streamChannel.channel.state!.isUpToDateStream,
streamChannel!.channel.state!.unreadCountStream, streamChannel.channel.state!.unreadCountStream,
(bool isUpToDate, int unreadCount) => Tuple2(isUpToDate, unreadCount), (bool isUpToDate, int unreadCount) => Tuple2(isUpToDate, unreadCount),
), ),
builder: (_, snapshot) { builder: (_, snapshot) {
@@ -633,8 +622,9 @@ class _MessageListViewState extends State<MessageListView> {
} }
final unreadCount = snapshot.data!.item2; final unreadCount = snapshot.data!.item2;
final showUnreadCount = unreadCount > 0 && final showUnreadCount = unreadCount > 0 &&
streamChannel!.channel.state!.members.any((e) => streamChannel.channel.state!.members.any((e) =>
e.userId == streamChannel!.channel.client.state.user!.id); e.userId == streamChannel.channel.client.state.user!.id);
final chatThemeData = StreamChatTheme.of(context);
return Positioned( return Positioned(
bottom: 8, bottom: 8,
right: 8, right: 8,
@@ -644,15 +634,15 @@ class _MessageListViewState extends State<MessageListView> {
clipBehavior: Clip.none, clipBehavior: Clip.none,
children: [ children: [
FloatingActionButton( FloatingActionButton(
backgroundColor: StreamChatTheme.of(context).colorTheme.white, backgroundColor: chatThemeData.colorTheme.white,
onPressed: () { onPressed: () {
if (unreadCount > 0) { if (unreadCount > 0) {
streamChannel!.channel.markRead(); streamChannel.channel.markRead();
} }
if (!_upToDate) { if (!_upToDate) {
_bottomPaginationActive = false; _bottomPaginationActive = false;
_topPaginationActive = false; _topPaginationActive = false;
streamChannel!.reloadChannel(); streamChannel.reloadChannel();
} else { } else {
setState(() => _showScrollToBottom = false); setState(() => _showScrollToBottom = false);
_scrollController!.scrollTo( _scrollController!.scrollTo(
@@ -663,7 +653,7 @@ class _MessageListViewState extends State<MessageListView> {
} }
}, },
child: StreamSvgIcon.down( child: StreamSvgIcon.down(
color: StreamChatTheme.of(context).colorTheme.black, color: chatThemeData.colorTheme.black,
), ),
), ),
if (showUnreadCount) if (showUnreadCount)
@@ -692,12 +682,12 @@ class _MessageListViewState extends State<MessageListView> {
); );
Widget _buildLoadingIndicator( Widget _buildLoadingIndicator(
StreamChannelState? streamChannel, StreamChannelState streamChannel,
QueryDirection direction, QueryDirection direction,
) { ) {
final stream = direction == QueryDirection.top final stream = direction == QueryDirection.top
? streamChannel!.queryTopMessages ? streamChannel.queryTopMessages
: streamChannel!.queryBottomMessages; : streamChannel.queryBottomMessages;
return StreamBuilder<bool>( return StreamBuilder<bool>(
key: const Key('LOADING-INDICATOR'), key: const Key('LOADING-INDICATOR'),
stream: stream, stream: stream,
@@ -764,7 +754,7 @@ class _MessageListViewState extends State<MessageListView> {
BuildContext context, BuildContext context,
Message message, Message message,
List<Message> messages, List<Message> messages,
StreamChannelState? streamChannel, StreamChannelState streamChannel,
) { ) {
Widget messageWidget; Widget messageWidget;
if (widget.messageBuilder != null) { if (widget.messageBuilder != null) {
@@ -790,7 +780,7 @@ class _MessageListViewState extends State<MessageListView> {
onVisibilityChanged: (visibility) { onVisibilityChanged: (visibility) {
final isVisible = visibility.visibleBounds != Rect.zero; final isVisible = visibility.visibleBounds != Rect.zero;
if (isVisible) { if (isVisible) {
final channel = streamChannel!.channel; final channel = streamChannel.channel;
if (_upToDate && if (_upToDate &&
channel.config?.readEvents == true && channel.config?.readEvents == true &&
channel.state!.unreadCount! > 0) { channel.state!.unreadCount! > 0) {
@@ -811,6 +801,7 @@ class _MessageListViewState extends State<MessageListView> {
final isMyMessage = message.user!.id == StreamChat.of(context).user!.id; final isMyMessage = message.user!.id == StreamChat.of(context).user!.id;
final isOnlyEmoji = message.text!.isOnlyEmoji; final isOnlyEmoji = message.text!.isOnlyEmoji;
final chatThemeData = StreamChatTheme.of(context);
return MessageWidget( return MessageWidget(
showReplyMessage: false, showReplyMessage: false,
showResendMessage: false, showResendMessage: false,
@@ -837,8 +828,8 @@ class _MessageListViewState extends State<MessageListView> {
borderSide: isMyMessage || isOnlyEmoji ? BorderSide.none : null, borderSide: isMyMessage || isOnlyEmoji ? BorderSide.none : null,
showUserAvatar: isMyMessage ? DisplayWidget.gone : DisplayWidget.show, showUserAvatar: isMyMessage ? DisplayWidget.gone : DisplayWidget.show,
messageTheme: isMyMessage messageTheme: isMyMessage
? StreamChatTheme.of(context).ownMessageTheme ? chatThemeData.ownMessageTheme
: StreamChatTheme.of(context).otherMessageTheme, : chatThemeData.otherMessageTheme,
onShowMessage: widget.onShowMessage, onShowMessage: widget.onShowMessage,
onReturnAction: (action) { onReturnAction: (action) {
switch (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) { final readList = channel.state?.read?.where((read) {
if (read.user.id == userId) return false; if (read.user.id == userId) return false;
return read.lastRead.isAfter(message.createdAt) || return read.lastRead.isAfter(message.createdAt) ||
@@ -946,6 +937,7 @@ class _MessageListViewState extends State<MessageListView> {
? BorderSide.none ? BorderSide.none
: null; : null;
final chatThemeData = StreamChatTheme.of(context);
Widget child = MessageWidget( Widget child = MessageWidget(
key: ValueKey<String>('MESSAGE-${message.id}'), key: ValueKey<String>('MESSAGE-${message.id}'),
message: message, message: message,
@@ -970,7 +962,7 @@ class _MessageListViewState extends State<MessageListView> {
if (messages.map((e) => e.id).contains(quotedMessageId)) { if (messages.map((e) => e.id).contains(quotedMessageId)) {
scrollToIndex(); scrollToIndex();
} else { } else {
await streamChannel!.loadChannelAtMessage(quotedMessageId).then((_) { await streamChannel.loadChannelAtMessage(quotedMessageId).then((_) {
WidgetsBinding.instance!.addPostFrameCallback((_) { WidgetsBinding.instance!.addPostFrameCallback((_) {
if (messages.map((e) => e.id).contains(quotedMessageId)) { if (messages.map((e) => e.id).contains(quotedMessageId)) {
scrollToIndex(); scrollToIndex();
@@ -1013,8 +1005,8 @@ class _MessageListViewState extends State<MessageListView> {
horizontal: isOnlyEmoji ? 0 : 16.0, horizontal: isOnlyEmoji ? 0 : 16.0,
), ),
messageTheme: isMyMessage messageTheme: isMyMessage
? StreamChatTheme.of(context).ownMessageTheme ? chatThemeData.ownMessageTheme
: StreamChatTheme.of(context).otherMessageTheme, : chatThemeData.otherMessageTheme,
readList: readList, readList: readList,
allRead: allRead, allRead: allRead,
onShowMessage: widget.onShowMessage, onShowMessage: widget.onShowMessage,
@@ -1054,7 +1046,7 @@ class _MessageListViewState extends State<MessageListView> {
widget.onMessageSwiped?.call(message); widget.onMessageSwiped?.call(message);
}, },
backgroundIcon: StreamSvgIcon.reply( backgroundIcon: StreamSvgIcon.reply(
color: StreamChatTheme.of(context).colorTheme.accentBlue, color: chatThemeData.colorTheme.accentBlue,
), ),
child: child, child: child,
), ),
@@ -1064,7 +1056,7 @@ class _MessageListViewState extends State<MessageListView> {
if (!initialMessageHighlightComplete && if (!initialMessageHighlightComplete &&
widget.highlightInitialMessage && widget.highlightInitialMessage &&
_isInitialMessage(message.id)) { _isInitialMessage(message.id)) {
final colorTheme = StreamChatTheme.of(context).colorTheme; final colorTheme = chatThemeData.colorTheme;
final highlightColor = final highlightColor =
widget.messageHighlightColor ?? colorTheme.highlight; widget.messageHighlightColor ?? colorTheme.highlight;
child = TweenAnimationBuilder<Color?>( child = TweenAnimationBuilder<Color?>(
@@ -1100,14 +1092,23 @@ class _MessageListViewState extends State<MessageListView> {
initialIndex = _initialIndex; initialIndex = _initialIndex;
initialAlignment = _initialAlignment; initialAlignment = _initialAlignment;
_getOnThreadTap();
super.initState();
}
@override
void didChangeDependencies() {
streamChannel = StreamChannel.of(context);
_messageNewListener?.cancel();
_messageNewListener = _messageNewListener =
streamChannel!.channel.on(EventType.messageNew).listen((event) { streamChannel.channel.on(EventType.messageNew).listen((event) {
if (_upToDate) { if (_upToDate) {
_bottomPaginationActive = false; _bottomPaginationActive = false;
_topPaginationActive = false; _topPaginationActive = false;
} }
if (event.message!.user!.id == if (event.message!.user!.id ==
streamChannel!.channel.client.state.user!.id) { streamChannel.channel.client.state.user!.id) {
WidgetsBinding.instance!.addPostFrameCallback((_) { WidgetsBinding.instance!.addPostFrameCallback((_) {
_scrollController?.jumpTo( _scrollController?.jumpTo(
index: 0, index: 0,
@@ -1117,11 +1118,9 @@ class _MessageListViewState extends State<MessageListView> {
}); });
if (_isThreadConversation) { if (_isThreadConversation) {
streamChannel!.getReplies(widget.parentMessage!.id); streamChannel.getReplies(widget.parentMessage!.id);
} }
super.didChangeDependencies();
_getOnThreadTap();
super.initState();
} }
void _getOnThreadTap() { void _getOnThreadTap() {
@@ -1139,12 +1138,12 @@ class _MessageListViewState extends State<MessageListView> {
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (_) => StreamBuilder<Message>( builder: (_) => StreamBuilder<Message>(
stream: streamChannel!.channel.state!.messagesStream.map( stream: streamChannel.channel.state!.messagesStream.map(
(messages) => (messages) =>
messages!.firstWhere((m) => m.id == message.id)), messages!.firstWhere((m) => m.id == message.id)),
initialData: message, initialData: message,
builder: (_, snapshot) => StreamChannel( builder: (_, snapshot) => StreamChannel(
channel: streamChannel!.channel, channel: streamChannel.channel,
child: widget.threadBuilder!(context, snapshot.data), child: widget.threadBuilder!(context, snapshot.data),
), ),
), ),
@@ -1157,7 +1156,7 @@ class _MessageListViewState extends State<MessageListView> {
@override @override
void dispose() { void dispose() {
if (!_upToDate) { if (!_upToDate) {
streamChannel!.reloadChannel(); streamChannel.reloadChannel();
} }
_messageNewListener?.cancel(); _messageNewListener?.cancel();
super.dispose(); super.dispose();
@@ -1,13 +1,13 @@
import 'dart:ui'; import 'dart:ui';
import 'package:flutter/material.dart'; 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_bubble.dart';
import 'package:stream_chat_flutter/src/reaction_picker.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/stream_chat.dart';
import 'package:stream_chat_flutter/src/user_avatar.dart'; import 'package:stream_chat_flutter/src/user_avatar.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.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_core/stream_chat_flutter_core.dart';
import 'package:stream_chat_flutter/src/extension.dart';
/// Modal widget for displaying message reactions /// Modal widget for displaying message reactions
class MessageReactionsModal extends StatelessWidget { class MessageReactionsModal extends StatelessWidget {
@@ -176,8 +176,9 @@ class MessageReactionsModal extends StatelessWidget {
Widget _buildReactionCard(BuildContext context) { Widget _buildReactionCard(BuildContext context) {
final currentUser = StreamChat.of(context).user; final currentUser = StreamChat.of(context).user;
final chatThemeData = StreamChatTheme.of(context);
return Card( return Card(
color: StreamChatTheme.of(context).colorTheme.white, color: chatThemeData.colorTheme.white,
clipBehavior: Clip.hardEdge, clipBehavior: Clip.hardEdge,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
@@ -190,7 +191,7 @@ class MessageReactionsModal extends StatelessWidget {
children: [ children: [
Text( Text(
'Message Reactions', 'Message Reactions',
style: StreamChatTheme.of(context).textTheme.headlineBold, style: chatThemeData.textTheme.headlineBold,
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
Flexible( Flexible(
@@ -220,6 +221,7 @@ class MessageReactionsModal extends StatelessWidget {
BuildContext context, BuildContext context,
) { ) {
final isCurrentUser = reaction.user?.id == currentUser.id; final isCurrentUser = reaction.user?.id == currentUser.id;
final chatThemeData = StreamChatTheme.of(context);
return ConstrainedBox( return ConstrainedBox(
constraints: BoxConstraints.loose(const Size( constraints: BoxConstraints.loose(const Size(
64, 64,
@@ -258,7 +260,7 @@ class MessageReactionsModal extends StatelessWidget {
messageTheme.reactionsBorderColor ?? Colors.transparent, messageTheme.reactionsBorderColor ?? Colors.transparent,
backgroundColor: messageTheme.reactionsBackgroundColor ?? backgroundColor: messageTheme.reactionsBackgroundColor ??
Colors.transparent, Colors.transparent,
maskColor: StreamChatTheme.of(context).colorTheme.white, maskColor: chatThemeData.colorTheme.white,
tailCirclesSpacing: 1, tailCirclesSpacing: 1,
highlightOwnReactions: false, highlightOwnReactions: false,
), ),
@@ -269,7 +271,7 @@ class MessageReactionsModal extends StatelessWidget {
const SizedBox(height: 8), const SizedBox(height: 8),
Text( Text(
reaction.user!.name.split(' ')[0], reaction.user!.name.split(' ')[0],
style: StreamChatTheme.of(context).textTheme.footnoteBold, style: chatThemeData.textTheme.footnoteBold,
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
], ],
@@ -35,6 +35,7 @@ class MessageSearchItem extends StatelessWidget {
final channel = getMessageResponse.channel; final channel = getMessageResponse.channel;
final channelName = channel?.extraData['name']; final channelName = channel?.extraData['name'];
final user = message.user!; final user = message.user!;
final chatThemeData = StreamChatTheme.of(context);
return ListTile( return ListTile(
onTap: onTap, onTap: onTap,
leading: UserAvatar( leading: UserAvatar(
@@ -49,21 +50,18 @@ class MessageSearchItem extends StatelessWidget {
children: [ children: [
Text( Text(
user.id == StreamChat.of(context).user?.id ? 'You' : user.name, user.id == StreamChat.of(context).user?.id ? 'You' : user.name,
style: StreamChatTheme.of(context).channelPreviewTheme.title, style: chatThemeData.channelPreviewTheme.title,
), ),
if (channelName != null) ...[ if (channelName != null) ...[
Text( Text(
' in ', ' in ',
style: StreamChatTheme.of(context) style: chatThemeData.channelPreviewTheme.title?.copyWith(
.channelPreviewTheme
.title
?.copyWith(
fontWeight: FontWeight.normal, fontWeight: FontWeight.normal,
), ),
), ),
Text( Text(
channelName as String, channelName as String,
style: StreamChatTheme.of(context).channelPreviewTheme.title, style: chatThemeData.channelPreviewTheme.title,
), ),
], ],
], ],
@@ -121,17 +119,18 @@ class MessageSearchItem extends StatelessWidget {
text = parts.join(' '); text = parts.join(' ');
} }
final chatThemeData = StreamChatTheme.of(context);
return Text.rich( return Text.rich(
_getDisplayText( _getDisplayText(
text!, text!,
message.mentionedUsers, message.mentionedUsers,
message.attachments, message.attachments,
StreamChatTheme.of(context).channelPreviewTheme.subtitle?.copyWith( chatThemeData.channelPreviewTheme.subtitle?.copyWith(
fontStyle: (message.isSystem || message.isDeleted) fontStyle: (message.isSystem || message.isDeleted)
? FontStyle.italic ? FontStyle.italic
: FontStyle.normal, : FontStyle.normal,
), ),
StreamChatTheme.of(context).channelPreviewTheme.subtitle?.copyWith( chatThemeData.channelPreviewTheme.subtitle?.copyWith(
fontStyle: (message.isSystem || message.isDeleted) fontStyle: (message.isSystem || message.isDeleted)
? FontStyle.italic ? FontStyle.italic
: FontStyle.normal, : FontStyle.normal,
@@ -1,8 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/src/info_tile.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/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/stream_chat_flutter.dart';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
/// Callback called when tapping on a user /// Callback called when tapping on a user
typedef MessageSearchItemTapCallback = void Function(GetMessageResponse); typedef MessageSearchItemTapCallback = void Function(GetMessageResponse);
@@ -270,12 +270,13 @@ class _MessageSearchListViewState extends State<MessageSearchListView> {
); );
if (widget.showResultCount) { if (widget.showResultCount) {
final chatThemeData = StreamChatTheme.of(context);
child = Column( child = Column(
children: [ children: [
Container( Container(
width: double.maxFinite, width: double.maxFinite,
decoration: BoxDecoration( decoration: BoxDecoration(
gradient: StreamChatTheme.of(context).colorTheme.bgGradient, gradient: chatThemeData.colorTheme.bgGradient,
), ),
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
@@ -285,7 +286,7 @@ class _MessageSearchListViewState extends State<MessageSearchListView> {
child: Text( child: Text(
'${items.length} results', '${items.length} results',
style: TextStyle( 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:collection/collection.dart' show IterableExtension;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_markdown/flutter_markdown.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/stream_chat_flutter.dart';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
/// Text widget to display in message /// Text widget to display in message
class MessageText extends StatelessWidget { class MessageText extends StatelessWidget {
@@ -31,6 +31,7 @@ class MessageText extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final text = _replaceMentions(message.text ?? '').replaceAll('\n', '\\\n'); final text = _replaceMentions(message.text ?? '').replaceAll('\n', '\\\n');
final themeData = Theme.of(context);
return MarkdownBody( return MarkdownBody(
data: text, data: text,
onTapLink: ( onTapLink: (
@@ -60,8 +61,8 @@ class MessageText extends StatelessWidget {
} }
}, },
styleSheet: MarkdownStyleSheet.fromTheme( styleSheet: MarkdownStyleSheet.fromTheme(
Theme.of(context).copyWith( themeData.copyWith(
textTheme: Theme.of(context).textTheme.apply( textTheme: themeData.textTheme.apply(
bodyColor: messageTheme.messageText?.color, bodyColor: messageTheme.messageText?.color,
decoration: messageTheme.messageText?.decoration, decoration: messageTheme.messageText?.decoration,
decorationColor: messageTheme.messageText?.decorationColor, decorationColor: messageTheme.messageText?.decorationColor,
@@ -8,6 +8,8 @@ import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_portal/flutter_portal.dart'; import 'package:flutter_portal/flutter_portal.dart';
import 'package:jiffy/jiffy.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_action.dart';
import 'package:stream_chat_flutter/src/message_actions_modal.dart'; import 'package:stream_chat_flutter/src/message_actions_modal.dart';
import 'package:stream_chat_flutter/src/message_reactions_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/reaction_bubble.dart';
import 'package:stream_chat_flutter/src/url_attachment.dart'; import 'package:stream_chat_flutter/src/url_attachment.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.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 /// Widget builder for building attachments
typedef AttachmentBuilder = Widget Function( typedef AttachmentBuilder = Widget Function(
@@ -109,6 +109,7 @@ class MessageWidget extends StatefulWidget {
borderRadius: attachmentBorderRadiusGeometry ?? BorderRadius.zero, borderRadius: attachmentBorderRadiusGeometry ?? BorderRadius.zero,
); );
final mediaQueryData = MediaQuery.of(context);
if (attachments.length > 1) { if (attachments.length > 1) {
return Padding( return Padding(
padding: attachmentPadding, padding: attachmentPadding,
@@ -118,8 +119,8 @@ class MessageWidget extends StatefulWidget {
color: messageTheme.messageBackgroundColor, color: messageTheme.messageBackgroundColor,
child: ImageGroup( child: ImageGroup(
size: Size( size: Size(
MediaQuery.of(context).size.width * 0.8, mediaQueryData.size.width * 0.8,
MediaQuery.of(context).size.height * 0.3, mediaQueryData.size.height * 0.3,
), ),
images: attachments, images: attachments,
message: message, message: message,
@@ -142,8 +143,8 @@ class MessageWidget extends StatefulWidget {
message: message, message: message,
messageTheme: messageTheme, messageTheme: messageTheme,
size: Size( size: Size(
MediaQuery.of(context).size.width * 0.8, mediaQueryData.size.width * 0.8,
MediaQuery.of(context).size.height * 0.3, mediaQueryData.size.height * 0.3,
), ),
onShowMessage: onShowMessage, onShowMessage: onShowMessage,
onReturnAction: onReturnAction, onReturnAction: onReturnAction,
@@ -167,13 +168,14 @@ class MessageWidget extends StatefulWidget {
return wrapAttachmentWidget( return wrapAttachmentWidget(
context, context,
Column( Column(
children: attachments children: attachments.map((attachment) {
.map((attachment) => VideoAttachment( final mediaQueryData = MediaQuery.of(context);
return VideoAttachment(
attachment: attachment, attachment: attachment,
messageTheme: messageTheme, messageTheme: messageTheme,
size: Size( size: Size(
MediaQuery.of(context).size.width * 0.8, mediaQueryData.size.width * 0.8,
MediaQuery.of(context).size.height * 0.3, mediaQueryData.size.height * 0.3,
), ),
message: message, message: message,
onShowMessage: onShowMessage, onShowMessage: onShowMessage,
@@ -183,8 +185,8 @@ class MessageWidget extends StatefulWidget {
onAttachmentTap(message, attachment); onAttachmentTap(message, attachment);
} }
: null, : null,
)) );
.toList(), }).toList(),
), ),
border, border,
reverse, reverse,
@@ -200,18 +202,19 @@ class MessageWidget extends StatefulWidget {
return wrapAttachmentWidget( return wrapAttachmentWidget(
context, context,
Column( Column(
children: attachments children: attachments.map((attachment) {
.map((attachment) => GiphyAttachment( final mediaQueryData = MediaQuery.of(context);
return GiphyAttachment(
attachment: attachment, attachment: attachment,
message: message, message: message,
size: Size( size: Size(
MediaQuery.of(context).size.width * 0.8, mediaQueryData.size.width * 0.8,
MediaQuery.of(context).size.height * 0.3, mediaQueryData.size.height * 0.3,
), ),
onShowMessage: onShowMessage, onShowMessage: onShowMessage,
onReturnAction: onReturnAction, onReturnAction: onReturnAction,
)) );
.toList(), }).toList(),
), ),
border, border,
reverse, reverse,
@@ -230,21 +233,24 @@ class MessageWidget extends StatefulWidget {
return Column( return Column(
children: attachments children: attachments
.map<Widget>((attachment) => wrapAttachmentWidget( .map<Widget>((attachment) {
final mediaQueryData = MediaQuery.of(context);
return wrapAttachmentWidget(
context, context,
FileAttachment( FileAttachment(
message: message, message: message,
attachment: attachment, attachment: attachment,
size: Size( size: Size(
MediaQuery.of(context).size.width * 0.8, mediaQueryData.size.width * 0.8,
MediaQuery.of(context).size.height * 0.3, mediaQueryData.size.height * 0.3,
), ),
), ),
border, border,
reverse, reverse,
attachmentBorderRadiusGeometry as BorderRadius? ?? attachmentBorderRadiusGeometry as BorderRadius? ??
BorderRadius.zero, BorderRadius.zero,
)) );
})
.insertBetween(SizedBox( .insertBetween(SizedBox(
height: attachmentPadding.vertical / 2, height: attachmentPadding.vertical / 2,
)) ))
@@ -658,12 +664,13 @@ class _MessageWidgetState extends State<MessageWidget>
widget.onQuotedMessageTap != null widget.onQuotedMessageTap != null
? () => widget.onQuotedMessageTap!(widget.message.quotedMessageId) ? () => widget.onQuotedMessageTap!(widget.message.quotedMessageId)
: null; : null;
final chatThemeData = StreamChatTheme.of(context);
return QuotedMessageWidget( return QuotedMessageWidget(
onTap: onTap, onTap: onTap,
message: widget.message.quotedMessage!, message: widget.message.quotedMessage!,
messageTheme: isMyMessage messageTheme: isMyMessage
? StreamChatTheme.of(context).otherMessageTheme ? chatThemeData.otherMessageTheme
: StreamChatTheme.of(context).ownMessageTheme, : chatThemeData.ownMessageTheme,
reverse: widget.reverse, reverse: widget.reverse,
padding: EdgeInsets.only( padding: EdgeInsets.only(
right: 8, left: 8, top: 8, bottom: hasNonUrlAttachments ? 8 : 0), right: 8, left: 8, top: 8, bottom: hasNonUrlAttachments ? 8 : 0),
@@ -672,6 +679,7 @@ class _MessageWidgetState extends State<MessageWidget>
Widget get _bottomRow { Widget get _bottomRow {
if (isDeleted) { if (isDeleted) {
final chatThemeData = StreamChatTheme.of(context);
return Transform( return Transform(
transform: Matrix4.rotationY(widget.reverse ? pi : 0), transform: Matrix4.rotationY(widget.reverse ? pi : 0),
alignment: Alignment.center, alignment: Alignment.center,
@@ -679,16 +687,14 @@ class _MessageWidgetState extends State<MessageWidget>
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
StreamSvgIcon.eye( StreamSvgIcon.eye(
color: StreamChatTheme.of(context).colorTheme.grey, color: chatThemeData.colorTheme.grey,
size: 16, size: 16,
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
Text( Text(
'Only visible to you', 'Only visible to you',
style: StreamChatTheme.of(context) style: chatThemeData.textTheme.footnote
.textTheme .copyWith(color: chatThemeData.colorTheme.grey),
.footnote
.copyWith(color: StreamChatTheme.of(context).colorTheme.grey),
), ),
], ],
), ),
@@ -41,15 +41,16 @@ class OptionListTile extends StatelessWidget {
final TextStyle? titleTextStyle; final TextStyle? titleTextStyle;
@override @override
Widget build(BuildContext context) => Column( Widget build(BuildContext context) {
final chatThemeData = StreamChatTheme.of(context);
return Column(
children: [ children: [
Container( Container(
color: separatorColor ?? color: separatorColor ?? chatThemeData.colorTheme.greyGainsboro,
StreamChatTheme.of(context).colorTheme.greyGainsboro,
height: 1, height: 1,
), ),
Material( Material(
color: tileColor ?? StreamChatTheme.of(context).colorTheme.white, color: tileColor ?? chatThemeData.colorTheme.white,
child: SizedBox( child: SizedBox(
height: 63, height: 63,
child: InkWell( child: InkWell(
@@ -67,11 +68,8 @@ class OptionListTile extends StatelessWidget {
title!, title!,
style: titleTextStyle ?? style: titleTextStyle ??
(titleColor == null (titleColor == null
? StreamChatTheme.of(context).textTheme.bodyBold ? chatThemeData.textTheme.bodyBold
: StreamChatTheme.of(context) : chatThemeData.textTheme.bodyBold.copyWith(
.textTheme
.bodyBold
.copyWith(
color: titleColor, color: titleColor,
)), )),
), ),
@@ -94,3 +92,4 @@ class OptionListTile extends StatelessWidget {
], ],
); );
} }
}
@@ -126,6 +126,8 @@ class ReactionBubble extends StatelessWidget {
(r) => r.type == reaction.type, (r) => r.type == reaction.type,
); );
final chatThemeData = StreamChatTheme.of(context);
final userId = StreamChat.of(context).user?.id;
return Padding( return Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 4, horizontal: 4,
@@ -135,24 +137,16 @@ class ReactionBubble extends StatelessWidget {
assetName: reactionIcon.assetName, assetName: reactionIcon.assetName,
width: 16, width: 16,
height: 16, height: 16,
color: (!highlightOwnReactions || color: (!highlightOwnReactions || reaction.user?.id == userId)
reaction.user?.id == StreamChat.of(context).user?.id) ? chatThemeData.colorTheme.accentBlue
? StreamChatTheme.of(context).colorTheme.accentBlue : chatThemeData.colorTheme.black.withOpacity(.5),
: StreamChatTheme.of(context)
.colorTheme
.black
.withOpacity(.5),
) )
: Icon( : Icon(
Icons.help_outline_rounded, Icons.help_outline_rounded,
size: 16, size: 16,
color: (!highlightOwnReactions || color: (!highlightOwnReactions || reaction.user?.id == userId)
reaction.user?.id == StreamChat.of(context).user?.id) ? chatThemeData.colorTheme.accentBlue
? StreamChatTheme.of(context).colorTheme.accentBlue : chatThemeData.colorTheme.black.withOpacity(.5),
: StreamChatTheme.of(context)
.colorTheme
.black
.withOpacity(.5),
), ),
); );
} }
@@ -2,8 +2,8 @@ import 'dart:math';
import 'package:ezanimation/ezanimation.dart'; import 'package:ezanimation/ezanimation.dart';
import 'package:flutter/material.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/extension.dart';
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/screenshots/reaction_picker.png) /// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/screenshots/reaction_picker.png)
@@ -33,7 +33,8 @@ class _ReactionPickerState extends State<ReactionPicker>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final reactionIcons = StreamChatTheme.of(context).reactionIcons; final chatThemeData = StreamChatTheme.of(context);
final reactionIcons = chatThemeData.reactionIcons;
if (animations.isEmpty && reactionIcons.isNotEmpty) { if (animations.isEmpty && reactionIcons.isNotEmpty) {
reactionIcons.forEach((element) { reactionIcons.forEach((element) {
@@ -57,7 +58,7 @@ class _ReactionPickerState extends State<ReactionPicker>
scale: val, scale: val,
child: Material( child: Material(
borderRadius: BorderRadius.circular(24), borderRadius: BorderRadius.circular(24),
color: StreamChatTheme.of(context).colorTheme.white, color: chatThemeData.colorTheme.white,
clipBehavior: Clip.hardEdge, clipBehavior: Clip.hardEdge,
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
@@ -119,9 +120,8 @@ class _ReactionPickerState extends State<ReactionPicker>
animations[index].value * 24.0, animations[index].value * 24.0,
), ),
color: ownReactionIndex != -1 color: ownReactionIndex != -1
? StreamChatTheme.of(context) ? chatThemeData
.colorTheme .colorTheme.accentBlue
.accentBlue
: Theme.of(context) : Theme.of(context)
.iconTheme .iconTheme
.color! .color!
@@ -97,7 +97,9 @@ class ThreadHeader extends StatelessWidget implements PreferredSizeWidget {
final List<Widget>? actions; final List<Widget>? actions;
@override @override
Widget build(BuildContext context) => AppBar( Widget build(BuildContext context) {
final chatThemeData = StreamChatTheme.of(context);
return AppBar(
automaticallyImplyLeading: false, automaticallyImplyLeading: false,
brightness: Theme.of(context).brightness, brightness: Theme.of(context).brightness,
elevation: 1, elevation: 1,
@@ -109,8 +111,7 @@ class ThreadHeader extends StatelessWidget implements PreferredSizeWidget {
showUnreads: true, showUnreads: true,
) )
: const SizedBox()), : const SizedBox()),
backgroundColor: backgroundColor: chatThemeData.channelTheme.channelHeaderTheme.color,
StreamChatTheme.of(context).channelTheme.channelHeaderTheme.color,
centerTitle: true, centerTitle: true,
actions: actions, actions: actions,
title: InkWell( title: InkWell(
@@ -123,10 +124,7 @@ class ThreadHeader extends StatelessWidget implements PreferredSizeWidget {
title ?? title ??
Text( Text(
'Thread Reply', 'Thread Reply',
style: StreamChatTheme.of(context) style: chatThemeData.channelTheme.channelHeaderTheme.title,
.channelTheme
.channelHeaderTheme
.title,
), ),
const SizedBox(height: 2), const SizedBox(height: 2),
subtitle ?? subtitle ??
@@ -136,17 +134,13 @@ class ThreadHeader extends StatelessWidget implements PreferredSizeWidget {
children: [ children: [
Text( Text(
'with ', 'with ',
style: StreamChatTheme.of(context) style: chatThemeData
.channelTheme .channelTheme.channelHeaderTheme.subtitle,
.channelHeaderTheme
.subtitle,
), ),
Flexible( Flexible(
child: ChannelName( child: ChannelName(
textStyle: StreamChatTheme.of(context) textStyle: chatThemeData
.channelTheme .channelTheme.channelHeaderTheme.subtitle,
.channelHeaderTheme
.subtitle,
), ),
), ),
], ],
@@ -156,6 +150,7 @@ class ThreadHeader extends StatelessWidget implements PreferredSizeWidget {
), ),
), ),
); );
}
@override @override
final Size preferredSize; final Size preferredSize;
@@ -26,7 +26,9 @@ class UrlAttachment extends StatelessWidget {
final EdgeInsets textPadding; final EdgeInsets textPadding;
@override @override
Widget build(BuildContext context) => GestureDetector( Widget build(BuildContext context) {
final chatThemeData = StreamChatTheme.of(context);
return GestureDetector(
onTap: () { onTap: () {
launchURL( launchURL(
context, context,
@@ -58,8 +60,7 @@ class UrlAttachment extends StatelessWidget {
borderRadius: const BorderRadius.only( borderRadius: const BorderRadius.only(
topRight: Radius.circular(16), topRight: Radius.circular(16),
), ),
color: color: chatThemeData.colorTheme.blueAlice,
StreamChatTheme.of(context).colorTheme.blueAlice,
), ),
child: Padding( child: Padding(
padding: const EdgeInsets.only( padding: const EdgeInsets.only(
@@ -69,13 +70,8 @@ class UrlAttachment extends StatelessWidget {
), ),
child: Text( child: Text(
hostDisplayName, hostDisplayName,
style: StreamChatTheme.of(context) style: chatThemeData.textTheme.bodyBold.copyWith(
.textTheme color: chatThemeData.colorTheme.accentBlue,
.bodyBold
.copyWith(
color: StreamChatTheme.of(context)
.colorTheme
.accentBlue,
), ),
), ),
), ),
@@ -94,17 +90,13 @@ class UrlAttachment extends StatelessWidget {
urlAttachment.title!.trim(), urlAttachment.title!.trim(),
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: StreamChatTheme.of(context) style: chatThemeData.textTheme.body
.textTheme
.body
.copyWith(fontWeight: FontWeight.w700), .copyWith(fontWeight: FontWeight.w700),
), ),
if (urlAttachment.text != null) if (urlAttachment.text != null)
Text( Text(
urlAttachment.text!, urlAttachment.text!,
style: StreamChatTheme.of(context) style: chatThemeData.textTheme.body
.textTheme
.body
.copyWith(fontWeight: FontWeight.w400), .copyWith(fontWeight: FontWeight.w400),
), ),
], ],
@@ -114,3 +106,4 @@ class UrlAttachment extends StatelessWidget {
), ),
); );
} }
}
@@ -1,7 +1,7 @@
import 'package:cached_network_image/cached_network_image.dart'; import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.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/stream_chat_flutter.dart';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
/// Widget that displays a user avatar /// Widget that displays a user avatar
class UserAvatar extends StatelessWidget { class UserAvatar extends StatelessWidget {
@@ -95,8 +95,7 @@ class UserAvatar extends StatelessWidget {
child: Container( child: Container(
constraints: constraints ?? constraints: constraints ??
streamChatTheme.ownMessageTheme.avatarTheme?.constraints, streamChatTheme.ownMessageTheme.avatarTheme?.constraints,
color: selectionColor ?? color: selectionColor ?? streamChatTheme.colorTheme.accentBlue,
StreamChatTheme.of(context).colorTheme.accentBlue,
child: Padding( child: Padding(
padding: EdgeInsets.all(selectionThickness), padding: EdgeInsets.all(selectionThickness),
child: avatar, child: avatar,
@@ -48,7 +48,9 @@ class UserItem extends StatelessWidget {
final bool showLastOnline; final bool showLastOnline;
@override @override
Widget build(BuildContext context) => ListTile( Widget build(BuildContext context) {
final chatThemeData = StreamChatTheme.of(context);
return ListTile(
onTap: () { onTap: () {
if (onTap != null) { if (onTap != null) {
onTap!(user); onTap!(user);
@@ -73,22 +75,25 @@ class UserItem extends StatelessWidget {
), ),
trailing: selected trailing: selected
? StreamSvgIcon.checkSend( ? StreamSvgIcon.checkSend(
color: StreamChatTheme.of(context).colorTheme.accentBlue, color: chatThemeData.colorTheme.accentBlue,
) )
: null, : null,
title: Text( title: Text(
user.name, user.name,
style: StreamChatTheme.of(context).textTheme.bodyBold, style: chatThemeData.textTheme.bodyBold,
), ),
subtitle: showLastOnline ? _buildLastActive(context) : null, subtitle: showLastOnline ? _buildLastActive(context) : null,
); );
}
Widget _buildLastActive(context) => Text( Widget _buildLastActive(context) {
final chatTheme = StreamChatTheme.of(context);
return Text(
user.online == true user.online == true
? 'Online' ? 'Online'
: 'Last online ${Jiffy(user.lastActive).fromNow()}', : 'Last online ${Jiffy(user.lastActive).fromNow()}',
style: StreamChatTheme.of(context).textTheme.footnote.copyWith( style: chatTheme.textTheme.footnote
color: .copyWith(color: chatTheme.colorTheme.black.withOpacity(.5)),
StreamChatTheme.of(context).colorTheme.black.withOpacity(.5)),
); );
} }
}
@@ -295,9 +295,11 @@ class _UserListViewState extends State<UserListView>
if (i < items.length) { if (i < items.length) {
final item = items[i]; final item = items[i];
return item.when( return item.when(
headerItem: (header) => Container( headerItem: (header) {
final chatThemeData = StreamChatTheme.of(context);
return Container(
key: ValueKey<String>('HEADER-$header'), key: ValueKey<String>('HEADER-$header'),
color: StreamChatTheme.of(context).colorTheme.black.withOpacity(0.05), color: chatThemeData.colorTheme.black.withOpacity(0.05),
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6), padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
child: Text( child: Text(
@@ -305,11 +307,12 @@ class _UserListViewState extends State<UserListView>
style: TextStyle( style: TextStyle(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
fontSize: 14.5, fontSize: 14.5,
color: StreamChatTheme.of(context).colorTheme.grey, color: chatThemeData.colorTheme.grey,
),
), ),
), ),
), ),
);
},
userItem: (user) { userItem: (user) {
final selected = widget.selectedUsers?.contains(user) ?? false; final selected = widget.selectedUsers?.contains(user) ?? false;
return Container( return Container(
+20 -27
View File
@@ -1,9 +1,9 @@
import 'dart:math'; import 'dart:math';
import 'package:flutter/material.dart'; 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:stream_chat_flutter_core/stream_chat_flutter_core.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
/// Launch URL /// Launch URL
Future<void> launchURL(BuildContext context, String? url) async { Future<void> launchURL(BuildContext context, String? url) async {
@@ -27,9 +27,10 @@ Future<bool?> showConfirmationDialog(
Widget? icon, Widget? icon,
String? question, String? question,
String? cancelText, String? cancelText,
}) => }) {
showModalBottomSheet( final chatThemeData = StreamChatTheme.of(context);
backgroundColor: StreamChatTheme.of(context).colorTheme.white, return showModalBottomSheet(
backgroundColor: chatThemeData.colorTheme.white,
context: context, context: context,
shape: const RoundedRectangleBorder( shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only( borderRadius: BorderRadius.only(
@@ -37,7 +38,7 @@ Future<bool?> showConfirmationDialog(
topRight: Radius.circular(16), topRight: Radius.circular(16),
)), )),
builder: (context) { builder: (context) {
final effect = StreamChatTheme.of(context).colorTheme.borderTop; final effect = chatThemeData.colorTheme.borderTop;
return SafeArea( return SafeArea(
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
@@ -47,7 +48,7 @@ Future<bool?> showConfirmationDialog(
const SizedBox(height: 26), const SizedBox(height: 26),
Text( Text(
title, title,
style: StreamChatTheme.of(context).textTheme.headlineBold, style: chatThemeData.textTheme.headlineBold,
), ),
const SizedBox(height: 7), const SizedBox(height: 7),
if (question != null) if (question != null)
@@ -72,13 +73,8 @@ Future<bool?> showConfirmationDialog(
}, },
child: Text( child: Text(
cancelText, cancelText,
style: StreamChatTheme.of(context) style: chatThemeData.textTheme.bodyBold.copyWith(
.textTheme color: chatThemeData.colorTheme.black
.bodyBold
.copyWith(
color: StreamChatTheme.of(context)
.colorTheme
.black
.withOpacity(0.5)), .withOpacity(0.5)),
), ),
), ),
@@ -93,13 +89,8 @@ Future<bool?> showConfirmationDialog(
}, },
child: Text( child: Text(
okText, okText,
style: StreamChatTheme.of(context) style: chatThemeData.textTheme.bodyBold.copyWith(
.textTheme color: chatThemeData.colorTheme.accentRed),
.bodyBold
.copyWith(
color: StreamChatTheme.of(context)
.colorTheme
.accentRed),
), ),
), ),
), ),
@@ -110,6 +101,7 @@ Future<bool?> showConfirmationDialog(
), ),
); );
}); });
}
/// Shows info dialog /// Shows info dialog
Future<bool?> showInfoDialog( Future<bool?> showInfoDialog(
@@ -119,10 +111,10 @@ Future<bool?> showInfoDialog(
Widget? icon, Widget? icon,
String? details, String? details,
StreamChatThemeData? theme, StreamChatThemeData? theme,
}) => }) {
showModalBottomSheet( final chatThemeData = StreamChatTheme.of(context);
backgroundColor: theme?.colorTheme.white ?? return showModalBottomSheet(
StreamChatTheme.of(context).colorTheme.white, backgroundColor: theme?.colorTheme.white ?? chatThemeData.colorTheme.white,
context: context, context: context,
shape: const RoundedRectangleBorder( shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only( borderRadius: BorderRadius.only(
@@ -143,7 +135,7 @@ Future<bool?> showInfoDialog(
Text( Text(
title, title,
style: theme?.textTheme.headlineBold ?? style: theme?.textTheme.headlineBold ??
StreamChatTheme.of(context).textTheme.headlineBold, chatThemeData.textTheme.headlineBold,
), ),
const SizedBox( const SizedBox(
height: 7, height: 7,
@@ -154,7 +146,7 @@ Future<bool?> showInfoDialog(
), ),
Container( Container(
color: theme?.colorTheme.black.withOpacity(.08) ?? color: theme?.colorTheme.black.withOpacity(.08) ??
StreamChatTheme.of(context).colorTheme.black.withOpacity(.08), chatThemeData.colorTheme.black.withOpacity(.08),
height: 1, height: 1,
), ),
Center( Center(
@@ -166,7 +158,7 @@ Future<bool?> showInfoDialog(
okText, okText,
style: TextStyle( style: TextStyle(
color: theme?.colorTheme.black.withOpacity(0.5) ?? color: theme?.colorTheme.black.withOpacity(0.5) ??
StreamChatTheme.of(context).colorTheme.accentBlue, chatThemeData.colorTheme.accentBlue,
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
), ),
), ),
@@ -176,6 +168,7 @@ Future<bool?> showInfoDialog(
), ),
), ),
); );
}
/// Get random png with initials /// Get random png with initials
String getRandomPicUrl(User user) => String getRandomPicUrl(User user) =>