Merge branch 'feature/new-ui' into feature/theme
This commit is contained in:
@@ -52,6 +52,9 @@ class ChannelImage extends StatelessWidget {
|
||||
this.onTap,
|
||||
this.showOnlineStatus = true,
|
||||
this.borderRadius,
|
||||
this.selected = false,
|
||||
this.selectionColor = const Color(0xFF006CFF),
|
||||
this.selectionThickness = 4,
|
||||
}) : super(key: key);
|
||||
|
||||
final BorderRadius borderRadius;
|
||||
@@ -67,6 +70,12 @@ class ChannelImage extends StatelessWidget {
|
||||
|
||||
final bool showOnlineStatus;
|
||||
|
||||
final bool selected;
|
||||
|
||||
final Color selectionColor;
|
||||
|
||||
final double selectionThickness;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final streamChat = StreamChat.of(context);
|
||||
@@ -94,11 +103,10 @@ class ChannelImage extends StatelessWidget {
|
||||
.channelPreviewTheme
|
||||
.avatarTheme
|
||||
.constraints,
|
||||
onTap: onTap != null
|
||||
? (_) {
|
||||
onTap();
|
||||
}
|
||||
: null,
|
||||
onTap: onTap != null ? (_) => onTap() : null,
|
||||
selected: selected,
|
||||
selectionColor: selectionColor,
|
||||
selectionThickness: selectionThickness,
|
||||
);
|
||||
});
|
||||
} else {
|
||||
@@ -111,16 +119,20 @@ class ChannelImage extends StatelessWidget {
|
||||
.toList();
|
||||
return GroupImage(
|
||||
images: images,
|
||||
borderRadius: borderRadius,
|
||||
constraints: constraints ??
|
||||
StreamChatTheme.of(context)
|
||||
.channelPreviewTheme
|
||||
.avatarTheme
|
||||
.constraints,
|
||||
onTap: onTap,
|
||||
selected: selected,
|
||||
selectionColor: selectionColor,
|
||||
selectionThickness: selectionThickness,
|
||||
);
|
||||
}
|
||||
|
||||
return ClipRRect(
|
||||
Widget child = ClipRRect(
|
||||
borderRadius: borderRadius ??
|
||||
StreamChatTheme.of(context)
|
||||
.channelPreviewTheme
|
||||
@@ -171,6 +183,29 @@ class ChannelImage extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
);
|
||||
if (selected) {
|
||||
child = ClipRRect(
|
||||
borderRadius: (borderRadius ??
|
||||
StreamChatTheme.of(context)
|
||||
.ownMessageTheme
|
||||
.avatarTheme
|
||||
.borderRadius) +
|
||||
BorderRadius.circular(selectionThickness),
|
||||
child: Container(
|
||||
constraints: constraints ??
|
||||
StreamChatTheme.of(context)
|
||||
.ownMessageTheme
|
||||
.avatarTheme
|
||||
.constraints,
|
||||
color: selectionColor,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(selectionThickness),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
return child;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+154
-70
@@ -74,6 +74,8 @@ class ChannelListView extends StatefulWidget {
|
||||
this.onStartChatPressed,
|
||||
this.swipeToAction = false,
|
||||
this.pullToRefresh = true,
|
||||
this.crossAxisCount = 1,
|
||||
this.selectedChannels = const [],
|
||||
}) : super(key: key);
|
||||
|
||||
/// The builder that will be used in case of error
|
||||
@@ -134,6 +136,11 @@ class ChannelListView extends StatefulWidget {
|
||||
/// Callback used in the default empty list widget
|
||||
final VoidCallback onStartChatPressed;
|
||||
|
||||
/// The number of children in the cross axis.
|
||||
final int crossAxisCount;
|
||||
|
||||
final List<Channel> selectedChannels;
|
||||
|
||||
@override
|
||||
_ChannelListViewState createState() => _ChannelListViewState();
|
||||
}
|
||||
@@ -268,22 +275,33 @@ class _ChannelListViewState extends State<ChannelListView>
|
||||
}
|
||||
|
||||
if (channels.isNotEmpty) {
|
||||
child = ListView.custom(
|
||||
physics: AlwaysScrollableScrollPhysics(),
|
||||
controller: _scrollController,
|
||||
childrenDelegate: SliverChildBuilderDelegate(
|
||||
(context, i) {
|
||||
return _itemBuilder(context, i, channels);
|
||||
if (widget.crossAxisCount > 1) {
|
||||
child = GridView.builder(
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: widget.crossAxisCount),
|
||||
itemCount: channels.length,
|
||||
physics: AlwaysScrollableScrollPhysics(),
|
||||
controller: _scrollController,
|
||||
itemBuilder: (context, index) {
|
||||
return _gridItemBuilder(context, index, channels);
|
||||
},
|
||||
childCount: (channels.length * 2) + 1,
|
||||
findChildIndexCallback: (key) {
|
||||
final ValueKey<String> valueKey = key;
|
||||
final index = channels.indexWhere(
|
||||
(channel) => 'CHANNEL-${channel.id}' == valueKey.value);
|
||||
return index != -1 ? (index * 2) : null;
|
||||
);
|
||||
} else {
|
||||
child = ListView.separated(
|
||||
physics: AlwaysScrollableScrollPhysics(),
|
||||
itemCount:
|
||||
channels.isNotEmpty ? channels.length + 1 : channels.length,
|
||||
separatorBuilder: (_, index) {
|
||||
if (widget.separatorBuilder != null) {
|
||||
return widget.separatorBuilder(context, index);
|
||||
}
|
||||
return _separatorBuilder(context, index);
|
||||
},
|
||||
),
|
||||
);
|
||||
itemBuilder: (context, index) {
|
||||
return _listItemBuilder(context, index, channels);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,11 +319,13 @@ class _ChannelListViewState extends State<ChannelListView>
|
||||
children: List.generate(
|
||||
25,
|
||||
(i) {
|
||||
if (i % 2 != 0) {
|
||||
if (widget.separatorBuilder != null) {
|
||||
return widget.separatorBuilder(context, i);
|
||||
if (widget.crossAxisCount == 1) {
|
||||
if (i % 2 != 0) {
|
||||
if (widget.separatorBuilder != null) {
|
||||
return widget.separatorBuilder(context, i);
|
||||
}
|
||||
return _separatorBuilder(context, i);
|
||||
}
|
||||
return _separatorBuilder(context, i);
|
||||
}
|
||||
return _buildLoadingItem();
|
||||
},
|
||||
@@ -314,63 +334,96 @@ class _ChannelListViewState extends State<ChannelListView>
|
||||
}
|
||||
|
||||
Shimmer _buildLoadingItem() {
|
||||
return Shimmer.fromColors(
|
||||
baseColor: Color(0xffE5E5E5),
|
||||
highlightColor: StreamChatTheme.of(context).colorTheme.white,
|
||||
child: ListTile(
|
||||
leading: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
constraints: BoxConstraints.tightFor(
|
||||
height: 40,
|
||||
width: 40,
|
||||
),
|
||||
if (widget.crossAxisCount > 1) {
|
||||
return Shimmer.fromColors(
|
||||
baseColor: Color(0xffE5E5E5),
|
||||
highlightColor: Color(0xffffffff),
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 4.0,
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
for (int i = 0; i < widget.crossAxisCount; i++)
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
constraints: BoxConstraints.tightFor(
|
||||
height: 70,
|
||||
width: 70,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(
|
||||
height: 16.0,
|
||||
),
|
||||
],
|
||||
),
|
||||
title: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Container(
|
||||
);
|
||||
} else {
|
||||
return Shimmer.fromColors(
|
||||
baseColor: Color(0xffE5E5E5),
|
||||
highlightColor: StreamChatTheme.of(context).colorTheme.white,
|
||||
child: ListTile(
|
||||
leading: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
borderRadius: BorderRadius.circular(11),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
constraints: BoxConstraints.tightFor(
|
||||
height: 16,
|
||||
width: 82,
|
||||
height: 40,
|
||||
width: 40,
|
||||
),
|
||||
),
|
||||
),
|
||||
subtitle: Row(
|
||||
children: [
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
borderRadius: BorderRadius.circular(11),
|
||||
),
|
||||
constraints: BoxConstraints.tightFor(
|
||||
height: 16,
|
||||
width: 238,
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
margin: const EdgeInsets.only(left: 16),
|
||||
title: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
borderRadius: BorderRadius.circular(11),
|
||||
),
|
||||
constraints: BoxConstraints.tightFor(
|
||||
height: 16,
|
||||
width: 42,
|
||||
width: 82,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
subtitle: Row(
|
||||
children: [
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
borderRadius: BorderRadius.circular(11),
|
||||
),
|
||||
constraints: BoxConstraints.tightFor(
|
||||
height: 16,
|
||||
width: 238,
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
margin: const EdgeInsets.only(left: 16),
|
||||
decoration: BoxDecoration(
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
borderRadius: BorderRadius.circular(11),
|
||||
),
|
||||
constraints: BoxConstraints.tightFor(
|
||||
height: 16,
|
||||
width: 42,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildErrorWidget(
|
||||
@@ -437,20 +490,10 @@ class _ChannelListViewState extends State<ChannelListView>
|
||||
);
|
||||
}
|
||||
|
||||
Widget _itemBuilder(context, int i, List<Channel> channels) {
|
||||
if (i % 2 != 0) {
|
||||
if (widget.separatorBuilder != null) {
|
||||
return widget.separatorBuilder(context, i);
|
||||
}
|
||||
return _separatorBuilder(context, i);
|
||||
}
|
||||
|
||||
i = i ~/ 2;
|
||||
|
||||
Widget _listItemBuilder(BuildContext context, int i, List<Channel> channels) {
|
||||
final channelsProvider = ChannelsBloc.of(context);
|
||||
if (i < channels.length) {
|
||||
final channel = channels[i];
|
||||
|
||||
ChannelTapCallback onTap;
|
||||
if (widget.onChannelTap != null) {
|
||||
onTap = widget.onChannelTap;
|
||||
@@ -590,6 +633,47 @@ class _ChannelListViewState extends State<ChannelListView>
|
||||
}
|
||||
}
|
||||
|
||||
Widget _gridItemBuilder(BuildContext context, int i, List<Channel> channels) {
|
||||
var channel = channels[i];
|
||||
|
||||
var selected = widget.selectedChannels.contains(channel);
|
||||
|
||||
return Container(
|
||||
key: ValueKey<String>('CHANNEL-${channel.id}'),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
ChannelImage(
|
||||
channel: channel,
|
||||
borderRadius: BorderRadius.circular(32),
|
||||
selected: selected,
|
||||
constraints: BoxConstraints.tightFor(
|
||||
width: 64,
|
||||
height: 64,
|
||||
),
|
||||
onTap: () {
|
||||
widget.onChannelTap(channel, null);
|
||||
},
|
||||
),
|
||||
SizedBox(height: 7),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
child: StreamChannel(
|
||||
child: ChannelName(
|
||||
textStyle: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
channel: channel,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildQueryProgressIndicator(
|
||||
context,
|
||||
ChannelsBlocState channelsProvider,
|
||||
|
||||
@@ -9,21 +9,33 @@ class GroupImage extends StatelessWidget {
|
||||
@required this.images,
|
||||
this.constraints,
|
||||
this.onTap,
|
||||
this.borderRadius,
|
||||
this.selected = false,
|
||||
this.selectionColor = const Color(0xFF006CFF),
|
||||
this.selectionThickness = 4,
|
||||
}) : super(key: key);
|
||||
|
||||
final List<String> images;
|
||||
final BoxConstraints constraints;
|
||||
final VoidCallback onTap;
|
||||
final bool selected;
|
||||
final BorderRadius borderRadius;
|
||||
final Color selectionColor;
|
||||
final double selectionThickness;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
var avatar;
|
||||
final streamChatTheme = StreamChatTheme.of(context);
|
||||
|
||||
avatar = GestureDetector(
|
||||
onTap: onTap,
|
||||
child: ClipRRect(
|
||||
borderRadius: StreamChatTheme.of(context)
|
||||
.ownMessageTheme
|
||||
.avatarTheme
|
||||
.borderRadius,
|
||||
borderRadius: borderRadius ??
|
||||
StreamChatTheme.of(context)
|
||||
.ownMessageTheme
|
||||
.avatarTheme
|
||||
.borderRadius,
|
||||
child: Container(
|
||||
constraints: constraints ??
|
||||
StreamChatTheme.of(context)
|
||||
@@ -91,5 +103,24 @@ class GroupImage extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
if (selected) {
|
||||
avatar = ClipRRect(
|
||||
borderRadius: (borderRadius ??
|
||||
streamChatTheme.ownMessageTheme.avatarTheme.borderRadius) +
|
||||
BorderRadius.circular(selectionThickness),
|
||||
child: Container(
|
||||
color: selectionColor,
|
||||
height: 64.0,
|
||||
width: 64.0,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(selectionThickness),
|
||||
child: avatar,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return avatar;
|
||||
}
|
||||
}
|
||||
|
||||
+79
-82
@@ -16,8 +16,6 @@ import 'package:stream_chat_flutter/src/stream_chat.dart';
|
||||
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
import 'stream_channel.dart';
|
||||
|
||||
class ImageFooter extends StatefulWidget {
|
||||
/// Callback to call when pressing the back button.
|
||||
/// By default it calls [Navigator.pop]
|
||||
@@ -64,11 +62,11 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
bool _userSearchMode = false;
|
||||
TextEditingController _searchController;
|
||||
TextEditingController _messageController = TextEditingController();
|
||||
FocusNode _messageFocusNode = FocusNode();
|
||||
|
||||
String _userNameQuery;
|
||||
bool _isSearchActive = false;
|
||||
String _channelNameQuery;
|
||||
|
||||
Set<User> _selectedUsers = {};
|
||||
List<Channel> _selectedChannels = [];
|
||||
bool _loading = false;
|
||||
|
||||
Timer _debounce;
|
||||
@@ -80,8 +78,7 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
_debounce = Timer(const Duration(milliseconds: 350), () {
|
||||
if (mounted && modalSetStateCallback != null) {
|
||||
modalSetStateCallback(() {
|
||||
_userNameQuery = _searchController.text;
|
||||
_isSearchActive = _userNameQuery.isNotEmpty;
|
||||
_channelNameQuery = _searchController.text;
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -91,6 +88,9 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
_searchController = TextEditingController()..addListener(_userNameListener);
|
||||
_messageFocusNode.addListener(() {
|
||||
setState(() {});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -251,7 +251,7 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildShareModal(context) {
|
||||
void _buildShareModal(context) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
@@ -259,7 +259,7 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
modalSetStateCallback = modalSetState;
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(
|
||||
top: _userSearchMode
|
||||
top: _userSearchMode || _messageFocusNode.hasFocus
|
||||
? 16.0
|
||||
: MediaQuery.of(context).size.height / 2,
|
||||
left: 8.0,
|
||||
@@ -275,74 +275,81 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
child: Column(
|
||||
children: [
|
||||
_buildTextInputSection(modalSetState),
|
||||
if (_userSearchMode)
|
||||
SizedBox(
|
||||
height: 22.0,
|
||||
),
|
||||
Expanded(
|
||||
child: UserListView(
|
||||
selectedUsers: _selectedUsers,
|
||||
onUserTap: (user, _) {
|
||||
_searchController.clear();
|
||||
if (!_selectedUsers.contains(user)) {
|
||||
modalSetState(() {
|
||||
_selectedUsers.add(user);
|
||||
});
|
||||
} else {
|
||||
modalSetState(() {
|
||||
_selectedUsers.remove(user);
|
||||
});
|
||||
}
|
||||
},
|
||||
crossAxisCount: 4,
|
||||
pagination: PaginationParams(
|
||||
limit: 25,
|
||||
),
|
||||
filter: {
|
||||
if (_searchController.text.isNotEmpty)
|
||||
'name': {
|
||||
r'$autocomplete': _userNameQuery,
|
||||
},
|
||||
'id': {
|
||||
r'$ne': StreamChat.of(context).user.id,
|
||||
child: ChannelsBloc(
|
||||
child: ChannelListView(
|
||||
selectedChannels: _selectedChannels,
|
||||
onChannelTap: (channel, _) {
|
||||
_searchController.clear();
|
||||
if (!_selectedChannels.contains(channel)) {
|
||||
modalSetState(() {
|
||||
_selectedChannels.add(channel);
|
||||
});
|
||||
} else {
|
||||
modalSetState(() {
|
||||
_selectedChannels.remove(channel);
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
sort: [
|
||||
SortOption(
|
||||
'name',
|
||||
direction: 1,
|
||||
crossAxisCount: 4,
|
||||
pagination: PaginationParams(
|
||||
limit: 25,
|
||||
),
|
||||
],
|
||||
emptyBuilder: (_) {
|
||||
return LayoutBuilder(
|
||||
builder: (context, viewportConstraints) {
|
||||
return SingleChildScrollView(
|
||||
physics: AlwaysScrollableScrollPhysics(),
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minHeight: viewportConstraints.maxHeight,
|
||||
),
|
||||
child: Center(
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: StreamSvgIcon.search(
|
||||
size: 96,
|
||||
color: Colors.grey,
|
||||
filter: {
|
||||
if (_searchController.text.isNotEmpty)
|
||||
'name': {
|
||||
r'$autocomplete': _channelNameQuery,
|
||||
},
|
||||
'id': {
|
||||
r'$ne': StreamChat.of(context).user.id,
|
||||
},
|
||||
},
|
||||
sort: [
|
||||
SortOption(
|
||||
'name',
|
||||
direction: 1,
|
||||
),
|
||||
],
|
||||
emptyBuilder: (_) {
|
||||
return LayoutBuilder(
|
||||
builder: (context, viewportConstraints) {
|
||||
return SingleChildScrollView(
|
||||
physics: AlwaysScrollableScrollPhysics(),
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minHeight:
|
||||
viewportConstraints.maxHeight,
|
||||
),
|
||||
child: Center(
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: StreamSvgIcon.search(
|
||||
size: 96,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'No user matches these keywords...'),
|
||||
],
|
||||
Text(
|
||||
'No chat matches these keywords...'),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
if (_selectedUsers.isNotEmpty)
|
||||
if (_selectedChannels.isNotEmpty)
|
||||
_buildShareTextInputSection(modalSetState),
|
||||
if (!_userSearchMode && _selectedUsers.isEmpty)
|
||||
if (!_userSearchMode && _selectedChannels.isEmpty)
|
||||
Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: Container(
|
||||
@@ -543,6 +550,7 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
padding: const EdgeInsets.only(left: 8.0),
|
||||
child: TextField(
|
||||
controller: _messageController,
|
||||
focusNode: _messageFocusNode,
|
||||
onChanged: (val) {
|
||||
modalSetState(() {});
|
||||
},
|
||||
@@ -619,27 +627,16 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
|
||||
_messageController.clear();
|
||||
|
||||
final client = StreamChat.of(context).client;
|
||||
|
||||
for (var user in _selectedUsers) {
|
||||
var c = client.channel('messaging', extraData: {
|
||||
'members': [
|
||||
user.id,
|
||||
StreamChat.of(context).user.id,
|
||||
],
|
||||
});
|
||||
|
||||
await c.watch();
|
||||
|
||||
for (var channel in _selectedChannels) {
|
||||
final message = Message(
|
||||
text: text,
|
||||
attachments: [attachments[widget.currentPage]],
|
||||
);
|
||||
|
||||
await c.sendMessage(message);
|
||||
await channel.sendMessage(message);
|
||||
}
|
||||
|
||||
_selectedUsers.clear();
|
||||
_selectedChannels.clear();
|
||||
Navigator.pop(context);
|
||||
}
|
||||
|
||||
|
||||
@@ -61,12 +61,15 @@ class UserAvatar extends StatelessWidget {
|
||||
: streamChatTheme.defaultUserImage(context, user),
|
||||
),
|
||||
);
|
||||
|
||||
if (selected) {
|
||||
avatar = ClipRRect(
|
||||
borderRadius: (borderRadius ??
|
||||
streamChatTheme.ownMessageTheme.avatarTheme.borderRadius) +
|
||||
BorderRadius.circular(selectionThickness),
|
||||
child: Container(
|
||||
constraints: constraints ??
|
||||
streamChatTheme.ownMessageTheme.avatarTheme.constraints,
|
||||
color: selectionColor,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(selectionThickness),
|
||||
|
||||
Reference in New Issue
Block a user