Merge branch 'feature/new-ui' of https://github.com/GetStream/stream-chat-flutter into feature/home-screen-fixes

 Conflicts:
	lib/src/channel_bottom_sheet.dart
This commit is contained in:
Deven Joshi
2020-12-31 17:40:31 +05:30
52 changed files with 1480 additions and 883 deletions
+5 -3
View File
@@ -3,6 +3,8 @@ import 'dart:io';
import 'package:flutter/material.dart';
import 'package:stream_chat/stream_chat.dart';
import '../stream_chat_flutter.dart';
class AttachmentError extends StatelessWidget {
final Attachment attachment;
final Size size;
@@ -23,12 +25,12 @@ class AttachmentError extends StatelessWidget {
return Center(
child: Container(
width: size?.width,
height: size?.height,
color: Color(0xffd0021B).withOpacity(.1),
height: size?.height ?? 200,
color: StreamChatTheme.of(context).colorTheme.accentRed.withOpacity(.1),
child: Center(
child: Icon(
Icons.error_outline,
color: Colors.white,
color: StreamChatTheme.of(context).colorTheme.black,
),
),
),
+1 -1
View File
@@ -32,7 +32,7 @@ class AttachmentTitle extends StatelessWidget {
attachment.title,
overflow: TextOverflow.ellipsis,
style: messageTheme.messageText.copyWith(
color: StreamChatTheme.of(context).accentColor,
color: StreamChatTheme.of(context).colorTheme.accentBlue,
fontWeight: FontWeight.bold,
),
),
+3 -3
View File
@@ -2,6 +2,8 @@ import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
import 'package:stream_chat_flutter/src/unread_indicator.dart';
import '../stream_chat_flutter.dart';
class StreamBackButton extends StatelessWidget {
const StreamBackButton({
Key key,
@@ -35,9 +37,7 @@ class StreamBackButton extends StatelessWidget {
},
child: StreamSvgIcon.left(
size: 24,
color: Theme.of(context).brightness == Brightness.dark
? Colors.white
: Colors.black,
color: StreamChatTheme.of(context).colorTheme.black,
),
),
),
+11 -6
View File
@@ -53,7 +53,7 @@ class ChannelImage extends StatelessWidget {
this.showOnlineStatus = true,
this.borderRadius,
this.selected = false,
this.selectionColor = const Color(0xFF006CFF),
this.selectionColor,
this.selectionThickness = 4,
}) : super(key: key);
@@ -105,7 +105,8 @@ class ChannelImage extends StatelessWidget {
.constraints,
onTap: onTap != null ? (_) => onTap() : null,
selected: selected,
selectionColor: selectionColor,
selectionColor: selectionColor ??
StreamChatTheme.of(context).colorTheme.accentBlue,
selectionThickness: selectionThickness,
);
});
@@ -127,7 +128,8 @@ class ChannelImage extends StatelessWidget {
.constraints,
onTap: onTap,
selected: selected,
selectionColor: selectionColor,
selectionColor: selectionColor ??
StreamChatTheme.of(context).colorTheme.accentBlue,
selectionThickness: selectionThickness,
);
}
@@ -145,7 +147,7 @@ class ChannelImage extends StatelessWidget {
.avatarTheme
.constraints,
decoration: BoxDecoration(
color: StreamChatTheme.of(context).accentColor,
color: StreamChatTheme.of(context).colorTheme.accentBlue,
),
child: Stack(
alignment: Alignment.center,
@@ -161,7 +163,9 @@ class ChannelImage extends StatelessWidget {
? snapshot.data['name'][0]
: '',
style: TextStyle(
color: Colors.white,
color: StreamChatTheme.of(context)
.colorTheme
.white,
fontWeight: FontWeight.bold,
),
),
@@ -195,7 +199,8 @@ class ChannelImage extends StatelessWidget {
.ownMessageTheme
.avatarTheme
.constraints,
color: selectionColor,
color: selectionColor ??
StreamChatTheme.of(context).colorTheme.accentBlue,
child: Padding(
padding: EdgeInsets.all(selectionThickness),
child: child,
+1 -1
View File
@@ -134,7 +134,7 @@ class ChannelInfo extends StatelessWidget {
child: Text(
'Try Again',
style: textStyle.copyWith(
color: Color(0xFF006CFF),
color: StreamChatTheme.of(context).colorTheme.accentBlue,
),
),
),
+4 -9
View File
@@ -99,7 +99,7 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
var color;
switch (status) {
case ConnectionStatus.connected:
color = Color(0xFF006CFF);
color = StreamChatTheme.of(context).colorTheme.accentBlue;
break;
case ConnectionStatus.connecting:
color = Colors.grey;
@@ -144,13 +144,8 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
Widget _buildConnectedTitleState(BuildContext context) => Text(
'Stream Chat',
style: StreamChatTheme.of(context)
.channelTheme
.channelHeaderTheme
.title
.copyWith(
fontSize: 16,
fontWeight: FontWeight.bold,
style: StreamChatTheme.of(context).textTheme.headlineBold.copyWith(
color: StreamChatTheme.of(context).colorTheme.black,
),
);
@@ -210,7 +205,7 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
.copyWith(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Color(0xFF006CFF),
color: StreamChatTheme.of(context).colorTheme.accentBlue,
),
),
),
+41 -31
View File
@@ -210,16 +210,18 @@ class _ChannelListViewState extends State<ChannelListView>
padding: const EdgeInsets.all(8.0),
child: StreamSvgIcon.message(
size: 136,
color: Color(0xffDBDBDB),
color: StreamChatTheme.of(context)
.colorTheme
.greyGainsboro,
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'Lets start chatting!',
style: TextStyle(
fontSize: 16,
),
style: StreamChatTheme.of(context)
.textTheme
.headline,
),
),
Padding(
@@ -230,10 +232,14 @@ class _ChannelListViewState extends State<ChannelListView>
child: Text(
'How about sending your first message to a friend?',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14,
color: Color(0xff7A7A7A),
),
style: StreamChatTheme.of(context)
.textTheme
.body
.copyWith(
color: StreamChatTheme.of(context)
.colorTheme
.grey,
),
),
),
],
@@ -249,11 +255,14 @@ class _ChannelListViewState extends State<ChannelListView>
onPressed: widget.onStartChatPressed,
child: Text(
'Start a chat',
style: TextStyle(
color:
StreamChatTheme.of(context).accentColor,
fontWeight: FontWeight.bold,
),
style: StreamChatTheme.of(context)
.textTheme
.bodyBold
.copyWith(
color: StreamChatTheme.of(context)
.colorTheme
.accentBlue,
),
),
),
),
@@ -327,8 +336,8 @@ class _ChannelListViewState extends State<ChannelListView>
Shimmer _buildLoadingItem() {
if (widget.crossAxisCount > 1) {
return Shimmer.fromColors(
baseColor: Color(0xffE5E5E5),
highlightColor: Color(0xffffffff),
baseColor: StreamChatTheme.of(context).colorTheme.greyGainsboro,
highlightColor: StreamChatTheme.of(context).colorTheme.whiteSmoke,
child: Column(
children: [
SizedBox(
@@ -358,12 +367,12 @@ class _ChannelListViewState extends State<ChannelListView>
);
} else {
return Shimmer.fromColors(
baseColor: Color(0xffE5E5E5),
highlightColor: Color(0xffffffff),
baseColor: StreamChatTheme.of(context).colorTheme.greyGainsboro,
highlightColor: StreamChatTheme.of(context).colorTheme.whiteSmoke,
child: ListTile(
leading: Container(
decoration: BoxDecoration(
color: Colors.white,
color: StreamChatTheme.of(context).colorTheme.white,
shape: BoxShape.circle,
),
constraints: BoxConstraints.tightFor(
@@ -375,7 +384,7 @@ class _ChannelListViewState extends State<ChannelListView>
alignment: Alignment.centerLeft,
child: Container(
decoration: BoxDecoration(
color: Colors.white,
color: StreamChatTheme.of(context).colorTheme.white,
borderRadius: BorderRadius.circular(11),
),
constraints: BoxConstraints.tightFor(
@@ -390,7 +399,7 @@ class _ChannelListViewState extends State<ChannelListView>
alignment: Alignment.centerLeft,
child: Container(
decoration: BoxDecoration(
color: Colors.white,
color: StreamChatTheme.of(context).colorTheme.white,
borderRadius: BorderRadius.circular(11),
),
constraints: BoxConstraints.tightFor(
@@ -402,7 +411,7 @@ class _ChannelListViewState extends State<ChannelListView>
Container(
margin: const EdgeInsets.only(left: 16),
decoration: BoxDecoration(
color: Colors.white,
color: StreamChatTheme.of(context).colorTheme.white,
borderRadius: BorderRadius.circular(11),
),
constraints: BoxConstraints.tightFor(
@@ -536,9 +545,7 @@ class _ChannelListViewState extends State<ChannelListView>
);
} else {
final backgroundColor =
Theme.of(context).brightness == Brightness.light
? Color(0xffEBEBEB)
: Color(0xff141414);
StreamChatTheme.of(context).colorTheme.whiteSmoke;
child = Slidable(
enabled: widget.swipeToAction,
actionPane: SlidableBehindActionPane(),
@@ -598,7 +605,7 @@ class _ChannelListViewState extends State<ChannelListView>
IconSlideAction(
color: backgroundColor,
iconWidget: StreamSvgIcon.delete(
color: Color(0xFFFF3742),
color: StreamChatTheme.of(context).colorTheme.accentRed,
),
onTap: () async {
final res = await showConfirmationDialog(
@@ -609,7 +616,9 @@ class _ChannelListViewState extends State<ChannelListView>
'Are you sure you want to delete this conversation?',
cancelText: 'CANCEL',
icon: StreamSvgIcon.delete(
color: Color(0xFFFF3742),
color: StreamChatTheme.of(context)
.colorTheme
.accentRed,
),
);
if (res == true) {
@@ -619,7 +628,7 @@ class _ChannelListViewState extends State<ChannelListView>
),
],
child: Container(
color: StreamChatTheme.of(context).backgroundColor,
color: StreamChatTheme.of(context).colorTheme.whiteSnow,
child: ChannelPreview(
onLongPress: widget.onChannelLongPress,
channel: channel,
@@ -695,7 +704,10 @@ class _ChannelListViewState extends State<ChannelListView>
builder: (context, snapshot) {
if (snapshot.hasError) {
return Container(
color: Color(0xffd0021B).withAlpha(26),
color: StreamChatTheme.of(context)
.colorTheme
.accentRed
.withOpacity(.2),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Center(
@@ -717,9 +729,7 @@ class _ChannelListViewState extends State<ChannelListView>
Widget _separatorBuilder(context, i) {
return Container(
height: 1,
color: Theme.of(context).brightness == Brightness.dark
? Colors.white.withOpacity(0.1)
: Colors.black.withOpacity(0.1),
color: StreamChatTheme.of(context).colorTheme.black.withOpacity(0.08),
);
}
+9 -2
View File
@@ -50,6 +50,9 @@ class ChannelPreview extends StatelessWidget {
return Opacity(
opacity: snapshot.data ? 0.5 : 1,
child: ListTile(
contentPadding: const EdgeInsets.symmetric(
horizontal: 8,
),
onTap: () {
if (onTap != null) {
onTap(channel);
@@ -122,12 +125,16 @@ class ChannelPreview extends StatelessWidget {
padding: const EdgeInsets.only(right: 4.0),
child: SendingIndicator(
message: channel.state.lastMessage,
allRead: channel.state.read
size: StreamChatTheme.of(context)
.channelPreviewTheme
.lastMessageAt
.fontSize,
isMessageRead: channel.state.read
.where((element) => element.lastRead
.isAfter(channel
.state.lastMessage.createdAt))
.length ==
(channel.memberCount ?? 0),
((channel.memberCount ?? 0) - 1),
),
);
}
+62 -32
View File
@@ -1,4 +1,3 @@
import 'package:emojis/emojis.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:jiffy/jiffy.dart';
@@ -21,7 +20,7 @@ class _ChatInfoScreenState extends State<ChatInfoScreen> {
Widget build(BuildContext context) {
final channel = StreamChannel.of(context).channel;
return Scaffold(
backgroundColor: Color(0xFFe6e6e6),
backgroundColor: StreamChatTheme.of(context).colorTheme.whiteSnow,
body: ListView(
children: [
_buildUserHeader(),
@@ -47,7 +46,7 @@ class _ChatInfoScreenState extends State<ChatInfoScreen> {
Widget _buildUserHeader() {
return Material(
color: Colors.white,
color: StreamChatTheme.of(context).colorTheme.whiteSnow,
child: SafeArea(
child: Stack(
children: [
@@ -81,7 +80,11 @@ class _ChatInfoScreenState extends State<ChatInfoScreen> {
child: Text(
widget.user.name,
style: TextStyle(
color: Colors.black.withOpacity(0.5), fontSize: 16.0),
color: StreamChatTheme.of(context)
.colorTheme
.black
.withOpacity(0.5),
fontSize: 16.0),
),
),
onTap: () {},
@@ -92,7 +95,9 @@ class _ChatInfoScreenState extends State<ChatInfoScreen> {
top: 21,
left: 16,
child: InkWell(
child: StreamSvgIcon.left(),
child: StreamSvgIcon.left(
color: StreamChatTheme.of(context).colorTheme.black,
),
onTap: () {
Navigator.of(context).pop();
},
@@ -113,7 +118,7 @@ class _ChatInfoScreenState extends State<ChatInfoScreen> {
// title: 'Notifications',
// leading: StreamSvgIcon.Icon_notification(
// size: 24.0,
// color: Colors.black.withOpacity(0.5),
// color: StreamChatTheme.of(context).colorTheme.black.withOpacity(0.5),
// ),
// trailing: CupertinoSwitch(
// value: true,
@@ -128,7 +133,10 @@ class _ChatInfoScreenState extends State<ChatInfoScreen> {
title: 'Mute user',
leading: StreamSvgIcon.mute(
size: 23.0,
color: Colors.black.withOpacity(0.5),
color: StreamChatTheme.of(context)
.colorTheme
.black
.withOpacity(0.5),
),
trailing: snapshot.data == null
? CircularProgressIndicator()
@@ -149,7 +157,7 @@ class _ChatInfoScreenState extends State<ChatInfoScreen> {
// title: 'Block User',
// leading: StreamSvgIcon.Icon_user_delete(
// size: 24.0,
// color: Colors.black.withOpacity(0.5),
// color: StreamChatTheme.of(context).colorTheme.black.withOpacity(0.5),
// ),
// trailing: CupertinoSwitch(
// value: widget.user.banned,
@@ -167,7 +175,8 @@ class _ChatInfoScreenState extends State<ChatInfoScreen> {
title: 'Photos & Videos',
leading: StreamSvgIcon.pictures(
size: 32.0,
color: Colors.black.withOpacity(0.5),
color:
StreamChatTheme.of(context).colorTheme.black.withOpacity(0.5),
),
trailing: StreamSvgIcon.right(),
onTap: () {
@@ -179,7 +188,8 @@ class _ChatInfoScreenState extends State<ChatInfoScreen> {
title: 'Files',
leading: StreamSvgIcon.files(
size: 32.0,
color: Colors.black.withOpacity(0.5),
color:
StreamChatTheme.of(context).colorTheme.black.withOpacity(0.5),
),
trailing: StreamSvgIcon.right(),
onTap: () {
@@ -191,7 +201,8 @@ class _ChatInfoScreenState extends State<ChatInfoScreen> {
title: 'Shared groups',
leading: StreamSvgIcon.Icon_group(
size: 24.0,
color: Colors.black.withOpacity(0.5),
color:
StreamChatTheme.of(context).colorTheme.black.withOpacity(0.5),
),
trailing: StreamSvgIcon.right(),
onTap: () {
@@ -208,7 +219,7 @@ class _ChatInfoScreenState extends State<ChatInfoScreen> {
Widget _buildDeleteListTile() {
return _OptionListTile(
title: 'Delete',
title: 'Delete conversation',
leading: StreamSvgIcon.delete(
color: Colors.red,
size: 24.0,
@@ -248,12 +259,20 @@ class _ChatInfoScreenState extends State<ChatInfoScreen> {
if (otherMember.online) {
alternativeWidget = Text(
'Online',
style: TextStyle(color: Colors.black.withOpacity(0.5)),
style: TextStyle(
color: StreamChatTheme.of(context)
.colorTheme
.black
.withOpacity(0.5)),
);
} else {
alternativeWidget = Text(
'Last seen ${Jiffy(otherMember.lastActive).fromNow()}',
style: TextStyle(color: Colors.black.withOpacity(0.5)),
style: TextStyle(
color: StreamChatTheme.of(context)
.colorTheme
.black
.withOpacity(0.5)),
);
}
}
@@ -272,10 +291,10 @@ class _ChatInfoScreenState extends State<ChatInfoScreen> {
),
child: Material(
shape: CircleBorder(),
color: Color(0xff20E070),
color: StreamChatTheme.of(context).colorTheme.accentGreen,
),
),
color: Colors.white,
color: StreamChatTheme.of(context).colorTheme.white,
),
alternativeWidget,
],
@@ -303,11 +322,11 @@ class _OptionListTile extends StatelessWidget {
return Column(
children: [
Container(
color: Color(0xffe6e6e6),
color: StreamChatTheme.of(context).colorTheme.white,
height: 2.0,
),
Material(
color: Colors.white,
color: StreamChatTheme.of(context).colorTheme.whiteSnow,
child: Container(
height: 56.0,
child: InkWell(
@@ -365,14 +384,16 @@ class __SharedGroupsScreenState extends State<_SharedGroupsScreen> {
var chat = StreamChat.of(context);
return Scaffold(
backgroundColor: Colors.white,
backgroundColor: StreamChatTheme.of(context).colorTheme.whiteSnow,
appBar: AppBar(
brightness: Theme.of(context).brightness,
elevation: 1,
centerTitle: true,
title: Text(
'Shared Groups',
style: TextStyle(color: Colors.black, fontSize: 16.0),
style: TextStyle(
color: StreamChatTheme.of(context).colorTheme.black,
fontSize: 16.0),
),
leading: Center(
child: InkWell(
@@ -381,7 +402,7 @@ class __SharedGroupsScreenState extends State<_SharedGroupsScreen> {
},
child: Container(
child: StreamSvgIcon.left(
color: Colors.black,
color: StreamChatTheme.of(context).colorTheme.black,
size: 24.0,
),
width: 24.0,
@@ -389,7 +410,7 @@ class __SharedGroupsScreenState extends State<_SharedGroupsScreen> {
),
),
),
backgroundColor: StreamChatTheme.of(context).primaryColor,
backgroundColor: StreamChatTheme.of(context).colorTheme.white,
),
body: FutureBuilder<List<Channel>>(
future: chat.client.queryChannels(
@@ -488,7 +509,11 @@ class __SharedGroupsScreenState extends State<_SharedGroupsScreen> {
padding: const EdgeInsets.all(8.0),
child: Text(
'${channel.memberCount} members',
style: TextStyle(color: Colors.black.withOpacity(0.5)),
style: TextStyle(
color: StreamChatTheme.of(context)
.colorTheme
.black
.withOpacity(0.5)),
),
)
],
@@ -496,7 +521,8 @@ class __SharedGroupsScreenState extends State<_SharedGroupsScreen> {
),
Container(
height: 1.0,
color: Color(0xffe6e6e6),
color:
StreamChatTheme.of(context).colorTheme.black.withOpacity(.08),
),
],
);
@@ -509,14 +535,16 @@ class _MediaDisplayScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
backgroundColor: StreamChatTheme.of(context).colorTheme.white,
appBar: AppBar(
brightness: Theme.of(context).brightness,
elevation: 1,
centerTitle: true,
title: Text(
'Photos & Videos',
style: TextStyle(color: Colors.black, fontSize: 16.0),
style: TextStyle(
color: StreamChatTheme.of(context).colorTheme.black,
fontSize: 16.0),
),
leading: Center(
child: InkWell(
@@ -525,7 +553,7 @@ class _MediaDisplayScreen extends StatelessWidget {
},
child: Container(
child: StreamSvgIcon.left(
color: Colors.black,
color: StreamChatTheme.of(context).colorTheme.black,
size: 24.0,
),
width: 24.0,
@@ -533,7 +561,7 @@ class _MediaDisplayScreen extends StatelessWidget {
),
),
),
backgroundColor: StreamChatTheme.of(context).primaryColor,
backgroundColor: StreamChatTheme.of(context).colorTheme.white,
),
);
}
@@ -543,14 +571,16 @@ class _FileDisplayScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
backgroundColor: StreamChatTheme.of(context).colorTheme.white,
appBar: AppBar(
brightness: Theme.of(context).brightness,
elevation: 1,
centerTitle: true,
title: Text(
'Files',
style: TextStyle(color: Colors.black, fontSize: 16.0),
style: TextStyle(
color: StreamChatTheme.of(context).colorTheme.black,
fontSize: 16.0),
),
leading: Center(
child: InkWell(
@@ -559,7 +589,7 @@ class _FileDisplayScreen extends StatelessWidget {
},
child: Container(
child: StreamSvgIcon.left(
color: Colors.black,
color: StreamChatTheme.of(context).colorTheme.black,
size: 24.0,
),
width: 24.0,
@@ -567,7 +597,7 @@ class _FileDisplayScreen extends StatelessWidget {
),
),
),
backgroundColor: StreamChatTheme.of(context).primaryColor,
backgroundColor: StreamChatTheme.of(context).colorTheme.white,
),
);
}
+6 -9
View File
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:jiffy/jiffy.dart';
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
/// It shows a date divider depending on the date difference
class DateDivider extends StatelessWidget {
@@ -39,21 +40,17 @@ class DateDivider extends StatelessWidget {
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 8),
decoration: BoxDecoration(
color: (Theme.of(context).brightness == Brightness.light
? Colors.black
: Colors.white)
.withOpacity(0.5),
color: StreamChatTheme.of(context).colorTheme.black.withOpacity(0.5),
borderRadius: BorderRadius.circular(
8,
),
),
child: Text(
dayInfo,
style: TextStyle(
fontSize: 12,
color: Colors.white,
fontWeight: FontWeight.bold,
),
style: StreamChatTheme.of(context).textTheme.footnoteBold.copyWith(
color: StreamChatTheme.of(context).colorTheme.white,
fontWeight: FontWeight.bold,
),
),
),
);
+10 -4
View File
@@ -41,8 +41,14 @@ class DeletedMessage extends StatelessWidget {
side: borderSide ??
BorderSide(
color: Theme.of(context).brightness == Brightness.dark
? Colors.white.withAlpha(24)
: Colors.black.withAlpha(24),
? StreamChatTheme.of(context)
.colorTheme
.white
.withAlpha(24)
: StreamChatTheme.of(context)
.colorTheme
.black
.withAlpha(24),
),
),
child: Padding(
@@ -58,8 +64,8 @@ class DeletedMessage extends StatelessWidget {
style: messageTheme.messageText.copyWith(
fontStyle: FontStyle.italic,
color: (Theme.of(context).brightness == Brightness.dark
? Colors.white
: Colors.black)
? StreamChatTheme.of(context).colorTheme.white
: StreamChatTheme.of(context).colorTheme.black)
.withOpacity(.5),
),
),
+15 -15
View File
@@ -9,6 +9,7 @@ import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
import 'package:stream_chat_flutter/src/utils.dart';
import 'package:video_compress/video_compress.dart';
import 'package:video_player/video_player.dart';
import 'media_utils.dart';
enum FileAttachmentType { local, online }
@@ -61,13 +62,13 @@ class _FileAttachmentState extends State<FileAttachment> {
child: Container(
width: widget.size?.width ?? 100,
height: 56.0,
margin: widget.trailing != null ? EdgeInsets.only(top: 4.0) : null,
decoration: BoxDecoration(
color: Colors.white,
color: StreamChatTheme.of(context).colorTheme.white,
borderRadius:
widget.trailing != null ? BorderRadius.circular(16.0) : null,
border: widget.trailing != null
? Border.fromBorderSide(BorderSide(color: Color(0xFFE6E6E6)))
? Border.fromBorderSide(BorderSide(
color: StreamChatTheme.of(context).colorTheme.greyWhisper))
: null,
),
child: Row(
@@ -88,10 +89,7 @@ class _FileAttachmentState extends State<FileAttachment> {
children: [
Text(
widget.attachment?.title ?? 'File',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14.0,
),
style: StreamChatTheme.of(context).textTheme.bodyBold,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
@@ -100,10 +98,11 @@ class _FileAttachmentState extends State<FileAttachment> {
),
Text(
'${_getSizeText(widget.attachment.extraData['file_size'])}',
style: TextStyle(
color: Colors.black.withOpacity(0.5),
fontSize: 14.0,
),
style: StreamChatTheme.of(context).textTheme.body.copyWith(
color: StreamChatTheme.of(context)
.colorTheme
.black
.withOpacity(0.5)),
),
],
),
@@ -113,7 +112,7 @@ class _FileAttachmentState extends State<FileAttachment> {
widget.trailing ??
IconButton(
icon: StreamSvgIcon.cloud_download(
color: Colors.black,
color: StreamChatTheme.of(context).colorTheme.black,
),
onPressed: () {
launchURL(context, widget.attachment.assetUrl);
@@ -141,13 +140,13 @@ class _FileAttachmentState extends State<FileAttachment> {
// subtitle: Text(
// '${attachment.extraData['file_size'] ?? 'N/A'} bytes',
// style: TextStyle(
// color: Colors.black.withOpacity(0.5),
// color: StreamChatTheme.of(context).colorTheme.black.withOpacity(0.5),
// ),
// ),
// trailing: trailing ??
// IconButton(
// icon: StreamSvgIcon.cloud_download(
// color: Colors.black,
// color: StreamChatTheme.of(context).colorTheme.black,
// ),
// onPressed: () {
// launchURL(context, attachment.assetUrl);
@@ -179,7 +178,8 @@ class _FileAttachmentState extends State<FileAttachment> {
width: 20.0,
height: 20.0,
child: CircularProgressIndicator(
backgroundColor: StreamChatTheme.of(context).accentColor,
backgroundColor:
StreamChatTheme.of(context).colorTheme.accentBlue,
),
),
);
+47 -17
View File
@@ -112,7 +112,10 @@ class GiphyAttachment extends StatelessWidget {
bottom: 16,
left: 16,
child: Material(
color: Colors.black.withOpacity(.5),
color: StreamChatTheme.of(context)
.colorTheme
.black
.withOpacity(.5),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
@@ -124,13 +127,16 @@ class GiphyAttachment extends StatelessWidget {
child: Row(
children: [
StreamSvgIcon.lightning(
color: Colors.white,
color:
StreamChatTheme.of(context).colorTheme.white,
size: 16,
),
Text(
'GIPHY',
style: TextStyle(
color: Colors.white,
color: StreamChatTheme.of(context)
.colorTheme
.white,
fontWeight: FontWeight.bold,
fontSize: 11,
),
@@ -202,7 +208,10 @@ class GiphyAttachment extends StatelessWidget {
height: 4.0,
),
Container(
color: Colors.black.withOpacity(0.2),
color: StreamChatTheme.of(context)
.colorTheme
.black
.withOpacity(0.2),
width: double.infinity,
height: 0.5,
),
@@ -220,15 +229,24 @@ class GiphyAttachment extends StatelessWidget {
},
child: Text(
'Cancel',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.black.withOpacity(0.5)),
style: StreamChatTheme.of(context)
.textTheme
.bodyBold
.copyWith(
color: StreamChatTheme.of(context)
.colorTheme
.black
.withOpacity(0.5),
),
),
),
),
Container(
width: 0.5,
color: Colors.black.withOpacity(0.2),
color: StreamChatTheme.of(context)
.colorTheme
.black
.withOpacity(0.2),
height: 50.0,
),
Expanded(
@@ -242,7 +260,9 @@ class GiphyAttachment extends StatelessWidget {
child: Text(
'Send',
style: TextStyle(
color: StreamChatTheme.of(context).accentColor,
color: StreamChatTheme.of(context)
.colorTheme
.accentBlue,
fontWeight: FontWeight.bold),
),
),
@@ -263,7 +283,10 @@ class GiphyAttachment extends StatelessWidget {
mainAxisSize: MainAxisSize.min,
children: [
StreamSvgIcon.eye(
color: Colors.black.withOpacity(0.5),
color: StreamChatTheme.of(context)
.colorTheme
.black
.withOpacity(0.5),
size: 16.0,
),
SizedBox(
@@ -271,10 +294,14 @@ class GiphyAttachment extends StatelessWidget {
),
Text(
'Only visible to you',
style: TextStyle(
color: Colors.black.withOpacity(0.5),
fontSize: 12.0,
),
style: StreamChatTheme.of(context)
.textTheme
.footnote
.copyWith(
color: StreamChatTheme.of(context)
.colorTheme
.black
.withOpacity(0.5)),
),
],
),
@@ -331,7 +358,10 @@ class GiphyAttachment extends StatelessWidget {
bottom: 8,
left: 8,
child: Material(
color: Colors.black.withOpacity(.5),
color: StreamChatTheme.of(context)
.colorTheme
.black
.withOpacity(.5),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
@@ -343,13 +373,13 @@ class GiphyAttachment extends StatelessWidget {
child: Row(
children: [
StreamSvgIcon.lightning(
color: Colors.white,
color: StreamChatTheme.of(context).colorTheme.white,
size: 16,
),
Text(
'GIPHY',
style: TextStyle(
color: Colors.white,
color: StreamChatTheme.of(context).colorTheme.white,
fontWeight: FontWeight.bold,
fontSize: 11,
),
+4 -3
View File
@@ -11,7 +11,7 @@ class GroupImage extends StatelessWidget {
this.onTap,
this.borderRadius,
this.selected = false,
this.selectionColor = const Color(0xFF006CFF),
this.selectionColor,
this.selectionThickness = 4,
}) : super(key: key);
@@ -43,7 +43,7 @@ class GroupImage extends StatelessWidget {
.avatarTheme
.constraints,
decoration: BoxDecoration(
color: StreamChatTheme.of(context).accentColor,
color: StreamChatTheme.of(context).colorTheme.accentBlue,
),
child: Flex(
direction: Axis.vertical,
@@ -110,7 +110,8 @@ class GroupImage extends StatelessWidget {
streamChatTheme.ownMessageTheme.avatarTheme.borderRadius) +
BorderRadius.circular(selectionThickness),
child: Container(
color: selectionColor,
color: selectionColor ??
StreamChatTheme.of(context).colorTheme.accentBlue,
height: 64.0,
width: 64.0,
child: Padding(
+34 -16
View File
@@ -1,13 +1,14 @@
import 'dart:typed_data';
import 'dart:ui';
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:dio/dio.dart';
import 'package:image_gallery_saver/image_gallery_saver.dart';
import '../stream_chat_flutter.dart';
import 'package:path_provider/path_provider.dart';
import '../stream_chat_flutter.dart';
class ImageActionsModal extends StatelessWidget {
final Message message;
final String userName;
@@ -66,11 +67,12 @@ class ImageActionsModal extends StatelessWidget {
padding: const EdgeInsets.only(top: 8.0),
child: Text(
userName,
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.w700,
color: Colors.white,
),
style: StreamChatTheme.of(context)
.textTheme
.headlineBold
.copyWith(
color:
StreamChatTheme.of(context).colorTheme.white),
),
),
Text(
@@ -78,14 +80,18 @@ class ImageActionsModal extends StatelessWidget {
style: StreamChatTheme.of(context)
.channelPreviewTheme
.subtitle
.copyWith(color: Colors.white.withOpacity(0.5)),
.copyWith(
color: StreamChatTheme.of(context)
.colorTheme
.white
.withOpacity(0.5)),
),
],
),
IconButton(
icon: StreamSvgIcon.close(
size: 24.0,
color: Colors.white,
color: StreamChatTheme.of(context).colorTheme.white,
),
onPressed: () {
Navigator.pop(context);
@@ -104,10 +110,10 @@ class ImageActionsModal extends StatelessWidget {
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
color: Color(0xffe5e5e5),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: ListTile.divideTiles(
color: StreamChatTheme.of(context).colorTheme.greyWhisper,
context: context,
tiles: [
_buildButton(
@@ -115,7 +121,10 @@ class ImageActionsModal extends StatelessWidget {
'Reply',
StreamSvgIcon.Icon_curve_line_left_up(
size: 24.0,
color: Colors.black.withOpacity(0.5),
color: StreamChatTheme.of(context)
.colorTheme
.black
.withOpacity(0.5),
),
() {}),
_buildButton(
@@ -123,7 +132,8 @@ class ImageActionsModal extends StatelessWidget {
'Show in Chat',
StreamSvgIcon.eye(
size: 24.0,
color: Colors.black,
color:
StreamChatTheme.of(context).colorTheme.black,
), () {
Navigator.pop(context);
Navigator.pop(context);
@@ -133,7 +143,10 @@ class ImageActionsModal extends StatelessWidget {
'Save ${urls[currentIndex].type == 'video' ? 'Video' : 'Image'}',
StreamSvgIcon.Icon_save(
size: 24.0,
color: Colors.black.withOpacity(0.5),
color: StreamChatTheme.of(context)
.colorTheme
.black
.withOpacity(0.5),
), () async {
var url = urls[currentIndex].imageUrl ??
urls[currentIndex].assetUrl ??
@@ -153,7 +166,9 @@ class ImageActionsModal extends StatelessWidget {
'Delete',
StreamSvgIcon.delete(
size: 24.0,
color: Color(0xffFF3742),
color: StreamChatTheme.of(context)
.colorTheme
.accentRed,
),
() {
Navigator.pop(context);
@@ -163,7 +178,9 @@ class ImageActionsModal extends StatelessWidget {
StreamChannel.of(context).channel.cid,
);
},
color: Color(0xffFF3742),
color: StreamChatTheme.of(context)
.colorTheme
.accentRed,
),
],
).toList(),
@@ -182,10 +199,11 @@ class ImageActionsModal extends StatelessWidget {
{Color color}) {
var titleStyle = TextStyle(
fontSize: 14.5,
color: Colors.black,
color: StreamChatTheme.of(context).colorTheme.black,
);
return Material(
color: StreamChatTheme.of(context).colorTheme.white,
child: InkWell(
onTap: onTap,
child: ListTile(
+47 -38
View File
@@ -5,10 +5,10 @@ import 'dart:typed_data';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:chewie/chewie.dart';
import 'package:dio/dio.dart';
import 'package:esys_flutter_share/esys_flutter_share.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:dio/dio.dart';
import 'package:image_gallery_saver/image_gallery_saver.dart';
import 'package:path_provider/path_provider.dart';
import 'package:stream_chat/stream_chat.dart';
@@ -114,7 +114,7 @@ class _ImageFooterState extends State<ImageFooter> {
IconButton(
icon: StreamSvgIcon.icon_SHARE(
size: 24.0,
color: Colors.black,
color: StreamChatTheme.of(context).colorTheme.black,
),
onPressed: () {
_buildShareModal(context);
@@ -130,11 +130,7 @@ class _ImageFooterState extends State<ImageFooter> {
children: <Widget>[
Text(
'${widget.currentPage + 1} of ${widget.totalPages}',
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.w700,
color: Colors.black,
),
style: StreamChatTheme.of(context).textTheme.headlineBold,
),
],
),
@@ -142,7 +138,7 @@ class _ImageFooterState extends State<ImageFooter> {
),
IconButton(
icon: StreamSvgIcon.Icon_grid(
color: Colors.black,
color: StreamChatTheme.of(context).colorTheme.black,
),
onPressed: () {
_buildPhotosModal(context);
@@ -170,7 +166,7 @@ class _ImageFooterState extends State<ImageFooter> {
children: [
Container(
decoration: BoxDecoration(
color: Colors.white,
color: StreamChatTheme.of(context).colorTheme.white,
borderRadius: BorderRadius.only(
topRight: Radius.circular(16.0),
topLeft: Radius.circular(16.0),
@@ -182,11 +178,8 @@ class _ImageFooterState extends State<ImageFooter> {
padding: const EdgeInsets.all(16.0),
child: Text(
'Photos',
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.w700,
color: Colors.black,
),
style:
StreamChatTheme.of(context).textTheme.headlineBold,
),
),
),
@@ -194,7 +187,7 @@ class _ImageFooterState extends State<ImageFooter> {
alignment: Alignment.centerRight,
child: IconButton(
icon: StreamSvgIcon.close(
color: Colors.black,
color: StreamChatTheme.of(context).colorTheme.black,
),
onPressed: () {
Navigator.pop(context);
@@ -205,7 +198,7 @@ class _ImageFooterState extends State<ImageFooter> {
),
),
Container(
color: Colors.white,
color: StreamChatTheme.of(context).colorTheme.white,
child: GridView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
@@ -307,7 +300,7 @@ class _ImageFooterState extends State<ImageFooter> {
limit: 25,
),
filter: {
if (_searchController.text.isNotEmpty)
if (_channelNameQuery?.trim()?.isNotEmpty == true)
'name': {
r'$autocomplete': _channelNameQuery,
},
@@ -318,7 +311,7 @@ class _ImageFooterState extends State<ImageFooter> {
sort: [
SortOption(
'name',
direction: 1,
direction: SortOption.ASC,
),
],
emptyBuilder: (_) {
@@ -360,9 +353,11 @@ class _ImageFooterState extends State<ImageFooter> {
Align(
alignment: Alignment.bottomCenter,
child: Container(
color: Colors.white,
color: StreamChatTheme.of(context).colorTheme.white,
height: 48.0,
child: Material(
color:
StreamChatTheme.of(context).colorTheme.white,
child: InkWell(
onTap: () async {
var url = widget
@@ -392,7 +387,8 @@ class _ImageFooterState extends State<ImageFooter> {
'Save to Photos',
style: TextStyle(
color: StreamChatTheme.of(context)
.accentColor,
.colorTheme
.accentBlue,
fontWeight: FontWeight.bold,
),
),
@@ -429,7 +425,7 @@ class _ImageFooterState extends State<ImageFooter> {
Expanded(
child: TextField(
controller: _searchController,
cursorColor: Colors.black,
cursorColor: StreamChatTheme.of(context).colorTheme.black,
autofocus: true,
decoration: InputDecoration(
isDense: true,
@@ -439,19 +435,25 @@ class _ImageFooterState extends State<ImageFooter> {
padding: const EdgeInsets.symmetric(
vertical: 2.0, horizontal: 6.0),
child: StreamSvgIcon.search(
color: Colors.black,
color: StreamChatTheme.of(context).colorTheme.black,
),
),
hintText: 'Search',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(32.0),
borderSide:
BorderSide(color: Colors.black.withOpacity(0.08)),
borderSide: BorderSide(
color: StreamChatTheme.of(context)
.colorTheme
.black
.withOpacity(0.08)),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(32.0),
borderSide:
BorderSide(color: Colors.black.withOpacity(0.08)),
borderSide: BorderSide(
color: StreamChatTheme.of(context)
.colorTheme
.black
.withOpacity(0.08)),
),
contentPadding: EdgeInsets.zero,
),
@@ -462,7 +464,10 @@ class _ImageFooterState extends State<ImageFooter> {
),
IconButton(
icon: StreamSvgIcon.close_small(
color: Colors.black.withOpacity(0.5),
color: StreamChatTheme.of(context)
.colorTheme
.black
.withOpacity(0.5),
),
onPressed: () {
modalSetState(() {
@@ -483,7 +488,7 @@ class _ImageFooterState extends State<ImageFooter> {
padding: const EdgeInsets.symmetric(horizontal: 6.0),
child: IconButton(
icon: StreamSvgIcon.search(
color: Colors.black,
color: StreamChatTheme.of(context).colorTheme.black,
),
iconSize: 24.0,
onPressed: () {
@@ -497,18 +502,14 @@ class _ImageFooterState extends State<ImageFooter> {
padding: const EdgeInsets.all(16.0),
child: Text(
'Select a Chat to Share',
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.w700,
color: Colors.black,
),
style: StreamChatTheme.of(context).textTheme.headlineBold,
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 6.0),
child: IconButton(
icon: StreamSvgIcon.share_arrow(
color: Colors.black,
color: StreamChatTheme.of(context).colorTheme.black,
),
onPressed: () async {
var url =
@@ -535,7 +536,7 @@ class _ImageFooterState extends State<ImageFooter> {
return Align(
alignment: Alignment.bottomCenter,
child: Container(
color: Colors.white,
color: StreamChatTheme.of(context).colorTheme.white,
height: 56.0,
child: _loading
? Center(
@@ -566,13 +567,19 @@ class _ImageFooterState extends State<ImageFooter> {
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(32.0),
borderSide: BorderSide(
color: Colors.black.withOpacity(0.16),
color: StreamChatTheme.of(context)
.colorTheme
.black
.withOpacity(0.16),
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(32.0),
borderSide: BorderSide(
color: Colors.black.withOpacity(0.16),
color: StreamChatTheme.of(context)
.colorTheme
.black
.withOpacity(0.16),
)),
contentPadding: EdgeInsets.symmetric(vertical: 12.0),
),
@@ -599,7 +606,9 @@ class _ImageFooterState extends State<ImageFooter> {
child: Transform.rotate(
angle: -pi / 2,
child: StreamSvgIcon.Icon_send_message(
color: StreamChatTheme.of(context).accentColor,
color: StreamChatTheme.of(context)
.colorTheme
.accentBlue,
),
),
),
+3 -1
View File
@@ -83,7 +83,9 @@ class ImageGroup extends StatelessWidget {
child: Text(
'${images.length - 4} +',
style: TextStyle(
color: Colors.white,
color: StreamChatTheme.of(context)
.colorTheme
.white,
fontSize: 28,
),
),
+3 -7
View File
@@ -50,7 +50,7 @@ class ImageHeader extends StatelessWidget implements PreferredSizeWidget {
leading: showBackButton
? IconButton(
icon: StreamSvgIcon.close(
color: Colors.black,
color: StreamChatTheme.of(context).colorTheme.black,
size: 24.0,
),
onPressed: onBackPressed,
@@ -61,7 +61,7 @@ class ImageHeader extends StatelessWidget implements PreferredSizeWidget {
actions: <Widget>[
IconButton(
icon: StreamSvgIcon.Icon_menu_point_v(
color: Colors.black,
color: StreamChatTheme.of(context).colorTheme.black,
),
onPressed: () {
_showMessageActionModalBottomSheet(context);
@@ -81,11 +81,7 @@ class ImageHeader extends StatelessWidget implements PreferredSizeWidget {
children: <Widget>[
Text(
userName,
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.w700,
color: Colors.black,
),
style: StreamChatTheme.of(context).textTheme.headlineBold,
),
Text(
sentAt,
+22 -6
View File
@@ -1,10 +1,13 @@
import 'dart:ui' as ui;
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:photo_manager/photo_manager.dart';
import 'package:stream_chat_flutter/src/lazy_load_scroll_view.dart';
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
import 'dart:ui' as ui;
import '../stream_chat_flutter.dart';
extension on Duration {
String format() {
@@ -80,7 +83,10 @@ class _MediaListViewState extends State<MediaListView> {
? 1.0
: 0.0,
child: Container(
color: Colors.black.withOpacity(0.5),
color: StreamChatTheme.of(context)
.colorTheme
.black
.withOpacity(0.5),
alignment: Alignment.topRight,
padding: const EdgeInsets.only(
top: 8,
@@ -88,10 +94,12 @@ class _MediaListViewState extends State<MediaListView> {
),
child: CircleAvatar(
radius: 12,
backgroundColor: Colors.white,
backgroundColor:
StreamChatTheme.of(context).colorTheme.white,
child: StreamSvgIcon.check(
size: 24,
color: Colors.black,
color:
StreamChatTheme.of(context).colorTheme.black,
),
),
),
@@ -113,7 +121,7 @@ class _MediaListViewState extends State<MediaListView> {
child: Text(
media.videoDuration.format(),
style: TextStyle(
color: Colors.white,
color: StreamChatTheme.of(context).colorTheme.white,
),
),
),
@@ -141,7 +149,15 @@ class _MediaListViewState extends State<MediaListView> {
void _getMedia() async {
final assetList = await PhotoManager.getAssetPathList(
hasAll: true,
).then((value) => value.singleWhere((element) => element.isAll));
).then((value) {
if (value?.isNotEmpty == true) {
return value.singleWhere((element) => element.isAll);
}
});
if (assetList == null) {
return;
}
final media = await assetList.getAssetListPaged(_currentPage, 50);
+41 -7
View File
@@ -22,6 +22,7 @@ class MessageActionsModal extends StatelessWidget {
final bool showDeleteMessage;
final bool showCopyMessage;
final bool showEditMessage;
final bool showResendMessage;
final bool showReply;
final bool reverse;
final ShapeBorder messageShape;
@@ -37,6 +38,7 @@ class MessageActionsModal extends StatelessWidget {
this.onThreadTap,
this.showCopyMessage = true,
this.showReply = true,
this.showResendMessage = true,
this.showUserAvatar = DisplayWidget.show,
this.editMessageInputBuilder,
this.messageShape,
@@ -69,7 +71,7 @@ class MessageActionsModal extends StatelessWidget {
sigmaY: 10.8731,
),
child: Container(
color: Colors.black.withOpacity(0.1),
color: Colors.black.withOpacity(.2),
),
),
),
@@ -117,7 +119,10 @@ class MessageActionsModal extends StatelessWidget {
showUserAvatar: showUserAvatar,
showTimestamp: false,
translateUserAvatar: false,
showReactionPickerIndicator: true,
showReactionPickerIndicator: showReactions &&
(message.status ==
MessageSendingStatus.SENT ||
message.status == null),
showInChannelIndicator: false,
showSendingIndicator: DisplayWidget.gone,
shape: messageShape,
@@ -125,9 +130,7 @@ class MessageActionsModal extends StatelessWidget {
),
);
}),
SizedBox(
height: 8,
),
SizedBox(height: 8),
TweenAnimationBuilder<double>(
tween: Tween(begin: 0.0, end: 1.0),
duration: Duration(milliseconds: 300),
@@ -143,6 +146,9 @@ class MessageActionsModal extends StatelessWidget {
horizontal: 48.0,
),
child: Material(
color: StreamChatTheme.of(context)
.colorTheme
.whiteSnow,
clipBehavior: Clip.hardEdge,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
@@ -159,6 +165,8 @@ class MessageActionsModal extends StatelessWidget {
message.status == null) &&
message.parentId == null)
_buildReplyButton(context),
if (showResendMessage)
_buildResendMessage(context),
if (showEditMessage)
_buildEditMessage(context),
if (showDeleteMessage)
@@ -183,9 +191,10 @@ class MessageActionsModal extends StatelessWidget {
}
Widget _buildDeleteButton(BuildContext context) {
final isDeleteFailed = message.status == MessageSendingStatus.FAILED_DELETE;
return ListTile(
title: Text(
'Delete message',
isDeleteFailed ? 'Retry deleting message' : 'Delete message',
style:
Theme.of(context).textTheme.headline6.copyWith(color: Colors.red),
),
@@ -234,6 +243,29 @@ class MessageActionsModal extends StatelessWidget {
);
}
Widget _buildResendMessage(BuildContext context) {
final isUpdateFailed = message.status == MessageSendingStatus.FAILED_UPDATE;
return ListTile(
title: Text(
isUpdateFailed ? 'Resend edited message' : 'Resend',
style: Theme.of(context).textTheme.headline6,
),
leading: StreamSvgIcon.circle_up(
color: StreamChatTheme.of(context).colorTheme.accentBlue,
),
onTap: () {
Navigator.pop(context);
final client = StreamChat.of(context).client;
final channel = StreamChannel.of(context).channel;
if (isUpdateFailed) {
client.updateMessage(message, channel.cid);
} else {
channel.sendMessage(message);
}
},
);
}
void _showEditBottomSheet(BuildContext context) {
final channel = StreamChannel.of(context).channel;
showModalBottomSheet(
@@ -241,6 +273,7 @@ class MessageActionsModal extends StatelessWidget {
elevation: 2,
clipBehavior: Clip.hardEdge,
isScrollControlled: true,
backgroundColor: StreamChatTheme.of(context).colorTheme.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(32),
@@ -301,9 +334,10 @@ class MessageActionsModal extends StatelessWidget {
? editMessageInputBuilder(context, message)
: MessageInput(
editMessage: message,
onMessageSent: (_) {
preMessageSending: (m) {
FocusScope.of(context).unfocus();
Navigator.pop(context);
return m;
},
),
),
+97 -69
View File
@@ -263,7 +263,10 @@ class MessageInputState extends State<MessageInput> {
border: _sendAsDm
? null
: Border.all(
color: Colors.black.withOpacity(.5),
color: StreamChatTheme.of(context)
.colorTheme
.black
.withOpacity(.5),
width: 2,
),
borderRadius: BorderRadius.circular(3),
@@ -272,8 +275,8 @@ class MessageInputState extends State<MessageInput> {
child: Material(
borderRadius: BorderRadius.circular(3),
color: _sendAsDm
? StreamChatTheme.of(context).accentColor
: Colors.white,
? StreamChatTheme.of(context).colorTheme.accentBlue
: StreamChatTheme.of(context).colorTheme.white,
child: InkWell(
onTap: () {
setState(() {
@@ -288,7 +291,7 @@ class MessageInputState extends State<MessageInput> {
: CrossFadeState.showSecond,
firstChild: StreamSvgIcon.check(
size: 16.0,
color: Colors.white,
color: StreamChatTheme.of(context).colorTheme.white,
),
secondChild: SizedBox(
height: 16,
@@ -334,7 +337,7 @@ class MessageInputState extends State<MessageInput> {
child: Padding(
padding: const EdgeInsets.all(8.0) + EdgeInsets.only(bottom: 3.0),
child: StreamSvgIcon.emptyCircleLeft(
color: StreamChatTheme.of(context).accentColor,
color: StreamChatTheme.of(context).colorTheme.accentBlue,
),
),
),
@@ -361,7 +364,7 @@ class MessageInputState extends State<MessageInput> {
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(24.0),
border: Border.all(
color: Colors.grey,
color: StreamChatTheme.of(context).colorTheme.greyGainsboro,
),
),
padding: _attachments.isEmpty ? null : EdgeInsets.all(6.0),
@@ -408,8 +411,9 @@ class MessageInputState extends State<MessageInput> {
padding:
const EdgeInsets.symmetric(horizontal: 8.0),
child: Chip(
backgroundColor:
StreamChatTheme.of(context).accentColor,
backgroundColor: StreamChatTheme.of(context)
.colorTheme
.accentBlue,
padding: EdgeInsets.zero,
labelPadding:
EdgeInsets.symmetric(horizontal: 9.0),
@@ -418,13 +422,20 @@ class MessageInputState extends State<MessageInput> {
mainAxisAlignment: MainAxisAlignment.center,
children: [
StreamSvgIcon.lightning(
color: Colors.white,
color: StreamChatTheme.of(context)
.colorTheme
.white,
size: 16.0,
),
Text(
_chosenCommand?.name?.toUpperCase() ?? "",
style: TextStyle(
color: Colors.white, fontSize: 12.0),
style: StreamChatTheme.of(context)
.textTheme
.footnote
.copyWith(
color: StreamChatTheme.of(context)
.colorTheme
.white),
),
],
),
@@ -602,12 +613,12 @@ class MessageInputState extends State<MessageInput> {
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
),
color: StreamChatTheme.of(context).primaryColor,
color: StreamChatTheme.of(context).colorTheme.white,
clipBehavior: Clip.antiAlias,
child: Container(
constraints: BoxConstraints.loose(Size.fromHeight(400)),
decoration: BoxDecoration(
color: StreamChatTheme.of(context).primaryColor,
color: StreamChatTheme.of(context).colorTheme.white,
borderRadius: BorderRadius.circular(8.0)),
child: ListView(
padding: const EdgeInsets.all(0),
@@ -625,13 +636,17 @@ class MessageInputState extends State<MessageInput> {
),
child: StreamSvgIcon.lightning(
color: StreamChatTheme.of(context)
.accentColor,
.colorTheme
.accentBlue,
),
),
Text(
'Instant Commands',
style: TextStyle(
color: Colors.black.withOpacity(.5),
color: StreamChatTheme.of(context)
.colorTheme
.black
.withOpacity(.5),
),
)
],
@@ -658,15 +673,6 @@ class MessageInputState extends State<MessageInput> {
],
),
),
trailing: CircleAvatar(
backgroundColor:
StreamChatTheme.of(context).accentColor,
child: StreamSvgIcon.lightning(
color: Colors.white,
size: 12.5,
),
maxRadius: 12,
),
//subtitle: Text(c.description),
onTap: () {
_setCommand(c);
@@ -693,27 +699,36 @@ class MessageInputState extends State<MessageInput> {
switch (index) {
case 0:
return _attachments.isEmpty
? StreamChatTheme.of(context).accentColor
? StreamChatTheme.of(context).colorTheme.accentBlue
: (!_attachmentContainsFile
? StreamChatTheme.of(context).accentColor
: Colors.black.withOpacity(0.2));
? StreamChatTheme.of(context).colorTheme.accentBlue
: StreamChatTheme.of(context)
.colorTheme
.black
.withOpacity(0.2));
break;
case 1:
return _attachmentContainsFile
? StreamChatTheme.of(context).accentColor
? StreamChatTheme.of(context).colorTheme.accentBlue
: (_attachments.isEmpty
? Colors.black.withOpacity(0.5)
: Colors.black.withOpacity(0.2));
? StreamChatTheme.of(context)
.colorTheme
.black
.withOpacity(0.5)
: StreamChatTheme.of(context)
.colorTheme
.black
.withOpacity(0.2));
break;
case 2:
return _attachmentContainsFile && _attachments.isNotEmpty
? Colors.black.withOpacity(0.2)
: Colors.black.withOpacity(0.5);
? StreamChatTheme.of(context).colorTheme.black.withOpacity(0.2)
: StreamChatTheme.of(context).colorTheme.black.withOpacity(0.5);
break;
case 3:
return _attachmentContainsFile && _attachments.isNotEmpty
? Colors.black.withOpacity(0.2)
: Colors.black.withOpacity(0.5);
? StreamChatTheme.of(context).colorTheme.black.withOpacity(0.2)
: StreamChatTheme.of(context).colorTheme.black.withOpacity(0.5);
break;
}
}
@@ -722,7 +737,7 @@ class MessageInputState extends State<MessageInput> {
duration: _animateContainer ? Duration(milliseconds: 300) : Duration.zero,
height: _openFilePickerSection ? _filePickerSize : 0,
child: Material(
color: Color(0xFFF2F2F2),
color: StreamChatTheme.of(context).colorTheme.whiteSmoke,
child: Column(
children: [
Row(
@@ -789,7 +804,7 @@ class MessageInputState extends State<MessageInput> {
},
child: Container(
decoration: BoxDecoration(
color: Colors.white,
color: StreamChatTheme.of(context).colorTheme.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(16.0),
topRight: Radius.circular(16.0),
@@ -804,7 +819,8 @@ class MessageInputState extends State<MessageInput> {
width: 40.0,
height: 4.0,
decoration: BoxDecoration(
color: Color(0xFFF2F2F2),
color:
StreamChatTheme.of(context).colorTheme.whiteSmoke,
borderRadius: BorderRadius.circular(4.0),
),
),
@@ -817,7 +833,7 @@ class MessageInputState extends State<MessageInput> {
Expanded(
child: Container(
decoration: BoxDecoration(
color: Colors.white,
color: StreamChatTheme.of(context).colorTheme.white,
borderRadius: BorderRadius.circular(8.0),
),
child: _buildPickerSection(),
@@ -852,11 +868,12 @@ class MessageInputState extends State<MessageInput> {
},
child: Container(
constraints: BoxConstraints.expand(),
color: Color(0xfff2f2f2),
color: StreamChatTheme.of(context).colorTheme.whiteSmoke,
child: Text(
'Add more files',
style: TextStyle(
color: StreamChatTheme.of(context).accentColor,
color:
StreamChatTheme.of(context).colorTheme.accentBlue,
fontWeight: FontWeight.bold,
),
),
@@ -885,7 +902,7 @@ class MessageInputState extends State<MessageInput> {
PhotoManager.openSetting();
},
child: Container(
color: Color(0xFFF2F2F2),
color: StreamChatTheme.of(context).colorTheme.whiteSmoke,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
@@ -894,16 +911,20 @@ class MessageInputState extends State<MessageInput> {
'svgs/icon_picture_empty_state.svg',
package: 'stream_chat_flutter',
height: 140,
color: StreamChatTheme.of(context).accentColor,
color:
StreamChatTheme.of(context).colorTheme.accentBlue,
),
Center(
child: Text(
'Allow access to your gallery',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: StreamChatTheme.of(context).accentColor,
),
style: StreamChatTheme.of(context)
.textTheme
.bodyBold
.copyWith(
color: StreamChatTheme.of(context)
.colorTheme
.accentBlue,
),
),
),
],
@@ -1019,7 +1040,7 @@ class MessageInputState extends State<MessageInput> {
CircleAvatar _buildGiphyIcon() {
if (kIsWeb) {
return CircleAvatar(
backgroundColor: Colors.black,
backgroundColor: StreamChatTheme.of(context).colorTheme.black,
child: Image.asset(
'images/giphy_icon.png',
package: 'stream_chat_flutter',
@@ -1080,7 +1101,7 @@ class MessageInputState extends State<MessageInput> {
child: Card(
margin: EdgeInsets.all(8.0),
elevation: 2.0,
color: StreamChatTheme.of(context).primaryColor,
color: StreamChatTheme.of(context).colorTheme.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
),
@@ -1088,7 +1109,7 @@ class MessageInputState extends State<MessageInput> {
child: Container(
constraints: BoxConstraints.loose(Size.fromHeight(400)),
decoration: BoxDecoration(
color: StreamChatTheme.of(context).primaryColor,
color: StreamChatTheme.of(context).colorTheme.white,
),
child: FutureBuilder<List<Member>>(
future: queryMembers ?? Future.value(members),
@@ -1116,7 +1137,8 @@ class MessageInputState extends State<MessageInput> {
subtitle: Text('@${m.userId}'),
trailing: StreamSvgIcon.mentions(
color: StreamChatTheme.of(context)
.accentColor,
.colorTheme
.accentBlue,
),
onTap: () {
_mentionedUsers.add(m.user);
@@ -1181,7 +1203,7 @@ class MessageInputState extends State<MessageInput> {
child: Card(
margin: EdgeInsets.all(8.0),
elevation: 2.0,
color: StreamChatTheme.of(context).primaryColor,
color: StreamChatTheme.of(context).colorTheme.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
),
@@ -1196,7 +1218,7 @@ class MessageInputState extends State<MessageInput> {
offset: Offset(0, -4),
),
],
color: StreamChatTheme.of(context).primaryColor,
color: StreamChatTheme.of(context).colorTheme.white,
),
child: ListView.builder(
padding: const EdgeInsets.all(0),
@@ -1212,14 +1234,19 @@ class MessageInputState extends State<MessageInput> {
padding:
const EdgeInsets.symmetric(horizontal: 8.0),
child: StreamSvgIcon.smile(
color: StreamChatTheme.of(context).accentColor,
color: StreamChatTheme.of(context)
.colorTheme
.accentBlue,
),
),
Flexible(
child: Text(
'Emoji matching "$query"',
style: TextStyle(
color: Colors.black.withOpacity(.5),
color: StreamChatTheme.of(context)
.colorTheme
.black
.withOpacity(.5),
),
),
)
@@ -1313,10 +1340,15 @@ class MessageInputState extends State<MessageInput> {
child: InkWell(
child: CircleAvatar(
backgroundColor:
Colors.black.withOpacity(0.2),
StreamChatTheme.of(context)
.colorTheme
.black
.withOpacity(0.6),
maxRadius: 12.0,
child: StreamSvgIcon.close(
color: Colors.white,
color: StreamChatTheme.of(context)
.colorTheme
.white,
),
),
onTap: () {
@@ -1401,11 +1433,11 @@ class MessageInputState extends State<MessageInput> {
_attachments.remove(attachment);
});
},
fillColor: Colors.black.withOpacity(.5),
fillColor: StreamChatTheme.of(context).colorTheme.black.withOpacity(.5),
child: Center(
child: StreamSvgIcon.close(
size: 24,
color: Colors.white,
color: StreamChatTheme.of(context).colorTheme.white,
),
),
),
@@ -1494,8 +1526,8 @@ class MessageInputState extends State<MessageInput> {
const EdgeInsets.only(left: 4.0, right: 8.0, top: 8.0, bottom: 8.0),
child: StreamSvgIcon.lightning(
color: _commandsOverlay != null
? StreamChatTheme.of(context).accentColor
: Color(0xFF000000).withAlpha(128),
? StreamChatTheme.of(context).colorTheme.accentBlue
: StreamChatTheme.of(context).colorTheme.grey,
),
),
onTap: () async {
@@ -1532,8 +1564,8 @@ class MessageInputState extends State<MessageInput> {
EdgeInsets.only(left: 8.0, right: padding, top: 8.0, bottom: 8.0),
child: StreamSvgIcon.attach(
color: _openFilePickerSection
? StreamChatTheme.of(context).accentColor
: Color(0xFF000000).withAlpha(128),
? StreamChatTheme.of(context).colorTheme.accentBlue
: StreamChatTheme.of(context).colorTheme.grey,
),
),
onTap: () async {
@@ -1862,7 +1894,7 @@ class MessageInputState extends State<MessageInput> {
},
child: StreamSvgIcon(
assetName: _getIdleSendIcon(),
color: Colors.grey,
color: StreamChatTheme.of(context).colorTheme.greyGainsboro,
height: 24.0,
width: 24.0,
),
@@ -1880,7 +1912,7 @@ class MessageInputState extends State<MessageInput> {
},
child: StreamSvgIcon(
assetName: _getSendIcon(),
color: StreamChatTheme.of(context).accentColor,
color: StreamChatTheme.of(context).colorTheme.accentBlue,
height: 24.0,
width: 24.0,
),
@@ -1976,10 +2008,6 @@ class MessageInputState extends State<MessageInput> {
return sendingFuture.then((resp) {
if (widget.onMessageSent != null) {
widget.onMessageSent(resp.message);
} else {
if (widget.editMessage != null) {
Navigator.pop(context);
}
}
});
}
+21 -11
View File
@@ -246,16 +246,19 @@ class _MessageListViewState extends State<MessageListView> {
}
final messageList = snapshot.data?.reversed?.toList() ?? [];
if (messageList.isEmpty) {
if (_upToDate) {
return Center(
child: Text(
'No chats here yet...',
style: TextStyle(
fontSize: 12,
color: Colors.black.withOpacity(.5),
),
style: StreamChatTheme.of(context)
.textTheme
.footnote
.copyWith(
color: StreamChatTheme.of(context)
.colorTheme
.black
.withOpacity(.5)),
),
);
}
@@ -342,8 +345,12 @@ class _MessageListViewState extends State<MessageListView> {
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color(0XFFF7F7F7),
Color(0XFFFCFCFC),
StreamChatTheme.of(context)
.colorTheme
.whiteSmoke,
StreamChatTheme.of(context)
.colorTheme
.whiteSnow,
],
),
),
@@ -523,9 +530,9 @@ class _MessageListViewState extends State<MessageListView> {
clipBehavior: Clip.none,
children: [
FloatingActionButton(
backgroundColor: Colors.white,
backgroundColor: StreamChatTheme.of(context).colorTheme.white,
child: StreamSvgIcon.down(
color: Colors.black,
color: StreamChatTheme.of(context).colorTheme.black,
),
onPressed: () {
if (unreadCount > 0) {
@@ -585,7 +592,10 @@ class _MessageListViewState extends State<MessageListView> {
builder: (context, snapshot) {
if (snapshot.hasError) {
return Container(
color: Color(0xffd0021B).withAlpha(26),
color: StreamChatTheme.of(context)
.colorTheme
.accentRed
.withOpacity(.2),
child: Center(
child: Text('Error loading messages'),
),
@@ -839,7 +849,7 @@ class _MessageListViewState extends State<MessageListView> {
}
if (event.message.user.id == streamChannel.channel.client.state.user.id) {
WidgetsBinding.instance.addPostFrameCallback((_) {
_scrollController.jumpTo(
_scrollController?.jumpTo(
index: 0,
);
});
+8 -5
View File
@@ -60,7 +60,7 @@ class MessageReactionsModal extends StatelessWidget {
sigmaY: 10.8731,
),
child: Container(
color: Colors.black.withOpacity(0.1),
color: Colors.black.withOpacity(0.2),
),
),
),
@@ -110,14 +110,16 @@ class MessageReactionsModal extends StatelessWidget {
translateUserAvatar: false,
showSendingIndicator: DisplayWidget.gone,
shape: messageShape,
showReactionPickerIndicator: true,
showInChannelIndicator: false,
showReactionPickerIndicator: showReactions &&
(message.status ==
MessageSendingStatus.SENT ||
message.status == null),
),
),
);
}),
SizedBox(
height: 16,
),
SizedBox(height: 8),
if (message.latestReactions?.isNotEmpty == true)
_buildReactionCard(context),
],
@@ -137,6 +139,7 @@ class MessageReactionsModal extends StatelessWidget {
horizontal: 8.0,
),
child: Card(
color: StreamChatTheme.of(context).colorTheme.white,
clipBehavior: Clip.hardEdge,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
-2
View File
@@ -126,8 +126,6 @@ class MessageSearchItem extends StatelessWidget {
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: StreamChatTheme.of(context).channelPreviewTheme.subtitle.copyWith(
color:
StreamChatTheme.of(context).channelPreviewTheme.subtitle.color,
fontStyle: (message.isSystem || message.isDeleted)
? FontStyle.italic
: FontStyle.normal,
+19 -6
View File
@@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter/src/message_search_item.dart';
import '../stream_chat_flutter.dart';
import 'lazy_load_scroll_view.dart';
import 'message_search_bloc.dart';
@@ -127,8 +128,8 @@ class _MessageSearchListViewState extends State<MessageSearchListView> {
return Container(
height: 1,
color: Theme.of(context).brightness == Brightness.dark
? Colors.white.withOpacity(0.1)
: Colors.black.withOpacity(0.1),
? StreamChatTheme.of(context).colorTheme.white.withOpacity(0.1)
: StreamChatTheme.of(context).colorTheme.black.withOpacity(0.1),
);
}
@@ -151,7 +152,10 @@ class _MessageSearchListViewState extends State<MessageSearchListView> {
builder: (context, snapshot) {
if (snapshot.hasError) {
return Container(
color: Color(0xffd0021B).withAlpha(26),
color: StreamChatTheme.of(context)
.colorTheme
.accentRed
.withOpacity(.2),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Center(
@@ -313,8 +317,14 @@ class _MessageSearchListViewState extends State<MessageSearchListView> {
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [
Colors.black.withOpacity(0.02),
Colors.white.withOpacity(0.05),
StreamChatTheme.of(context)
.colorTheme
.black
.withOpacity(0.02),
StreamChatTheme.of(context)
.colorTheme
.white
.withOpacity(0.05),
],
stops: [0, 1],
),
@@ -327,7 +337,10 @@ class _MessageSearchListViewState extends State<MessageSearchListView> {
child: Text(
'${items.length} results',
style: TextStyle(
color: Colors.black.withOpacity(0.5),
color: StreamChatTheme.of(context)
.colorTheme
.black
.withOpacity(0.5),
),
),
),
+112 -133
View File
@@ -114,6 +114,9 @@ class MessageWidget extends StatefulWidget {
/// Used in [MessageReactionsModal] and [MessageActionsModal]
final bool showReactionPickerIndicator;
/// If true the widget will show the resendMessage indicator
final bool showResendMessage;
final List<Read> readList;
/// If true show the users username next to the timestamp of the message
@@ -157,6 +160,7 @@ class MessageWidget extends StatefulWidget {
this.editMessageInputBuilder,
this.textBuilder,
Map<String, AttachmentBuilder> customAttachmentBuilders,
this.showResendMessage = true,
this.readList,
this.padding,
this.textPadding = const EdgeInsets.symmetric(
@@ -224,11 +228,27 @@ class _MessageWidgetState extends State<MessageWidget> {
bool get showTimeStamp =>
widget.message.createdAt != null && widget.showTimestamp;
bool get showReadList => widget.readList?.isNotEmpty == true;
bool get isMessageRead => widget.readList?.isNotEmpty == true;
bool get showInChannel =>
widget.showInChannelIndicator && widget.message?.showInChannel == true;
bool get isSendFailed => widget.message.status == MessageSendingStatus.FAILED;
bool get isUpdateFailed =>
widget.message.status == MessageSendingStatus.FAILED_UPDATE;
bool get isDeleteFailed =>
widget.message.status == MessageSendingStatus.FAILED_DELETE;
bool get isFailedState => isSendFailed || isUpdateFailed || isDeleteFailed;
bool get showBottomRow =>
showThreadReplyIndicator ||
showUsername ||
showTimeStamp ||
showInChannel;
@override
Widget build(BuildContext context) {
var leftPadding = widget.showUserAvatar != DisplayWidget.gone
@@ -253,6 +273,7 @@ class _MessageWidgetState extends State<MessageWidget> {
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Stack(
clipBehavior: Clip.none,
alignment: AlignmentDirectional.bottomStart,
children: [
Column(
@@ -298,9 +319,7 @@ class _MessageWidgetState extends State<MessageWidget> {
)
: EdgeInsets.zero,
child: (widget.message.isDeleted &&
widget.message.status !=
MessageSendingStatus
.FAILED_DELETE)
!isFailedState)
? Transform(
alignment: Alignment.center,
transform: Matrix4.rotationY(
@@ -327,10 +346,16 @@ class _MessageWidgetState extends State<MessageWidget> {
.brightness ==
Brightness
.dark
? Colors.white
? StreamChatTheme.of(
context)
.colorTheme
.white
.withAlpha(
24)
: Colors.black
: StreamChatTheme.of(
context)
.colorTheme
.black
.withAlpha(
24),
),
@@ -383,18 +408,23 @@ class _MessageWidgetState extends State<MessageWidget> {
),
],
),
if (showThreadReplyIndicator ||
showUsername ||
showTimeStamp ||
showInChannel)
SizedBox(height: 20.0),
if (showBottomRow) SizedBox(height: 20.0),
],
),
if (showThreadReplyIndicator ||
showUsername ||
showTimeStamp ||
showInChannel)
_buildBottomRow(leftPadding)
if (showBottomRow) _buildBottomRow(leftPadding),
if (isFailedState)
Positioned(
left: widget.reverse ? -3 : null,
right: widget.reverse ? null : -9,
bottom: showBottomRow ? 20 : 0,
child: Container(
decoration: BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
),
child: StreamSvgIcon.error(size: 20),
),
),
],
),
],
@@ -414,16 +444,18 @@ class _MessageWidgetState extends State<MessageWidget> {
mainAxisSize: MainAxisSize.min,
children: [
StreamSvgIcon.eye(
color: Colors.black.withOpacity(0.5),
color:
StreamChatTheme.of(context).colorTheme.black.withOpacity(0.5),
size: 16.0,
),
SizedBox(width: 8.0),
Text(
'Only visible to you',
style: TextStyle(
color: Colors.black.withOpacity(0.5),
fontSize: 12.0,
),
style: StreamChatTheme.of(context).textTheme.footnote.copyWith(
color: StreamChatTheme.of(context)
.colorTheme
.black
.withOpacity(0.5)),
),
],
),
@@ -461,14 +493,14 @@ class _MessageWidgetState extends State<MessageWidget> {
children.addAll([
if (showSendingIndicator) _buildSendingIndicator(),
if (showReadList)
SizedBox.fromSize(
size: Size((widget.readList.length * 10.0) + 10, 17),
child: Padding(
padding: const EdgeInsets.only(left: 4.0),
child: _buildReadIndicator(),
),
),
// if (showReadList)
// SizedBox.fromSize(
// size: Size((widget.readList.length * 10.0) + 10, 17),
// child: Padding(
// padding: const EdgeInsets.only(left: 4.0),
// child: _buildReadIndicator(),
// ),
// ),
if (showThreadReplyIndicator)
InkWell(
onTap: widget.onThreadTap != null ? onThreadTap : null,
@@ -502,6 +534,7 @@ class _MessageWidgetState extends State<MessageWidget> {
child: CustomPaint(
size: const Size(16, 32),
painter: _ThreadReplyPainter(
context: context,
color: widget.messageTheme.replyThreadColor,
),
),
@@ -550,7 +583,7 @@ class _MessageWidgetState extends State<MessageWidget> {
bottom: 0,
top: 0,
child: Material(
color: Colors.white,
color: StreamChatTheme.of(context).colorTheme.white,
clipBehavior: Clip.antiAlias,
shape: CircleBorder(),
child: Padding(
@@ -619,14 +652,19 @@ class _MessageWidgetState extends State<MessageWidget> {
message: widget.message,
editMessageInputBuilder: widget.editMessageInputBuilder,
onThreadTap: widget.onThreadTap,
showCopyMessage: widget.message.text?.trim()?.isNotEmpty == true,
showResendMessage:
widget.showResendMessage && (isSendFailed || isUpdateFailed),
showCopyMessage: !isFailedState &&
widget.message.text?.trim()?.isNotEmpty == true,
showEditMessage: widget.showEditMessage &&
!isDeleteFailed &&
widget.message.attachments
?.any((element) => element.type == 'giphy') !=
true,
showReactions: widget.showReactions,
showReply:
widget.showThreadReplyIndicator && widget.onThreadTap != null,
showReply: widget.showThreadReplyIndicator &&
!isFailedState &&
widget.onThreadTap != null,
),
);
});
@@ -663,8 +701,8 @@ class _MessageWidgetState extends State<MessageWidget> {
widget.borderSide ??
BorderSide(
color: Theme.of(context).brightness == Brightness.dark
? Colors.white.withAlpha(24)
: Colors.black.withAlpha(24),
? StreamChatTheme.of(context).colorTheme.white.withAlpha(24)
: StreamChatTheme.of(context).colorTheme.black.withAlpha(24),
),
borderRadius: widget.attachmentBorderRadiusGeometry ??
widget.borderRadiusGeometry ??
@@ -729,7 +767,6 @@ class _MessageWidgetState extends State<MessageWidget> {
final attachmentShape =
widget.attachmentShape ?? widget.shape ?? _getDefaultShape(context);
return GestureDetector(
onTap: () => retryMessage(context),
onLongPress: () => onLongPress(context),
child: Material(
color: _getBackgroundColor(),
@@ -744,17 +781,7 @@ class _MessageWidgetState extends State<MessageWidget> {
child: Transform(
transform: Matrix4.rotationY(widget.reverse ? pi : 0),
alignment: Alignment.center,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
getFailedMessageWidget(
context,
padding: const EdgeInsets.all(8.0),
),
attachmentWidget,
],
),
child: attachmentWidget,
),
),
),
@@ -777,14 +804,27 @@ class _MessageWidgetState extends State<MessageWidget> {
}
Widget _buildSendingIndicator() {
return Container(
height: widget.messageTheme.createdAt.fontSize + 2,
width: widget.messageTheme.createdAt.fontSize + 2,
child: SendingIndicator(
message: widget.message,
allRead: widget.allRead,
),
final style = widget.messageTheme.createdAt;
Widget child = SendingIndicator(
message: widget.message,
isMessageRead: isMessageRead,
size: style.fontSize,
);
if (isMessageRead) {
child = Row(
children: [
Text(
widget.readList.length.toString(),
style: style.copyWith(
color: StreamChatTheme.of(context).colorTheme.accentBlue,
),
),
SizedBox(width: 2),
child,
],
);
}
return child;
}
Widget _buildUserAvatar() => Transform(
@@ -809,55 +849,6 @@ class _MessageWidgetState extends State<MessageWidget> {
),
);
Widget getFailedMessageWidget(
BuildContext context, {
EdgeInsetsGeometry padding,
}) {
Widget failedWidget;
if (widget.message.status == MessageSendingStatus.FAILED) {
failedWidget = Text(
'MESSAGE FAILED · CLICK TO TRY AGAIN',
style: widget.messageTheme.messageText.copyWith(
color: Theme.of(context).brightness == Brightness.dark
? Colors.white.withOpacity(.5)
: Colors.black.withOpacity(.5),
fontSize: 11,
),
);
}
if (widget.message.status == MessageSendingStatus.FAILED_UPDATE) {
failedWidget = Text(
'MESSAGE UPDATE FAILED · CLICK TO TRY AGAIN',
style: widget.messageTheme.messageText.copyWith(
color: Theme.of(context).brightness == Brightness.dark
? Colors.white.withOpacity(.5)
: Colors.black.withOpacity(.5),
fontSize: 11,
),
);
}
if (widget.message.status == MessageSendingStatus.FAILED_DELETE) {
failedWidget = Text(
'MESSAGE DELETE FAILED · CLICK TO TRY AGAIN',
style: widget.messageTheme.messageText.copyWith(
color: Theme.of(context).brightness == Brightness.dark
? Colors.white.withOpacity(.5)
: Colors.black.withOpacity(.5),
fontSize: 11,
),
);
}
if (failedWidget != null) {
return Padding(
padding: padding ?? EdgeInsets.zero,
child: failedWidget,
);
}
return SizedBox();
}
Widget _buildTextBubble(BuildContext context) {
Widget child = Transform(
transform: Matrix4.rotationY(widget.reverse ? pi : 0),
@@ -867,26 +858,20 @@ class _MessageWidgetState extends State<MessageWidget> {
children: [
Padding(
padding: widget.textPadding,
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
getFailedMessageWidget(context),
widget.textBuilder != null
? widget.textBuilder(context, widget.message)
: MessageText(
onLinkTap: widget.onLinkTap,
message: widget.message,
onMentionTap: widget.onMentionTap,
messageTheme: isOnlyEmoji
? widget.messageTheme.copyWith(
messageText:
widget.messageTheme.messageText.copyWith(
fontSize: 40,
))
: widget.messageTheme,
),
],
),
child: widget.textBuilder != null
? widget.textBuilder(context, widget.message)
: MessageText(
onLinkTap: widget.onLinkTap,
message: widget.message,
onMentionTap: widget.onMentionTap,
messageTheme: isOnlyEmoji
? widget.messageTheme.copyWith(
messageText:
widget.messageTheme.messageText.copyWith(
fontSize: 40,
))
: widget.messageTheme,
),
),
if (widget.message.attachments
?.any((element) => element.ogScrapeUrl != null) ==
@@ -896,7 +881,6 @@ class _MessageWidgetState extends State<MessageWidget> {
),
);
return GestureDetector(
onTap: () => retryMessage(context),
onLongPress: () => onLongPress(context),
child: child,
);
@@ -907,16 +891,10 @@ class _MessageWidgetState extends State<MessageWidget> {
widget.message.text.characters.every((c) => Emoji.byChar(c) != null);
Color _getBackgroundColor() {
if ((widget.message.status == MessageSendingStatus.FAILED ||
widget.message.status == MessageSendingStatus.FAILED_UPDATE ||
widget.message.status == MessageSendingStatus.FAILED_DELETE)) {
return Color(0xffd0021B).withOpacity(.1);
}
if (widget.message.attachments
?.any((element) => element.ogScrapeUrl != null) ==
true) {
return Color(0xFFE9F2FF);
return StreamChatTheme.of(context).colorTheme.blueAlice;
}
if (isOnlyEmoji) {
@@ -997,13 +975,14 @@ class _MessageWidgetState extends State<MessageWidget> {
class _ThreadReplyPainter extends CustomPainter {
final Color color;
final BuildContext context;
const _ThreadReplyPainter({@required this.color});
const _ThreadReplyPainter({this.context, @required this.color});
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = color ?? Color(0XFFDBDBDB)
..color = color ?? StreamChatTheme.of(context).colorTheme.greyGainsboro
..style = PaintingStyle.stroke
..strokeWidth = 1
..strokeCap = StrokeCap.round;
+10 -4
View File
@@ -107,16 +107,22 @@ class ReactionBubble extends StatelessWidget {
height: 16,
color: (!highlightOwnReactions ||
reaction.user.id == StreamChat.of(context).user.id)
? StreamChatTheme.of(context).accentColor
: Colors.black.withOpacity(.5),
? StreamChatTheme.of(context).colorTheme.accentBlue
: StreamChatTheme.of(context)
.colorTheme
.black
.withOpacity(.5),
)
: Icon(
Icons.help_outline_rounded,
size: 16,
color: (!highlightOwnReactions ||
reaction.user.id == StreamChat.of(context).user.id)
? StreamChatTheme.of(context).accentColor
: Colors.black.withOpacity(.5),
? StreamChatTheme.of(context).colorTheme.accentBlue
: StreamChatTheme.of(context)
.colorTheme
.black
.withOpacity(.5),
),
);
}
+3 -1
View File
@@ -89,7 +89,9 @@ class _ReactionPickerState extends State<ReactionPicker>
height: animations[index].value * 24.0,
width: animations[index].value * 24.0,
color: ownReactionIndex != -1
? StreamChatTheme.of(context).accentColor
? StreamChatTheme.of(context)
.colorTheme
.accentBlue
: Theme.of(context)
.iconTheme
.color
+10 -17
View File
@@ -4,27 +4,29 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart';
/// Used to show the sending status of the message
class SendingIndicator extends StatelessWidget {
final Message message;
final bool allRead;
final bool isMessageRead;
final double size;
const SendingIndicator({
Key key,
this.message,
this.allRead = false,
this.isMessageRead = false,
this.size = 12,
}) : super(key: key);
@override
Widget build(BuildContext context) {
if (allRead) {
if (isMessageRead) {
return Icon(
Icons.done_all,
size: 8,
color: StreamChatTheme.of(context).accentColor,
Icons.done_all_rounded,
size: size,
color: StreamChatTheme.of(context).colorTheme.accentBlue,
);
}
if (message.status == MessageSendingStatus.SENT || message.status == null) {
return Icon(
Icons.done,
size: 8,
size: size,
color: IconTheme.of(context).color.withOpacity(0.5),
);
}
@@ -32,18 +34,9 @@ class SendingIndicator extends StatelessWidget {
message.status == MessageSendingStatus.UPDATING) {
return Icon(
Icons.access_time,
size: 8,
size: size,
);
}
if (message.status == MessageSendingStatus.FAILED ||
message.status == MessageSendingStatus.FAILED_UPDATE ||
message.status == MessageSendingStatus.FAILED_DELETE) {
return Icon(
Icons.error_outline,
size: 8,
);
}
return SizedBox();
}
}
+2 -6
View File
@@ -76,8 +76,8 @@ class StreamChatState extends State<StreamChat> with WidgetsBindingObserver {
return Theme(
data: materialTheme.copyWith(
primaryIconTheme: streamTheme.primaryIconTheme,
accentColor: streamTheme.accentColor,
scaffoldBackgroundColor: streamTheme.backgroundColor,
accentColor: streamTheme.colorTheme.accentBlue,
scaffoldBackgroundColor: streamTheme.colorTheme.white,
),
child: widget.child,
);
@@ -92,11 +92,9 @@ class StreamChatState extends State<StreamChat> with WidgetsBindingObserver {
) {
final defaultTheme = StreamChatThemeData.getDefaultTheme(Theme.of(context));
final theme = defaultTheme.copyWith(
primaryColor: themeData?.primaryColor,
defaultChannelImage: themeData?.defaultChannelImage,
primaryIconTheme: themeData?.primaryIconTheme,
defaultUserImage: themeData?.defaultUserImage,
backgroundColor: themeData?.backgroundColor,
channelTheme: defaultTheme.channelTheme.copyWith(
channelHeaderTheme:
defaultTheme.channelTheme.channelHeaderTheme.copyWith(
@@ -151,8 +149,6 @@ class StreamChatState extends State<StreamChat> with WidgetsBindingObserver {
reactionsBackgroundColor:
themeData?.otherMessageTheme?.reactionsBackgroundColor,
),
accentColor: themeData?.accentColor,
secondaryColor: themeData?.secondaryColor,
channelPreviewTheme: defaultTheme.channelPreviewTheme.copyWith(
avatarTheme: defaultTheme.channelPreviewTheme.avatarTheme.copyWith(
constraints: themeData?.channelPreviewTheme?.avatarTheme?.constraints,
+285 -81
View File
@@ -42,17 +42,11 @@ class StreamChatTheme extends InheritedWidget {
/// Theme data
class StreamChatThemeData {
/// Primary color of the chat widgets
final Color primaryColor;
/// The text themes used in the widgets
final TextTheme textTheme;
/// Secondary color of the chat widgets
final Color secondaryColor;
/// Accent color of the chat widgets
final Color accentColor;
/// Background color of the chat widgets
final Color backgroundColor;
/// The text themes used in the widgets
final ColorTheme colorTheme;
/// Theme of the [ChannelPreview]
final ChannelPreviewTheme channelPreviewTheme;
@@ -80,10 +74,8 @@ class StreamChatThemeData {
/// Create a theme from scratch
StreamChatThemeData({
this.primaryColor,
this.secondaryColor,
this.accentColor,
this.backgroundColor,
this.textTheme,
this.colorTheme,
this.channelPreviewTheme,
this.channelTheme,
this.otherMessageTheme,
@@ -99,11 +91,7 @@ class StreamChatThemeData {
final defaultTheme = getDefaultTheme(theme);
return defaultTheme.copyWith(
accentColor: theme.accentColor,
primaryIconTheme: theme.primaryIconTheme,
primaryColor: theme.colorScheme.primary,
secondaryColor: theme.colorScheme.secondary,
backgroundColor: theme.scaffoldBackgroundColor,
channelTheme: defaultTheme.channelTheme.copyWith(
inputGradient: LinearGradient(colors: [
theme.accentColor.withOpacity(.5),
@@ -131,10 +119,8 @@ class StreamChatThemeData {
/// Creates a copy of [StreamChatThemeData] with specified attributes overridden.
StreamChatThemeData copyWith({
Color primaryColor,
Color secondaryColor,
Color accentColor,
Color backgroundColor,
TextTheme textTheme,
ColorTheme colorTheme,
ChannelPreviewTheme channelPreviewTheme,
ChannelTheme channelTheme,
MessageTheme ownMessageTheme,
@@ -145,24 +131,35 @@ class StreamChatThemeData {
List<ReactionIcon> reactionIcons,
}) =>
StreamChatThemeData(
primaryColor: primaryColor ?? this.primaryColor,
secondaryColor: secondaryColor ?? this.secondaryColor,
textTheme: this.textTheme?.copyWith(
title: textTheme?.title,
body: textTheme?.body,
bodyBold: textTheme?.bodyBold,
captionBold: textTheme?.captionBold,
footnote: textTheme?.footnote,
footnoteBold: textTheme?.footnoteBold,
headline: textTheme?.headline,
headlineBold: textTheme?.headlineBold,
),
colorTheme: this.colorTheme?.copyWith(
black: colorTheme?.black,
grey: colorTheme?.grey,
greyGainsboro: colorTheme?.greyGainsboro,
greyWhisper: colorTheme?.greyWhisper,
whiteSmoke: colorTheme?.whiteSmoke,
whiteSnow: colorTheme?.whiteSnow,
white: colorTheme?.white,
blueAlice: colorTheme?.blueAlice,
),
primaryIconTheme: primaryIconTheme ?? this.primaryIconTheme,
accentColor: accentColor ?? this.accentColor,
defaultChannelImage: defaultChannelImage ?? this.defaultChannelImage,
defaultUserImage: defaultUserImage ?? this.defaultUserImage,
backgroundColor: backgroundColor ?? this.backgroundColor,
channelPreviewTheme: channelPreviewTheme?.copyWith(
title:
channelPreviewTheme.title ?? this.channelPreviewTheme.title,
subtitle: channelPreviewTheme.subtitle ??
this.channelPreviewTheme.subtitle,
lastMessageAt: channelPreviewTheme.lastMessageAt ??
this.channelPreviewTheme.lastMessageAt,
avatarTheme: channelPreviewTheme.avatarTheme ??
this.channelPreviewTheme.avatarTheme,
) ??
this.channelPreviewTheme,
channelPreviewTheme: this.channelPreviewTheme?.copyWith(
title: channelPreviewTheme.title,
subtitle: channelPreviewTheme.subtitle,
lastMessageAt: channelPreviewTheme.lastMessageAt,
avatarTheme: channelPreviewTheme.avatarTheme,
),
channelTheme: channelTheme?.copyWith(
channelHeaderTheme: channelTheme.channelHeaderTheme ??
this.channelTheme.channelHeaderTheme,
@@ -222,14 +219,13 @@ class StreamChatThemeData {
static StreamChatThemeData getDefaultTheme(ThemeData theme) {
final accentColor = Color(0xff006cff);
final isDark = theme.brightness == Brightness.dark;
final textTheme = isDark ? TextTheme.dark() : TextTheme.light();
final colorTheme = isDark ? ColorTheme.dark() : ColorTheme.light();
return StreamChatThemeData(
secondaryColor: Color(0xffEAEAEA),
accentColor: accentColor,
primaryColor: isDark ? Colors.black : Colors.white,
primaryIconTheme: IconThemeData(
color: isDark ? Colors.white : Colors.black.withOpacity(.5)),
textTheme: textTheme,
colorTheme: colorTheme,
primaryIconTheme: IconThemeData(color: colorTheme.black.withOpacity(.5)),
defaultChannelImage: (context, channel) => SizedBox(),
backgroundColor: isDark ? Colors.black : Colors.white,
defaultUserImage: (context, user) => Center(
child: CachedNetworkImage(
filterQuality: FilterQuality.high,
@@ -238,7 +234,7 @@ class StreamChatThemeData {
),
),
channelPreviewTheme: ChannelPreviewTheme(
unreadCounterColor: Color(0xffff3742),
unreadCounterColor: colorTheme.accentRed,
avatarTheme: AvatarTheme(
borderRadius: BorderRadius.circular(20),
constraints: BoxConstraints.tightFor(
@@ -246,19 +242,12 @@ class StreamChatThemeData {
width: 40,
),
),
title: TextStyle(
fontSize: 14,
color: isDark ? Colors.white : Colors.black,
fontWeight: FontWeight.bold),
subtitle: TextStyle(
fontSize: 12.5,
color: (isDark ? Colors.white : Colors.black).withOpacity(0.5),
title: textTheme.bodyBold,
subtitle: textTheme.footnote.copyWith(
color: Color(0xff7A7A7A),
),
lastMessageAt: TextStyle(
fontSize: 11,
color: isDark
? Colors.white.withOpacity(.5)
: Colors.black.withOpacity(.5),
lastMessageAt: textTheme.footnote.copyWith(
color: colorTheme.black.withOpacity(.5),
),
),
channelTheme: ChannelTheme(
@@ -273,20 +262,17 @@ class StreamChatThemeData {
width: 40,
),
),
color: isDark ? Colors.black : Colors.white,
color: colorTheme.white,
title: TextStyle(
fontSize: 14,
color: isDark ? Colors.white : Colors.black,
color: colorTheme.black,
),
lastMessageAt: TextStyle(
fontSize: 11,
color: isDark
? Colors.white.withOpacity(.5)
: Colors.black.withOpacity(.5),
color: colorTheme.black.withOpacity(.5),
),
),
inputBackground:
isDark ? Colors.black.withAlpha(12) : Colors.white.withAlpha(12),
inputBackground: colorTheme.white.withAlpha(12),
inputGradient: LinearGradient(colors: [
Color(0xFF00AEFF),
Color(0xFF0076FF),
@@ -295,12 +281,10 @@ class StreamChatThemeData {
ownMessageTheme: MessageTheme(
messageText: TextStyle(
fontSize: 14.5,
color: isDark ? Colors.white : Colors.black,
color: colorTheme.black,
),
createdAt: TextStyle(
color: isDark
? Colors.white.withOpacity(.5)
: Colors.black.withOpacity(.5),
color: colorTheme.black.withOpacity(.5),
fontSize: 12,
),
replies: TextStyle(
@@ -308,10 +292,10 @@ class StreamChatThemeData {
fontWeight: FontWeight.w600,
fontSize: 12,
),
messageBackgroundColor: isDark ? Color(0xff191919) : Color(0xffEAEAEA),
reactionsBackgroundColor: isDark ? Colors.black : Colors.white,
reactionsBorderColor: isDark ? Color(0xff191919) : Color(0xffEAEAEA),
replyThreadColor: isDark ? Color(0xff191919) : Color(0xffEAEAEA),
messageBackgroundColor: colorTheme.greyGainsboro,
reactionsBackgroundColor: colorTheme.white,
reactionsBorderColor: colorTheme.greyWhisper,
replyThreadColor: colorTheme.greyWhisper,
avatarTheme: AvatarTheme(
borderRadius: BorderRadius.circular(20),
constraints: BoxConstraints.tightFor(
@@ -324,17 +308,14 @@ class StreamChatThemeData {
),
),
otherMessageTheme: MessageTheme(
reactionsBackgroundColor:
isDark ? Color(0xff191919) : Color(0xffEAEAEA),
reactionsBorderColor: isDark ? Colors.black : Colors.white,
reactionsBackgroundColor: colorTheme.greyGainsboro,
reactionsBorderColor: colorTheme.white,
messageText: TextStyle(
fontSize: 14.5,
color: isDark ? Colors.white : Colors.black,
color: colorTheme.black,
),
createdAt: TextStyle(
color: isDark
? Colors.white.withOpacity(.5)
: Colors.black.withOpacity(.5),
color: colorTheme.black.withOpacity(.5),
fontSize: 12,
),
replies: TextStyle(
@@ -345,9 +326,8 @@ class StreamChatThemeData {
messageLinks: TextStyle(
color: accentColor,
),
messageBackgroundColor: isDark ? Colors.black : Colors.white,
replyThreadColor:
isDark ? Colors.white.withAlpha(24) : Colors.black.withAlpha(24),
messageBackgroundColor: colorTheme.white,
replyThreadColor: colorTheme.black.withAlpha(24),
avatarTheme: AvatarTheme(
borderRadius: BorderRadius.circular(20),
constraints: BoxConstraints.tightFor(
@@ -382,6 +362,230 @@ class StreamChatThemeData {
}
}
enum TextThemeType {
light,
dark,
}
class TextTheme {
final TextStyle title;
final TextStyle headlineBold;
final TextStyle headline;
final TextStyle bodyBold;
final TextStyle body;
final TextStyle footnoteBold;
final TextStyle footnote;
final TextStyle captionBold;
TextTheme.light({
this.title = const TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: Colors.black,
),
this.headlineBold = const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.black,
),
this.headline = const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Colors.black,
),
this.bodyBold = const TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.black,
),
this.body = const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Colors.black,
),
this.footnoteBold = const TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Colors.black,
),
this.footnote = const TextStyle(
fontSize: 12,
color: Colors.black,
),
this.captionBold = const TextStyle(
fontSize: 10,
fontWeight: FontWeight.bold,
color: Colors.black,
),
});
TextTheme.dark({
this.title = const TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: Colors.white,
),
this.headlineBold = const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.white,
),
this.headline = const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Colors.white,
),
this.bodyBold = const TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.white,
),
this.body = const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Colors.white,
),
this.footnoteBold = const TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Colors.white,
),
this.footnote = const TextStyle(
fontSize: 12,
color: Colors.white,
),
this.captionBold = const TextStyle(
fontSize: 10,
fontWeight: FontWeight.bold,
color: Colors.white,
),
});
TextTheme copyWith({
TextThemeType type = TextThemeType.light,
TextStyle body,
TextStyle title,
TextStyle headlineBold,
TextStyle headline,
TextStyle bodyBold,
TextStyle footnoteBold,
TextStyle footnote,
TextStyle captionBold,
}) {
return type == TextThemeType.light
? TextTheme.light(
body: body ?? this.body,
title: title ?? this.title,
headlineBold: headlineBold ?? this.headlineBold,
headline: headline ?? this.headline,
bodyBold: bodyBold ?? this.bodyBold,
footnoteBold: footnoteBold ?? this.footnoteBold,
footnote: footnote ?? this.footnote,
captionBold: captionBold ?? this.captionBold,
)
: TextTheme.dark(
body: body ?? this.body,
title: title ?? this.title,
headlineBold: headlineBold ?? this.headlineBold,
headline: headline ?? this.headline,
bodyBold: bodyBold ?? this.bodyBold,
footnoteBold: footnoteBold ?? this.footnoteBold,
footnote: footnote ?? this.footnote,
captionBold: captionBold ?? this.captionBold,
);
}
}
enum ColorThemeType {
light,
dark,
}
class ColorTheme {
final Color black;
final Color grey;
final Color greyGainsboro;
final Color greyWhisper;
final Color whiteSmoke;
final Color whiteSnow;
final Color white;
final Color blueAlice;
final Color accentBlue;
final Color accentRed;
final Color accentGreen;
ColorTheme.light({
this.black = const Color(0xff000000),
this.grey = const Color(0xff7a7a7a),
this.greyGainsboro = const Color(0xffdbdbdb),
this.greyWhisper = const Color(0xffecebeb),
this.whiteSmoke = const Color(0xfff2f2f2),
this.whiteSnow = const Color(0xfffcfcfc),
this.white = const Color(0xffffffff),
this.blueAlice = const Color(0xffe9f2ff),
this.accentBlue = const Color(0xff005FFF),
this.accentRed = const Color(0xffFF3742),
this.accentGreen = const Color(0xff20E070),
});
ColorTheme.dark({
this.black = const Color(0xffffffff),
this.grey = const Color(0xff7a7a7a),
this.greyGainsboro = const Color(0xff2d2f2f),
this.greyWhisper = const Color(0xff1c1e22),
this.whiteSmoke = const Color(0xff13151b),
this.whiteSnow = const Color(0xff070A0D),
this.white = const Color(0xff101418),
this.blueAlice = const Color(0xff00193D),
this.accentBlue = const Color(0xff005FFF),
this.accentRed = const Color(0xffFF3742),
this.accentGreen = const Color(0xff20E070),
});
ColorTheme copyWith({
ColorThemeType type = ColorThemeType.light,
Color black,
Color grey,
Color greyGainsboro,
Color greyWhisper,
Color whiteSmoke,
Color whiteSnow,
Color white,
Color blueAlice,
Color accentBlue,
Color accentRed,
Color accentGreen,
}) {
return type == ColorThemeType.light
? ColorTheme.light(
black: black ?? this.black,
grey: grey ?? this.grey,
greyGainsboro: greyGainsboro ?? this.greyGainsboro,
greyWhisper: greyWhisper ?? this.greyWhisper,
whiteSmoke: whiteSmoke ?? this.whiteSmoke,
whiteSnow: whiteSnow ?? this.whiteSnow,
white: white ?? this.white,
blueAlice: blueAlice ?? this.blueAlice,
accentBlue: accentBlue ?? this.accentBlue,
accentRed: accentRed ?? this.accentRed,
accentGreen: accentGreen ?? this.accentGreen,
)
: ColorTheme.dark(
black: black ?? this.black,
grey: grey ?? this.grey,
greyGainsboro: greyGainsboro ?? this.greyGainsboro,
greyWhisper: greyWhisper ?? this.greyWhisper,
whiteSmoke: whiteSmoke ?? this.whiteSmoke,
whiteSnow: whiteSnow ?? this.whiteSnow,
white: white ?? this.white,
blueAlice: blueAlice ?? this.blueAlice,
accentBlue: accentBlue ?? this.accentBlue,
accentRed: accentRed ?? this.accentRed,
accentGreen: accentGreen ?? this.accentGreen,
);
}
}
/// Channel theme data
class ChannelTheme {
/// Theme of the [ChannelHeader] widget
+24
View File
@@ -757,4 +757,28 @@ class StreamSvgIcon extends StatelessWidget {
height: size,
);
}
factory StreamSvgIcon.error({
double size,
Color color,
}) {
return StreamSvgIcon(
assetName: 'Icon_error.svg',
color: color,
width: size,
height: size,
);
}
factory StreamSvgIcon.circle_up({
double size,
Color color,
}) {
return StreamSvgIcon(
assetName: 'Icon_circle_up.svg',
color: color,
width: size,
height: size,
);
}
}
+17 -13
View File
@@ -51,17 +51,21 @@ class UrlAttachment extends StatelessWidget {
),
child: Text(
hostDisplayName,
style: TextStyle(
fontWeight: FontWeight.w700,
color: Color(0xFF006CFF),
),
style: StreamChatTheme.of(context)
.textTheme
.bodyBold
.copyWith(
color: StreamChatTheme.of(context)
.colorTheme
.accentBlue,
),
),
),
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topRight: Radius.circular(16.0),
),
color: Color(0xFFE9F2FF),
color: StreamChatTheme.of(context).colorTheme.blueAlice,
),
),
),
@@ -81,18 +85,18 @@ class UrlAttachment extends StatelessWidget {
urlAttachment.title.trim(),
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontWeight: FontWeight.w700,
fontSize: 12.0,
),
style: StreamChatTheme.of(context)
.textTheme
.body
.copyWith(fontWeight: FontWeight.w700),
),
if (urlAttachment.text != null)
Text(
urlAttachment.text,
style: TextStyle(
fontWeight: FontWeight.w400,
fontSize: 12.0,
),
style: StreamChatTheme.of(context)
.textTheme
.body
.copyWith(fontWeight: FontWeight.w400),
),
],
),
+6 -5
View File
@@ -16,7 +16,7 @@ class UserAvatar extends StatelessWidget {
this.borderRadius,
this.onlineIndicatorAlignment = Alignment.topRight,
this.selected = false,
this.selectionColor = const Color(0xFF006CFF),
this.selectionColor,
this.selectionThickness = 4,
}) : super(key: key);
@@ -47,7 +47,7 @@ class UserAvatar extends StatelessWidget {
constraints: constraints ??
streamChatTheme.ownMessageTheme.avatarTheme.constraints,
decoration: BoxDecoration(
color: streamChatTheme.accentColor,
color: streamChatTheme.colorTheme.accentBlue,
),
child: hasImage
? CachedNetworkImage(
@@ -70,7 +70,8 @@ class UserAvatar extends StatelessWidget {
child: Container(
constraints: constraints ??
streamChatTheme.ownMessageTheme.avatarTheme.constraints,
color: selectionColor,
color: selectionColor ??
StreamChatTheme.of(context).colorTheme.accentBlue,
child: Padding(
padding: EdgeInsets.all(selectionThickness),
child: avatar,
@@ -99,10 +100,10 @@ class UserAvatar extends StatelessWidget {
),
child: Material(
shape: CircleBorder(),
color: Color(0xff20E070),
color: streamChatTheme.colorTheme.accentGreen,
),
),
color: Colors.white,
color: StreamChatTheme.of(context).colorTheme.white,
),
),
),
+11 -5
View File
@@ -74,24 +74,30 @@ class UserItem extends StatelessWidget {
),
trailing: selected
? CircleAvatar(
backgroundColor:
StreamChatTheme.of(context).colorTheme.accentBlue,
child: StreamSvgIcon.check(
size: 20,
color: Colors.white,
color: StreamChatTheme.of(context).colorTheme.white,
),
radius: 10,
)
: null,
title: Text(
user.name,
style: TextStyle(fontWeight: FontWeight.bold),
style: StreamChatTheme.of(context).textTheme.bodyBold,
),
subtitle: showLastOnline ? _buildLastActive(context) : null,
);
}
Widget _buildLastActive(context) {
return user.online == true
? Text('Online')
: Text('Last online ${Jiffy(user.lastActive).fromNow()}');
return Text(
user.online == true
? 'Online'
: 'Last online ${Jiffy(user.lastActive).fromNow()}',
style: StreamChatTheme.of(context).textTheme.footnote.copyWith(
color: StreamChatTheme.of(context).colorTheme.black.withOpacity(.5)),
);
}
}
+13 -6
View File
@@ -356,15 +356,19 @@ class _UserListViewState extends State<UserListView>
headerItem: (header) {
return Container(
key: ValueKey<String>('HEADER-$header'),
color: Colors.black.withOpacity(0.05),
color:
StreamChatTheme.of(context).colorTheme.black.withOpacity(0.05),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 6),
child: Text(
header,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 12,
color: Colors.black.withOpacity(0.3)),
fontSize: 14.5,
color: StreamChatTheme.of(context)
.colorTheme
.black
.withOpacity(0.3)),
),
),
);
@@ -449,7 +453,10 @@ class _UserListViewState extends State<UserListView>
builder: (context, snapshot) {
if (snapshot.hasError) {
return Container(
color: Color(0xffd0021B).withAlpha(26),
color: StreamChatTheme.of(context)
.colorTheme
.accentRed
.withOpacity(.2),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Center(
@@ -472,8 +479,8 @@ class _UserListViewState extends State<UserListView>
return Container(
height: 1,
color: Theme.of(context).brightness == Brightness.dark
? Colors.white.withOpacity(0.1)
: Colors.black.withOpacity(0.1),
? StreamChatTheme.of(context).colorTheme.white.withOpacity(0.1)
: StreamChatTheme.of(context).colorTheme.black.withOpacity(0.1),
);
}
+8 -4
View File
@@ -25,7 +25,7 @@ Future<bool> showConfirmationDialog(
String cancelText,
}) {
return showModalBottomSheet(
backgroundColor: Colors.white,
backgroundColor: StreamChatTheme.of(context).colorTheme.white,
context: context,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
@@ -45,7 +45,7 @@ Future<bool> showConfirmationDialog(
),
Text(
title,
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16.0),
style: StreamChatTheme.of(context).textTheme.headlineBold,
),
SizedBox(
height: 7.0,
@@ -55,7 +55,8 @@ Future<bool> showConfirmationDialog(
height: 36.0,
),
Container(
color: Color(0xffe6e6e6),
color:
StreamChatTheme.of(context).colorTheme.black.withOpacity(.08),
height: 1.0,
),
Row(
@@ -65,7 +66,10 @@ Future<bool> showConfirmationDialog(
child: Text(
cancelText,
style: TextStyle(
color: Colors.black.withOpacity(0.5),
color: StreamChatTheme.of(context)
.colorTheme
.black
.withOpacity(0.5),
fontWeight: FontWeight.w400),
),
onPressed: () {