Merge branches 'develop' and 'rfac/transform' of https://github.com/GetStream/stream-chat-flutter into rfac/transform
Conflicts: packages/stream_chat_flutter/lib/src/deleted_message.dart packages/stream_chat_flutter/lib/src/message_widget.dart
This commit is contained in:
@@ -79,21 +79,21 @@ class HomeScreen extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final messages = channel.state!.channelStateStream;
|
final messages = channel.state!.messagesStream;
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text('Channel: ${channel.id}'),
|
title: Text('Channel: ${channel.id}'),
|
||||||
),
|
),
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: StreamBuilder<ChannelState?>(
|
child: StreamBuilder<List<Message>?>(
|
||||||
stream: messages,
|
stream: messages,
|
||||||
builder: (
|
builder: (
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
AsyncSnapshot<ChannelState?> snapshot,
|
AsyncSnapshot<List<Message>?> snapshot,
|
||||||
) {
|
) {
|
||||||
if (snapshot.hasData && snapshot.data != null) {
|
if (snapshot.hasData && snapshot.data != null) {
|
||||||
return MessageView(
|
return MessageView(
|
||||||
messages: snapshot.data!.messages.reversed.toList(),
|
messages: snapshot.data!.reversed.toList(),
|
||||||
channel: channel,
|
channel: channel,
|
||||||
);
|
);
|
||||||
} else if (snapshot.hasError) {
|
} else if (snapshot.hasError) {
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ import 'dart:async';
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:collection/collection.dart' show IterableExtension;
|
import 'package:collection/collection.dart'
|
||||||
|
show IterableExtension, ListEquality;
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:rxdart/rxdart.dart';
|
import 'package:rxdart/rxdart.dart';
|
||||||
@@ -1601,8 +1602,9 @@ class ChannelClientState {
|
|||||||
List<Message> get messages => _channelState.messages;
|
List<Message> get messages => _channelState.messages;
|
||||||
|
|
||||||
/// Channel message list as a stream
|
/// Channel message list as a stream
|
||||||
Stream<List<Message>?> get messagesStream =>
|
Stream<List<Message>?> get messagesStream => channelStateStream
|
||||||
channelStateStream.map((cs) => cs.messages);
|
.map((cs) => cs.messages)
|
||||||
|
.distinct((prev, next) => const ListEquality().equals(prev, next));
|
||||||
|
|
||||||
/// Channel pinned message list
|
/// Channel pinned message list
|
||||||
List<Message>? get pinnedMessages => _channelState.pinnedMessages.toList();
|
List<Message>? get pinnedMessages => _channelState.pinnedMessages.toList();
|
||||||
|
|||||||
@@ -17,18 +17,22 @@ class ChannelBottomSheet extends StatefulWidget {
|
|||||||
class _ChannelBottomSheetState extends State<ChannelBottomSheet> {
|
class _ChannelBottomSheetState extends State<ChannelBottomSheet> {
|
||||||
bool _showActions = true;
|
bool _showActions = true;
|
||||||
|
|
||||||
|
late StreamChannelState _streamChannelState;
|
||||||
|
late StreamChatThemeData _streamChatThemeData;
|
||||||
|
late StreamChatState _streamChatState;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final channel = StreamChannel.of(context).channel;
|
final channel = _streamChannelState.channel;
|
||||||
|
|
||||||
final members = channel.state?.members ?? [];
|
final members = channel.state?.members ?? [];
|
||||||
|
|
||||||
final userAsMember = members
|
final userAsMember =
|
||||||
.firstWhere((e) => e.user?.id == StreamChat.of(context).user?.id);
|
members.firstWhere((e) => e.user?.id == _streamChatState.user?.id);
|
||||||
final isOwner = userAsMember.role == 'owner';
|
final isOwner = userAsMember.role == 'owner';
|
||||||
|
|
||||||
return Material(
|
return Material(
|
||||||
color: StreamChatTheme.of(context).colorTheme.white,
|
color: _streamChatThemeData.colorTheme.white,
|
||||||
clipBehavior: Clip.antiAlias,
|
clipBehavior: Clip.antiAlias,
|
||||||
shape: const RoundedRectangleBorder(
|
shape: const RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.only(
|
borderRadius: BorderRadius.only(
|
||||||
@@ -48,8 +52,7 @@ class _ChannelBottomSheetState extends State<ChannelBottomSheet> {
|
|||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
child: ChannelName(
|
child: ChannelName(
|
||||||
textStyle:
|
textStyle: _streamChatThemeData.textTheme.headlineBold,
|
||||||
StreamChatTheme.of(context).textTheme.headlineBold,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -59,10 +62,9 @@ class _ChannelBottomSheetState extends State<ChannelBottomSheet> {
|
|||||||
Center(
|
Center(
|
||||||
child: ChannelInfo(
|
child: ChannelInfo(
|
||||||
showTypingIndicator: false,
|
showTypingIndicator: false,
|
||||||
channel: StreamChannel.of(context).channel,
|
channel: _streamChannelState.channel,
|
||||||
textStyle: StreamChatTheme.of(context)
|
textStyle:
|
||||||
.channelPreviewTheme
|
_streamChatThemeData.channelPreviewTheme.subtitle,
|
||||||
.subtitle,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
@@ -94,8 +96,7 @@ class _ChannelBottomSheetState extends State<ChannelBottomSheet> {
|
|||||||
.user
|
.user
|
||||||
?.name ??
|
?.name ??
|
||||||
'',
|
'',
|
||||||
style:
|
style: _streamChatThemeData.textTheme.footnoteBold,
|
||||||
StreamChatTheme.of(context).textTheme.footnoteBold,
|
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
@@ -128,9 +129,8 @@ class _ChannelBottomSheetState extends State<ChannelBottomSheet> {
|
|||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
members[index].user?.name ?? '',
|
members[index].user?.name ?? '',
|
||||||
style: StreamChatTheme.of(context)
|
style:
|
||||||
.textTheme
|
_streamChatThemeData.textTheme.footnoteBold,
|
||||||
.footnoteBold,
|
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
@@ -146,7 +146,7 @@ class _ChannelBottomSheetState extends State<ChannelBottomSheet> {
|
|||||||
leading: Padding(
|
leading: Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
child: StreamSvgIcon.user(
|
child: StreamSvgIcon.user(
|
||||||
color: StreamChatTheme.of(context).colorTheme.grey,
|
color: _streamChatThemeData.colorTheme.grey,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
title: 'View Info',
|
title: 'View Info',
|
||||||
@@ -157,7 +157,7 @@ class _ChannelBottomSheetState extends State<ChannelBottomSheet> {
|
|||||||
leading: Padding(
|
leading: Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
child: StreamSvgIcon.userRemove(
|
child: StreamSvgIcon.userRemove(
|
||||||
color: StreamChatTheme.of(context).colorTheme.grey,
|
color: _streamChatThemeData.colorTheme.grey,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
title: 'Leave Group',
|
title: 'Leave Group',
|
||||||
@@ -176,12 +176,11 @@ class _ChannelBottomSheetState extends State<ChannelBottomSheet> {
|
|||||||
leading: Padding(
|
leading: Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
child: StreamSvgIcon.delete(
|
child: StreamSvgIcon.delete(
|
||||||
color: StreamChatTheme.of(context).colorTheme.accentRed,
|
color: _streamChatThemeData.colorTheme.accentRed,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
title: 'Delete Conversation',
|
title: 'Delete Conversation',
|
||||||
titleColor:
|
titleColor: _streamChatThemeData.colorTheme.accentRed,
|
||||||
StreamChatTheme.of(context).colorTheme.accentRed,
|
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
setState(() {
|
setState(() {
|
||||||
_showActions = false;
|
_showActions = false;
|
||||||
@@ -196,7 +195,7 @@ class _ChannelBottomSheetState extends State<ChannelBottomSheet> {
|
|||||||
leading: Padding(
|
leading: Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
child: StreamSvgIcon.closeSmall(
|
child: StreamSvgIcon.closeSmall(
|
||||||
color: StreamChatTheme.of(context).colorTheme.grey,
|
color: _streamChatThemeData.colorTheme.grey,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
title: 'Cancel',
|
title: 'Cancel',
|
||||||
@@ -209,6 +208,14 @@ class _ChannelBottomSheetState extends State<ChannelBottomSheet> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didChangeDependencies() {
|
||||||
|
_streamChannelState = StreamChannel.of(context);
|
||||||
|
_streamChatThemeData = StreamChatTheme.of(context);
|
||||||
|
_streamChatState = StreamChat.of(context);
|
||||||
|
super.didChangeDependencies();
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _showDeleteDialog() async {
|
Future<void> _showDeleteDialog() async {
|
||||||
final res = await showConfirmationDialog(
|
final res = await showConfirmationDialog(
|
||||||
context,
|
context,
|
||||||
@@ -217,10 +224,10 @@ class _ChannelBottomSheetState extends State<ChannelBottomSheet> {
|
|||||||
question: 'Are you sure you want to delete this conversation?',
|
question: 'Are you sure you want to delete this conversation?',
|
||||||
cancelText: 'CANCEL',
|
cancelText: 'CANCEL',
|
||||||
icon: StreamSvgIcon.delete(
|
icon: StreamSvgIcon.delete(
|
||||||
color: StreamChatTheme.of(context).colorTheme.accentRed,
|
color: _streamChatThemeData.colorTheme.accentRed,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
final channel = StreamChannel.of(context).channel;
|
final channel = _streamChannelState.channel;
|
||||||
if (res == true) {
|
if (res == true) {
|
||||||
await channel.delete();
|
await channel.delete();
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
@@ -235,12 +242,12 @@ class _ChannelBottomSheetState extends State<ChannelBottomSheet> {
|
|||||||
question: 'Are you sure you want to leave this conversation?',
|
question: 'Are you sure you want to leave this conversation?',
|
||||||
cancelText: 'CANCEL',
|
cancelText: 'CANCEL',
|
||||||
icon: StreamSvgIcon.userRemove(
|
icon: StreamSvgIcon.userRemove(
|
||||||
color: StreamChatTheme.of(context).colorTheme.accentRed,
|
color: _streamChatThemeData.colorTheme.accentRed,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
if (res == true) {
|
if (res == true) {
|
||||||
final channel = StreamChannel.of(context).channel;
|
final channel = _streamChannelState.channel;
|
||||||
final user = StreamChat.of(context).user;
|
final user = _streamChatState.user;
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
await channel.removeMembers([user.id]);
|
await channel.removeMembers([user.id]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ class ChannelHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final channel = StreamChannel.of(context).channel;
|
final channel = StreamChannel.of(context).channel;
|
||||||
|
final chatThemeData = StreamChatTheme.of(context);
|
||||||
|
|
||||||
final leadingWidget = leading ??
|
final leadingWidget = leading ??
|
||||||
(showBackButton
|
(showBackButton
|
||||||
@@ -139,26 +140,18 @@ class ChannelHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
brightness: Theme.of(context).brightness,
|
brightness: Theme.of(context).brightness,
|
||||||
elevation: 1,
|
elevation: 1,
|
||||||
leading: leadingWidget,
|
leading: leadingWidget,
|
||||||
backgroundColor: StreamChatTheme.of(context)
|
backgroundColor:
|
||||||
.channelTheme
|
chatThemeData.channelTheme.channelHeaderTheme.color,
|
||||||
.channelHeaderTheme
|
|
||||||
.color,
|
|
||||||
actions: actions ??
|
actions: actions ??
|
||||||
<Widget>[
|
<Widget>[
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(right: 10),
|
padding: const EdgeInsets.only(right: 10),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: ChannelImage(
|
child: ChannelImage(
|
||||||
borderRadius: StreamChatTheme.of(context)
|
borderRadius: chatThemeData.channelTheme
|
||||||
.channelTheme
|
.channelHeaderTheme.avatarTheme?.borderRadius,
|
||||||
.channelHeaderTheme
|
constraints: chatThemeData.channelTheme
|
||||||
.avatarTheme
|
.channelHeaderTheme.avatarTheme?.constraints,
|
||||||
?.borderRadius,
|
|
||||||
constraints: StreamChatTheme.of(context)
|
|
||||||
.channelTheme
|
|
||||||
.channelHeaderTheme
|
|
||||||
.avatarTheme
|
|
||||||
?.constraints,
|
|
||||||
onTap: onImageTap,
|
onTap: onImageTap,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -175,20 +168,16 @@ class ChannelHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
title ??
|
title ??
|
||||||
ChannelName(
|
ChannelName(
|
||||||
textStyle: StreamChatTheme.of(context)
|
textStyle: chatThemeData
|
||||||
.channelTheme
|
.channelTheme.channelHeaderTheme.title,
|
||||||
.channelHeaderTheme
|
|
||||||
.title,
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 2),
|
const SizedBox(height: 2),
|
||||||
subtitle ??
|
subtitle ??
|
||||||
ChannelInfo(
|
ChannelInfo(
|
||||||
showTypingIndicator: showTypingIndicator,
|
showTypingIndicator: showTypingIndicator,
|
||||||
channel: channel,
|
channel: channel,
|
||||||
textStyle: StreamChatTheme.of(context)
|
textStyle: chatThemeData
|
||||||
.channelTheme
|
.channelTheme.channelHeaderTheme.subtitle,
|
||||||
.channelHeaderTheme
|
|
||||||
.subtitle,
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ class ChannelImage extends StatelessWidget {
|
|||||||
initialData: channel.extraData,
|
initialData: channel.extraData,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
String? image;
|
String? image;
|
||||||
|
final chatThemeData = StreamChatTheme.of(context);
|
||||||
if (snapshot.data!.containsKey('image') == true) {
|
if (snapshot.data!.containsKey('image') == true) {
|
||||||
image = snapshot.data!['image'];
|
image = snapshot.data!['image'];
|
||||||
} else if (channel.state?.members.length == 2) {
|
} else if (channel.state?.members.length == 2) {
|
||||||
@@ -99,20 +100,16 @@ class ChannelImage extends StatelessWidget {
|
|||||||
initialData: otherMember!.user,
|
initialData: otherMember!.user,
|
||||||
builder: (context, snapshot) => UserAvatar(
|
builder: (context, snapshot) => UserAvatar(
|
||||||
borderRadius: borderRadius ??
|
borderRadius: borderRadius ??
|
||||||
StreamChatTheme.of(context)
|
chatThemeData
|
||||||
.channelPreviewTheme
|
.channelPreviewTheme.avatarTheme?.borderRadius,
|
||||||
.avatarTheme
|
|
||||||
?.borderRadius,
|
|
||||||
user: snapshot.data ?? otherMember.user!,
|
user: snapshot.data ?? otherMember.user!,
|
||||||
constraints: constraints ??
|
constraints: constraints ??
|
||||||
StreamChatTheme.of(context)
|
chatThemeData
|
||||||
.channelPreviewTheme
|
.channelPreviewTheme.avatarTheme?.constraints,
|
||||||
.avatarTheme
|
|
||||||
?.constraints,
|
|
||||||
onTap: onTap != null ? (_) => onTap!() : null,
|
onTap: onTap != null ? (_) => onTap!() : null,
|
||||||
selected: selected,
|
selected: selected,
|
||||||
selectionColor: selectionColor ??
|
selectionColor:
|
||||||
StreamChatTheme.of(context).colorTheme.accentBlue,
|
selectionColor ?? chatThemeData.colorTheme.accentBlue,
|
||||||
selectionThickness: selectionThickness,
|
selectionThickness: selectionThickness,
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
@@ -127,37 +124,25 @@ class ChannelImage extends StatelessWidget {
|
|||||||
return GroupImage(
|
return GroupImage(
|
||||||
images: images ?? [],
|
images: images ?? [],
|
||||||
borderRadius: borderRadius ??
|
borderRadius: borderRadius ??
|
||||||
StreamChatTheme.of(context)
|
chatThemeData.channelPreviewTheme.avatarTheme?.borderRadius,
|
||||||
.channelPreviewTheme
|
|
||||||
.avatarTheme
|
|
||||||
?.borderRadius,
|
|
||||||
constraints: constraints ??
|
constraints: constraints ??
|
||||||
StreamChatTheme.of(context)
|
chatThemeData.channelPreviewTheme.avatarTheme?.constraints,
|
||||||
.channelPreviewTheme
|
|
||||||
.avatarTheme
|
|
||||||
?.constraints,
|
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
selected: selected,
|
selected: selected,
|
||||||
selectionColor: selectionColor ??
|
selectionColor:
|
||||||
StreamChatTheme.of(context).colorTheme.accentBlue,
|
selectionColor ?? chatThemeData.colorTheme.accentBlue,
|
||||||
selectionThickness: selectionThickness,
|
selectionThickness: selectionThickness,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget child = ClipRRect(
|
Widget child = ClipRRect(
|
||||||
borderRadius: borderRadius ??
|
borderRadius: borderRadius ??
|
||||||
StreamChatTheme.of(context)
|
chatThemeData.channelPreviewTheme.avatarTheme?.borderRadius,
|
||||||
.channelPreviewTheme
|
|
||||||
.avatarTheme
|
|
||||||
?.borderRadius,
|
|
||||||
child: Container(
|
child: Container(
|
||||||
constraints: constraints ??
|
constraints: constraints ??
|
||||||
StreamChatTheme.of(context)
|
chatThemeData.channelPreviewTheme.avatarTheme?.constraints,
|
||||||
.channelPreviewTheme
|
|
||||||
.avatarTheme
|
|
||||||
?.constraints,
|
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: StreamChatTheme.of(context).colorTheme.accentBlue,
|
color: chatThemeData.colorTheme.accentBlue,
|
||||||
),
|
),
|
||||||
child: Stack(
|
child: Stack(
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
@@ -172,7 +157,7 @@ class ChannelImage extends StatelessWidget {
|
|||||||
? snapshot.data!['name'][0]
|
? snapshot.data!['name'][0]
|
||||||
: '',
|
: '',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: StreamChatTheme.of(context).colorTheme.white,
|
color: chatThemeData.colorTheme.white,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -180,7 +165,7 @@ class ChannelImage extends StatelessWidget {
|
|||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
StreamChatTheme.of(context).defaultChannelImage(
|
chatThemeData.defaultChannelImage(
|
||||||
context,
|
context,
|
||||||
channel,
|
channel,
|
||||||
),
|
),
|
||||||
@@ -198,20 +183,13 @@ class ChannelImage extends StatelessWidget {
|
|||||||
child = ClipRRect(
|
child = ClipRRect(
|
||||||
key: const Key('selectedImage'),
|
key: const Key('selectedImage'),
|
||||||
borderRadius: (borderRadius ??
|
borderRadius: (borderRadius ??
|
||||||
StreamChatTheme.of(context)
|
chatThemeData.ownMessageTheme.avatarTheme?.borderRadius ??
|
||||||
.ownMessageTheme
|
|
||||||
.avatarTheme
|
|
||||||
?.borderRadius ??
|
|
||||||
BorderRadius.zero) +
|
BorderRadius.zero) +
|
||||||
BorderRadius.circular(selectionThickness),
|
BorderRadius.circular(selectionThickness),
|
||||||
child: Container(
|
child: Container(
|
||||||
constraints: constraints ??
|
constraints: constraints ??
|
||||||
StreamChatTheme.of(context)
|
chatThemeData.ownMessageTheme.avatarTheme?.constraints,
|
||||||
.ownMessageTheme
|
color: selectionColor ?? chatThemeData.colorTheme.accentBlue,
|
||||||
.avatarTheme
|
|
||||||
?.constraints,
|
|
||||||
color: selectionColor ??
|
|
||||||
StreamChatTheme.of(context).colorTheme.accentBlue,
|
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.all(selectionThickness),
|
padding: EdgeInsets.all(selectionThickness),
|
||||||
child: child,
|
child: child,
|
||||||
|
|||||||
@@ -46,7 +46,9 @@ class ChannelInfo extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildConnectedTitleState(
|
Widget _buildConnectedTitleState(
|
||||||
BuildContext context, List<Member>? members) {
|
BuildContext context,
|
||||||
|
List<Member>? members,
|
||||||
|
) {
|
||||||
Widget? alternativeWidget;
|
Widget? alternativeWidget;
|
||||||
|
|
||||||
if (channel.memberCount != null && channel.memberCount! > 2) {
|
if (channel.memberCount != null && channel.memberCount! > 2) {
|
||||||
@@ -61,8 +63,9 @@ class ChannelInfo extends StatelessWidget {
|
|||||||
.subtitle,
|
.subtitle,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
final userId = StreamChat.of(context).user?.id;
|
||||||
final otherMember = members?.firstWhereOrNull(
|
final otherMember = members?.firstWhereOrNull(
|
||||||
(element) => element.userId != StreamChat.of(context).user?.id,
|
(element) => element.userId != userId,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (otherMember != null) {
|
if (otherMember != null) {
|
||||||
|
|||||||
@@ -114,6 +114,7 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final chatThemeData = StreamChatTheme.of(context);
|
||||||
return InfoTile(
|
return InfoTile(
|
||||||
// ignore: avoid_bool_literals_in_conditional_expressions
|
// ignore: avoid_bool_literals_in_conditional_expressions
|
||||||
showMessage: showConnectionStateTile ? showStatus : false,
|
showMessage: showConnectionStateTile ? showStatus : false,
|
||||||
@@ -121,8 +122,7 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
child: AppBar(
|
child: AppBar(
|
||||||
brightness: Theme.of(context).brightness,
|
brightness: Theme.of(context).brightness,
|
||||||
elevation: 1,
|
elevation: 1,
|
||||||
backgroundColor:
|
backgroundColor: chatThemeData.channelListHeaderTheme.color,
|
||||||
StreamChatTheme.of(context).channelListHeaderTheme.color,
|
|
||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
leading: leading ??
|
leading: leading ??
|
||||||
Center(
|
Center(
|
||||||
@@ -137,14 +137,10 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
}
|
}
|
||||||
Scaffold.of(context).openDrawer();
|
Scaffold.of(context).openDrawer();
|
||||||
},
|
},
|
||||||
borderRadius: StreamChatTheme.of(context)
|
borderRadius: chatThemeData
|
||||||
.channelListHeaderTheme
|
.channelListHeaderTheme.avatarTheme?.borderRadius,
|
||||||
.avatarTheme
|
constraints: chatThemeData
|
||||||
?.borderRadius,
|
.channelListHeaderTheme.avatarTheme?.constraints,
|
||||||
constraints: StreamChatTheme.of(context)
|
|
||||||
.channelListHeaderTheme
|
|
||||||
.avatarTheme
|
|
||||||
?.constraints,
|
|
||||||
)
|
)
|
||||||
: const Offstage(),
|
: const Offstage(),
|
||||||
),
|
),
|
||||||
@@ -157,9 +153,7 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
Color? color;
|
Color? color;
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case ConnectionStatus.connected:
|
case ConnectionStatus.connected:
|
||||||
color = StreamChatTheme.of(context)
|
color = chatThemeData.colorTheme.accentBlue;
|
||||||
.colorTheme
|
|
||||||
.accentBlue;
|
|
||||||
break;
|
break;
|
||||||
case ConnectionStatus.connecting:
|
case ConnectionStatus.connecting:
|
||||||
color = Colors.grey;
|
color = Colors.grey;
|
||||||
@@ -209,12 +203,15 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildConnectedTitleState(BuildContext context) => Text(
|
Widget _buildConnectedTitleState(BuildContext context) {
|
||||||
'Stream Chat',
|
final chatThemeData = StreamChatTheme.of(context);
|
||||||
style: StreamChatTheme.of(context).textTheme.headlineBold.copyWith(
|
return Text(
|
||||||
color: StreamChatTheme.of(context).colorTheme.black,
|
'Stream Chat',
|
||||||
),
|
style: chatThemeData.textTheme.headlineBold.copyWith(
|
||||||
);
|
color: chatThemeData.colorTheme.black,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildConnectingTitleState(BuildContext context) => Row(
|
Widget _buildConnectingTitleState(BuildContext context) => Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
@@ -243,39 +240,35 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
Widget _buildDisconnectedTitleState(
|
Widget _buildDisconnectedTitleState(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
StreamChatClient client,
|
StreamChatClient client,
|
||||||
) =>
|
) {
|
||||||
Row(
|
final chatThemeData = StreamChatTheme.of(context);
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
return Row(
|
||||||
children: [
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
Text(
|
children: [
|
||||||
'Offline...',
|
Text(
|
||||||
style: StreamChatTheme.of(context)
|
'Offline...',
|
||||||
.channelListHeaderTheme
|
style: chatThemeData.channelListHeaderTheme.title?.copyWith(
|
||||||
.title
|
fontSize: 16,
|
||||||
?.copyWith(
|
fontWeight: FontWeight.bold,
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
TextButton(
|
),
|
||||||
onPressed: () async {
|
TextButton(
|
||||||
await client.disconnect();
|
onPressed: () async {
|
||||||
await client.connect();
|
await client.disconnect();
|
||||||
},
|
await client.connect();
|
||||||
child: Text(
|
},
|
||||||
'Try Again',
|
child: Text(
|
||||||
style: StreamChatTheme.of(context)
|
'Try Again',
|
||||||
.channelListHeaderTheme
|
style: chatThemeData.channelListHeaderTheme.title?.copyWith(
|
||||||
.title
|
fontSize: 16,
|
||||||
?.copyWith(
|
fontWeight: FontWeight.bold,
|
||||||
fontSize: 16,
|
color: chatThemeData.colorTheme.accentBlue,
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: StreamChatTheme.of(context).colorTheme.accentBlue,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
);
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
|
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
|
||||||
|
|||||||
@@ -237,78 +237,70 @@ class _ChannelListViewState extends State<ChannelListView> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildEmptyWidget(BuildContext context) => LayoutBuilder(
|
Widget _buildEmptyWidget(BuildContext context) => LayoutBuilder(
|
||||||
builder: (context, viewportConstraints) => SingleChildScrollView(
|
builder: (context, viewportConstraints) {
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
final chatThemeData = StreamChatTheme.of(context);
|
||||||
child: Stack(
|
return SingleChildScrollView(
|
||||||
children: [
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
ConstrainedBox(
|
child: Stack(
|
||||||
constraints: BoxConstraints(
|
children: [
|
||||||
minHeight: viewportConstraints.maxHeight,
|
ConstrainedBox(
|
||||||
|
constraints: BoxConstraints(
|
||||||
|
minHeight: viewportConstraints.maxHeight,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
child: StreamSvgIcon.message(
|
||||||
|
size: 136,
|
||||||
|
color: chatThemeData.colorTheme.greyGainsboro,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
child: Text(
|
||||||
|
'Let’s start chatting!',
|
||||||
|
style: chatThemeData.textTheme.headline,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: 8,
|
||||||
|
horizontal: 52,
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
'How about sending your first message to a friend?',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: chatThemeData.textTheme.body.copyWith(
|
||||||
|
color: chatThemeData.colorTheme.grey,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
child: Column(
|
if (widget.onStartChatPressed != null)
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
Positioned(
|
||||||
children: [
|
right: 0,
|
||||||
Padding(
|
left: 0,
|
||||||
padding: const EdgeInsets.all(8),
|
bottom: 32,
|
||||||
child: StreamSvgIcon.message(
|
child: Center(
|
||||||
size: 136,
|
child: TextButton(
|
||||||
color: StreamChatTheme.of(context)
|
onPressed: widget.onStartChatPressed,
|
||||||
.colorTheme
|
child: Text(
|
||||||
.greyGainsboro,
|
'Start a chat',
|
||||||
),
|
style: chatThemeData.textTheme.bodyBold.copyWith(
|
||||||
),
|
color: chatThemeData.colorTheme.accentBlue,
|
||||||
Padding(
|
),
|
||||||
padding: const EdgeInsets.all(8),
|
),
|
||||||
child: Text(
|
|
||||||
'Let’s start chatting!',
|
|
||||||
style: StreamChatTheme.of(context).textTheme.headline,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
vertical: 8,
|
|
||||||
horizontal: 52,
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
'How about sending your first message to a friend?',
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: StreamChatTheme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.body
|
|
||||||
.copyWith(
|
|
||||||
color:
|
|
||||||
StreamChatTheme.of(context).colorTheme.grey,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (widget.onStartChatPressed != null)
|
|
||||||
Positioned(
|
|
||||||
right: 0,
|
|
||||||
left: 0,
|
|
||||||
bottom: 32,
|
|
||||||
child: Center(
|
|
||||||
child: TextButton(
|
|
||||||
onPressed: widget.onStartChatPressed,
|
|
||||||
child: Text(
|
|
||||||
'Start a chat',
|
|
||||||
style: StreamChatTheme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.bodyBold
|
|
||||||
.copyWith(
|
|
||||||
color: StreamChatTheme.of(context)
|
|
||||||
.colorTheme
|
|
||||||
.accentBlue,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
);
|
||||||
),
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildLoadingWidget(BuildContext context) => ListView(
|
Widget _buildLoadingWidget(BuildContext context) => ListView(
|
||||||
@@ -331,10 +323,11 @@ class _ChannelListViewState extends State<ChannelListView> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
Shimmer _buildLoadingItem(BuildContext context) {
|
Shimmer _buildLoadingItem(BuildContext context) {
|
||||||
|
final chatThemeData = StreamChatTheme.of(context);
|
||||||
if (widget.crossAxisCount > 1) {
|
if (widget.crossAxisCount > 1) {
|
||||||
return Shimmer.fromColors(
|
return Shimmer.fromColors(
|
||||||
baseColor: StreamChatTheme.of(context).colorTheme.greyGainsboro,
|
baseColor: chatThemeData.colorTheme.greyGainsboro,
|
||||||
highlightColor: StreamChatTheme.of(context).colorTheme.whiteSmoke,
|
highlightColor: chatThemeData.colorTheme.whiteSmoke,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
@@ -362,12 +355,12 @@ class _ChannelListViewState extends State<ChannelListView> {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return Shimmer.fromColors(
|
return Shimmer.fromColors(
|
||||||
baseColor: StreamChatTheme.of(context).colorTheme.greyGainsboro,
|
baseColor: chatThemeData.colorTheme.greyGainsboro,
|
||||||
highlightColor: StreamChatTheme.of(context).colorTheme.whiteSmoke,
|
highlightColor: chatThemeData.colorTheme.whiteSmoke,
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: Container(
|
leading: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: StreamChatTheme.of(context).colorTheme.white,
|
color: chatThemeData.colorTheme.white,
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
constraints: const BoxConstraints.tightFor(
|
constraints: const BoxConstraints.tightFor(
|
||||||
@@ -383,7 +376,7 @@ class _ChannelListViewState extends State<ChannelListView> {
|
|||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: StreamChatTheme.of(context).colorTheme.white,
|
color: chatThemeData.colorTheme.white,
|
||||||
borderRadius: BorderRadius.circular(11),
|
borderRadius: BorderRadius.circular(11),
|
||||||
),
|
),
|
||||||
constraints: const BoxConstraints.tightFor(
|
constraints: const BoxConstraints.tightFor(
|
||||||
@@ -400,7 +393,7 @@ class _ChannelListViewState extends State<ChannelListView> {
|
|||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: StreamChatTheme.of(context).colorTheme.white,
|
color: chatThemeData.colorTheme.white,
|
||||||
borderRadius: BorderRadius.circular(11),
|
borderRadius: BorderRadius.circular(11),
|
||||||
),
|
),
|
||||||
constraints: const BoxConstraints.expand(
|
constraints: const BoxConstraints.expand(
|
||||||
@@ -412,7 +405,7 @@ class _ChannelListViewState extends State<ChannelListView> {
|
|||||||
Container(
|
Container(
|
||||||
margin: const EdgeInsets.only(left: 16),
|
margin: const EdgeInsets.only(left: 16),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: StreamChatTheme.of(context).colorTheme.white,
|
color: chatThemeData.colorTheme.white,
|
||||||
borderRadius: BorderRadius.circular(11),
|
borderRadius: BorderRadius.circular(11),
|
||||||
),
|
),
|
||||||
constraints: const BoxConstraints.tightFor(
|
constraints: const BoxConstraints.tightFor(
|
||||||
@@ -456,12 +449,13 @@ class _ChannelListViewState extends State<ChannelListView> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
Widget _listItemBuilder(BuildContext context, int i, List<Channel> channels) {
|
Widget _listItemBuilder(BuildContext context, int i, List<Channel> channels) {
|
||||||
final channelsProvider = ChannelsBloc.of(context);
|
final channelsBloc = ChannelsBloc.of(context);
|
||||||
if (i < channels.length) {
|
if (i < channels.length) {
|
||||||
final channel = channels[i];
|
final channel = channels[i];
|
||||||
final onTap = _getChannelTap(context);
|
final onTap = _getChannelTap(context);
|
||||||
|
|
||||||
final backgroundColor = StreamChatTheme.of(context).colorTheme.whiteSmoke;
|
final chatThemeData = StreamChatTheme.of(context);
|
||||||
|
final backgroundColor = chatThemeData.colorTheme.whiteSmoke;
|
||||||
return StreamChannel(
|
return StreamChannel(
|
||||||
key: ValueKey<String>('CHANNEL-${channel.id}'),
|
key: ValueKey<String>('CHANNEL-${channel.id}'),
|
||||||
channel: channel,
|
channel: channel,
|
||||||
@@ -506,7 +500,7 @@ class _ChannelListViewState extends State<ChannelListView> {
|
|||||||
IconSlideAction(
|
IconSlideAction(
|
||||||
color: backgroundColor,
|
color: backgroundColor,
|
||||||
iconWidget: StreamSvgIcon.delete(
|
iconWidget: StreamSvgIcon.delete(
|
||||||
color: StreamChatTheme.of(context).colorTheme.accentRed,
|
color: chatThemeData.colorTheme.accentRed,
|
||||||
),
|
),
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
final res = await showConfirmationDialog(
|
final res = await showConfirmationDialog(
|
||||||
@@ -517,7 +511,7 @@ class _ChannelListViewState extends State<ChannelListView> {
|
|||||||
'Are you sure you want to delete this conversation?',
|
'Are you sure you want to delete this conversation?',
|
||||||
cancelText: 'CANCEL',
|
cancelText: 'CANCEL',
|
||||||
icon: StreamSvgIcon.delete(
|
icon: StreamSvgIcon.delete(
|
||||||
color: StreamChatTheme.of(context).colorTheme.accentRed,
|
color: chatThemeData.colorTheme.accentRed,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
if (res == true) {
|
if (res == true) {
|
||||||
@@ -527,7 +521,7 @@ class _ChannelListViewState extends State<ChannelListView> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
child: Container(
|
child: Container(
|
||||||
color: StreamChatTheme.of(context).colorTheme.whiteSnow,
|
color: chatThemeData.colorTheme.whiteSnow,
|
||||||
child: widget.channelPreviewBuilder?.call(context, channel) ??
|
child: widget.channelPreviewBuilder?.call(context, channel) ??
|
||||||
ChannelPreview(
|
ChannelPreview(
|
||||||
onLongPress: widget.onChannelLongPress,
|
onLongPress: widget.onChannelLongPress,
|
||||||
@@ -540,7 +534,7 @@ class _ChannelListViewState extends State<ChannelListView> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return _buildQueryProgressIndicator(context, channelsProvider);
|
return _buildQueryProgressIndicator(context, channelsBloc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -68,6 +68,8 @@ class ChannelPreview extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final channelPreviewTheme = StreamChatTheme.of(context).channelPreviewTheme;
|
final channelPreviewTheme = StreamChatTheme.of(context).channelPreviewTheme;
|
||||||
|
final streamChatState = StreamChat.of(context);
|
||||||
|
|
||||||
return StreamBuilder<bool>(
|
return StreamBuilder<bool>(
|
||||||
stream: channel.isMutedStream,
|
stream: channel.isMutedStream,
|
||||||
initialData: channel.isMuted,
|
initialData: channel.isMuted,
|
||||||
@@ -130,7 +132,7 @@ class ChannelPreview extends StatelessWidget {
|
|||||||
(m) => !m.isDeleted && m.shadowed != true,
|
(m) => !m.isDeleted && m.shadowed != true,
|
||||||
);
|
);
|
||||||
if (lastMessage?.user?.id ==
|
if (lastMessage?.user?.id ==
|
||||||
StreamChat.of(context).user?.id) {
|
streamChatState.user?.id) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(right: 4),
|
padding: const EdgeInsets.only(right: 4),
|
||||||
child: SendingIndicator(
|
child: SendingIndicator(
|
||||||
@@ -194,6 +196,7 @@ class ChannelPreview extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildSubtitle(BuildContext context) {
|
Widget _buildSubtitle(BuildContext context) {
|
||||||
|
final chatThemeData = StreamChatTheme.of(context);
|
||||||
if (channel.isMuted) {
|
if (channel.isMuted) {
|
||||||
return Row(
|
return Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
@@ -203,7 +206,7 @@ class ChannelPreview extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
' Channel is muted',
|
' Channel is muted',
|
||||||
style: StreamChatTheme.of(context).channelPreviewTheme.subtitle,
|
style: chatThemeData.channelPreviewTheme.subtitle,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -211,7 +214,7 @@ class ChannelPreview extends StatelessWidget {
|
|||||||
return TypingIndicator(
|
return TypingIndicator(
|
||||||
channel: channel,
|
channel: channel,
|
||||||
alternativeWidget: _buildLastMessage(context),
|
alternativeWidget: _buildLastMessage(context),
|
||||||
style: StreamChatTheme.of(context).channelPreviewTheme.subtitle,
|
style: chatThemeData.channelPreviewTheme.subtitle,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,35 +248,24 @@ class ChannelPreview extends StatelessWidget {
|
|||||||
|
|
||||||
text = parts.join(' ');
|
text = parts.join(' ');
|
||||||
|
|
||||||
|
final chatThemeData = StreamChatTheme.of(context);
|
||||||
return Text.rich(
|
return Text.rich(
|
||||||
_getDisplayText(
|
_getDisplayText(
|
||||||
text,
|
text,
|
||||||
lastMessage.mentionedUsers,
|
lastMessage.mentionedUsers,
|
||||||
lastMessage.attachments,
|
lastMessage.attachments,
|
||||||
StreamChatTheme.of(context)
|
chatThemeData.channelPreviewTheme.subtitle?.copyWith(
|
||||||
.channelPreviewTheme
|
color: chatThemeData.channelPreviewTheme.subtitle?.color,
|
||||||
.subtitle
|
fontStyle: (lastMessage.isSystem || lastMessage.isDeleted)
|
||||||
?.copyWith(
|
? FontStyle.italic
|
||||||
color: StreamChatTheme.of(context)
|
: FontStyle.normal),
|
||||||
.channelPreviewTheme
|
chatThemeData.channelPreviewTheme.subtitle?.copyWith(
|
||||||
.subtitle
|
color: chatThemeData.channelPreviewTheme.subtitle?.color,
|
||||||
?.color,
|
fontStyle: (lastMessage.isSystem || lastMessage.isDeleted)
|
||||||
fontStyle: (lastMessage.isSystem || lastMessage.isDeleted)
|
? FontStyle.italic
|
||||||
? FontStyle.italic
|
: FontStyle.normal,
|
||||||
: FontStyle.normal),
|
fontWeight: FontWeight.bold,
|
||||||
StreamChatTheme.of(context)
|
),
|
||||||
.channelPreviewTheme
|
|
||||||
.subtitle
|
|
||||||
?.copyWith(
|
|
||||||
color: StreamChatTheme.of(context)
|
|
||||||
.channelPreviewTheme
|
|
||||||
.subtitle
|
|
||||||
?.color,
|
|
||||||
fontStyle: (lastMessage.isSystem || lastMessage.isDeleted)
|
|
||||||
? FontStyle.italic
|
|
||||||
: FontStyle.normal,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
|
|||||||
@@ -44,18 +44,19 @@ class DateDivider extends StatelessWidget {
|
|||||||
|
|
||||||
if (uppercase) dayInfo = dayInfo.toUpperCase();
|
if (uppercase) dayInfo = dayInfo.toUpperCase();
|
||||||
|
|
||||||
|
final chatThemeData = StreamChatTheme.of(context);
|
||||||
return Center(
|
return Center(
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 1),
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 1),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: StreamChatTheme.of(context).colorTheme.overlayDark,
|
color: chatThemeData.colorTheme.overlayDark,
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
dayInfo,
|
dayInfo,
|
||||||
style: StreamChatTheme.of(context).textTheme.footnote.copyWith(
|
style: chatThemeData.textTheme.footnote.copyWith(
|
||||||
color: StreamChatTheme.of(context).colorTheme.white,
|
color: chatThemeData.colorTheme.white,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -31,36 +31,33 @@ class DeletedMessage extends StatelessWidget {
|
|||||||
final bool reverse;
|
final bool reverse;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => Material(
|
Widget build(BuildContext context) {
|
||||||
color: messageTheme.messageBackgroundColor,
|
final chatThemeData = StreamChatTheme.of(context);
|
||||||
shape: shape ??
|
return Material(
|
||||||
RoundedRectangleBorder(
|
color: messageTheme.messageBackgroundColor,
|
||||||
borderRadius: borderRadiusGeometry ?? BorderRadius.zero,
|
shape: shape ??
|
||||||
side: borderSide ??
|
RoundedRectangleBorder(
|
||||||
BorderSide(
|
borderRadius: borderRadiusGeometry ?? BorderRadius.zero,
|
||||||
color: Theme.of(context).brightness == Brightness.dark
|
side: borderSide ??
|
||||||
? StreamChatTheme.of(context)
|
BorderSide(
|
||||||
.colorTheme
|
color: Theme.of(context).brightness == Brightness.dark
|
||||||
.white
|
? chatThemeData.colorTheme.white.withAlpha(24)
|
||||||
.withAlpha(24)
|
: chatThemeData.colorTheme.black.withAlpha(24),
|
||||||
: StreamChatTheme.of(context)
|
),
|
||||||
.colorTheme
|
|
||||||
.black
|
|
||||||
.withAlpha(24),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
vertical: 8,
|
|
||||||
horizontal: 16,
|
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Padding(
|
||||||
'Message deleted',
|
padding: const EdgeInsets.symmetric(
|
||||||
style: messageTheme.messageText?.copyWith(
|
vertical: 8,
|
||||||
fontStyle: FontStyle.italic,
|
horizontal: 16,
|
||||||
color: messageTheme.createdAt?.color,
|
),
|
||||||
),
|
child: Text(
|
||||||
|
'Message deleted',
|
||||||
|
style: messageTheme.messageText?.copyWith(
|
||||||
|
fontStyle: FontStyle.italic,
|
||||||
|
color: messageTheme.createdAt?.color,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,18 +46,12 @@ class GroupImage extends StatelessWidget {
|
|||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
borderRadius: borderRadius ??
|
borderRadius: borderRadius ??
|
||||||
StreamChatTheme.of(context)
|
streamChatTheme.ownMessageTheme.avatarTheme?.borderRadius,
|
||||||
.ownMessageTheme
|
|
||||||
.avatarTheme
|
|
||||||
?.borderRadius,
|
|
||||||
child: Container(
|
child: Container(
|
||||||
constraints: constraints ??
|
constraints: constraints ??
|
||||||
StreamChatTheme.of(context)
|
streamChatTheme.ownMessageTheme.avatarTheme?.constraints,
|
||||||
.ownMessageTheme
|
|
||||||
.avatarTheme
|
|
||||||
?.constraints,
|
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: StreamChatTheme.of(context).colorTheme.accentBlue,
|
color: streamChatTheme.colorTheme.accentBlue,
|
||||||
),
|
),
|
||||||
child: Flex(
|
child: Flex(
|
||||||
direction: Axis.vertical,
|
direction: Axis.vertical,
|
||||||
@@ -125,8 +119,7 @@ class GroupImage extends StatelessWidget {
|
|||||||
BorderRadius.zero) +
|
BorderRadius.zero) +
|
||||||
BorderRadius.circular(selectionThickness),
|
BorderRadius.circular(selectionThickness),
|
||||||
child: Container(
|
child: Container(
|
||||||
color: selectionColor ??
|
color: selectionColor ?? streamChatTheme.colorTheme.accentBlue,
|
||||||
StreamChatTheme.of(context).colorTheme.accentBlue,
|
|
||||||
height: 64,
|
height: 64,
|
||||||
width: 64,
|
width: 64,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
|
|||||||
@@ -77,16 +77,18 @@ class _ImageFooterState extends State<ImageFooter> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final showShareButton = !kIsWeb;
|
final showShareButton = !kIsWeb;
|
||||||
|
final mediaQueryData = MediaQuery.of(context);
|
||||||
|
final chatThemeData = StreamChatTheme.of(context);
|
||||||
return SizedBox.fromSize(
|
return SizedBox.fromSize(
|
||||||
size: Size(
|
size: Size(
|
||||||
MediaQuery.of(context).size.width,
|
mediaQueryData.size.width,
|
||||||
MediaQuery.of(context).padding.bottom + widget.preferredSize.height,
|
mediaQueryData.padding.bottom + widget.preferredSize.height,
|
||||||
),
|
),
|
||||||
child: MediaQuery.removePadding(
|
child: MediaQuery.removePadding(
|
||||||
context: context,
|
context: context,
|
||||||
removeTop: true,
|
removeTop: true,
|
||||||
child: BottomAppBar(
|
child: BottomAppBar(
|
||||||
color: StreamChatTheme.of(context).colorTheme.white,
|
color: chatThemeData.colorTheme.white,
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
@@ -98,7 +100,7 @@ class _ImageFooterState extends State<ImageFooter> {
|
|||||||
IconButton(
|
IconButton(
|
||||||
icon: StreamSvgIcon.iconShare(
|
icon: StreamSvgIcon.iconShare(
|
||||||
size: 24,
|
size: 24,
|
||||||
color: StreamChatTheme.of(context).colorTheme.black,
|
color: chatThemeData.colorTheme.black,
|
||||||
),
|
),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
final attachment =
|
final attachment =
|
||||||
@@ -134,8 +136,7 @@ class _ImageFooterState extends State<ImageFooter> {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
'${widget.currentPage + 1} of ${widget.totalPages}',
|
'${widget.currentPage + 1} of ${widget.totalPages}',
|
||||||
style:
|
style: chatThemeData.textTheme.headlineBold,
|
||||||
StreamChatTheme.of(context).textTheme.headlineBold,
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -143,7 +144,7 @@ class _ImageFooterState extends State<ImageFooter> {
|
|||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: StreamSvgIcon.iconGrid(
|
icon: StreamSvgIcon.iconGrid(
|
||||||
color: StreamChatTheme.of(context).colorTheme.black,
|
color: chatThemeData.colorTheme.black,
|
||||||
),
|
),
|
||||||
onPressed: () => _showPhotosModal(context),
|
onPressed: () => _showPhotosModal(context),
|
||||||
),
|
),
|
||||||
@@ -155,10 +156,11 @@ class _ImageFooterState extends State<ImageFooter> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _showPhotosModal(context) {
|
void _showPhotosModal(context) {
|
||||||
|
final chatThemeData = StreamChatTheme.of(context);
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
barrierColor: StreamChatTheme.of(context).colorTheme.overlay,
|
barrierColor: chatThemeData.colorTheme.overlay,
|
||||||
backgroundColor: StreamChatTheme.of(context).colorTheme.white,
|
backgroundColor: chatThemeData.colorTheme.white,
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
shape: const RoundedRectangleBorder(
|
shape: const RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.only(
|
borderRadius: BorderRadius.only(
|
||||||
@@ -189,9 +191,7 @@ class _ImageFooterState extends State<ImageFooter> {
|
|||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
child: Text(
|
child: Text(
|
||||||
'Photos',
|
'Photos',
|
||||||
style: StreamChatTheme.of(context)
|
style: chatThemeData.textTheme.headlineBold,
|
||||||
.textTheme
|
|
||||||
.headlineBold,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -199,7 +199,7 @@ class _ImageFooterState extends State<ImageFooter> {
|
|||||||
alignment: Alignment.centerRight,
|
alignment: Alignment.centerRight,
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
icon: StreamSvgIcon.close(
|
icon: StreamSvgIcon.close(
|
||||||
color: StreamChatTheme.of(context).colorTheme.black,
|
color: chatThemeData.colorTheme.black,
|
||||||
),
|
),
|
||||||
onPressed: () => Navigator.maybePop(context),
|
onPressed: () => Navigator.maybePop(context),
|
||||||
),
|
),
|
||||||
@@ -262,9 +262,7 @@ class _ImageFooterState extends State<ImageFooter> {
|
|||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
blurRadius: 8,
|
blurRadius: 8,
|
||||||
color: StreamChatTheme.of(context)
|
color: chatThemeData.colorTheme.black
|
||||||
.colorTheme
|
|
||||||
.black
|
|
||||||
.withOpacity(0.3),
|
.withOpacity(0.3),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -50,59 +50,58 @@ class ImageHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
final int currentIndex;
|
final int currentIndex;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => AppBar(
|
Widget build(BuildContext context) {
|
||||||
brightness: Theme.of(context).brightness,
|
final chatThemeData = StreamChatTheme.of(context);
|
||||||
elevation: 1,
|
return AppBar(
|
||||||
leading: showBackButton
|
brightness: Theme.of(context).brightness,
|
||||||
? IconButton(
|
elevation: 1,
|
||||||
icon: StreamSvgIcon.close(
|
leading: showBackButton
|
||||||
color: StreamChatTheme.of(context).colorTheme.black,
|
? IconButton(
|
||||||
size: 24,
|
icon: StreamSvgIcon.close(
|
||||||
),
|
color: chatThemeData.colorTheme.black,
|
||||||
onPressed: onBackPressed,
|
size: 24,
|
||||||
)
|
|
||||||
: const SizedBox(),
|
|
||||||
backgroundColor:
|
|
||||||
StreamChatTheme.of(context).channelTheme.channelHeaderTheme.color,
|
|
||||||
actions: <Widget>[
|
|
||||||
if (message.type != 'ephemeral')
|
|
||||||
IconButton(
|
|
||||||
icon: StreamSvgIcon.iconMenuPoint(
|
|
||||||
color: StreamChatTheme.of(context).colorTheme.black,
|
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: onBackPressed,
|
||||||
_showMessageActionModalBottomSheet(context);
|
)
|
||||||
},
|
: const SizedBox(),
|
||||||
|
backgroundColor: chatThemeData.channelTheme.channelHeaderTheme.color,
|
||||||
|
actions: <Widget>[
|
||||||
|
if (message.type != 'ephemeral')
|
||||||
|
IconButton(
|
||||||
|
icon: StreamSvgIcon.iconMenuPoint(
|
||||||
|
color: chatThemeData.colorTheme.black,
|
||||||
),
|
),
|
||||||
],
|
onPressed: () {
|
||||||
centerTitle: true,
|
_showMessageActionModalBottomSheet(context);
|
||||||
title: message.type != 'ephemeral'
|
},
|
||||||
? InkWell(
|
),
|
||||||
onTap: onTitleTap,
|
],
|
||||||
child: SizedBox(
|
centerTitle: true,
|
||||||
height: preferredSize.height,
|
title: message.type != 'ephemeral'
|
||||||
width: preferredSize.width,
|
? InkWell(
|
||||||
child: Column(
|
onTap: onTitleTap,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
child: SizedBox(
|
||||||
mainAxisSize: MainAxisSize.min,
|
height: preferredSize.height,
|
||||||
children: <Widget>[
|
width: preferredSize.width,
|
||||||
Text(
|
child: Column(
|
||||||
userName,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
style:
|
mainAxisSize: MainAxisSize.min,
|
||||||
StreamChatTheme.of(context).textTheme.headlineBold,
|
children: <Widget>[
|
||||||
),
|
Text(
|
||||||
Text(
|
userName,
|
||||||
sentAt,
|
style: chatThemeData.textTheme.headlineBold,
|
||||||
style: StreamChatTheme.of(context)
|
),
|
||||||
.channelPreviewTheme
|
Text(
|
||||||
.subtitle,
|
sentAt,
|
||||||
),
|
style: chatThemeData.channelPreviewTheme.subtitle,
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
: const SizedBox(),
|
)
|
||||||
);
|
: const SizedBox(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final Size preferredSize;
|
final Size preferredSize;
|
||||||
|
|||||||
@@ -38,26 +38,29 @@ class InfoTile extends StatelessWidget {
|
|||||||
final Color? backgroundColor;
|
final Color? backgroundColor;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => PortalEntry(
|
Widget build(BuildContext context) {
|
||||||
visible: showMessage,
|
final chatThemeData = StreamChatTheme.of(context);
|
||||||
portalAnchor: tileAnchor ?? Alignment.topCenter,
|
return PortalEntry(
|
||||||
childAnchor: childAnchor ?? Alignment.bottomCenter,
|
visible: showMessage,
|
||||||
portal: Container(
|
portalAnchor: tileAnchor ?? Alignment.topCenter,
|
||||||
height: 25,
|
childAnchor: childAnchor ?? Alignment.bottomCenter,
|
||||||
color: backgroundColor ??
|
portal: Container(
|
||||||
StreamChatTheme.of(context).colorTheme.grey.withOpacity(0.9),
|
height: 25,
|
||||||
child: Center(
|
color:
|
||||||
child: Text(
|
backgroundColor ?? chatThemeData.colorTheme.grey.withOpacity(0.9),
|
||||||
message,
|
child: Center(
|
||||||
style: textStyle ??
|
child: Text(
|
||||||
StreamChatTheme.of(context).textTheme.body.copyWith(
|
message,
|
||||||
color: Colors.white,
|
style: textStyle ??
|
||||||
),
|
chatThemeData.textTheme.body.copyWith(
|
||||||
maxLines: 1,
|
color: Colors.white,
|
||||||
overflow: TextOverflow.ellipsis,
|
),
|
||||||
),
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: child,
|
),
|
||||||
);
|
child: child,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ class _MediaListViewState extends State<MediaListView> {
|
|||||||
position,
|
position,
|
||||||
) {
|
) {
|
||||||
final media = _media.elementAt(position);
|
final media = _media.elementAt(position);
|
||||||
|
final chatThemeData = StreamChatTheme.of(context);
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 1, vertical: 1),
|
padding: const EdgeInsets.symmetric(horizontal: 1, vertical: 1),
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
@@ -89,10 +90,8 @@ class _MediaListViewState extends State<MediaListView> {
|
|||||||
? 1.0
|
? 1.0
|
||||||
: 0.0,
|
: 0.0,
|
||||||
child: Container(
|
child: Container(
|
||||||
color: StreamChatTheme.of(context)
|
color:
|
||||||
.colorTheme
|
chatThemeData.colorTheme.black.withOpacity(0.5),
|
||||||
.black
|
|
||||||
.withOpacity(0.5),
|
|
||||||
alignment: Alignment.topRight,
|
alignment: Alignment.topRight,
|
||||||
padding: const EdgeInsets.only(
|
padding: const EdgeInsets.only(
|
||||||
top: 8,
|
top: 8,
|
||||||
@@ -100,13 +99,10 @@ class _MediaListViewState extends State<MediaListView> {
|
|||||||
),
|
),
|
||||||
child: CircleAvatar(
|
child: CircleAvatar(
|
||||||
radius: 12,
|
radius: 12,
|
||||||
backgroundColor:
|
backgroundColor: chatThemeData.colorTheme.white,
|
||||||
StreamChatTheme.of(context).colorTheme.white,
|
|
||||||
child: StreamSvgIcon.check(
|
child: StreamSvgIcon.check(
|
||||||
size: 24,
|
size: 24,
|
||||||
color: StreamChatTheme.of(context)
|
color: chatThemeData.colorTheme.black,
|
||||||
.colorTheme
|
|
||||||
.black,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -128,7 +124,7 @@ class _MediaListViewState extends State<MediaListView> {
|
|||||||
child: Text(
|
child: Text(
|
||||||
media.videoDuration.format(),
|
media.videoDuration.format(),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: StreamChatTheme.of(context).colorTheme.white,
|
color: chatThemeData.colorTheme.white,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
/// This widget is used for showing user tiles for mentions
|
/// This widget is used for showing user tiles for mentions
|
||||||
@@ -32,71 +31,70 @@ class MentionTile extends StatelessWidget {
|
|||||||
final Widget? trailing;
|
final Widget? trailing;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => SizedBox(
|
Widget build(BuildContext context) {
|
||||||
height: 56,
|
final chatThemeData = StreamChatTheme.of(context);
|
||||||
child: Row(
|
return SizedBox(
|
||||||
children: [
|
height: 56,
|
||||||
const SizedBox(
|
child: Row(
|
||||||
width: 16,
|
children: [
|
||||||
),
|
const SizedBox(
|
||||||
leading ??
|
width: 16,
|
||||||
UserAvatar(
|
),
|
||||||
constraints: BoxConstraints.tight(
|
leading ??
|
||||||
const Size(
|
UserAvatar(
|
||||||
40,
|
constraints: BoxConstraints.tight(
|
||||||
40,
|
const Size(
|
||||||
),
|
40,
|
||||||
|
40,
|
||||||
),
|
),
|
||||||
user: member.user!,
|
|
||||||
),
|
),
|
||||||
const SizedBox(
|
user: member.user!,
|
||||||
width: 8,
|
),
|
||||||
),
|
const SizedBox(
|
||||||
Expanded(
|
width: 8,
|
||||||
child: Align(
|
),
|
||||||
alignment: Alignment.centerLeft,
|
Expanded(
|
||||||
child: Column(
|
child: Align(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
alignment: Alignment.centerLeft,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
child: Column(
|
||||||
children: [
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
title ??
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
Text(
|
children: [
|
||||||
member.user!.name,
|
title ??
|
||||||
maxLines: 1,
|
Text(
|
||||||
overflow: TextOverflow.ellipsis,
|
member.user!.name,
|
||||||
style: StreamChatTheme.of(context).textTheme.bodyBold,
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: chatThemeData.textTheme.bodyBold,
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 2,
|
||||||
|
),
|
||||||
|
subtitle ??
|
||||||
|
Text(
|
||||||
|
'@${member.userId}',
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: chatThemeData.textTheme.footnoteBold.copyWith(
|
||||||
|
color: chatThemeData.colorTheme.grey,
|
||||||
),
|
),
|
||||||
const SizedBox(
|
),
|
||||||
height: 2,
|
],
|
||||||
),
|
|
||||||
subtitle ??
|
|
||||||
Text(
|
|
||||||
'@${member.userId}',
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
style: StreamChatTheme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.footnoteBold
|
|
||||||
.copyWith(
|
|
||||||
color:
|
|
||||||
StreamChatTheme.of(context).colorTheme.grey,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
trailing ??
|
),
|
||||||
Padding(
|
trailing ??
|
||||||
padding: const EdgeInsets.only(
|
Padding(
|
||||||
right: 18,
|
padding: const EdgeInsets.only(
|
||||||
left: 8,
|
right: 18,
|
||||||
),
|
left: 8,
|
||||||
child: StreamSvgIcon.mentions(
|
|
||||||
color: StreamChatTheme.of(context).colorTheme.accentBlue,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
child: StreamSvgIcon.mentions(
|
||||||
),
|
color: chatThemeData.colorTheme.accentBlue,
|
||||||
);
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ import 'dart:ui';
|
|||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:stream_chat_flutter/src/extension.dart';
|
||||||
import 'package:stream_chat_flutter/src/message_action.dart';
|
import 'package:stream_chat_flutter/src/message_action.dart';
|
||||||
import 'package:stream_chat_flutter/src/reaction_picker.dart';
|
import 'package:stream_chat_flutter/src/reaction_picker.dart';
|
||||||
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
||||||
import 'package:stream_chat_flutter/src/utils.dart';
|
import 'package:stream_chat_flutter/src/utils.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
import 'package:stream_chat_flutter/src/extension.dart';
|
|
||||||
|
|
||||||
/// Constructs a modal with actions for a message
|
/// Constructs a modal with actions for a message
|
||||||
class MessageActionsModal extends StatefulWidget {
|
class MessageActionsModal extends StatefulWidget {
|
||||||
@@ -108,7 +108,8 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
|||||||
Widget build(BuildContext context) => _showMessageOptionsModal();
|
Widget build(BuildContext context) => _showMessageOptionsModal();
|
||||||
|
|
||||||
Widget _showMessageOptionsModal() {
|
Widget _showMessageOptionsModal() {
|
||||||
final size = MediaQuery.of(context).size;
|
final mediaQueryData = MediaQuery.of(context);
|
||||||
|
final size = mediaQueryData.size;
|
||||||
final user = StreamChat.of(context).user;
|
final user = StreamChat.of(context).user;
|
||||||
|
|
||||||
final roughMaxSize = 2 * size.width / 3;
|
final roughMaxSize = 2 * size.width / 3;
|
||||||
@@ -133,6 +134,7 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
|||||||
final hasFileAttachment =
|
final hasFileAttachment =
|
||||||
widget.message.attachments.any((it) => it.type == 'file') == true;
|
widget.message.attachments.any((it) => it.type == 'file') == true;
|
||||||
|
|
||||||
|
final streamChatThemeData = StreamChatTheme.of(context);
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
behavior: HitTestBehavior.translucent,
|
behavior: HitTestBehavior.translucent,
|
||||||
onTap: () => Navigator.maybePop(context),
|
onTap: () => Navigator.maybePop(context),
|
||||||
@@ -145,7 +147,7 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
|||||||
sigmaY: 10,
|
sigmaY: 10,
|
||||||
),
|
),
|
||||||
child: Container(
|
child: Container(
|
||||||
color: StreamChatTheme.of(context).colorTheme.overlay,
|
color: streamChatThemeData.colorTheme.overlay,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -226,11 +228,9 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
|||||||
left: widget.reverse ? 0 : 40,
|
left: widget.reverse ? 0 : 40,
|
||||||
),
|
),
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: MediaQuery.of(context).size.width * 0.75,
|
width: mediaQueryData.size.width * 0.75,
|
||||||
child: Material(
|
child: Material(
|
||||||
color: StreamChatTheme.of(context)
|
color: streamChatThemeData.colorTheme.whiteSnow,
|
||||||
.colorTheme
|
|
||||||
.whiteSnow,
|
|
||||||
clipBehavior: Clip.hardEdge,
|
clipBehavior: Clip.hardEdge,
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(16),
|
||||||
@@ -266,9 +266,8 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
|||||||
].insertBetween(
|
].insertBetween(
|
||||||
Container(
|
Container(
|
||||||
height: 1,
|
height: 1,
|
||||||
color: StreamChatTheme.of(context)
|
color: streamChatThemeData
|
||||||
.colorTheme
|
.colorTheme.greyWhisper,
|
||||||
.greyWhisper,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -310,11 +309,12 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
|||||||
void _showFlagDialog() async {
|
void _showFlagDialog() async {
|
||||||
final client = StreamChat.of(context).client;
|
final client = StreamChat.of(context).client;
|
||||||
|
|
||||||
|
final streamChatThemeData = StreamChatTheme.of(context);
|
||||||
final answer = await showConfirmationDialog(
|
final answer = await showConfirmationDialog(
|
||||||
context,
|
context,
|
||||||
title: 'Flag Message',
|
title: 'Flag Message',
|
||||||
icon: StreamSvgIcon.flag(
|
icon: StreamSvgIcon.flag(
|
||||||
color: StreamChatTheme.of(context).colorTheme.accentRed,
|
color: streamChatThemeData.colorTheme.accentRed,
|
||||||
size: 24,
|
size: 24,
|
||||||
),
|
),
|
||||||
question:
|
question:
|
||||||
@@ -324,7 +324,7 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
|||||||
cancelText: 'CANCEL',
|
cancelText: 'CANCEL',
|
||||||
);
|
);
|
||||||
|
|
||||||
final theme = StreamChatTheme.of(context);
|
final theme = streamChatThemeData;
|
||||||
if (answer == true) {
|
if (answer == true) {
|
||||||
try {
|
try {
|
||||||
await client.flagMessage(widget.message.id);
|
await client.flagMessage(widget.message.id);
|
||||||
@@ -400,48 +400,54 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildReplyButton(BuildContext context) => InkWell(
|
Widget _buildReplyButton(BuildContext context) {
|
||||||
onTap: () {
|
final streamChatThemeData = StreamChatTheme.of(context);
|
||||||
Navigator.pop(context);
|
return InkWell(
|
||||||
if (widget.onReplyTap != null) {
|
onTap: () {
|
||||||
widget.onReplyTap!(widget.message);
|
Navigator.pop(context);
|
||||||
}
|
if (widget.onReplyTap != null) {
|
||||||
},
|
widget.onReplyTap!(widget.message);
|
||||||
child: Padding(
|
}
|
||||||
padding: const EdgeInsets.symmetric(vertical: 11, horizontal: 16),
|
},
|
||||||
child: Row(
|
child: Padding(
|
||||||
children: [
|
padding: const EdgeInsets.symmetric(vertical: 11, horizontal: 16),
|
||||||
StreamSvgIcon.reply(
|
child: Row(
|
||||||
color: StreamChatTheme.of(context).primaryIconTheme.color,
|
children: [
|
||||||
),
|
StreamSvgIcon.reply(
|
||||||
const SizedBox(width: 16),
|
color: streamChatThemeData.primaryIconTheme.color,
|
||||||
Text(
|
),
|
||||||
'Reply',
|
const SizedBox(width: 16),
|
||||||
style: StreamChatTheme.of(context).textTheme.body,
|
Text(
|
||||||
),
|
'Reply',
|
||||||
],
|
style: streamChatThemeData.textTheme.body,
|
||||||
),
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildFlagButton(BuildContext context) => InkWell(
|
Widget _buildFlagButton(BuildContext context) {
|
||||||
onTap: _showFlagDialog,
|
final streamChatThemeData = StreamChatTheme.of(context);
|
||||||
child: Padding(
|
return InkWell(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 11, horizontal: 16),
|
onTap: _showFlagDialog,
|
||||||
child: Row(
|
child: Padding(
|
||||||
children: [
|
padding: const EdgeInsets.symmetric(vertical: 11, horizontal: 16),
|
||||||
StreamSvgIcon.iconFlag(
|
child: Row(
|
||||||
color: StreamChatTheme.of(context).primaryIconTheme.color,
|
children: [
|
||||||
),
|
StreamSvgIcon.iconFlag(
|
||||||
const SizedBox(width: 16),
|
color: streamChatThemeData.primaryIconTheme.color,
|
||||||
Text(
|
),
|
||||||
'Flag Message',
|
const SizedBox(width: 16),
|
||||||
style: StreamChatTheme.of(context).textTheme.body,
|
Text(
|
||||||
),
|
'Flag Message',
|
||||||
],
|
style: streamChatThemeData.textTheme.body,
|
||||||
),
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildDeleteButton(BuildContext context) {
|
Widget _buildDeleteButton(BuildContext context) {
|
||||||
final isDeleteFailed =
|
final isDeleteFailed =
|
||||||
@@ -469,54 +475,61 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildCopyButton(BuildContext context) => InkWell(
|
Widget _buildCopyButton(BuildContext context) {
|
||||||
onTap: () async {
|
final streamChatThemeData = StreamChatTheme.of(context);
|
||||||
widget.onCopyTap?.call(widget.message);
|
return InkWell(
|
||||||
Navigator.pop(context);
|
onTap: () async {
|
||||||
},
|
widget.onCopyTap?.call(widget.message);
|
||||||
child: Padding(
|
Navigator.pop(context);
|
||||||
padding: const EdgeInsets.symmetric(vertical: 11, horizontal: 16),
|
},
|
||||||
child: Row(
|
child: Padding(
|
||||||
children: [
|
padding: const EdgeInsets.symmetric(vertical: 11, horizontal: 16),
|
||||||
StreamSvgIcon.copy(
|
child: Row(
|
||||||
size: 24,
|
children: [
|
||||||
color: StreamChatTheme.of(context).primaryIconTheme.color,
|
StreamSvgIcon.copy(
|
||||||
),
|
size: 24,
|
||||||
const SizedBox(width: 16),
|
color: streamChatThemeData.primaryIconTheme.color,
|
||||||
Text(
|
),
|
||||||
'Copy Message',
|
const SizedBox(width: 16),
|
||||||
style: StreamChatTheme.of(context).textTheme.body,
|
Text(
|
||||||
),
|
'Copy Message',
|
||||||
],
|
style: streamChatThemeData.textTheme.body,
|
||||||
),
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildEditMessage(BuildContext context) => InkWell(
|
Widget _buildEditMessage(BuildContext context) {
|
||||||
onTap: () async {
|
final streamChatThemeData = StreamChatTheme.of(context);
|
||||||
Navigator.pop(context);
|
return InkWell(
|
||||||
_showEditBottomSheet(context);
|
onTap: () async {
|
||||||
},
|
Navigator.pop(context);
|
||||||
child: Padding(
|
_showEditBottomSheet(context);
|
||||||
padding: const EdgeInsets.symmetric(vertical: 11, horizontal: 16),
|
},
|
||||||
child: Row(
|
child: Padding(
|
||||||
children: [
|
padding: const EdgeInsets.symmetric(vertical: 11, horizontal: 16),
|
||||||
StreamSvgIcon.edit(
|
child: Row(
|
||||||
color: StreamChatTheme.of(context).primaryIconTheme.color,
|
children: [
|
||||||
),
|
StreamSvgIcon.edit(
|
||||||
const SizedBox(width: 16),
|
color: streamChatThemeData.primaryIconTheme.color,
|
||||||
Text(
|
),
|
||||||
'Edit Message',
|
const SizedBox(width: 16),
|
||||||
style: StreamChatTheme.of(context).textTheme.body,
|
Text(
|
||||||
),
|
'Edit Message',
|
||||||
],
|
style: streamChatThemeData.textTheme.body,
|
||||||
),
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildResendMessage(BuildContext context) {
|
Widget _buildResendMessage(BuildContext context) {
|
||||||
final isUpdateFailed =
|
final isUpdateFailed =
|
||||||
widget.message.status == MessageSendingStatus.failed_update;
|
widget.message.status == MessageSendingStatus.failed_update;
|
||||||
|
final streamChatThemeData = StreamChatTheme.of(context);
|
||||||
return InkWell(
|
return InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
@@ -532,12 +545,12 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
|||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
StreamSvgIcon.circleUp(
|
StreamSvgIcon.circleUp(
|
||||||
color: StreamChatTheme.of(context).colorTheme.accentBlue,
|
color: streamChatThemeData.colorTheme.accentBlue,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 16),
|
const SizedBox(width: 16),
|
||||||
Text(
|
Text(
|
||||||
isUpdateFailed ? 'Resend Edited Message' : 'Resend',
|
isUpdateFailed ? 'Resend Edited Message' : 'Resend',
|
||||||
style: StreamChatTheme.of(context).textTheme.body,
|
style: streamChatThemeData.textTheme.body,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -547,13 +560,13 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
|||||||
|
|
||||||
void _showEditBottomSheet(BuildContext context) {
|
void _showEditBottomSheet(BuildContext context) {
|
||||||
final channel = StreamChannel.of(context).channel;
|
final channel = StreamChannel.of(context).channel;
|
||||||
|
final streamChatThemeData = StreamChatTheme.of(context);
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
elevation: 2,
|
elevation: 2,
|
||||||
clipBehavior: Clip.hardEdge,
|
clipBehavior: Clip.hardEdge,
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
backgroundColor:
|
backgroundColor: streamChatThemeData.messageInputTheme.inputBackground,
|
||||||
StreamChatTheme.of(context).messageInputTheme.inputBackground,
|
|
||||||
shape: const RoundedRectangleBorder(
|
shape: const RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.only(
|
borderRadius: BorderRadius.only(
|
||||||
topLeft: Radius.circular(16),
|
topLeft: Radius.circular(16),
|
||||||
@@ -575,8 +588,7 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
|||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
child: StreamSvgIcon.edit(
|
child: StreamSvgIcon.edit(
|
||||||
color:
|
color: streamChatThemeData.colorTheme.greyGainsboro,
|
||||||
StreamChatTheme.of(context).colorTheme.greyGainsboro,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Text(
|
const Text(
|
||||||
@@ -608,27 +620,30 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildThreadReplyButton(BuildContext context) => InkWell(
|
Widget _buildThreadReplyButton(BuildContext context) {
|
||||||
onTap: () {
|
final streamChatThemeData = StreamChatTheme.of(context);
|
||||||
Navigator.pop(context);
|
return InkWell(
|
||||||
if (widget.onThreadReplyTap != null) {
|
onTap: () {
|
||||||
widget.onThreadReplyTap!(widget.message);
|
Navigator.pop(context);
|
||||||
}
|
if (widget.onThreadReplyTap != null) {
|
||||||
},
|
widget.onThreadReplyTap!(widget.message);
|
||||||
child: Padding(
|
}
|
||||||
padding: const EdgeInsets.symmetric(vertical: 11, horizontal: 16),
|
},
|
||||||
child: Row(
|
child: Padding(
|
||||||
children: [
|
padding: const EdgeInsets.symmetric(vertical: 11, horizontal: 16),
|
||||||
StreamSvgIcon.thread(
|
child: Row(
|
||||||
color: StreamChatTheme.of(context).primaryIconTheme.color,
|
children: [
|
||||||
),
|
StreamSvgIcon.thread(
|
||||||
const SizedBox(width: 16),
|
color: streamChatThemeData.primaryIconTheme.color,
|
||||||
Text(
|
),
|
||||||
'Thread Reply',
|
const SizedBox(width: 16),
|
||||||
style: StreamChatTheme.of(context).textTheme.body,
|
Text(
|
||||||
),
|
'Thread Reply',
|
||||||
],
|
style: streamChatThemeData.textTheme.body,
|
||||||
),
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -6,15 +6,15 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:jiffy/jiffy.dart';
|
import 'package:jiffy/jiffy.dart';
|
||||||
import 'package:rxdart/rxdart.dart';
|
import 'package:rxdart/rxdart.dart';
|
||||||
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
||||||
|
import 'package:stream_chat_flutter/src/extension.dart';
|
||||||
import 'package:stream_chat_flutter/src/info_tile.dart';
|
import 'package:stream_chat_flutter/src/info_tile.dart';
|
||||||
import 'package:stream_chat_flutter/src/message_widget.dart';
|
import 'package:stream_chat_flutter/src/message_widget.dart';
|
||||||
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
||||||
|
import 'package:stream_chat_flutter/src/swipeable.dart';
|
||||||
import 'package:stream_chat_flutter/src/system_message.dart';
|
import 'package:stream_chat_flutter/src/system_message.dart';
|
||||||
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
import 'package:visibility_detector/visibility_detector.dart';
|
import 'package:visibility_detector/visibility_detector.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
|
||||||
import 'package:stream_chat_flutter/src/extension.dart';
|
|
||||||
import 'package:stream_chat_flutter/src/swipeable.dart';
|
|
||||||
|
|
||||||
/// Widget builder for message
|
/// Widget builder for message
|
||||||
typedef MessageBuilder = Widget Function(
|
typedef MessageBuilder = Widget Function(
|
||||||
@@ -316,46 +316,37 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
final MessageListController _messageListController = MessageListController();
|
final MessageListController _messageListController = MessageListController();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => MessageListCore(
|
Widget build(BuildContext context) {
|
||||||
messageFilter: widget.messageFilter,
|
final chatThemeData = StreamChatTheme.of(context);
|
||||||
loadingBuilder: widget.loadingBuilder ??
|
return MessageListCore(
|
||||||
(context) => const Center(
|
messageFilter: widget.messageFilter,
|
||||||
child: CircularProgressIndicator(),
|
loadingBuilder: widget.loadingBuilder ??
|
||||||
|
(context) => const Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
),
|
||||||
|
emptyBuilder: widget.emptyBuilder ??
|
||||||
|
(context) => Center(
|
||||||
|
child: Text(
|
||||||
|
'No chats here yet...',
|
||||||
|
style: chatThemeData.textTheme.footnote.copyWith(
|
||||||
|
color: chatThemeData.colorTheme.black.withOpacity(.5)),
|
||||||
),
|
),
|
||||||
emptyBuilder: widget.emptyBuilder ??
|
),
|
||||||
(context) => Center(
|
messageListBuilder:
|
||||||
child: Text(
|
widget.messageListBuilder ?? (context, list) => _buildListView(list),
|
||||||
'No chats here yet...',
|
messageListController: _messageListController,
|
||||||
style: StreamChatTheme.of(context)
|
parentMessage: widget.parentMessage,
|
||||||
.textTheme
|
showScrollToBottom: widget.showScrollToBottom,
|
||||||
.footnote
|
errorWidgetBuilder: widget.errorWidgetBuilder ??
|
||||||
.copyWith(
|
(BuildContext context, Object error) => Center(
|
||||||
color: StreamChatTheme.of(context)
|
child: Text(
|
||||||
.colorTheme
|
'Something went wrong',
|
||||||
.black
|
style: chatThemeData.textTheme.footnote.copyWith(
|
||||||
.withOpacity(.5)),
|
color: chatThemeData.colorTheme.black.withOpacity(.5)),
|
||||||
),
|
|
||||||
),
|
),
|
||||||
messageListBuilder: widget.messageListBuilder ??
|
),
|
||||||
(context, list) => _buildListView(list),
|
);
|
||||||
messageListController: _messageListController,
|
}
|
||||||
parentMessage: widget.parentMessage,
|
|
||||||
showScrollToBottom: widget.showScrollToBottom,
|
|
||||||
errorWidgetBuilder: widget.errorWidgetBuilder ??
|
|
||||||
(BuildContext context, Object error) => Center(
|
|
||||||
child: Text(
|
|
||||||
'Something went wrong',
|
|
||||||
style: StreamChatTheme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.footnote
|
|
||||||
.copyWith(
|
|
||||||
color: StreamChatTheme.of(context)
|
|
||||||
.colorTheme
|
|
||||||
.black
|
|
||||||
.withOpacity(.5)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildListView(List<Message> data) {
|
Widget _buildListView(List<Message> data) {
|
||||||
messages = data;
|
messages = data;
|
||||||
@@ -448,10 +439,10 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
if (i == 0) return const SizedBox(height: 30);
|
if (i == 0) return const SizedBox(height: 30);
|
||||||
if (i == messages.length + 1) {
|
if (i == messages.length + 1) {
|
||||||
final replyCount = widget.parentMessage!.replyCount;
|
final replyCount = widget.parentMessage!.replyCount;
|
||||||
|
final chatThemeData = StreamChatTheme.of(context);
|
||||||
return Container(
|
return Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
gradient:
|
gradient: chatThemeData.colorTheme.bgGradient,
|
||||||
StreamChatTheme.of(context).colorTheme.bgGradient,
|
|
||||||
),
|
),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
@@ -459,10 +450,8 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
// ignore: lines_longer_than_80_chars
|
// ignore: lines_longer_than_80_chars
|
||||||
'$replyCount ${replyCount == 1 ? 'Reply' : 'Replies'}',
|
'$replyCount ${replyCount == 1 ? 'Reply' : 'Replies'}',
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: StreamChatTheme.of(context)
|
style: chatThemeData
|
||||||
.channelTheme
|
.channelTheme.channelHeaderTheme.subtitle,
|
||||||
.channelHeaderTheme
|
|
||||||
.subtitle,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -517,13 +506,13 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
}
|
}
|
||||||
if (i == messages.length + 1) {
|
if (i == messages.length + 1) {
|
||||||
return _buildLoadingIndicator(
|
return _buildLoadingIndicator(
|
||||||
streamChannel,
|
streamChannel!,
|
||||||
QueryDirection.top,
|
QueryDirection.top,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
return _buildLoadingIndicator(
|
return _buildLoadingIndicator(
|
||||||
streamChannel,
|
streamChannel!,
|
||||||
QueryDirection.bottom,
|
QueryDirection.bottom,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -536,7 +525,7 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
context,
|
context,
|
||||||
message,
|
message,
|
||||||
messages,
|
messages,
|
||||||
streamChannel,
|
streamChannel!,
|
||||||
);
|
);
|
||||||
} else if (i == messages.length - 1) {
|
} else if (i == messages.length - 1) {
|
||||||
messageWidget = _buildTopMessage(
|
messageWidget = _buildTopMessage(
|
||||||
@@ -635,6 +624,7 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
final showUnreadCount = unreadCount > 0 &&
|
final showUnreadCount = unreadCount > 0 &&
|
||||||
streamChannel!.channel.state!.members.any((e) =>
|
streamChannel!.channel.state!.members.any((e) =>
|
||||||
e.userId == streamChannel!.channel.client.state.user!.id);
|
e.userId == streamChannel!.channel.client.state.user!.id);
|
||||||
|
final chatThemeData = StreamChatTheme.of(context);
|
||||||
return Positioned(
|
return Positioned(
|
||||||
bottom: 8,
|
bottom: 8,
|
||||||
right: 8,
|
right: 8,
|
||||||
@@ -644,7 +634,7 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
clipBehavior: Clip.none,
|
clipBehavior: Clip.none,
|
||||||
children: [
|
children: [
|
||||||
FloatingActionButton(
|
FloatingActionButton(
|
||||||
backgroundColor: StreamChatTheme.of(context).colorTheme.white,
|
backgroundColor: chatThemeData.colorTheme.white,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (unreadCount > 0) {
|
if (unreadCount > 0) {
|
||||||
streamChannel!.channel.markRead();
|
streamChannel!.channel.markRead();
|
||||||
@@ -663,7 +653,7 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: StreamSvgIcon.down(
|
child: StreamSvgIcon.down(
|
||||||
color: StreamChatTheme.of(context).colorTheme.black,
|
color: chatThemeData.colorTheme.black,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (showUnreadCount)
|
if (showUnreadCount)
|
||||||
@@ -692,12 +682,12 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildLoadingIndicator(
|
Widget _buildLoadingIndicator(
|
||||||
StreamChannelState? streamChannel,
|
StreamChannelState streamChannel,
|
||||||
QueryDirection direction,
|
QueryDirection direction,
|
||||||
) {
|
) {
|
||||||
final stream = direction == QueryDirection.top
|
final stream = direction == QueryDirection.top
|
||||||
? streamChannel!.queryTopMessages
|
? streamChannel.queryTopMessages
|
||||||
: streamChannel!.queryBottomMessages;
|
: streamChannel.queryBottomMessages;
|
||||||
return StreamBuilder<bool>(
|
return StreamBuilder<bool>(
|
||||||
key: const Key('LOADING-INDICATOR'),
|
key: const Key('LOADING-INDICATOR'),
|
||||||
stream: stream,
|
stream: stream,
|
||||||
@@ -764,7 +754,7 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
BuildContext context,
|
BuildContext context,
|
||||||
Message message,
|
Message message,
|
||||||
List<Message> messages,
|
List<Message> messages,
|
||||||
StreamChannelState? streamChannel,
|
StreamChannelState streamChannel,
|
||||||
) {
|
) {
|
||||||
Widget messageWidget;
|
Widget messageWidget;
|
||||||
if (widget.messageBuilder != null) {
|
if (widget.messageBuilder != null) {
|
||||||
@@ -790,7 +780,7 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
onVisibilityChanged: (visibility) {
|
onVisibilityChanged: (visibility) {
|
||||||
final isVisible = visibility.visibleBounds != Rect.zero;
|
final isVisible = visibility.visibleBounds != Rect.zero;
|
||||||
if (isVisible) {
|
if (isVisible) {
|
||||||
final channel = streamChannel!.channel;
|
final channel = streamChannel.channel;
|
||||||
if (_upToDate &&
|
if (_upToDate &&
|
||||||
channel.config?.readEvents == true &&
|
channel.config?.readEvents == true &&
|
||||||
channel.state!.unreadCount! > 0) {
|
channel.state!.unreadCount! > 0) {
|
||||||
@@ -811,6 +801,7 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
final isMyMessage = message.user!.id == StreamChat.of(context).user!.id;
|
final isMyMessage = message.user!.id == StreamChat.of(context).user!.id;
|
||||||
final isOnlyEmoji = message.text!.isOnlyEmoji;
|
final isOnlyEmoji = message.text!.isOnlyEmoji;
|
||||||
|
|
||||||
|
final chatThemeData = StreamChatTheme.of(context);
|
||||||
return MessageWidget(
|
return MessageWidget(
|
||||||
showReplyMessage: false,
|
showReplyMessage: false,
|
||||||
showResendMessage: false,
|
showResendMessage: false,
|
||||||
@@ -837,8 +828,8 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
borderSide: isMyMessage || isOnlyEmoji ? BorderSide.none : null,
|
borderSide: isMyMessage || isOnlyEmoji ? BorderSide.none : null,
|
||||||
showUserAvatar: isMyMessage ? DisplayWidget.gone : DisplayWidget.show,
|
showUserAvatar: isMyMessage ? DisplayWidget.gone : DisplayWidget.show,
|
||||||
messageTheme: isMyMessage
|
messageTheme: isMyMessage
|
||||||
? StreamChatTheme.of(context).ownMessageTheme
|
? chatThemeData.ownMessageTheme
|
||||||
: StreamChatTheme.of(context).otherMessageTheme,
|
: chatThemeData.otherMessageTheme,
|
||||||
onShowMessage: widget.onShowMessage,
|
onShowMessage: widget.onShowMessage,
|
||||||
onReturnAction: (action) {
|
onReturnAction: (action) {
|
||||||
switch (action) {
|
switch (action) {
|
||||||
@@ -946,6 +937,7 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
? BorderSide.none
|
? BorderSide.none
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
|
final chatThemeData = StreamChatTheme.of(context);
|
||||||
Widget child = MessageWidget(
|
Widget child = MessageWidget(
|
||||||
key: ValueKey<String>('MESSAGE-${message.id}'),
|
key: ValueKey<String>('MESSAGE-${message.id}'),
|
||||||
message: message,
|
message: message,
|
||||||
@@ -1023,8 +1015,8 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
horizontal: isOnlyEmoji ? 0 : 16.0,
|
horizontal: isOnlyEmoji ? 0 : 16.0,
|
||||||
),
|
),
|
||||||
messageTheme: isMyMessage
|
messageTheme: isMyMessage
|
||||||
? StreamChatTheme.of(context).ownMessageTheme
|
? chatThemeData.ownMessageTheme
|
||||||
: StreamChatTheme.of(context).otherMessageTheme,
|
: chatThemeData.otherMessageTheme,
|
||||||
readList: readList,
|
readList: readList,
|
||||||
allRead: allRead,
|
allRead: allRead,
|
||||||
onShowMessage: widget.onShowMessage,
|
onShowMessage: widget.onShowMessage,
|
||||||
@@ -1064,7 +1056,7 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
widget.onMessageSwiped?.call(message);
|
widget.onMessageSwiped?.call(message);
|
||||||
},
|
},
|
||||||
backgroundIcon: StreamSvgIcon.reply(
|
backgroundIcon: StreamSvgIcon.reply(
|
||||||
color: StreamChatTheme.of(context).colorTheme.accentBlue,
|
color: chatThemeData.colorTheme.accentBlue,
|
||||||
),
|
),
|
||||||
child: child,
|
child: child,
|
||||||
),
|
),
|
||||||
@@ -1074,7 +1066,7 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
if (!initialMessageHighlightComplete &&
|
if (!initialMessageHighlightComplete &&
|
||||||
widget.highlightInitialMessage &&
|
widget.highlightInitialMessage &&
|
||||||
_isInitialMessage(message.id)) {
|
_isInitialMessage(message.id)) {
|
||||||
final colorTheme = StreamChatTheme.of(context).colorTheme;
|
final colorTheme = chatThemeData.colorTheme;
|
||||||
final highlightColor =
|
final highlightColor =
|
||||||
widget.messageHighlightColor ?? colorTheme.highlight;
|
widget.messageHighlightColor ?? colorTheme.highlight;
|
||||||
child = TweenAnimationBuilder<Color?>(
|
child = TweenAnimationBuilder<Color?>(
|
||||||
@@ -1105,35 +1097,44 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
_itemPositionListener =
|
_itemPositionListener =
|
||||||
widget.itemPositionListener ?? ItemPositionsListener.create();
|
widget.itemPositionListener ?? ItemPositionsListener.create();
|
||||||
|
|
||||||
streamChannel = StreamChannel.of(context);
|
|
||||||
|
|
||||||
initialIndex = _initialIndex;
|
|
||||||
initialAlignment = _initialAlignment;
|
|
||||||
|
|
||||||
_messageNewListener =
|
|
||||||
streamChannel!.channel.on(EventType.messageNew).listen((event) {
|
|
||||||
if (_upToDate) {
|
|
||||||
_bottomPaginationActive = false;
|
|
||||||
_topPaginationActive = false;
|
|
||||||
}
|
|
||||||
if (event.message!.user!.id ==
|
|
||||||
streamChannel!.channel.client.state.user!.id) {
|
|
||||||
WidgetsBinding.instance!.addPostFrameCallback((_) {
|
|
||||||
_scrollController?.jumpTo(
|
|
||||||
index: 0,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (_isThreadConversation) {
|
|
||||||
streamChannel!.getReplies(widget.parentMessage!.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
_getOnThreadTap();
|
_getOnThreadTap();
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didChangeDependencies() {
|
||||||
|
final newStreamChannel = StreamChannel.of(context);
|
||||||
|
|
||||||
|
if (newStreamChannel != streamChannel) {
|
||||||
|
streamChannel = newStreamChannel;
|
||||||
|
_messageNewListener?.cancel();
|
||||||
|
initialIndex = _initialIndex;
|
||||||
|
initialAlignment = _initialAlignment;
|
||||||
|
|
||||||
|
_messageNewListener =
|
||||||
|
streamChannel!.channel.on(EventType.messageNew).listen((event) {
|
||||||
|
if (_upToDate) {
|
||||||
|
_bottomPaginationActive = false;
|
||||||
|
_topPaginationActive = false;
|
||||||
|
}
|
||||||
|
if (event.message!.user!.id ==
|
||||||
|
streamChannel!.channel.client.state.user!.id) {
|
||||||
|
WidgetsBinding.instance!.addPostFrameCallback((_) {
|
||||||
|
_scrollController?.jumpTo(
|
||||||
|
index: 0,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (_isThreadConversation) {
|
||||||
|
streamChannel!.getReplies(widget.parentMessage!.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
super.didChangeDependencies();
|
||||||
|
}
|
||||||
|
|
||||||
void _getOnThreadTap() {
|
void _getOnThreadTap() {
|
||||||
if (widget.onThreadTap != null) {
|
if (widget.onThreadTap != null) {
|
||||||
_onThreadTap = (Message message) {
|
_onThreadTap = (Message message) {
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:stream_chat_flutter/src/extension.dart';
|
||||||
import 'package:stream_chat_flutter/src/reaction_bubble.dart';
|
import 'package:stream_chat_flutter/src/reaction_bubble.dart';
|
||||||
import 'package:stream_chat_flutter/src/reaction_picker.dart';
|
import 'package:stream_chat_flutter/src/reaction_picker.dart';
|
||||||
import 'package:stream_chat_flutter/src/stream_chat.dart';
|
import 'package:stream_chat_flutter/src/stream_chat.dart';
|
||||||
import 'package:stream_chat_flutter/src/user_avatar.dart';
|
import 'package:stream_chat_flutter/src/user_avatar.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
import 'package:stream_chat_flutter/src/extension.dart';
|
|
||||||
|
|
||||||
/// Modal widget for displaying message reactions
|
/// Modal widget for displaying message reactions
|
||||||
class MessageReactionsModal extends StatelessWidget {
|
class MessageReactionsModal extends StatelessWidget {
|
||||||
@@ -176,8 +176,9 @@ class MessageReactionsModal extends StatelessWidget {
|
|||||||
|
|
||||||
Widget _buildReactionCard(BuildContext context) {
|
Widget _buildReactionCard(BuildContext context) {
|
||||||
final currentUser = StreamChat.of(context).user;
|
final currentUser = StreamChat.of(context).user;
|
||||||
|
final chatThemeData = StreamChatTheme.of(context);
|
||||||
return Card(
|
return Card(
|
||||||
color: StreamChatTheme.of(context).colorTheme.white,
|
color: chatThemeData.colorTheme.white,
|
||||||
clipBehavior: Clip.hardEdge,
|
clipBehavior: Clip.hardEdge,
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(16),
|
||||||
@@ -190,7 +191,7 @@ class MessageReactionsModal extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'Message Reactions',
|
'Message Reactions',
|
||||||
style: StreamChatTheme.of(context).textTheme.headlineBold,
|
style: chatThemeData.textTheme.headlineBold,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Flexible(
|
Flexible(
|
||||||
@@ -220,6 +221,7 @@ class MessageReactionsModal extends StatelessWidget {
|
|||||||
BuildContext context,
|
BuildContext context,
|
||||||
) {
|
) {
|
||||||
final isCurrentUser = reaction.user?.id == currentUser.id;
|
final isCurrentUser = reaction.user?.id == currentUser.id;
|
||||||
|
final chatThemeData = StreamChatTheme.of(context);
|
||||||
return ConstrainedBox(
|
return ConstrainedBox(
|
||||||
constraints: BoxConstraints.loose(const Size(
|
constraints: BoxConstraints.loose(const Size(
|
||||||
64,
|
64,
|
||||||
@@ -258,7 +260,7 @@ class MessageReactionsModal extends StatelessWidget {
|
|||||||
messageTheme.reactionsBorderColor ?? Colors.transparent,
|
messageTheme.reactionsBorderColor ?? Colors.transparent,
|
||||||
backgroundColor: messageTheme.reactionsBackgroundColor ??
|
backgroundColor: messageTheme.reactionsBackgroundColor ??
|
||||||
Colors.transparent,
|
Colors.transparent,
|
||||||
maskColor: StreamChatTheme.of(context).colorTheme.white,
|
maskColor: chatThemeData.colorTheme.white,
|
||||||
tailCirclesSpacing: 1,
|
tailCirclesSpacing: 1,
|
||||||
highlightOwnReactions: false,
|
highlightOwnReactions: false,
|
||||||
),
|
),
|
||||||
@@ -269,7 +271,7 @@ class MessageReactionsModal extends StatelessWidget {
|
|||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Text(
|
Text(
|
||||||
reaction.user!.name.split(' ')[0],
|
reaction.user!.name.split(' ')[0],
|
||||||
style: StreamChatTheme.of(context).textTheme.footnoteBold,
|
style: chatThemeData.textTheme.footnoteBold,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ class MessageSearchItem extends StatelessWidget {
|
|||||||
final channel = getMessageResponse.channel;
|
final channel = getMessageResponse.channel;
|
||||||
final channelName = channel?.extraData['name'];
|
final channelName = channel?.extraData['name'];
|
||||||
final user = message.user!;
|
final user = message.user!;
|
||||||
|
final chatThemeData = StreamChatTheme.of(context);
|
||||||
return ListTile(
|
return ListTile(
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
leading: UserAvatar(
|
leading: UserAvatar(
|
||||||
@@ -49,21 +50,18 @@ class MessageSearchItem extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
user.id == StreamChat.of(context).user?.id ? 'You' : user.name,
|
user.id == StreamChat.of(context).user?.id ? 'You' : user.name,
|
||||||
style: StreamChatTheme.of(context).channelPreviewTheme.title,
|
style: chatThemeData.channelPreviewTheme.title,
|
||||||
),
|
),
|
||||||
if (channelName != null) ...[
|
if (channelName != null) ...[
|
||||||
Text(
|
Text(
|
||||||
' in ',
|
' in ',
|
||||||
style: StreamChatTheme.of(context)
|
style: chatThemeData.channelPreviewTheme.title?.copyWith(
|
||||||
.channelPreviewTheme
|
fontWeight: FontWeight.normal,
|
||||||
.title
|
),
|
||||||
?.copyWith(
|
|
||||||
fontWeight: FontWeight.normal,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
channelName as String,
|
channelName as String,
|
||||||
style: StreamChatTheme.of(context).channelPreviewTheme.title,
|
style: chatThemeData.channelPreviewTheme.title,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
@@ -121,22 +119,23 @@ class MessageSearchItem extends StatelessWidget {
|
|||||||
text = parts.join(' ');
|
text = parts.join(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final chatThemeData = StreamChatTheme.of(context);
|
||||||
return Text.rich(
|
return Text.rich(
|
||||||
_getDisplayText(
|
_getDisplayText(
|
||||||
text!,
|
text!,
|
||||||
message.mentionedUsers,
|
message.mentionedUsers,
|
||||||
message.attachments,
|
message.attachments,
|
||||||
StreamChatTheme.of(context).channelPreviewTheme.subtitle?.copyWith(
|
chatThemeData.channelPreviewTheme.subtitle?.copyWith(
|
||||||
fontStyle: (message.isSystem || message.isDeleted)
|
fontStyle: (message.isSystem || message.isDeleted)
|
||||||
? FontStyle.italic
|
? FontStyle.italic
|
||||||
: FontStyle.normal,
|
: FontStyle.normal,
|
||||||
),
|
),
|
||||||
StreamChatTheme.of(context).channelPreviewTheme.subtitle?.copyWith(
|
chatThemeData.channelPreviewTheme.subtitle?.copyWith(
|
||||||
fontStyle: (message.isSystem || message.isDeleted)
|
fontStyle: (message.isSystem || message.isDeleted)
|
||||||
? FontStyle.italic
|
? FontStyle.italic
|
||||||
: FontStyle.normal,
|
: FontStyle.normal,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:stream_chat_flutter/src/info_tile.dart';
|
import 'package:stream_chat_flutter/src/info_tile.dart';
|
||||||
import 'package:stream_chat_flutter/src/message_search_item.dart';
|
import 'package:stream_chat_flutter/src/message_search_item.dart';
|
||||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
|
|
||||||
/// Callback called when tapping on a user
|
/// Callback called when tapping on a user
|
||||||
typedef MessageSearchItemTapCallback = void Function(GetMessageResponse);
|
typedef MessageSearchItemTapCallback = void Function(GetMessageResponse);
|
||||||
@@ -270,12 +270,13 @@ class _MessageSearchListViewState extends State<MessageSearchListView> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (widget.showResultCount) {
|
if (widget.showResultCount) {
|
||||||
|
final chatThemeData = StreamChatTheme.of(context);
|
||||||
child = Column(
|
child = Column(
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
width: double.maxFinite,
|
width: double.maxFinite,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
gradient: StreamChatTheme.of(context).colorTheme.bgGradient,
|
gradient: chatThemeData.colorTheme.bgGradient,
|
||||||
),
|
),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
@@ -285,7 +286,7 @@ class _MessageSearchListViewState extends State<MessageSearchListView> {
|
|||||||
child: Text(
|
child: Text(
|
||||||
'${items.length} results',
|
'${items.length} results',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: StreamChatTheme.of(context).colorTheme.grey,
|
color: chatThemeData.colorTheme.grey,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import 'package:collection/collection.dart' show IterableExtension;
|
import 'package:collection/collection.dart' show IterableExtension;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_markdown/flutter_markdown.dart';
|
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
|
|
||||||
/// Text widget to display in message
|
/// Text widget to display in message
|
||||||
class MessageText extends StatelessWidget {
|
class MessageText extends StatelessWidget {
|
||||||
@@ -31,6 +31,7 @@ class MessageText extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final text = _replaceMentions(message.text ?? '').replaceAll('\n', '\\\n');
|
final text = _replaceMentions(message.text ?? '').replaceAll('\n', '\\\n');
|
||||||
|
|
||||||
|
final themeData = Theme.of(context);
|
||||||
return MarkdownBody(
|
return MarkdownBody(
|
||||||
data: text,
|
data: text,
|
||||||
onTapLink: (
|
onTapLink: (
|
||||||
@@ -60,14 +61,14 @@ class MessageText extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
styleSheet: MarkdownStyleSheet.fromTheme(
|
styleSheet: MarkdownStyleSheet.fromTheme(
|
||||||
Theme.of(context).copyWith(
|
themeData.copyWith(
|
||||||
textTheme: Theme.of(context).textTheme.apply(
|
textTheme: themeData.textTheme.apply(
|
||||||
bodyColor: messageTheme.messageText?.color,
|
bodyColor: messageTheme.messageText?.color,
|
||||||
decoration: messageTheme.messageText?.decoration,
|
decoration: messageTheme.messageText?.decoration,
|
||||||
decorationColor: messageTheme.messageText?.decorationColor,
|
decorationColor: messageTheme.messageText?.decorationColor,
|
||||||
decorationStyle: messageTheme.messageText?.decorationStyle,
|
decorationStyle: messageTheme.messageText?.decorationStyle,
|
||||||
fontFamily: messageTheme.messageText?.fontFamily,
|
fontFamily: messageTheme.messageText?.fontFamily,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
).copyWith(
|
).copyWith(
|
||||||
a: messageTheme.messageLinks,
|
a: messageTheme.messageLinks,
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import 'package:flutter/rendering.dart';
|
|||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_portal/flutter_portal.dart';
|
import 'package:flutter_portal/flutter_portal.dart';
|
||||||
import 'package:jiffy/jiffy.dart';
|
import 'package:jiffy/jiffy.dart';
|
||||||
|
import 'package:stream_chat_flutter/src/extension.dart';
|
||||||
|
import 'package:stream_chat_flutter/src/image_group.dart';
|
||||||
import 'package:stream_chat_flutter/src/message_action.dart';
|
import 'package:stream_chat_flutter/src/message_action.dart';
|
||||||
import 'package:stream_chat_flutter/src/message_actions_modal.dart';
|
import 'package:stream_chat_flutter/src/message_actions_modal.dart';
|
||||||
import 'package:stream_chat_flutter/src/message_reactions_modal.dart';
|
import 'package:stream_chat_flutter/src/message_reactions_modal.dart';
|
||||||
@@ -15,8 +17,6 @@ import 'package:stream_chat_flutter/src/quoted_message_widget.dart';
|
|||||||
import 'package:stream_chat_flutter/src/reaction_bubble.dart';
|
import 'package:stream_chat_flutter/src/reaction_bubble.dart';
|
||||||
import 'package:stream_chat_flutter/src/url_attachment.dart';
|
import 'package:stream_chat_flutter/src/url_attachment.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
import 'package:stream_chat_flutter/src/image_group.dart';
|
|
||||||
import 'package:stream_chat_flutter/src/extension.dart';
|
|
||||||
|
|
||||||
/// Widget builder for building attachments
|
/// Widget builder for building attachments
|
||||||
typedef AttachmentBuilder = Widget Function(
|
typedef AttachmentBuilder = Widget Function(
|
||||||
@@ -109,6 +109,7 @@ class MessageWidget extends StatefulWidget {
|
|||||||
borderRadius: attachmentBorderRadiusGeometry ?? BorderRadius.zero,
|
borderRadius: attachmentBorderRadiusGeometry ?? BorderRadius.zero,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
final mediaQueryData = MediaQuery.of(context);
|
||||||
if (attachments.length > 1) {
|
if (attachments.length > 1) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: attachmentPadding,
|
padding: attachmentPadding,
|
||||||
@@ -118,8 +119,8 @@ class MessageWidget extends StatefulWidget {
|
|||||||
color: messageTheme.messageBackgroundColor,
|
color: messageTheme.messageBackgroundColor,
|
||||||
child: ImageGroup(
|
child: ImageGroup(
|
||||||
size: Size(
|
size: Size(
|
||||||
MediaQuery.of(context).size.width * 0.8,
|
mediaQueryData.size.width * 0.8,
|
||||||
MediaQuery.of(context).size.height * 0.3,
|
mediaQueryData.size.height * 0.3,
|
||||||
),
|
),
|
||||||
images: attachments,
|
images: attachments,
|
||||||
message: message,
|
message: message,
|
||||||
@@ -142,8 +143,8 @@ class MessageWidget extends StatefulWidget {
|
|||||||
message: message,
|
message: message,
|
||||||
messageTheme: messageTheme,
|
messageTheme: messageTheme,
|
||||||
size: Size(
|
size: Size(
|
||||||
MediaQuery.of(context).size.width * 0.8,
|
mediaQueryData.size.width * 0.8,
|
||||||
MediaQuery.of(context).size.height * 0.3,
|
mediaQueryData.size.height * 0.3,
|
||||||
),
|
),
|
||||||
onShowMessage: onShowMessage,
|
onShowMessage: onShowMessage,
|
||||||
onReturnAction: onReturnAction,
|
onReturnAction: onReturnAction,
|
||||||
@@ -167,24 +168,25 @@ class MessageWidget extends StatefulWidget {
|
|||||||
return wrapAttachmentWidget(
|
return wrapAttachmentWidget(
|
||||||
context,
|
context,
|
||||||
Column(
|
Column(
|
||||||
children: attachments
|
children: attachments.map((attachment) {
|
||||||
.map((attachment) => VideoAttachment(
|
final mediaQueryData = MediaQuery.of(context);
|
||||||
attachment: attachment,
|
return VideoAttachment(
|
||||||
messageTheme: messageTheme,
|
attachment: attachment,
|
||||||
size: Size(
|
messageTheme: messageTheme,
|
||||||
MediaQuery.of(context).size.width * 0.8,
|
size: Size(
|
||||||
MediaQuery.of(context).size.height * 0.3,
|
mediaQueryData.size.width * 0.8,
|
||||||
),
|
mediaQueryData.size.height * 0.3,
|
||||||
message: message,
|
),
|
||||||
onShowMessage: onShowMessage,
|
message: message,
|
||||||
onReturnAction: onReturnAction,
|
onShowMessage: onShowMessage,
|
||||||
onAttachmentTap: onAttachmentTap != null
|
onReturnAction: onReturnAction,
|
||||||
? () {
|
onAttachmentTap: onAttachmentTap != null
|
||||||
onAttachmentTap(message, attachment);
|
? () {
|
||||||
}
|
onAttachmentTap(message, attachment);
|
||||||
: null,
|
}
|
||||||
))
|
: null,
|
||||||
.toList(),
|
);
|
||||||
|
}).toList(),
|
||||||
),
|
),
|
||||||
border,
|
border,
|
||||||
reverse,
|
reverse,
|
||||||
@@ -200,18 +202,19 @@ class MessageWidget extends StatefulWidget {
|
|||||||
return wrapAttachmentWidget(
|
return wrapAttachmentWidget(
|
||||||
context,
|
context,
|
||||||
Column(
|
Column(
|
||||||
children: attachments
|
children: attachments.map((attachment) {
|
||||||
.map((attachment) => GiphyAttachment(
|
final mediaQueryData = MediaQuery.of(context);
|
||||||
attachment: attachment,
|
return GiphyAttachment(
|
||||||
message: message,
|
attachment: attachment,
|
||||||
size: Size(
|
message: message,
|
||||||
MediaQuery.of(context).size.width * 0.8,
|
size: Size(
|
||||||
MediaQuery.of(context).size.height * 0.3,
|
mediaQueryData.size.width * 0.8,
|
||||||
),
|
mediaQueryData.size.height * 0.3,
|
||||||
onShowMessage: onShowMessage,
|
),
|
||||||
onReturnAction: onReturnAction,
|
onShowMessage: onShowMessage,
|
||||||
))
|
onReturnAction: onReturnAction,
|
||||||
.toList(),
|
);
|
||||||
|
}).toList(),
|
||||||
),
|
),
|
||||||
border,
|
border,
|
||||||
reverse,
|
reverse,
|
||||||
@@ -230,21 +233,24 @@ class MessageWidget extends StatefulWidget {
|
|||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
children: attachments
|
children: attachments
|
||||||
.map<Widget>((attachment) => wrapAttachmentWidget(
|
.map<Widget>((attachment) {
|
||||||
context,
|
final mediaQueryData = MediaQuery.of(context);
|
||||||
FileAttachment(
|
return wrapAttachmentWidget(
|
||||||
message: message,
|
context,
|
||||||
attachment: attachment,
|
FileAttachment(
|
||||||
size: Size(
|
message: message,
|
||||||
MediaQuery.of(context).size.width * 0.8,
|
attachment: attachment,
|
||||||
MediaQuery.of(context).size.height * 0.3,
|
size: Size(
|
||||||
),
|
mediaQueryData.size.width * 0.8,
|
||||||
|
mediaQueryData.size.height * 0.3,
|
||||||
),
|
),
|
||||||
border,
|
),
|
||||||
reverse,
|
border,
|
||||||
attachmentBorderRadiusGeometry as BorderRadius? ??
|
reverse,
|
||||||
BorderRadius.zero,
|
attachmentBorderRadiusGeometry as BorderRadius? ??
|
||||||
))
|
BorderRadius.zero,
|
||||||
|
);
|
||||||
|
})
|
||||||
.insertBetween(SizedBox(
|
.insertBetween(SizedBox(
|
||||||
height: attachmentPadding.vertical / 2,
|
height: attachmentPadding.vertical / 2,
|
||||||
))
|
))
|
||||||
@@ -648,12 +654,13 @@ class _MessageWidgetState extends State<MessageWidget>
|
|||||||
widget.onQuotedMessageTap != null
|
widget.onQuotedMessageTap != null
|
||||||
? () => widget.onQuotedMessageTap!(widget.message.quotedMessageId)
|
? () => widget.onQuotedMessageTap!(widget.message.quotedMessageId)
|
||||||
: null;
|
: null;
|
||||||
|
final chatThemeData = StreamChatTheme.of(context);
|
||||||
return QuotedMessageWidget(
|
return QuotedMessageWidget(
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
message: widget.message.quotedMessage!,
|
message: widget.message.quotedMessage!,
|
||||||
messageTheme: isMyMessage
|
messageTheme: isMyMessage
|
||||||
? StreamChatTheme.of(context).otherMessageTheme
|
? chatThemeData.otherMessageTheme
|
||||||
: StreamChatTheme.of(context).ownMessageTheme,
|
: chatThemeData.ownMessageTheme,
|
||||||
reverse: widget.reverse,
|
reverse: widget.reverse,
|
||||||
padding: EdgeInsets.only(
|
padding: EdgeInsets.only(
|
||||||
right: 8, left: 8, top: 8, bottom: hasNonUrlAttachments ? 8 : 0),
|
right: 8, left: 8, top: 8, bottom: hasNonUrlAttachments ? 8 : 0),
|
||||||
@@ -662,20 +669,19 @@ class _MessageWidgetState extends State<MessageWidget>
|
|||||||
|
|
||||||
Widget get _bottomRow {
|
Widget get _bottomRow {
|
||||||
if (isDeleted) {
|
if (isDeleted) {
|
||||||
|
final chatThemeData = StreamChatTheme.of(context);
|
||||||
return Row(
|
return Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
StreamSvgIcon.eye(
|
StreamSvgIcon.eye(
|
||||||
color: StreamChatTheme.of(context).colorTheme.grey,
|
color: chatThemeData.colorTheme.grey,
|
||||||
size: 16,
|
size: 16,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Text(
|
Text(
|
||||||
'Only visible to you',
|
'Only visible to you',
|
||||||
style: StreamChatTheme.of(context)
|
style: chatThemeData.textTheme.footnote
|
||||||
.textTheme
|
.copyWith(color: chatThemeData.colorTheme.grey),
|
||||||
.footnote
|
|
||||||
.copyWith(color: StreamChatTheme.of(context).colorTheme.grey),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -41,56 +41,55 @@ class OptionListTile extends StatelessWidget {
|
|||||||
final TextStyle? titleTextStyle;
|
final TextStyle? titleTextStyle;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => Column(
|
Widget build(BuildContext context) {
|
||||||
children: [
|
final chatThemeData = StreamChatTheme.of(context);
|
||||||
Container(
|
return Column(
|
||||||
color: separatorColor ??
|
children: [
|
||||||
StreamChatTheme.of(context).colorTheme.greyGainsboro,
|
Container(
|
||||||
height: 1,
|
color: separatorColor ?? chatThemeData.colorTheme.greyGainsboro,
|
||||||
),
|
height: 1,
|
||||||
Material(
|
),
|
||||||
color: tileColor ?? StreamChatTheme.of(context).colorTheme.white,
|
Material(
|
||||||
child: SizedBox(
|
color: tileColor ?? chatThemeData.colorTheme.white,
|
||||||
height: 63,
|
child: SizedBox(
|
||||||
child: InkWell(
|
height: 63,
|
||||||
onTap: onTap,
|
child: InkWell(
|
||||||
child: Row(
|
onTap: onTap,
|
||||||
children: [
|
child: Row(
|
||||||
if (leading != null) Center(child: leading),
|
children: [
|
||||||
if (leading == null)
|
if (leading != null) Center(child: leading),
|
||||||
const SizedBox(
|
if (leading == null)
|
||||||
width: 16,
|
const SizedBox(
|
||||||
),
|
width: 16,
|
||||||
Expanded(
|
),
|
||||||
flex: 4,
|
Expanded(
|
||||||
child: Text(
|
flex: 4,
|
||||||
title!,
|
child: Text(
|
||||||
style: titleTextStyle ??
|
title!,
|
||||||
(titleColor == null
|
style: titleTextStyle ??
|
||||||
? StreamChatTheme.of(context).textTheme.bodyBold
|
(titleColor == null
|
||||||
: StreamChatTheme.of(context)
|
? chatThemeData.textTheme.bodyBold
|
||||||
.textTheme
|
: chatThemeData.textTheme.bodyBold.copyWith(
|
||||||
.bodyBold
|
color: titleColor,
|
||||||
.copyWith(
|
)),
|
||||||
color: titleColor,
|
),
|
||||||
)),
|
),
|
||||||
|
Expanded(
|
||||||
|
flex: 2,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(right: 16),
|
||||||
|
child: Align(
|
||||||
|
alignment: Alignment.centerRight,
|
||||||
|
child: trailing ?? Container(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
),
|
||||||
flex: 2,
|
],
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.only(right: 16),
|
|
||||||
child: Align(
|
|
||||||
alignment: Alignment.centerRight,
|
|
||||||
child: trailing ?? Container(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
);
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -121,6 +121,8 @@ class ReactionBubble extends StatelessWidget {
|
|||||||
(r) => r.type == reaction.type,
|
(r) => r.type == reaction.type,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
final chatThemeData = StreamChatTheme.of(context);
|
||||||
|
final userId = StreamChat.of(context).user?.id;
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
horizontal: 4,
|
horizontal: 4,
|
||||||
@@ -130,24 +132,16 @@ class ReactionBubble extends StatelessWidget {
|
|||||||
assetName: reactionIcon.assetName,
|
assetName: reactionIcon.assetName,
|
||||||
width: 16,
|
width: 16,
|
||||||
height: 16,
|
height: 16,
|
||||||
color: (!highlightOwnReactions ||
|
color: (!highlightOwnReactions || reaction.user?.id == userId)
|
||||||
reaction.user?.id == StreamChat.of(context).user?.id)
|
? chatThemeData.colorTheme.accentBlue
|
||||||
? StreamChatTheme.of(context).colorTheme.accentBlue
|
: chatThemeData.colorTheme.black.withOpacity(.5),
|
||||||
: StreamChatTheme.of(context)
|
|
||||||
.colorTheme
|
|
||||||
.black
|
|
||||||
.withOpacity(.5),
|
|
||||||
)
|
)
|
||||||
: Icon(
|
: Icon(
|
||||||
Icons.help_outline_rounded,
|
Icons.help_outline_rounded,
|
||||||
size: 16,
|
size: 16,
|
||||||
color: (!highlightOwnReactions ||
|
color: (!highlightOwnReactions || reaction.user?.id == userId)
|
||||||
reaction.user?.id == StreamChat.of(context).user?.id)
|
? chatThemeData.colorTheme.accentBlue
|
||||||
? StreamChatTheme.of(context).colorTheme.accentBlue
|
: chatThemeData.colorTheme.black.withOpacity(.5),
|
||||||
: StreamChatTheme.of(context)
|
|
||||||
.colorTheme
|
|
||||||
.black
|
|
||||||
.withOpacity(.5),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import 'dart:math';
|
|||||||
|
|
||||||
import 'package:ezanimation/ezanimation.dart';
|
import 'package:ezanimation/ezanimation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
|
||||||
import 'package:stream_chat_flutter/src/extension.dart';
|
import 'package:stream_chat_flutter/src/extension.dart';
|
||||||
|
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
/// 
|
/// 
|
||||||
@@ -33,7 +33,8 @@ class _ReactionPickerState extends State<ReactionPicker>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final reactionIcons = StreamChatTheme.of(context).reactionIcons;
|
final chatThemeData = StreamChatTheme.of(context);
|
||||||
|
final reactionIcons = chatThemeData.reactionIcons;
|
||||||
|
|
||||||
if (animations.isEmpty && reactionIcons.isNotEmpty) {
|
if (animations.isEmpty && reactionIcons.isNotEmpty) {
|
||||||
reactionIcons.forEach((element) {
|
reactionIcons.forEach((element) {
|
||||||
@@ -57,7 +58,7 @@ class _ReactionPickerState extends State<ReactionPicker>
|
|||||||
scale: val,
|
scale: val,
|
||||||
child: Material(
|
child: Material(
|
||||||
borderRadius: BorderRadius.circular(24),
|
borderRadius: BorderRadius.circular(24),
|
||||||
color: StreamChatTheme.of(context).colorTheme.white,
|
color: chatThemeData.colorTheme.white,
|
||||||
clipBehavior: Clip.hardEdge,
|
clipBehavior: Clip.hardEdge,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
@@ -119,9 +120,8 @@ class _ReactionPickerState extends State<ReactionPicker>
|
|||||||
animations[index].value * 24.0,
|
animations[index].value * 24.0,
|
||||||
),
|
),
|
||||||
color: ownReactionIndex != -1
|
color: ownReactionIndex != -1
|
||||||
? StreamChatTheme.of(context)
|
? chatThemeData
|
||||||
.colorTheme
|
.colorTheme.accentBlue
|
||||||
.accentBlue
|
|
||||||
: Theme.of(context)
|
: Theme.of(context)
|
||||||
.iconTheme
|
.iconTheme
|
||||||
.color!
|
.color!
|
||||||
|
|||||||
@@ -97,65 +97,60 @@ class ThreadHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
final List<Widget>? actions;
|
final List<Widget>? actions;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => AppBar(
|
Widget build(BuildContext context) {
|
||||||
automaticallyImplyLeading: false,
|
final chatThemeData = StreamChatTheme.of(context);
|
||||||
brightness: Theme.of(context).brightness,
|
return AppBar(
|
||||||
elevation: 1,
|
automaticallyImplyLeading: false,
|
||||||
leading: leading ??
|
brightness: Theme.of(context).brightness,
|
||||||
(showBackButton
|
elevation: 1,
|
||||||
? StreamBackButton(
|
leading: leading ??
|
||||||
cid: StreamChannel.of(context).channel.cid,
|
(showBackButton
|
||||||
onPressed: onBackPressed,
|
? StreamBackButton(
|
||||||
showUnreads: true,
|
cid: StreamChannel.of(context).channel.cid,
|
||||||
)
|
onPressed: onBackPressed,
|
||||||
: const SizedBox()),
|
showUnreads: true,
|
||||||
backgroundColor:
|
)
|
||||||
StreamChatTheme.of(context).channelTheme.channelHeaderTheme.color,
|
: const SizedBox()),
|
||||||
centerTitle: true,
|
backgroundColor: chatThemeData.channelTheme.channelHeaderTheme.color,
|
||||||
actions: actions,
|
centerTitle: true,
|
||||||
title: InkWell(
|
actions: actions,
|
||||||
onTap: onTitleTap,
|
title: InkWell(
|
||||||
child: SizedBox(
|
onTap: onTitleTap,
|
||||||
height: preferredSize.height,
|
child: SizedBox(
|
||||||
child: Column(
|
height: preferredSize.height,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
child: Column(
|
||||||
children: [
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
title ??
|
children: [
|
||||||
Text(
|
title ??
|
||||||
'Thread Reply',
|
Text(
|
||||||
style: StreamChatTheme.of(context)
|
'Thread Reply',
|
||||||
.channelTheme
|
style: chatThemeData.channelTheme.channelHeaderTheme.title,
|
||||||
.channelHeaderTheme
|
),
|
||||||
.title,
|
const SizedBox(height: 2),
|
||||||
),
|
subtitle ??
|
||||||
const SizedBox(height: 2),
|
Row(
|
||||||
subtitle ??
|
mainAxisSize: MainAxisSize.min,
|
||||||
Row(
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
mainAxisSize: MainAxisSize.min,
|
children: [
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
Text(
|
||||||
children: [
|
'with ',
|
||||||
Text(
|
style: chatThemeData
|
||||||
'with ',
|
.channelTheme.channelHeaderTheme.subtitle,
|
||||||
style: StreamChatTheme.of(context)
|
),
|
||||||
.channelTheme
|
Flexible(
|
||||||
.channelHeaderTheme
|
child: ChannelName(
|
||||||
.subtitle,
|
textStyle: chatThemeData
|
||||||
|
.channelTheme.channelHeaderTheme.subtitle,
|
||||||
),
|
),
|
||||||
Flexible(
|
),
|
||||||
child: ChannelName(
|
],
|
||||||
textStyle: StreamChatTheme.of(context)
|
),
|
||||||
.channelTheme
|
],
|
||||||
.channelHeaderTheme
|
|
||||||
.subtitle,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final Size preferredSize;
|
final Size preferredSize;
|
||||||
|
|||||||
@@ -26,91 +26,84 @@ class UrlAttachment extends StatelessWidget {
|
|||||||
final EdgeInsets textPadding;
|
final EdgeInsets textPadding;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => GestureDetector(
|
Widget build(BuildContext context) {
|
||||||
onTap: () {
|
final chatThemeData = StreamChatTheme.of(context);
|
||||||
launchURL(
|
return GestureDetector(
|
||||||
context,
|
onTap: () {
|
||||||
urlAttachment.ogScrapeUrl,
|
launchURL(
|
||||||
);
|
context,
|
||||||
},
|
urlAttachment.ogScrapeUrl,
|
||||||
child: Column(
|
);
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
},
|
||||||
children: [
|
child: Column(
|
||||||
if (urlAttachment.imageUrl != null)
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
Container(
|
children: [
|
||||||
clipBehavior: Clip.antiAliasWithSaveLayer,
|
if (urlAttachment.imageUrl != null)
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 8),
|
Container(
|
||||||
decoration: BoxDecoration(
|
clipBehavior: Clip.antiAliasWithSaveLayer,
|
||||||
borderRadius: BorderRadius.circular(8),
|
margin: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
),
|
decoration: BoxDecoration(
|
||||||
child: Stack(
|
borderRadius: BorderRadius.circular(8),
|
||||||
children: [
|
),
|
||||||
CachedNetworkImage(
|
child: Stack(
|
||||||
width: double.infinity,
|
children: [
|
||||||
imageUrl: urlAttachment.imageUrl!,
|
CachedNetworkImage(
|
||||||
fit: BoxFit.cover,
|
width: double.infinity,
|
||||||
),
|
imageUrl: urlAttachment.imageUrl!,
|
||||||
Positioned(
|
fit: BoxFit.cover,
|
||||||
left: 0,
|
),
|
||||||
bottom: -1,
|
Positioned(
|
||||||
child: Container(
|
left: 0,
|
||||||
decoration: BoxDecoration(
|
bottom: -1,
|
||||||
borderRadius: const BorderRadius.only(
|
child: Container(
|
||||||
topRight: Radius.circular(16),
|
decoration: BoxDecoration(
|
||||||
),
|
borderRadius: const BorderRadius.only(
|
||||||
color:
|
topRight: Radius.circular(16),
|
||||||
StreamChatTheme.of(context).colorTheme.blueAlice,
|
|
||||||
),
|
),
|
||||||
child: Padding(
|
color: chatThemeData.colorTheme.blueAlice,
|
||||||
padding: const EdgeInsets.only(
|
),
|
||||||
top: 8,
|
child: Padding(
|
||||||
left: 8,
|
padding: const EdgeInsets.only(
|
||||||
right: 8,
|
top: 8,
|
||||||
),
|
left: 8,
|
||||||
child: Text(
|
right: 8,
|
||||||
hostDisplayName,
|
),
|
||||||
style: StreamChatTheme.of(context)
|
child: Text(
|
||||||
.textTheme
|
hostDisplayName,
|
||||||
.bodyBold
|
style: chatThemeData.textTheme.bodyBold.copyWith(
|
||||||
.copyWith(
|
color: chatThemeData.colorTheme.accentBlue,
|
||||||
color: StreamChatTheme.of(context)
|
|
||||||
.colorTheme
|
|
||||||
.accentBlue,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: textPadding,
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
if (urlAttachment.title != null)
|
|
||||||
Text(
|
|
||||||
urlAttachment.title!.trim(),
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
style: StreamChatTheme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.body
|
|
||||||
.copyWith(fontWeight: FontWeight.w700),
|
|
||||||
),
|
|
||||||
if (urlAttachment.text != null)
|
|
||||||
Text(
|
|
||||||
urlAttachment.text!,
|
|
||||||
style: StreamChatTheme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.body
|
|
||||||
.copyWith(fontWeight: FontWeight.w400),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
Padding(
|
||||||
),
|
padding: textPadding,
|
||||||
);
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
if (urlAttachment.title != null)
|
||||||
|
Text(
|
||||||
|
urlAttachment.title!.trim(),
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: chatThemeData.textTheme.body
|
||||||
|
.copyWith(fontWeight: FontWeight.w700),
|
||||||
|
),
|
||||||
|
if (urlAttachment.text != null)
|
||||||
|
Text(
|
||||||
|
urlAttachment.text!,
|
||||||
|
style: chatThemeData.textTheme.body
|
||||||
|
.copyWith(fontWeight: FontWeight.w400),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
|
|
||||||
/// Widget that displays a user avatar
|
/// Widget that displays a user avatar
|
||||||
class UserAvatar extends StatelessWidget {
|
class UserAvatar extends StatelessWidget {
|
||||||
@@ -95,8 +95,7 @@ class UserAvatar extends StatelessWidget {
|
|||||||
child: Container(
|
child: Container(
|
||||||
constraints: constraints ??
|
constraints: constraints ??
|
||||||
streamChatTheme.ownMessageTheme.avatarTheme?.constraints,
|
streamChatTheme.ownMessageTheme.avatarTheme?.constraints,
|
||||||
color: selectionColor ??
|
color: selectionColor ?? streamChatTheme.colorTheme.accentBlue,
|
||||||
StreamChatTheme.of(context).colorTheme.accentBlue,
|
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.all(selectionThickness),
|
padding: EdgeInsets.all(selectionThickness),
|
||||||
child: avatar,
|
child: avatar,
|
||||||
|
|||||||
@@ -48,47 +48,52 @@ class UserItem extends StatelessWidget {
|
|||||||
final bool showLastOnline;
|
final bool showLastOnline;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => ListTile(
|
Widget build(BuildContext context) {
|
||||||
onTap: () {
|
final chatThemeData = StreamChatTheme.of(context);
|
||||||
if (onTap != null) {
|
return ListTile(
|
||||||
onTap!(user);
|
onTap: () {
|
||||||
|
if (onTap != null) {
|
||||||
|
onTap!(user);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onLongPress: () {
|
||||||
|
if (onLongPress != null) {
|
||||||
|
onLongPress!(user);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
leading: UserAvatar(
|
||||||
|
user: user,
|
||||||
|
onTap: (user) {
|
||||||
|
if (onImageTap != null) {
|
||||||
|
onImageTap!(user);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLongPress: () {
|
constraints: const BoxConstraints.tightFor(
|
||||||
if (onLongPress != null) {
|
height: 40,
|
||||||
onLongPress!(user);
|
width: 40,
|
||||||
}
|
|
||||||
},
|
|
||||||
leading: UserAvatar(
|
|
||||||
user: user,
|
|
||||||
onTap: (user) {
|
|
||||||
if (onImageTap != null) {
|
|
||||||
onImageTap!(user);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
constraints: const BoxConstraints.tightFor(
|
|
||||||
height: 40,
|
|
||||||
width: 40,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
trailing: selected
|
),
|
||||||
? StreamSvgIcon.checkSend(
|
trailing: selected
|
||||||
color: StreamChatTheme.of(context).colorTheme.accentBlue,
|
? StreamSvgIcon.checkSend(
|
||||||
)
|
color: chatThemeData.colorTheme.accentBlue,
|
||||||
: null,
|
)
|
||||||
title: Text(
|
: null,
|
||||||
user.name,
|
title: Text(
|
||||||
style: StreamChatTheme.of(context).textTheme.bodyBold,
|
user.name,
|
||||||
),
|
style: chatThemeData.textTheme.bodyBold,
|
||||||
subtitle: showLastOnline ? _buildLastActive(context) : null,
|
),
|
||||||
);
|
subtitle: showLastOnline ? _buildLastActive(context) : null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildLastActive(context) => Text(
|
Widget _buildLastActive(context) {
|
||||||
user.online == true
|
final chatTheme = StreamChatTheme.of(context);
|
||||||
? 'Online'
|
return Text(
|
||||||
: 'Last online ${Jiffy(user.lastActive).fromNow()}',
|
user.online == true
|
||||||
style: StreamChatTheme.of(context).textTheme.footnote.copyWith(
|
? 'Online'
|
||||||
color:
|
: 'Last online ${Jiffy(user.lastActive).fromNow()}',
|
||||||
StreamChatTheme.of(context).colorTheme.black.withOpacity(.5)),
|
style: chatTheme.textTheme.footnote
|
||||||
);
|
.copyWith(color: chatTheme.colorTheme.black.withOpacity(.5)),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -295,21 +295,24 @@ class _UserListViewState extends State<UserListView>
|
|||||||
if (i < items.length) {
|
if (i < items.length) {
|
||||||
final item = items[i];
|
final item = items[i];
|
||||||
return item.when(
|
return item.when(
|
||||||
headerItem: (header) => Container(
|
headerItem: (header) {
|
||||||
key: ValueKey<String>('HEADER-$header'),
|
final chatThemeData = StreamChatTheme.of(context);
|
||||||
color: StreamChatTheme.of(context).colorTheme.black.withOpacity(0.05),
|
return Container(
|
||||||
child: Padding(
|
key: ValueKey<String>('HEADER-$header'),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
|
color: chatThemeData.colorTheme.black.withOpacity(0.05),
|
||||||
child: Text(
|
child: Padding(
|
||||||
header,
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
|
||||||
style: TextStyle(
|
child: Text(
|
||||||
fontWeight: FontWeight.bold,
|
header,
|
||||||
fontSize: 14.5,
|
style: TextStyle(
|
||||||
color: StreamChatTheme.of(context).colorTheme.grey,
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 14.5,
|
||||||
|
color: chatThemeData.colorTheme.grey,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
),
|
},
|
||||||
userItem: (user) {
|
userItem: (user) {
|
||||||
final selected = widget.selectedUsers?.contains(user) ?? false;
|
final selected = widget.selectedUsers?.contains(user) ?? false;
|
||||||
return Container(
|
return Container(
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
|
||||||
|
|
||||||
/// Launch URL
|
/// Launch URL
|
||||||
Future<void> launchURL(BuildContext context, String? url) async {
|
Future<void> launchURL(BuildContext context, String? url) async {
|
||||||
@@ -27,89 +27,81 @@ Future<bool?> showConfirmationDialog(
|
|||||||
Widget? icon,
|
Widget? icon,
|
||||||
String? question,
|
String? question,
|
||||||
String? cancelText,
|
String? cancelText,
|
||||||
}) =>
|
}) {
|
||||||
showModalBottomSheet(
|
final chatThemeData = StreamChatTheme.of(context);
|
||||||
backgroundColor: StreamChatTheme.of(context).colorTheme.white,
|
return showModalBottomSheet(
|
||||||
context: context,
|
backgroundColor: chatThemeData.colorTheme.white,
|
||||||
shape: const RoundedRectangleBorder(
|
context: context,
|
||||||
borderRadius: BorderRadius.only(
|
shape: const RoundedRectangleBorder(
|
||||||
topLeft: Radius.circular(16),
|
borderRadius: BorderRadius.only(
|
||||||
topRight: Radius.circular(16),
|
topLeft: Radius.circular(16),
|
||||||
)),
|
topRight: Radius.circular(16),
|
||||||
builder: (context) {
|
)),
|
||||||
final effect = StreamChatTheme.of(context).colorTheme.borderTop;
|
builder: (context) {
|
||||||
return SafeArea(
|
final effect = chatThemeData.colorTheme.borderTop;
|
||||||
child: Column(
|
return SafeArea(
|
||||||
mainAxisSize: MainAxisSize.min,
|
child: Column(
|
||||||
children: [
|
mainAxisSize: MainAxisSize.min,
|
||||||
const SizedBox(height: 26),
|
children: [
|
||||||
if (icon != null) icon,
|
const SizedBox(height: 26),
|
||||||
const SizedBox(height: 26),
|
if (icon != null) icon,
|
||||||
|
const SizedBox(height: 26),
|
||||||
|
Text(
|
||||||
|
title,
|
||||||
|
style: chatThemeData.textTheme.headlineBold,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 7),
|
||||||
|
if (question != null)
|
||||||
Text(
|
Text(
|
||||||
title,
|
question,
|
||||||
style: StreamChatTheme.of(context).textTheme.headlineBold,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 7),
|
const SizedBox(height: 36),
|
||||||
if (question != null)
|
Container(
|
||||||
Text(
|
color: effect.color!.withOpacity(effect.alpha ?? 1),
|
||||||
question,
|
height: 1,
|
||||||
textAlign: TextAlign.center,
|
),
|
||||||
),
|
Row(
|
||||||
const SizedBox(height: 36),
|
children: [
|
||||||
Container(
|
if (cancelText != null)
|
||||||
color: effect.color!.withOpacity(effect.alpha ?? 1),
|
|
||||||
height: 1,
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
if (cancelText != null)
|
|
||||||
Flexible(
|
|
||||||
child: Container(
|
|
||||||
alignment: Alignment.center,
|
|
||||||
child: TextButton(
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context).pop(false);
|
|
||||||
},
|
|
||||||
child: Text(
|
|
||||||
cancelText,
|
|
||||||
style: StreamChatTheme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.bodyBold
|
|
||||||
.copyWith(
|
|
||||||
color: StreamChatTheme.of(context)
|
|
||||||
.colorTheme
|
|
||||||
.black
|
|
||||||
.withOpacity(0.5)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Flexible(
|
Flexible(
|
||||||
child: Container(
|
child: Container(
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: TextButton(
|
child: TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.pop(context, true);
|
Navigator.of(context).pop(false);
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
okText,
|
cancelText,
|
||||||
style: StreamChatTheme.of(context)
|
style: chatThemeData.textTheme.bodyBold.copyWith(
|
||||||
.textTheme
|
color: chatThemeData.colorTheme.black
|
||||||
.bodyBold
|
.withOpacity(0.5)),
|
||||||
.copyWith(
|
|
||||||
color: StreamChatTheme.of(context)
|
|
||||||
.colorTheme
|
|
||||||
.accentRed),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
Flexible(
|
||||||
),
|
child: Container(
|
||||||
],
|
alignment: Alignment.center,
|
||||||
),
|
child: TextButton(
|
||||||
);
|
onPressed: () {
|
||||||
});
|
Navigator.pop(context, true);
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
okText,
|
||||||
|
style: chatThemeData.textTheme.bodyBold.copyWith(
|
||||||
|
color: chatThemeData.colorTheme.accentRed),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// Shows info dialog
|
/// Shows info dialog
|
||||||
Future<bool?> showInfoDialog(
|
Future<bool?> showInfoDialog(
|
||||||
@@ -119,63 +111,64 @@ Future<bool?> showInfoDialog(
|
|||||||
Widget? icon,
|
Widget? icon,
|
||||||
String? details,
|
String? details,
|
||||||
StreamChatThemeData? theme,
|
StreamChatThemeData? theme,
|
||||||
}) =>
|
}) {
|
||||||
showModalBottomSheet(
|
final chatThemeData = StreamChatTheme.of(context);
|
||||||
backgroundColor: theme?.colorTheme.white ??
|
return showModalBottomSheet(
|
||||||
StreamChatTheme.of(context).colorTheme.white,
|
backgroundColor: theme?.colorTheme.white ?? chatThemeData.colorTheme.white,
|
||||||
context: context,
|
context: context,
|
||||||
shape: const RoundedRectangleBorder(
|
shape: const RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.only(
|
borderRadius: BorderRadius.only(
|
||||||
topLeft: Radius.circular(16),
|
topLeft: Radius.circular(16),
|
||||||
topRight: Radius.circular(16),
|
topRight: Radius.circular(16),
|
||||||
)),
|
)),
|
||||||
builder: (context) => SafeArea(
|
builder: (context) => SafeArea(
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 26,
|
height: 26,
|
||||||
),
|
),
|
||||||
if (icon != null) icon,
|
if (icon != null) icon,
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 26,
|
height: 26,
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
title,
|
title,
|
||||||
style: theme?.textTheme.headlineBold ??
|
style: theme?.textTheme.headlineBold ??
|
||||||
StreamChatTheme.of(context).textTheme.headlineBold,
|
chatThemeData.textTheme.headlineBold,
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 7,
|
height: 7,
|
||||||
),
|
),
|
||||||
if (details != null) Text(details),
|
if (details != null) Text(details),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 36,
|
height: 36,
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
color: theme?.colorTheme.black.withOpacity(.08) ??
|
color: theme?.colorTheme.black.withOpacity(.08) ??
|
||||||
StreamChatTheme.of(context).colorTheme.black.withOpacity(.08),
|
chatThemeData.colorTheme.black.withOpacity(.08),
|
||||||
height: 1,
|
height: 1,
|
||||||
),
|
),
|
||||||
Center(
|
Center(
|
||||||
child: TextButton(
|
child: TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
okText,
|
okText,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: theme?.colorTheme.black.withOpacity(0.5) ??
|
color: theme?.colorTheme.black.withOpacity(0.5) ??
|
||||||
StreamChatTheme.of(context).colorTheme.accentBlue,
|
chatThemeData.colorTheme.accentBlue,
|
||||||
fontWeight: FontWeight.w400,
|
fontWeight: FontWeight.w400,
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// Get random png with initials
|
/// Get random png with initials
|
||||||
String getRandomPicUrl(User user) =>
|
String getRandomPicUrl(User user) =>
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ import 'dart:typed_data';
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:shimmer/shimmer.dart';
|
import 'package:shimmer/shimmer.dart';
|
||||||
|
import 'package:stream_chat_flutter/src/video_service.dart';
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
import 'package:video_thumbnail/video_thumbnail.dart';
|
import 'package:video_thumbnail/video_thumbnail.dart';
|
||||||
import 'package:stream_chat_flutter/src/video_service.dart';
|
|
||||||
|
|
||||||
/// Widget for creating video thumbnail image
|
/// Widget for creating video thumbnail image
|
||||||
class VideoThumbnailImage extends StatefulWidget {
|
class VideoThumbnailImage extends StatefulWidget {
|
||||||
@@ -47,6 +47,7 @@ class VideoThumbnailImage extends StatefulWidget {
|
|||||||
|
|
||||||
class _VideoThumbnailImageState extends State<VideoThumbnailImage> {
|
class _VideoThumbnailImageState extends State<VideoThumbnailImage> {
|
||||||
late Future<Uint8List?> thumbnailFuture;
|
late Future<Uint8List?> thumbnailFuture;
|
||||||
|
late StreamChatThemeData _streamChatTheme;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -57,6 +58,12 @@ class _VideoThumbnailImageState extends State<VideoThumbnailImage> {
|
|||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didChangeDependencies() {
|
||||||
|
_streamChatTheme = StreamChatTheme.of(context);
|
||||||
|
super.didChangeDependencies();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didUpdateWidget(covariant VideoThumbnailImage oldWidget) {
|
void didUpdateWidget(covariant VideoThumbnailImage oldWidget) {
|
||||||
if (oldWidget.video != widget.video || oldWidget.format != widget.format) {
|
if (oldWidget.video != widget.video || oldWidget.format != widget.format) {
|
||||||
@@ -87,11 +94,8 @@ class _VideoThumbnailImageState extends State<VideoThumbnailImage> {
|
|||||||
constraints: const BoxConstraints.expand(),
|
constraints: const BoxConstraints.expand(),
|
||||||
child: widget.placeholderBuilder?.call(context) ??
|
child: widget.placeholderBuilder?.call(context) ??
|
||||||
Shimmer.fromColors(
|
Shimmer.fromColors(
|
||||||
baseColor: StreamChatTheme.of(context)
|
baseColor: _streamChatTheme.colorTheme.greyGainsboro,
|
||||||
.colorTheme
|
highlightColor: _streamChatTheme.colorTheme.whiteSmoke,
|
||||||
.greyGainsboro,
|
|
||||||
highlightColor:
|
|
||||||
StreamChatTheme.of(context).colorTheme.whiteSmoke,
|
|
||||||
child: Image.asset(
|
child: Image.asset(
|
||||||
'images/placeholder.png',
|
'images/placeholder.png',
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
|
|||||||
@@ -119,12 +119,11 @@ class ChannelListCore extends StatefulWidget {
|
|||||||
|
|
||||||
/// The current state of the [ChannelListCore].
|
/// The current state of the [ChannelListCore].
|
||||||
class ChannelListCoreState extends State<ChannelListCore> {
|
class ChannelListCoreState extends State<ChannelListCore> {
|
||||||
@override
|
late ChannelsBlocState _channelsBloc;
|
||||||
Widget build(BuildContext context) {
|
StreamChatCoreState? _streamChatCoreState;
|
||||||
final channelsBloc = ChannelsBloc.of(context);
|
|
||||||
|
|
||||||
return _buildListView(channelsBloc);
|
@override
|
||||||
}
|
Widget build(BuildContext context) => _buildListView(_channelsBloc);
|
||||||
|
|
||||||
StreamBuilder<List<Channel>> _buildListView(
|
StreamBuilder<List<Channel>> _buildListView(
|
||||||
ChannelsBlocState channelsBlocState,
|
ChannelsBlocState channelsBlocState,
|
||||||
@@ -147,49 +146,52 @@ class ChannelListCoreState extends State<ChannelListCore> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
/// Fetches initial channels and updates the widget
|
/// Fetches initial channels and updates the widget
|
||||||
Future<void> loadData() {
|
Future<void> loadData() => _channelsBloc.queryChannels(
|
||||||
final channelsBloc = ChannelsBloc.of(context);
|
filter: widget.filter,
|
||||||
return channelsBloc.queryChannels(
|
sortOptions: widget.sort,
|
||||||
filter: widget.filter,
|
paginationParams: widget.pagination,
|
||||||
sortOptions: widget.sort,
|
options: widget.options,
|
||||||
paginationParams: widget.pagination,
|
);
|
||||||
options: widget.options,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fetches more channels with updated pagination and updates the widget
|
/// Fetches more channels with updated pagination and updates the widget
|
||||||
Future<void> paginateData() {
|
Future<void> paginateData() => _channelsBloc.queryChannels(
|
||||||
final channelsBloc = ChannelsBloc.of(context);
|
filter: widget.filter,
|
||||||
return channelsBloc.queryChannels(
|
sortOptions: widget.sort,
|
||||||
filter: widget.filter,
|
paginationParams: widget.pagination.copyWith(
|
||||||
sortOptions: widget.sort,
|
offset: _channelsBloc.channels?.length ?? 0,
|
||||||
paginationParams: widget.pagination.copyWith(
|
),
|
||||||
offset: channelsBloc.channels?.length ?? 0,
|
options: widget.options,
|
||||||
),
|
);
|
||||||
options: widget.options,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
late StreamSubscription<Event> _subscription;
|
StreamSubscription<Event>? _subscription;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
loadData();
|
_setupController();
|
||||||
final client = StreamChatCore.of(context).client;
|
}
|
||||||
_subscription = client
|
|
||||||
.on(
|
|
||||||
EventType.connectionRecovered,
|
|
||||||
EventType.notificationAddedToChannel,
|
|
||||||
EventType.notificationMessageNew,
|
|
||||||
EventType.channelVisible,
|
|
||||||
)
|
|
||||||
.listen((event) => loadData());
|
|
||||||
|
|
||||||
if (widget.channelListController != null) {
|
@override
|
||||||
widget.channelListController!.loadData = loadData;
|
void didChangeDependencies() {
|
||||||
widget.channelListController!.paginateData = paginateData;
|
_channelsBloc = ChannelsBloc.of(context);
|
||||||
|
final newStreamChatCoreState = StreamChatCore.of(context);
|
||||||
|
|
||||||
|
if (newStreamChatCoreState != _streamChatCoreState) {
|
||||||
|
_streamChatCoreState = newStreamChatCoreState;
|
||||||
|
loadData();
|
||||||
|
final client = _streamChatCoreState!.client;
|
||||||
|
_subscription?.cancel();
|
||||||
|
_subscription = client
|
||||||
|
.on(
|
||||||
|
EventType.connectionRecovered,
|
||||||
|
EventType.notificationAddedToChannel,
|
||||||
|
EventType.notificationMessageNew,
|
||||||
|
EventType.channelVisible,
|
||||||
|
)
|
||||||
|
.listen((event) => loadData());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
super.didChangeDependencies();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -203,11 +205,22 @@ class ChannelListCoreState extends State<ChannelListCore> {
|
|||||||
oldWidget.pagination.toJson().toString()) {
|
oldWidget.pagination.toJson().toString()) {
|
||||||
loadData();
|
loadData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (widget.channelListController != oldWidget.channelListController) {
|
||||||
|
_setupController();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _setupController() {
|
||||||
|
if (widget.channelListController != null) {
|
||||||
|
widget.channelListController!.loadData = loadData;
|
||||||
|
widget.channelListController!.paginateData = paginateData;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_subscription.cancel();
|
_subscription?.cancel();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,17 +50,20 @@ class ChannelsBloc extends StatefulWidget {
|
|||||||
|
|
||||||
streamChatState = context.findAncestorStateOfType<ChannelsBlocState>();
|
streamChatState = context.findAncestorStateOfType<ChannelsBlocState>();
|
||||||
|
|
||||||
if (streamChatState == null) {
|
assert(
|
||||||
throw Exception('You must have a ChannelsBloc widget as ancestor');
|
streamChatState != null,
|
||||||
}
|
'You must have a ChannelsBloc widget as ancestor',
|
||||||
|
);
|
||||||
|
|
||||||
return streamChatState;
|
return streamChatState!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The current state of the [ChannelsBloc].
|
/// The current state of the [ChannelsBloc].
|
||||||
class ChannelsBlocState extends State<ChannelsBloc>
|
class ChannelsBlocState extends State<ChannelsBloc>
|
||||||
with AutomaticKeepAliveClientMixin {
|
with AutomaticKeepAliveClientMixin {
|
||||||
|
StreamChatCoreState? _streamChatCoreState;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
super.build(context);
|
super.build(context);
|
||||||
@@ -86,6 +89,8 @@ class ChannelsBlocState extends State<ChannelsBloc>
|
|||||||
|
|
||||||
bool _paginationEnded = false;
|
bool _paginationEnded = false;
|
||||||
|
|
||||||
|
final List<StreamSubscription> _subscriptions = [];
|
||||||
|
|
||||||
/// Calls [client.queryChannels] updating [queryChannelsLoading] stream
|
/// Calls [client.queryChannels] updating [queryChannelsLoading] stream
|
||||||
Future<void> queryChannels({
|
Future<void> queryChannels({
|
||||||
Filter? filter,
|
Filter? filter,
|
||||||
@@ -93,7 +98,7 @@ class ChannelsBlocState extends State<ChannelsBloc>
|
|||||||
PaginationParams paginationParams = const PaginationParams(limit: 30),
|
PaginationParams paginationParams = const PaginationParams(limit: 30),
|
||||||
Map<String, dynamic>? options,
|
Map<String, dynamic>? options,
|
||||||
}) async {
|
}) async {
|
||||||
final client = StreamChatCore.of(context).client;
|
final client = _streamChatCoreState!.client;
|
||||||
|
|
||||||
final clear = paginationParams.offset == 0;
|
final clear = paginationParams.offset == 0;
|
||||||
|
|
||||||
@@ -139,77 +144,88 @@ class ChannelsBlocState extends State<ChannelsBloc>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<StreamSubscription> _subscriptions = [];
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void didChangeDependencies() {
|
||||||
super.initState();
|
final newStreamChatCoreState = StreamChatCore.of(context);
|
||||||
|
|
||||||
final client = StreamChatCore.of(context).client;
|
if (newStreamChatCoreState != _streamChatCoreState) {
|
||||||
|
_streamChatCoreState = newStreamChatCoreState;
|
||||||
|
final client = _streamChatCoreState!.client;
|
||||||
|
|
||||||
if (!widget.lockChannelsOrder) {
|
_cancelSubscriptions();
|
||||||
_subscriptions.add(client
|
if (!widget.lockChannelsOrder) {
|
||||||
.on(
|
_subscriptions.add(client
|
||||||
EventType.messageNew,
|
.on(
|
||||||
)
|
EventType.messageNew,
|
||||||
.listen((e) {
|
)
|
||||||
final newChannels = List<Channel>.from(channels ?? []);
|
.listen((e) {
|
||||||
final index = newChannels.indexWhere((c) => c.cid == e.cid);
|
final newChannels = List<Channel>.from(channels ?? []);
|
||||||
if (index != -1) {
|
final index = newChannels.indexWhere((c) => c.cid == e.cid);
|
||||||
if (index > 0) {
|
if (index != -1) {
|
||||||
final channel = newChannels.removeAt(index);
|
if (index > 0) {
|
||||||
newChannels.insert(0, channel);
|
final channel = newChannels.removeAt(index);
|
||||||
}
|
newChannels.insert(0, channel);
|
||||||
} else if (widget.shouldAddChannel?.call(e) == true) {
|
}
|
||||||
final hiddenIndex = _hiddenChannels.indexWhere((c) => c.cid == e.cid);
|
} else if (widget.shouldAddChannel?.call(e) == true) {
|
||||||
if (hiddenIndex != -1) {
|
final hiddenIndex =
|
||||||
newChannels.insert(0, _hiddenChannels[hiddenIndex]);
|
_hiddenChannels.indexWhere((c) => c.cid == e.cid);
|
||||||
_hiddenChannels.removeAt(hiddenIndex);
|
if (hiddenIndex != -1) {
|
||||||
} else {
|
newChannels.insert(0, _hiddenChannels[hiddenIndex]);
|
||||||
if (client.state.channels[e.cid] != null) {
|
_hiddenChannels.removeAt(hiddenIndex);
|
||||||
newChannels.insert(0, client.state.channels[e.cid]!);
|
} else {
|
||||||
|
if (client.state.channels[e.cid] != null) {
|
||||||
|
newChannels.insert(0, client.state.channels[e.cid]!);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (widget.channelsComparator != null) {
|
if (widget.channelsComparator != null) {
|
||||||
newChannels.sort(widget.channelsComparator);
|
newChannels.sort(widget.channelsComparator);
|
||||||
}
|
}
|
||||||
_channelsController.add(newChannels);
|
_channelsController.add(newChannels);
|
||||||
}));
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
_subscriptions
|
||||||
|
..add(client.on(EventType.channelHidden).listen((event) async {
|
||||||
|
final newChannels = List<Channel>.from(channels ?? []);
|
||||||
|
final channelIndex =
|
||||||
|
newChannels.indexWhere((c) => c.cid == event.cid);
|
||||||
|
if (channelIndex > -1) {
|
||||||
|
final channel = newChannels.removeAt(channelIndex);
|
||||||
|
_hiddenChannels.add(channel);
|
||||||
|
_channelsController.add(newChannels);
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
..add(client
|
||||||
|
.on(
|
||||||
|
EventType.channelDeleted,
|
||||||
|
EventType.notificationRemovedFromChannel,
|
||||||
|
)
|
||||||
|
.listen((e) {
|
||||||
|
final channel = e.channel;
|
||||||
|
_channelsController.add(List.from(
|
||||||
|
(channels ?? [])..removeWhere((c) => c.cid == channel?.cid)));
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
_subscriptions.add(client.on(EventType.channelHidden).listen((event) async {
|
super.didChangeDependencies();
|
||||||
final newChannels = List<Channel>.from(channels ?? []);
|
|
||||||
final channelIndex = newChannels.indexWhere((c) => c.cid == event.cid);
|
|
||||||
if (channelIndex > -1) {
|
|
||||||
final channel = newChannels.removeAt(channelIndex);
|
|
||||||
_hiddenChannels.add(channel);
|
|
||||||
_channelsController.add(newChannels);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
// ignore: cascade_invocations
|
|
||||||
_subscriptions.add(client
|
|
||||||
.on(
|
|
||||||
EventType.channelDeleted,
|
|
||||||
EventType.notificationRemovedFromChannel,
|
|
||||||
)
|
|
||||||
.listen((e) {
|
|
||||||
// ignore: cascade_invocations
|
|
||||||
final channel = e.channel;
|
|
||||||
_channelsController.add(List.from(
|
|
||||||
(channels ?? [])..removeWhere((c) => c.cid == channel?.cid)));
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_channelsController.close();
|
_channelsController.close();
|
||||||
_queryChannelsLoadingController.close();
|
_queryChannelsLoadingController.close();
|
||||||
_subscriptions.forEach((s) => s.cancel());
|
_cancelSubscriptions();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _cancelSubscriptions() {
|
||||||
|
_subscriptions
|
||||||
|
..forEach((s) => s.cancel())
|
||||||
|
..clear();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool get wantKeepAlive => true;
|
bool get wantKeepAlive => true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,23 +109,23 @@ class MessageListCore extends StatefulWidget {
|
|||||||
|
|
||||||
/// The current state of the [MessageListCore].
|
/// The current state of the [MessageListCore].
|
||||||
class MessageListCoreState extends State<MessageListCore> {
|
class MessageListCoreState extends State<MessageListCore> {
|
||||||
late StreamChannelState _streamChannel;
|
StreamChannelState? _streamChannel;
|
||||||
|
|
||||||
bool get _upToDate => _streamChannel.channel.state?.isUpToDate ?? true;
|
bool get _upToDate => _streamChannel!.channel.state?.isUpToDate ?? true;
|
||||||
|
|
||||||
bool get _isThreadConversation => widget.parentMessage != null;
|
bool get _isThreadConversation => widget.parentMessage != null;
|
||||||
|
|
||||||
OwnUser? get _currentUser => _streamChannel.channel.client.state.user;
|
OwnUser? get _currentUser => _streamChannel!.channel.client.state.user;
|
||||||
|
|
||||||
var _messages = <Message>[];
|
var _messages = <Message>[];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final messagesStream = _isThreadConversation
|
final messagesStream = _isThreadConversation
|
||||||
? _streamChannel.channel.state?.threadsStream
|
? _streamChannel!.channel.state?.threadsStream
|
||||||
.where((threads) => threads.containsKey(widget.parentMessage!.id))
|
.where((threads) => threads.containsKey(widget.parentMessage!.id))
|
||||||
.map((threads) => threads[widget.parentMessage!.id])
|
.map((threads) => threads[widget.parentMessage!.id])
|
||||||
: _streamChannel.channel.state?.messagesStream;
|
: _streamChannel!.channel.state?.messagesStream;
|
||||||
|
|
||||||
bool defaultFilter(Message m) {
|
bool defaultFilter(Message m) {
|
||||||
final isMyMessage = m.user?.id == _currentUser?.id;
|
final isMyMessage = m.user?.id == _currentUser?.id;
|
||||||
@@ -167,31 +167,58 @@ class MessageListCoreState extends State<MessageListCore> {
|
|||||||
QueryDirection direction = QueryDirection.top,
|
QueryDirection direction = QueryDirection.top,
|
||||||
}) {
|
}) {
|
||||||
if (!_isThreadConversation) {
|
if (!_isThreadConversation) {
|
||||||
return _streamChannel.queryMessages(direction: direction);
|
return _streamChannel!.queryMessages(direction: direction);
|
||||||
} else {
|
} else {
|
||||||
return _streamChannel.getReplies(widget.parentMessage!.id);
|
return _streamChannel!.getReplies(widget.parentMessage!.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didChangeDependencies() {
|
||||||
|
final newStreamChannel = StreamChannel.of(context);
|
||||||
|
|
||||||
|
if (newStreamChannel != _streamChannel) {
|
||||||
|
if (_streamChannel == null /*only first time*/ && _isThreadConversation) {
|
||||||
|
newStreamChannel.getReplies(widget.parentMessage!.id);
|
||||||
|
}
|
||||||
|
_streamChannel = newStreamChannel;
|
||||||
|
}
|
||||||
|
|
||||||
|
super.didChangeDependencies();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(covariant MessageListCore oldWidget) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
|
||||||
|
if (widget.messageListController != oldWidget.messageListController) {
|
||||||
|
_setupController();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (widget.parentMessage?.id != widget.parentMessage?.id) {
|
||||||
|
if (_isThreadConversation) {
|
||||||
|
_streamChannel!.getReplies(widget.parentMessage!.id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
_streamChannel = StreamChannel.of(context);
|
_setupController();
|
||||||
|
|
||||||
if (_isThreadConversation) {
|
super.initState();
|
||||||
_streamChannel.getReplies(widget.parentMessage!.id);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
void _setupController() {
|
||||||
if (widget.messageListController != null) {
|
if (widget.messageListController != null) {
|
||||||
widget.messageListController!.paginateData = paginateData;
|
widget.messageListController!.paginateData = paginateData;
|
||||||
}
|
}
|
||||||
|
|
||||||
super.initState();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
if (!_upToDate) {
|
if (!_upToDate) {
|
||||||
_streamChannel.reloadChannel();
|
_streamChannel!.reloadChannel();
|
||||||
}
|
}
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,17 +29,20 @@ class MessageSearchBloc extends StatefulWidget {
|
|||||||
|
|
||||||
state = context.findAncestorStateOfType<MessageSearchBlocState>();
|
state = context.findAncestorStateOfType<MessageSearchBlocState>();
|
||||||
|
|
||||||
if (state == null) {
|
assert(
|
||||||
throw Exception('You must have a MessageSearchBloc widget as ancestor');
|
state != null,
|
||||||
}
|
'You must have a MessageSearchBloc widget as ancestor',
|
||||||
|
);
|
||||||
|
|
||||||
return state;
|
return state!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The current state of the [MessageSearchBloc]
|
/// The current state of the [MessageSearchBloc]
|
||||||
class MessageSearchBlocState extends State<MessageSearchBloc>
|
class MessageSearchBlocState extends State<MessageSearchBloc>
|
||||||
with AutomaticKeepAliveClientMixin {
|
with AutomaticKeepAliveClientMixin {
|
||||||
|
late StreamChatCoreState _streamChatCoreState;
|
||||||
|
|
||||||
/// The current messages list
|
/// The current messages list
|
||||||
List<GetMessageResponse>? get messageResponses => _messageResponses.value;
|
List<GetMessageResponse>? get messageResponses => _messageResponses.value;
|
||||||
|
|
||||||
@@ -64,7 +67,7 @@ class MessageSearchBlocState extends State<MessageSearchBloc>
|
|||||||
String? query,
|
String? query,
|
||||||
PaginationParams? pagination,
|
PaginationParams? pagination,
|
||||||
}) async {
|
}) async {
|
||||||
final client = StreamChatCore.of(context).client;
|
final client = _streamChatCoreState.client;
|
||||||
|
|
||||||
if (_queryMessagesLoadingController.value == true) return;
|
if (_queryMessagesLoadingController.value == true) return;
|
||||||
|
|
||||||
@@ -109,6 +112,12 @@ class MessageSearchBlocState extends State<MessageSearchBloc>
|
|||||||
return widget.child;
|
return widget.child;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didChangeDependencies() {
|
||||||
|
_streamChatCoreState = StreamChatCore.of(context);
|
||||||
|
super.didChangeDependencies();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_messageResponses.close();
|
_messageResponses.close();
|
||||||
|
|||||||
@@ -105,21 +105,26 @@ class MessageSearchListCore extends StatefulWidget {
|
|||||||
|
|
||||||
/// The current state of the [MessageSearchListCore].
|
/// The current state of the [MessageSearchListCore].
|
||||||
class MessageSearchListCoreState extends State<MessageSearchListCore> {
|
class MessageSearchListCoreState extends State<MessageSearchListCore> {
|
||||||
|
MessageSearchBlocState? _messageSearchBloc;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didChangeDependencies() {
|
void didChangeDependencies() {
|
||||||
super.didChangeDependencies();
|
final newMessageSearchBloc = MessageSearchBloc.of(context);
|
||||||
loadData();
|
|
||||||
if (widget.messageSearchListController != null) {
|
if (newMessageSearchBloc != _messageSearchBloc) {
|
||||||
widget.messageSearchListController!.loadData = loadData;
|
_messageSearchBloc = newMessageSearchBloc;
|
||||||
widget.messageSearchListController!.paginateData = paginateData;
|
loadData();
|
||||||
|
if (widget.messageSearchListController != null) {
|
||||||
|
widget.messageSearchListController!.loadData = loadData;
|
||||||
|
widget.messageSearchListController!.paginateData = paginateData;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
super.didChangeDependencies();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) => _buildListView(_messageSearchBloc!);
|
||||||
final messageSearchBloc = MessageSearchBloc.of(context);
|
|
||||||
return _buildListView(messageSearchBloc);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildListView(MessageSearchBlocState messageSearchBloc) =>
|
Widget _buildListView(MessageSearchBlocState messageSearchBloc) =>
|
||||||
StreamBuilder<List<GetMessageResponse>>(
|
StreamBuilder<List<GetMessageResponse>>(
|
||||||
@@ -140,30 +145,24 @@ class MessageSearchListCoreState extends State<MessageSearchListCore> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
/// Fetches initial messages and updates the widget
|
/// Fetches initial messages and updates the widget
|
||||||
Future<void> loadData() {
|
Future<void> loadData() => _messageSearchBloc!.search(
|
||||||
final messageSearchBloc = MessageSearchBloc.of(context);
|
filter: widget.filters,
|
||||||
return messageSearchBloc.search(
|
sort: widget.sortOptions,
|
||||||
filter: widget.filters,
|
query: widget.messageQuery,
|
||||||
sort: widget.sortOptions,
|
pagination: widget.paginationParams,
|
||||||
query: widget.messageQuery,
|
messageFilter: widget.messageFilters,
|
||||||
pagination: widget.paginationParams,
|
);
|
||||||
messageFilter: widget.messageFilters,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fetches more messages with updated pagination and updates the widget
|
/// Fetches more messages with updated pagination and updates the widget
|
||||||
Future<void> paginateData() {
|
Future<void> paginateData() => _messageSearchBloc!.search(
|
||||||
final messageSearchBloc = MessageSearchBloc.of(context);
|
filter: widget.filters,
|
||||||
return messageSearchBloc.search(
|
sort: widget.sortOptions,
|
||||||
filter: widget.filters,
|
pagination: widget.paginationParams!.copyWith(
|
||||||
sort: widget.sortOptions,
|
offset: _messageSearchBloc!.messageResponses?.length ?? 0,
|
||||||
pagination: widget.paginationParams!.copyWith(
|
),
|
||||||
offset: messageSearchBloc.messageResponses?.length ?? 0,
|
query: widget.messageQuery,
|
||||||
),
|
messageFilter: widget.messageFilters,
|
||||||
query: widget.messageQuery,
|
);
|
||||||
messageFilter: widget.messageFilters,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didUpdateWidget(MessageSearchListCore oldWidget) {
|
void didUpdateWidget(MessageSearchListCore oldWidget) {
|
||||||
|
|||||||
@@ -47,13 +47,12 @@ class StreamChannel extends StatefulWidget {
|
|||||||
|
|
||||||
streamChannelState = context.findAncestorStateOfType<StreamChannelState>();
|
streamChannelState = context.findAncestorStateOfType<StreamChannelState>();
|
||||||
|
|
||||||
if (streamChannelState == null) {
|
assert(
|
||||||
throw Exception(
|
streamChannelState != null,
|
||||||
'You must have a StreamChannel widget at the top of your widget tree',
|
'You must have a StreamChannel widget at the top of your widget tree',
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
return streamChannelState;
|
return streamChannelState!;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -70,12 +70,12 @@ class StreamChatCore extends StatefulWidget {
|
|||||||
|
|
||||||
streamChatState = context.findAncestorStateOfType<StreamChatCoreState>();
|
streamChatState = context.findAncestorStateOfType<StreamChatCoreState>();
|
||||||
|
|
||||||
if (streamChatState == null) {
|
assert(
|
||||||
throw Exception(
|
streamChatState != null,
|
||||||
'You must have a StreamChat widget at the top of your widget tree');
|
'You must have a StreamChat widget at the top of your widget tree',
|
||||||
}
|
);
|
||||||
|
|
||||||
return streamChatState;
|
return streamChatState!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -122,29 +122,29 @@ class UserListCore extends StatefulWidget {
|
|||||||
/// The current state of the [UserListCore].
|
/// The current state of the [UserListCore].
|
||||||
class UserListCoreState extends State<UserListCore>
|
class UserListCoreState extends State<UserListCore>
|
||||||
with WidgetsBindingObserver {
|
with WidgetsBindingObserver {
|
||||||
|
UsersBlocState? _usersBloc;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didChangeDependencies() {
|
void didChangeDependencies() {
|
||||||
super.didChangeDependencies();
|
final newUsersBloc = UsersBloc.of(context);
|
||||||
loadData();
|
if (newUsersBloc != _usersBloc) {
|
||||||
if (widget.userListController != null) {
|
_usersBloc = newUsersBloc;
|
||||||
widget.userListController!.loadData = loadData;
|
loadData();
|
||||||
widget.userListController!.paginateData = paginateData;
|
if (widget.userListController != null) {
|
||||||
|
widget.userListController!.loadData = loadData;
|
||||||
|
widget.userListController!.paginateData = paginateData;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
super.didChangeDependencies();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) => _buildListView();
|
||||||
final _usersBloc = UsersBloc.of(context);
|
|
||||||
return _buildListView(_usersBloc);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get _isListAlreadySorted =>
|
bool get _isListAlreadySorted =>
|
||||||
widget.sort?.any((e) => e.field == 'name' && e.direction == 1) ?? false;
|
widget.sort?.any((e) => e.field == 'name' && e.direction == 1) ?? false;
|
||||||
|
|
||||||
Stream<List<ListItem>> _buildUserStream(
|
Stream<List<ListItem>> _buildUserStream() => _usersBloc!.usersStream.map(
|
||||||
UsersBlocState usersBlocState,
|
|
||||||
) =>
|
|
||||||
usersBlocState.usersStream.map(
|
|
||||||
(users) {
|
(users) {
|
||||||
if (widget.groupAlphabetically) {
|
if (widget.groupAlphabetically) {
|
||||||
var temp = users;
|
var temp = users;
|
||||||
@@ -169,11 +169,8 @@ class UserListCoreState extends State<UserListCore>
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
StreamBuilder<List<ListItem>> _buildListView(
|
StreamBuilder<List<ListItem>> _buildListView() => StreamBuilder(
|
||||||
UsersBlocState usersBlocState,
|
stream: _buildUserStream(),
|
||||||
) =>
|
|
||||||
StreamBuilder(
|
|
||||||
stream: _buildUserStream(usersBlocState),
|
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.hasError) {
|
if (snapshot.hasError) {
|
||||||
return widget.errorBuilder(snapshot.error!);
|
return widget.errorBuilder(snapshot.error!);
|
||||||
@@ -190,28 +187,22 @@ class UserListCoreState extends State<UserListCore>
|
|||||||
);
|
);
|
||||||
|
|
||||||
// ignore: public_member_api_docs
|
// ignore: public_member_api_docs
|
||||||
Future<void> loadData() {
|
Future<void> loadData() => _usersBloc!.queryUsers(
|
||||||
final _usersBloc = UsersBloc.of(context);
|
filter: widget.filter,
|
||||||
return _usersBloc.queryUsers(
|
sort: widget.sort,
|
||||||
filter: widget.filter,
|
pagination: widget.pagination,
|
||||||
sort: widget.sort,
|
options: widget.options,
|
||||||
pagination: widget.pagination,
|
);
|
||||||
options: widget.options,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ignore: public_member_api_docs
|
// ignore: public_member_api_docs
|
||||||
Future<void> paginateData() {
|
Future<void> paginateData() => _usersBloc!.queryUsers(
|
||||||
final _usersBloc = UsersBloc.of(context);
|
filter: widget.filter,
|
||||||
return _usersBloc.queryUsers(
|
sort: widget.sort,
|
||||||
filter: widget.filter,
|
pagination: widget.pagination!.copyWith(
|
||||||
sort: widget.sort,
|
offset: _usersBloc!.users?.length ?? 0,
|
||||||
pagination: widget.pagination!.copyWith(
|
),
|
||||||
offset: _usersBloc.users?.length ?? 0,
|
options: widget.options,
|
||||||
),
|
);
|
||||||
options: widget.options,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didUpdateWidget(UserListCore oldWidget) {
|
void didUpdateWidget(UserListCore oldWidget) {
|
||||||
|
|||||||
@@ -30,11 +30,12 @@ class UsersBloc extends StatefulWidget {
|
|||||||
|
|
||||||
state = context.findAncestorStateOfType<UsersBlocState>();
|
state = context.findAncestorStateOfType<UsersBlocState>();
|
||||||
|
|
||||||
if (state == null) {
|
assert(
|
||||||
throw Exception('You must have a UsersBloc widget as ancestor');
|
state != null,
|
||||||
}
|
'You must have a UsersBloc widget as ancestor',
|
||||||
|
);
|
||||||
|
|
||||||
return state;
|
return state!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,6 +55,8 @@ class UsersBlocState extends State<UsersBloc>
|
|||||||
/// The stream notifying the state of queryUsers call
|
/// The stream notifying the state of queryUsers call
|
||||||
Stream<bool> get queryUsersLoading => _queryUsersLoadingController.stream;
|
Stream<bool> get queryUsersLoading => _queryUsersLoadingController.stream;
|
||||||
|
|
||||||
|
late StreamChatCoreState _streamChatCore;
|
||||||
|
|
||||||
/// The Query Users method allows you to search for users and see if they are
|
/// The Query Users method allows you to search for users and see if they are
|
||||||
/// online/offline.
|
/// online/offline.
|
||||||
/// [API Reference](https://getstream.io/chat/docs/flutter-dart/query_users/?language=dart)
|
/// [API Reference](https://getstream.io/chat/docs/flutter-dart/query_users/?language=dart)
|
||||||
@@ -63,7 +66,7 @@ class UsersBlocState extends State<UsersBloc>
|
|||||||
Map<String, dynamic>? options,
|
Map<String, dynamic>? options,
|
||||||
PaginationParams? pagination,
|
PaginationParams? pagination,
|
||||||
}) async {
|
}) async {
|
||||||
final client = StreamChatCore.of(context).client;
|
final client = _streamChatCore.client;
|
||||||
|
|
||||||
if (_queryUsersLoadingController.value == true) return;
|
if (_queryUsersLoadingController.value == true) return;
|
||||||
|
|
||||||
@@ -101,6 +104,12 @@ class UsersBlocState extends State<UsersBloc>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didChangeDependencies() {
|
||||||
|
_streamChatCore = StreamChatCore.of(context);
|
||||||
|
super.didChangeDependencies();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
super.build(context);
|
super.build(context);
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ void main() {
|
|||||||
await tester.pumpWidget(channelListCore);
|
await tester.pumpWidget(channelListCore);
|
||||||
|
|
||||||
expect(find.byKey(channelListCoreKey), findsNothing);
|
expect(find.byKey(channelListCoreKey), findsNothing);
|
||||||
expect(tester.takeException(), isInstanceOf<Exception>());
|
expect(tester.takeException(), isInstanceOf<AssertionError>());
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ void main() {
|
|||||||
|
|
||||||
expect(find.byKey(channelsBlocKey), findsNothing);
|
expect(find.byKey(channelsBlocKey), findsNothing);
|
||||||
expect(find.byKey(childKey), findsNothing);
|
expect(find.byKey(childKey), findsNothing);
|
||||||
expect(tester.takeException(), isInstanceOf<Exception>());
|
expect(tester.takeException(), isInstanceOf<AssertionError>());
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ void main() {
|
|||||||
await tester.pumpWidget(messageListCore);
|
await tester.pumpWidget(messageListCore);
|
||||||
|
|
||||||
expect(find.byKey(messageListCoreKey), findsNothing);
|
expect(find.byKey(messageListCoreKey), findsNothing);
|
||||||
expect(tester.takeException(), isInstanceOf<Exception>());
|
expect(tester.takeException(), isInstanceOf<AssertionError>());
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -147,6 +147,56 @@ void main() {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
testWidgets(
|
||||||
|
'should assign paginateData callback and paginate data correctly if a MessageListController is passed',
|
||||||
|
(tester) async {
|
||||||
|
const messageListCoreKey = Key('messageListCore');
|
||||||
|
final controller = MessageListController();
|
||||||
|
final messageListCore = MessageListCore(
|
||||||
|
key: messageListCoreKey,
|
||||||
|
messageListBuilder: (_, __) => Offstage(),
|
||||||
|
loadingBuilder: (BuildContext context) => Offstage(),
|
||||||
|
emptyBuilder: (BuildContext context) => Offstage(),
|
||||||
|
errorWidgetBuilder: (BuildContext context, Object error) => Offstage(),
|
||||||
|
messageListController: controller,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(controller.paginateData, isNull);
|
||||||
|
|
||||||
|
final mockChannel = MockChannel();
|
||||||
|
|
||||||
|
when(() => mockChannel.state.isUpToDate).thenReturn(true);
|
||||||
|
// when(() => mockChannel.query(
|
||||||
|
// messagesPagination: any(named: 'messagesPagination'),
|
||||||
|
// preferOffline: any(named: 'preferOffline'),
|
||||||
|
// )).thenAnswer((_) => mockChannel.state);
|
||||||
|
final messages = _generateMessages();
|
||||||
|
when(() => mockChannel.state.messages).thenReturn(messages);
|
||||||
|
when(() => mockChannel.state.messagesStream)
|
||||||
|
.thenAnswer((_) => Stream.value(messages));
|
||||||
|
when(() => mockChannel.initialized).thenAnswer((_) => Future.value(true));
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
StreamChannel(
|
||||||
|
channel: mockChannel,
|
||||||
|
child: messageListCore,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final finder = find.byKey(messageListCoreKey);
|
||||||
|
final coreState = tester.firstState<MessageListCoreState>(finder);
|
||||||
|
expect(finder, findsOneWidget);
|
||||||
|
expect(controller.paginateData, isNotNull);
|
||||||
|
|
||||||
|
await coreState.paginateData();
|
||||||
|
|
||||||
|
verify(() => mockChannel.query(
|
||||||
|
messagesPagination: any(named: 'messagesPagination'),
|
||||||
|
preferOffline: any(named: 'preferOffline'),
|
||||||
|
)).called(1);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
testWidgets(
|
testWidgets(
|
||||||
'should build error widget if messagesStream emits error',
|
'should build error widget if messagesStream emits error',
|
||||||
(tester) async {
|
(tester) async {
|
||||||
|
|||||||
@@ -35,27 +35,12 @@ void main() {
|
|||||||
'messageSearchBlocState.search() should throw if used where '
|
'messageSearchBlocState.search() should throw if used where '
|
||||||
'StreamChat is not present in the widget tree',
|
'StreamChat is not present in the widget tree',
|
||||||
(tester) async {
|
(tester) async {
|
||||||
const messageSearchBlocKey = Key('messageSearchBloc');
|
|
||||||
const childKey = Key('child');
|
|
||||||
final messageSearchBloc = MessageSearchBloc(
|
final messageSearchBloc = MessageSearchBloc(
|
||||||
key: messageSearchBlocKey,
|
child: Offstage(),
|
||||||
child: Offstage(key: childKey),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
await tester.pumpWidget(messageSearchBloc);
|
await tester.pumpWidget(messageSearchBloc);
|
||||||
|
expect(tester.takeException(), isInstanceOf<AssertionError>());
|
||||||
expect(find.byKey(messageSearchBlocKey), findsOneWidget);
|
|
||||||
expect(find.byKey(childKey), findsOneWidget);
|
|
||||||
|
|
||||||
final usersBlocState = tester.state<MessageSearchBlocState>(
|
|
||||||
find.byKey(messageSearchBlocKey),
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await usersBlocState.search(filter: testFilter);
|
|
||||||
} catch (e) {
|
|
||||||
expect(e, isInstanceOf<Exception>());
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ void main() {
|
|||||||
await tester.pumpWidget(messageSearchListCore);
|
await tester.pumpWidget(messageSearchListCore);
|
||||||
|
|
||||||
expect(find.byKey(messageSearchListCoreKey), findsNothing);
|
expect(find.byKey(messageSearchListCoreKey), findsNothing);
|
||||||
expect(tester.takeException(), isInstanceOf<Exception>());
|
expect(tester.takeException(), isInstanceOf<AssertionError>());
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -107,6 +107,7 @@ void main() {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
expect(find.byKey(messageSearchListCoreKey), findsOneWidget);
|
expect(find.byKey(messageSearchListCoreKey), findsOneWidget);
|
||||||
expect(controller.loadData, isNotNull);
|
expect(controller.loadData, isNotNull);
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ void main() {
|
|||||||
await tester.pumpWidget(userListCore);
|
await tester.pumpWidget(userListCore);
|
||||||
|
|
||||||
expect(find.byKey(userListCoreKey), findsNothing);
|
expect(find.byKey(userListCoreKey), findsNothing);
|
||||||
expect(tester.takeException(), isInstanceOf<Exception>());
|
expect(tester.takeException(), isInstanceOf<AssertionError>());
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -35,27 +35,12 @@ void main() {
|
|||||||
'usersBlocState.queryUsers() should throw if used where '
|
'usersBlocState.queryUsers() should throw if used where '
|
||||||
'StreamChat is not present in the widget tree',
|
'StreamChat is not present in the widget tree',
|
||||||
(tester) async {
|
(tester) async {
|
||||||
const usersBlocKey = Key('usersBloc');
|
|
||||||
const childKey = Key('child');
|
|
||||||
final usersBloc = UsersBloc(
|
final usersBloc = UsersBloc(
|
||||||
key: usersBlocKey,
|
child: Offstage(),
|
||||||
child: Offstage(key: childKey),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
await tester.pumpWidget(usersBloc);
|
await tester.pumpWidget(usersBloc);
|
||||||
|
expect(tester.takeException(), isInstanceOf<AssertionError>());
|
||||||
expect(find.byKey(usersBlocKey), findsOneWidget);
|
|
||||||
expect(find.byKey(childKey), findsOneWidget);
|
|
||||||
|
|
||||||
final usersBlocState = tester.state<UsersBlocState>(
|
|
||||||
find.byKey(usersBlocKey),
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await usersBlocState.queryUsers();
|
|
||||||
} catch (e) {
|
|
||||||
expect(e, isInstanceOf<Exception>());
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user