Merge branch 'feature/new-ui' into feature/mono-repo
This commit is contained in:
@@ -612,7 +612,10 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
|||||||
body: UsersBloc(
|
body: UsersBloc(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
_buildTextInputSection(modalSetState),
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: _buildTextInputSection(modalSetState),
|
||||||
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: UserListView(
|
child: UserListView(
|
||||||
selectedUsers: {},
|
selectedUsers: {},
|
||||||
@@ -690,66 +693,57 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildTextInputSection(modalSetState) {
|
Widget _buildTextInputSection(modalSetState) {
|
||||||
|
final theme = StreamChatTheme.of(context);
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
SizedBox(
|
|
||||||
height: 16.0,
|
|
||||||
),
|
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
SizedBox(
|
|
||||||
width: 16.0,
|
|
||||||
),
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: TextField(
|
child: Container(
|
||||||
controller: _searchController,
|
height: 36,
|
||||||
cursorColor: StreamChatTheme.of(context).colorTheme.black,
|
child: TextField(
|
||||||
autofocus: true,
|
controller: _searchController,
|
||||||
decoration: InputDecoration(
|
cursorColor: theme.colorTheme.black,
|
||||||
isDense: true,
|
autofocus: true,
|
||||||
prefixIconConstraints: BoxConstraints.tight(Size(36.0, 44.0)),
|
decoration: InputDecoration(
|
||||||
prefixIcon: Padding(
|
hintText: 'Search',
|
||||||
padding: const EdgeInsets.symmetric(
|
hintStyle: theme.textTheme.body.copyWith(
|
||||||
vertical: 2.0, horizontal: 6.0),
|
color: theme.colorTheme.grey,
|
||||||
child: StreamSvgIcon.search(
|
|
||||||
color: StreamChatTheme.of(context).colorTheme.black,
|
|
||||||
),
|
),
|
||||||
|
prefixIconConstraints: BoxConstraints.tight(Size(40, 24)),
|
||||||
|
prefixIcon: StreamSvgIcon.search(
|
||||||
|
color: theme.colorTheme.black,
|
||||||
|
size: 24,
|
||||||
|
),
|
||||||
|
enabledBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(24.0),
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: theme.colorTheme.greyWhisper,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(24.0),
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: theme.colorTheme.greyWhisper,
|
||||||
|
)),
|
||||||
|
contentPadding: const EdgeInsets.all(0),
|
||||||
),
|
),
|
||||||
hintText: 'Search',
|
|
||||||
border: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(32.0),
|
|
||||||
borderSide: BorderSide(
|
|
||||||
color: StreamChatTheme.of(context)
|
|
||||||
.colorTheme
|
|
||||||
.black
|
|
||||||
.withOpacity(0.08)),
|
|
||||||
),
|
|
||||||
focusedBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(32.0),
|
|
||||||
borderSide: BorderSide(
|
|
||||||
color: StreamChatTheme.of(context)
|
|
||||||
.colorTheme
|
|
||||||
.black
|
|
||||||
.withOpacity(0.08)),
|
|
||||||
),
|
|
||||||
contentPadding: EdgeInsets.zero,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(
|
SizedBox(width: 16.0),
|
||||||
width: 8.0,
|
|
||||||
),
|
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: StreamSvgIcon.close_small(
|
icon: StreamSvgIcon.close_small(
|
||||||
color: StreamChatTheme.of(context)
|
color: theme.colorTheme.grey,
|
||||||
.colorTheme
|
|
||||||
.black
|
|
||||||
.withOpacity(0.5),
|
|
||||||
),
|
),
|
||||||
onPressed: () {
|
constraints: BoxConstraints.tightFor(
|
||||||
Navigator.pop(context);
|
height: 24,
|
||||||
},
|
width: 24,
|
||||||
|
),
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
splashRadius: 24,
|
||||||
|
onPressed: () => Navigator.pop(context),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -457,133 +457,132 @@ class _ChannelListPageState extends State<ChannelListPage> {
|
|||||||
},
|
},
|
||||||
child: ChannelsBloc(
|
child: ChannelsBloc(
|
||||||
child: MessageSearchBloc(
|
child: MessageSearchBloc(
|
||||||
child: Column(
|
child: NestedScrollView(
|
||||||
children: [
|
floatHeaderSlivers: true,
|
||||||
SearchTextField(
|
headerSliverBuilder: (_, __) => [
|
||||||
controller: _controller,
|
SliverToBoxAdapter(
|
||||||
showCloseButton: _isSearchActive,
|
child: SearchTextField(
|
||||||
),
|
controller: _controller,
|
||||||
Expanded(
|
showCloseButton: _isSearchActive,
|
||||||
child: AnimatedSwitcher(
|
|
||||||
duration: const Duration(milliseconds: 350),
|
|
||||||
child: GestureDetector(
|
|
||||||
behavior: HitTestBehavior.opaque,
|
|
||||||
onPanDown: (_) => FocusScope.of(context).unfocus(),
|
|
||||||
child: _isSearchActive
|
|
||||||
? MessageSearchListView(
|
|
||||||
messageQuery: _channelQuery,
|
|
||||||
filters: {
|
|
||||||
'members': {
|
|
||||||
r'$in': [user.id]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
sortOptions: [
|
|
||||||
SortOption(
|
|
||||||
'created_at',
|
|
||||||
direction: SortOption.ASC,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
pullToRefresh: false,
|
|
||||||
paginationParams: PaginationParams(limit: 20),
|
|
||||||
emptyBuilder: (_, query) {
|
|
||||||
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 results for \"$query\"...',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
onItemTap: (messageResponse) async {
|
|
||||||
final client = StreamChat.of(context).client;
|
|
||||||
final message = messageResponse.message;
|
|
||||||
final channel = client.channel(
|
|
||||||
messageResponse.channel.type,
|
|
||||||
id: messageResponse.channel.id,
|
|
||||||
);
|
|
||||||
if (channel.state == null) {
|
|
||||||
await channel.watch();
|
|
||||||
}
|
|
||||||
Navigator.pushNamed(
|
|
||||||
context,
|
|
||||||
Routes.CHANNEL_PAGE,
|
|
||||||
arguments: ChannelPageArgs(
|
|
||||||
channel: channel,
|
|
||||||
initialMessage: message,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
)
|
|
||||||
: ChannelListView(
|
|
||||||
onStartChatPressed: () {
|
|
||||||
Navigator.pushNamed(context, Routes.NEW_CHAT);
|
|
||||||
},
|
|
||||||
swipeToAction: true,
|
|
||||||
filter: {
|
|
||||||
'members': {
|
|
||||||
r'$in': [user.id],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
'presence': true,
|
|
||||||
},
|
|
||||||
pagination: PaginationParams(
|
|
||||||
limit: 20,
|
|
||||||
),
|
|
||||||
channelWidget: ChannelPage(),
|
|
||||||
onViewInfoTap: (channel) {
|
|
||||||
if (channel.memberCount == 2 &&
|
|
||||||
channel.isDistinct) {
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => StreamChannel(
|
|
||||||
channel: channel,
|
|
||||||
child: ChatInfoScreen(
|
|
||||||
user: channel.state.members.first.user,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => StreamChannel(
|
|
||||||
channel: channel,
|
|
||||||
child: GroupInfoScreen(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
body: AnimatedSwitcher(
|
||||||
|
duration: const Duration(milliseconds: 350),
|
||||||
|
child: GestureDetector(
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
|
onPanDown: (_) => FocusScope.of(context).unfocus(),
|
||||||
|
child: _isSearchActive
|
||||||
|
? MessageSearchListView(
|
||||||
|
messageQuery: _channelQuery,
|
||||||
|
filters: {
|
||||||
|
'members': {
|
||||||
|
r'$in': [user.id]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sortOptions: [
|
||||||
|
SortOption(
|
||||||
|
'created_at',
|
||||||
|
direction: SortOption.ASC,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
pullToRefresh: false,
|
||||||
|
paginationParams: PaginationParams(limit: 20),
|
||||||
|
emptyBuilder: (_, query) {
|
||||||
|
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 results for \"$query\"...',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onItemTap: (messageResponse) async {
|
||||||
|
final client = StreamChat.of(context).client;
|
||||||
|
final message = messageResponse.message;
|
||||||
|
final channel = client.channel(
|
||||||
|
messageResponse.channel.type,
|
||||||
|
id: messageResponse.channel.id,
|
||||||
|
);
|
||||||
|
if (channel.state == null) {
|
||||||
|
await channel.watch();
|
||||||
|
}
|
||||||
|
Navigator.pushNamed(
|
||||||
|
context,
|
||||||
|
Routes.CHANNEL_PAGE,
|
||||||
|
arguments: ChannelPageArgs(
|
||||||
|
channel: channel,
|
||||||
|
initialMessage: message,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
: ChannelListView(
|
||||||
|
onStartChatPressed: () {
|
||||||
|
Navigator.pushNamed(context, Routes.NEW_CHAT);
|
||||||
|
},
|
||||||
|
swipeToAction: true,
|
||||||
|
filter: {
|
||||||
|
'members': {
|
||||||
|
r'$in': [user.id],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
'presence': true,
|
||||||
|
},
|
||||||
|
pagination: PaginationParams(
|
||||||
|
limit: 20,
|
||||||
|
),
|
||||||
|
channelWidget: ChannelPage(),
|
||||||
|
onViewInfoTap: (channel) {
|
||||||
|
if (channel.memberCount == 2 && channel.isDistinct) {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => StreamChannel(
|
||||||
|
channel: channel,
|
||||||
|
child: ChatInfoScreen(
|
||||||
|
user: channel.state.members.first.user,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => StreamChannel(
|
||||||
|
channel: channel,
|
||||||
|
child: GroupInfoScreen(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
name: example
|
name: example
|
||||||
description: A new Flutter project.
|
description: A new Flutter project.
|
||||||
version: 1.1.1+3
|
version: 1.1.2
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.2.2 <3.0.0"
|
sdk: ">=2.2.2 <3.0.0"
|
||||||
|
|||||||
@@ -729,17 +729,11 @@ class _ChannelListViewState extends State<ChannelListView>
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _separatorBuilder(context, i) {
|
Widget _separatorBuilder(context, i) {
|
||||||
var effect = StreamChatTheme.of(context).colorTheme.borderTop;
|
var effect = StreamChatTheme.of(context).colorTheme.borderBottom;
|
||||||
|
|
||||||
return BackdropFilter(
|
return Container(
|
||||||
filter: ui.ImageFilter.blur(
|
height: 1,
|
||||||
sigmaX: effect.sigmaX,
|
color: effect.color.withOpacity(effect.alpha ?? 1.0),
|
||||||
sigmaY: effect.sigmaY,
|
|
||||||
),
|
|
||||||
child: Container(
|
|
||||||
height: 1,
|
|
||||||
color: effect.color.withOpacity(0.08),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,13 +15,7 @@ extension StringExtension on String {
|
|||||||
final characters = this.trim().characters;
|
final characters = this.trim().characters;
|
||||||
if (characters.isEmpty) return false;
|
if (characters.isEmpty) return false;
|
||||||
if (characters.length > 3) return false;
|
if (characters.length > 3) return false;
|
||||||
return characters.every((c) {
|
return characters.every((c) => _emojis.map((e) => e.char).contains(c));
|
||||||
return _emojis.firstWhere(
|
|
||||||
(Emoji emoji) => emoji.char.contains(c),
|
|
||||||
orElse: () => null,
|
|
||||||
) !=
|
|
||||||
null;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -174,117 +174,138 @@ class _ImageFooterState extends State<ImageFooter> {
|
|||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
barrierColor: StreamChatTheme.of(context).colorTheme.overlay,
|
barrierColor: StreamChatTheme.of(context).colorTheme.overlay,
|
||||||
shape: RoundedRectangleBorder(
|
backgroundColor: StreamChatTheme.of(context).colorTheme.white,
|
||||||
borderRadius: BorderRadius.circular(16.0),
|
isScrollControlled: true,
|
||||||
|
shape: const RoundedRectangleBorder(
|
||||||
|
borderRadius: const BorderRadius.only(
|
||||||
|
topLeft: const Radius.circular(16.0),
|
||||||
|
topRight: const Radius.circular(16.0),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return Column(
|
final crossAxisCount = 3;
|
||||||
mainAxisSize: MainAxisSize.min,
|
final noOfRowToShowInitially =
|
||||||
children: [
|
widget.mediaAttachments.length > crossAxisCount ? 2 : 1;
|
||||||
Container(
|
final size = MediaQuery.of(context).size;
|
||||||
decoration: BoxDecoration(
|
final initialChildSize =
|
||||||
color: StreamChatTheme.of(context).colorTheme.white,
|
48 + (size.width * noOfRowToShowInitially) / crossAxisCount;
|
||||||
borderRadius: BorderRadius.only(
|
return DraggableScrollableSheet(
|
||||||
topRight: Radius.circular(16.0),
|
expand: false,
|
||||||
topLeft: Radius.circular(16.0),
|
initialChildSize: initialChildSize / size.height,
|
||||||
)),
|
builder: (context, scrollController) {
|
||||||
child: Stack(
|
return SingleChildScrollView(
|
||||||
|
controller: scrollController,
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Center(
|
Stack(
|
||||||
child: Padding(
|
children: [
|
||||||
padding: const EdgeInsets.all(16.0),
|
Center(
|
||||||
child: Text(
|
child: Padding(
|
||||||
'Photos',
|
padding: const EdgeInsets.all(16.0),
|
||||||
style:
|
child: Text(
|
||||||
StreamChatTheme.of(context).textTheme.headlineBold,
|
'Photos',
|
||||||
|
style: StreamChatTheme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.headlineBold,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
Align(
|
||||||
|
alignment: Alignment.centerRight,
|
||||||
|
child: IconButton(
|
||||||
|
icon: StreamSvgIcon.close(
|
||||||
|
color: StreamChatTheme.of(context).colorTheme.black,
|
||||||
|
),
|
||||||
|
onPressed: () => Navigator.maybePop(context),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
Align(
|
Flexible(
|
||||||
alignment: Alignment.centerRight,
|
child: GridView.builder(
|
||||||
child: IconButton(
|
shrinkWrap: true,
|
||||||
icon: StreamSvgIcon.close(
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
color: StreamChatTheme.of(context).colorTheme.black,
|
itemCount: widget.mediaAttachments.length,
|
||||||
|
padding: const EdgeInsets.all(1),
|
||||||
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
|
crossAxisCount: crossAxisCount,
|
||||||
|
mainAxisSpacing: 2.0,
|
||||||
|
crossAxisSpacing: 2.0,
|
||||||
),
|
),
|
||||||
onPressed: () => Navigator.maybePop(context),
|
itemBuilder: (context, index) {
|
||||||
|
Widget media;
|
||||||
|
final attachment = widget.mediaAttachments[index];
|
||||||
|
|
||||||
|
if (attachment.type == 'video') {
|
||||||
|
var controllerPackage = widget.videoPackages[
|
||||||
|
videoAttachments.indexOf(attachment)];
|
||||||
|
|
||||||
|
media = InkWell(
|
||||||
|
onTap: () => widget.mediaSelectedCallBack(index),
|
||||||
|
child: FittedBox(
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
child: Chewie(
|
||||||
|
controller: controllerPackage.chewieController,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
media = InkWell(
|
||||||
|
onTap: () => widget.mediaSelectedCallBack(index),
|
||||||
|
child: AspectRatio(
|
||||||
|
child: CachedNetworkImage(
|
||||||
|
imageUrl: attachment.imageUrl ??
|
||||||
|
attachment.assetUrl ??
|
||||||
|
attachment.thumbUrl,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
aspectRatio: 1.0,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Stack(
|
||||||
|
children: [
|
||||||
|
media,
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.all(8.0),
|
||||||
|
child: Container(
|
||||||
|
clipBehavior: Clip.antiAlias,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
color: StreamChatTheme.of(context)
|
||||||
|
.colorTheme
|
||||||
|
.white
|
||||||
|
.withOpacity(0.6),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
blurRadius: 8.0,
|
||||||
|
color: StreamChatTheme.of(context)
|
||||||
|
.colorTheme
|
||||||
|
.black
|
||||||
|
.withOpacity(0.3),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.all(1),
|
||||||
|
child: UserAvatar(
|
||||||
|
user: widget.message.user,
|
||||||
|
constraints:
|
||||||
|
BoxConstraints.tight(Size(24, 24)),
|
||||||
|
showOnlineStatus: false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
Container(
|
},
|
||||||
color: StreamChatTheme.of(context).colorTheme.white,
|
|
||||||
child: GridView.builder(
|
|
||||||
shrinkWrap: true,
|
|
||||||
physics: NeverScrollableScrollPhysics(),
|
|
||||||
itemBuilder: (context, position) {
|
|
||||||
Widget media;
|
|
||||||
|
|
||||||
if (widget.mediaAttachments[position].type == 'video') {
|
|
||||||
var controllerPackage = widget.videoPackages[
|
|
||||||
videoAttachments
|
|
||||||
.indexOf(widget.mediaAttachments[position])];
|
|
||||||
|
|
||||||
media = InkWell(
|
|
||||||
onTap: () {
|
|
||||||
widget.mediaSelectedCallBack(position);
|
|
||||||
},
|
|
||||||
child: FittedBox(
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
child: Chewie(
|
|
||||||
controller: controllerPackage.chewieController,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
media = InkWell(
|
|
||||||
onTap: () {
|
|
||||||
widget.mediaSelectedCallBack(position);
|
|
||||||
},
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(1.0),
|
|
||||||
child: AspectRatio(
|
|
||||||
child: CachedNetworkImage(
|
|
||||||
imageUrl: widget
|
|
||||||
.mediaAttachments[position].imageUrl ??
|
|
||||||
widget.mediaAttachments[position].assetUrl ??
|
|
||||||
widget.mediaAttachments[position].thumbUrl,
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
),
|
|
||||||
aspectRatio: 1.0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Stack(
|
|
||||||
children: [
|
|
||||||
media,
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsets.all(8.0),
|
|
||||||
child: Align(
|
|
||||||
alignment: Alignment.topLeft,
|
|
||||||
child: UserAvatar(
|
|
||||||
user: widget.message.user,
|
|
||||||
constraints: BoxConstraints.tight(
|
|
||||||
Size(
|
|
||||||
24,
|
|
||||||
24,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
showOnlineStatus: false,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
itemCount: widget.mediaAttachments.length,
|
|
||||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
|
||||||
crossAxisCount: 3),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ class MessageActionsModal extends StatefulWidget {
|
|||||||
final bool showFlagButton;
|
final bool showFlagButton;
|
||||||
final bool reverse;
|
final bool reverse;
|
||||||
final ShapeBorder messageShape;
|
final ShapeBorder messageShape;
|
||||||
|
final ShapeBorder attachmentShape;
|
||||||
final DisplayWidget showUserAvatar;
|
final DisplayWidget showUserAvatar;
|
||||||
|
|
||||||
const MessageActionsModal({
|
const MessageActionsModal({
|
||||||
@@ -51,6 +52,7 @@ class MessageActionsModal extends StatefulWidget {
|
|||||||
this.showUserAvatar = DisplayWidget.show,
|
this.showUserAvatar = DisplayWidget.show,
|
||||||
this.editMessageInputBuilder,
|
this.editMessageInputBuilder,
|
||||||
this.messageShape,
|
this.messageShape,
|
||||||
|
this.attachmentShape,
|
||||||
this.reverse = false,
|
this.reverse = false,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@@ -173,6 +175,7 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
|||||||
showInChannelIndicator: false,
|
showInChannelIndicator: false,
|
||||||
showSendingIndicator: false,
|
showSendingIndicator: false,
|
||||||
shape: widget.messageShape,
|
shape: widget.messageShape,
|
||||||
|
attachmentShape: widget.attachmentShape,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(height: 8),
|
SizedBox(height: 8),
|
||||||
@@ -461,7 +464,7 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
|||||||
),
|
),
|
||||||
const SizedBox(width: 16),
|
const SizedBox(width: 16),
|
||||||
Text(
|
Text(
|
||||||
'Flag',
|
'Flag Message',
|
||||||
style: StreamChatTheme.of(context).textTheme.body,
|
style: StreamChatTheme.of(context).textTheme.body,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ class MessageReactionsModal extends StatelessWidget {
|
|||||||
final bool showReactions;
|
final bool showReactions;
|
||||||
final DisplayWidget showUserAvatar;
|
final DisplayWidget showUserAvatar;
|
||||||
final ShapeBorder messageShape;
|
final ShapeBorder messageShape;
|
||||||
|
final ShapeBorder attachmentShape;
|
||||||
final void Function(User) onUserAvatarTap;
|
final void Function(User) onUserAvatarTap;
|
||||||
|
|
||||||
const MessageReactionsModal({
|
const MessageReactionsModal({
|
||||||
@@ -30,6 +31,7 @@ class MessageReactionsModal extends StatelessWidget {
|
|||||||
this.onThreadTap,
|
this.onThreadTap,
|
||||||
this.editMessageInputBuilder,
|
this.editMessageInputBuilder,
|
||||||
this.messageShape,
|
this.messageShape,
|
||||||
|
this.attachmentShape,
|
||||||
this.reverse = false,
|
this.reverse = false,
|
||||||
this.showUserAvatar = DisplayWidget.show,
|
this.showUserAvatar = DisplayWidget.show,
|
||||||
this.onUserAvatarTap,
|
this.onUserAvatarTap,
|
||||||
@@ -127,6 +129,7 @@ class MessageReactionsModal extends StatelessWidget {
|
|||||||
translateUserAvatar: false,
|
translateUserAvatar: false,
|
||||||
showSendingIndicator: false,
|
showSendingIndicator: false,
|
||||||
shape: messageShape,
|
shape: messageShape,
|
||||||
|
attachmentShape: attachmentShape,
|
||||||
padding: const EdgeInsets.all(0),
|
padding: const EdgeInsets.all(0),
|
||||||
attachmentPadding: EdgeInsets.all(
|
attachmentPadding: EdgeInsets.all(
|
||||||
hasFileAttachment ? 4 : 2,
|
hasFileAttachment ? 4 : 2,
|
||||||
|
|||||||
@@ -295,10 +295,6 @@ class _MessageWidgetState extends State<MessageWidget> {
|
|||||||
var leftPadding =
|
var leftPadding =
|
||||||
widget.showUserAvatar != DisplayWidget.gone ? avatarWidth + 8.5 : 0.5;
|
widget.showUserAvatar != DisplayWidget.gone ? avatarWidth + 8.5 : 0.5;
|
||||||
|
|
||||||
final hasFiles =
|
|
||||||
widget.message.attachments?.any((element) => element.type == 'file') ==
|
|
||||||
true;
|
|
||||||
|
|
||||||
return Material(
|
return Material(
|
||||||
type: MaterialType.transparency,
|
type: MaterialType.transparency,
|
||||||
child: Portal(
|
child: Portal(
|
||||||
@@ -406,29 +402,19 @@ class _MessageWidgetState extends State<MessageWidget> {
|
|||||||
BorderRadius.zero,
|
BorderRadius.zero,
|
||||||
),
|
),
|
||||||
color: _getBackgroundColor(),
|
color: _getBackgroundColor(),
|
||||||
child: Padding(
|
child: Column(
|
||||||
padding: EdgeInsets.all(
|
crossAxisAlignment:
|
||||||
hasFiles ? 2.0 : 0.0),
|
CrossAxisAlignment.end,
|
||||||
child: Column(
|
mainAxisSize:
|
||||||
crossAxisAlignment:
|
MainAxisSize.min,
|
||||||
CrossAxisAlignment
|
children: <Widget>[
|
||||||
.end,
|
if (hasQuotedMessage)
|
||||||
mainAxisSize:
|
_buildQuotedMessage(),
|
||||||
MainAxisSize.min,
|
if (hasNonUrlAttachments)
|
||||||
children: <Widget>[
|
_parseAttachments(),
|
||||||
if (hasQuotedMessage)
|
if (!isGiphy)
|
||||||
_buildQuotedMessage(),
|
_buildTextBubble(),
|
||||||
if (hasNonUrlAttachments)
|
],
|
||||||
_parseAttachments(
|
|
||||||
context),
|
|
||||||
if (widget.message.text
|
|
||||||
.trim()
|
|
||||||
.isNotEmpty &&
|
|
||||||
!isGiphy)
|
|
||||||
_buildTextBubble(
|
|
||||||
context),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -536,7 +522,7 @@ class _MessageWidgetState extends State<MessageWidget> {
|
|||||||
|
|
||||||
var children = <Widget>[];
|
var children = <Widget>[];
|
||||||
|
|
||||||
final threadParticipants = widget.message.threadParticipants;
|
final threadParticipants = widget.message?.threadParticipants?.take(2);
|
||||||
final showThreadParticipants = threadParticipants?.isNotEmpty == true;
|
final showThreadParticipants = threadParticipants?.isNotEmpty == true;
|
||||||
final replyCount = widget.message.replyCount;
|
final replyCount = widget.message.replyCount;
|
||||||
|
|
||||||
@@ -565,7 +551,7 @@ class _MessageWidgetState extends State<MessageWidget> {
|
|||||||
if (showThreadParticipants)
|
if (showThreadParticipants)
|
||||||
SizedBox.fromSize(
|
SizedBox.fromSize(
|
||||||
size: Size((threadParticipants.length * 8.0) + 8, 16),
|
size: Size((threadParticipants.length * 8.0) + 8, 16),
|
||||||
child: _buildThreadParticipantsIndicator(),
|
child: _buildThreadParticipantsIndicator(threadParticipants),
|
||||||
),
|
),
|
||||||
InkWell(
|
InkWell(
|
||||||
onTap: widget.onThreadTap != null ? onThreadTap : null,
|
onTap: widget.onThreadTap != null ? onThreadTap : null,
|
||||||
@@ -642,10 +628,10 @@ class _MessageWidgetState extends State<MessageWidget> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildThreadParticipantsIndicator() {
|
Widget _buildThreadParticipantsIndicator(Iterable<User> threadParticipants) {
|
||||||
var padding = 0.0;
|
var padding = 0.0;
|
||||||
return Stack(
|
return Stack(
|
||||||
children: widget.message.threadParticipants.map((user) {
|
children: threadParticipants.map((user) {
|
||||||
padding += 8.0;
|
padding += 8.0;
|
||||||
return Positioned(
|
return Positioned(
|
||||||
right: padding - 8,
|
right: padding - 8,
|
||||||
@@ -718,6 +704,8 @@ class _MessageWidgetState extends State<MessageWidget> {
|
|||||||
: DisplayWidget.show,
|
: DisplayWidget.show,
|
||||||
messageTheme: widget.messageTheme,
|
messageTheme: widget.messageTheme,
|
||||||
messageShape: widget.shape ?? _getDefaultShape(context),
|
messageShape: widget.shape ?? _getDefaultShape(context),
|
||||||
|
attachmentShape:
|
||||||
|
widget.attachmentShape ?? _getDefaultAttachmentShape(context),
|
||||||
reverse: widget.reverse,
|
reverse: widget.reverse,
|
||||||
showDeleteMessage: widget.showDeleteMessage || isDeleteFailed,
|
showDeleteMessage: widget.showDeleteMessage || isDeleteFailed,
|
||||||
message: widget.message,
|
message: widget.message,
|
||||||
@@ -763,6 +751,8 @@ class _MessageWidgetState extends State<MessageWidget> {
|
|||||||
onUserAvatarTap: widget.onUserAvatarTap,
|
onUserAvatarTap: widget.onUserAvatarTap,
|
||||||
messageTheme: widget.messageTheme,
|
messageTheme: widget.messageTheme,
|
||||||
messageShape: widget.shape ?? _getDefaultShape(context),
|
messageShape: widget.shape ?? _getDefaultShape(context),
|
||||||
|
attachmentShape:
|
||||||
|
widget.attachmentShape ?? _getDefaultAttachmentShape(context),
|
||||||
reverse: widget.reverse,
|
reverse: widget.reverse,
|
||||||
message: widget.message,
|
message: widget.message,
|
||||||
editMessageInputBuilder: widget.editMessageInputBuilder,
|
editMessageInputBuilder: widget.editMessageInputBuilder,
|
||||||
@@ -773,20 +763,31 @@ class _MessageWidgetState extends State<MessageWidget> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ShapeBorder _getDefaultShape(BuildContext context) {
|
ShapeBorder _getDefaultAttachmentShape(BuildContext context) {
|
||||||
|
final hasFiles =
|
||||||
|
widget.message.attachments?.any((it) => it.type == 'file') == true;
|
||||||
return RoundedRectangleBorder(
|
return RoundedRectangleBorder(
|
||||||
side: widget.attachmentBorderSide ??
|
side: hasFiles
|
||||||
widget.borderSide ??
|
? widget.attachmentBorderSide ??
|
||||||
BorderSide(
|
BorderSide(
|
||||||
color: StreamChatTheme.of(context).colorTheme.greyWhisper,
|
color: StreamChatTheme.of(context).colorTheme.greyWhisper,
|
||||||
),
|
)
|
||||||
borderRadius: widget.attachmentBorderRadiusGeometry ??
|
: BorderSide.none,
|
||||||
widget.borderRadiusGeometry ??
|
borderRadius: widget.attachmentBorderRadiusGeometry ?? BorderRadius.zero,
|
||||||
BorderRadius.zero,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _parseAttachments(BuildContext context) {
|
ShapeBorder _getDefaultShape(BuildContext context) {
|
||||||
|
return RoundedRectangleBorder(
|
||||||
|
side: widget.borderSide ??
|
||||||
|
BorderSide(
|
||||||
|
color: StreamChatTheme.of(context).colorTheme.greyWhisper,
|
||||||
|
),
|
||||||
|
borderRadius: widget.borderRadiusGeometry ?? BorderRadius.zero,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _parseAttachments() {
|
||||||
final images = widget.message.attachments
|
final images = widget.message.attachments
|
||||||
?.where((element) =>
|
?.where((element) =>
|
||||||
element.type == 'image' && element.ogScrapeUrl == null)
|
element.type == 'image' && element.ogScrapeUrl == null)
|
||||||
@@ -849,7 +850,7 @@ class _MessageWidgetState extends State<MessageWidget> {
|
|||||||
Attachment attachment,
|
Attachment attachment,
|
||||||
}) {
|
}) {
|
||||||
final attachmentShape =
|
final attachmentShape =
|
||||||
widget.attachmentShape ?? widget.shape ?? _getDefaultShape(context);
|
widget.attachmentShape ?? _getDefaultAttachmentShape(context);
|
||||||
return Material(
|
return Material(
|
||||||
clipBehavior: Clip.antiAlias,
|
clipBehavior: Clip.antiAlias,
|
||||||
shape: attachmentShape,
|
shape: attachmentShape,
|
||||||
@@ -920,7 +921,8 @@ class _MessageWidgetState extends State<MessageWidget> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildTextBubble(BuildContext context) {
|
Widget _buildTextBubble() {
|
||||||
|
if (widget.message.text.trim().isEmpty) return Offstage();
|
||||||
return Transform(
|
return Transform(
|
||||||
transform: Matrix4.rotationY(widget.reverse ? pi : 0),
|
transform: Matrix4.rotationY(widget.reverse ? pi : 0),
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
|
|||||||
@@ -492,8 +492,8 @@ class ColorTheme {
|
|||||||
),
|
),
|
||||||
this.borderTop = const Effect(
|
this.borderTop = const Effect(
|
||||||
sigmaX: 0, sigmaY: -1, color: Color(0xff000000), blur: 0.0),
|
sigmaX: 0, sigmaY: -1, color: Color(0xff000000), blur: 0.0),
|
||||||
this.borderBottom =
|
this.borderBottom = const Effect(
|
||||||
const Effect(sigmaX: 0, sigmaY: 1, color: Color(0xff000000), blur: 0.0),
|
sigmaX: 0, sigmaY: 1, color: Color(0xff000000), blur: 0.0, alpha: 0.08),
|
||||||
this.shadowIconButton = const Effect(
|
this.shadowIconButton = const Effect(
|
||||||
sigmaX: 0, sigmaY: 2, color: Color(0xff000000), alpha: 0.5, blur: 4.0),
|
sigmaX: 0, sigmaY: 2, color: Color(0xff000000), alpha: 0.5, blur: 4.0),
|
||||||
this.modalShadow = const Effect(
|
this.modalShadow = const Effect(
|
||||||
@@ -514,8 +514,8 @@ class ColorTheme {
|
|||||||
this.accentGreen = const Color(0xff20E070),
|
this.accentGreen = const Color(0xff20E070),
|
||||||
this.borderTop = const Effect(
|
this.borderTop = const Effect(
|
||||||
sigmaX: 0, sigmaY: -1, color: Color(0xff141924), blur: 0.0),
|
sigmaX: 0, sigmaY: -1, color: Color(0xff141924), blur: 0.0),
|
||||||
this.borderBottom =
|
this.borderBottom = const Effect(
|
||||||
const Effect(sigmaX: 0, sigmaY: 1, color: Color(0xff141924), blur: 0.0),
|
sigmaX: 0, sigmaY: 1, color: Color(0xff141924), blur: 0.0, alpha: 1.0),
|
||||||
this.shadowIconButton = const Effect(
|
this.shadowIconButton = const Effect(
|
||||||
sigmaX: 0, sigmaY: 2, color: Color(0xff000000), alpha: 0.5, blur: 4.0),
|
sigmaX: 0, sigmaY: 2, color: Color(0xff000000), alpha: 0.5, blur: 4.0),
|
||||||
this.modalShadow = const Effect(
|
this.modalShadow = const Effect(
|
||||||
|
|||||||
@@ -416,6 +416,10 @@ class _UserListViewState extends State<UserListView>
|
|||||||
height: 64,
|
height: 64,
|
||||||
width: 64,
|
width: 64,
|
||||||
),
|
),
|
||||||
|
onlineIndicatorConstraints: BoxConstraints.tightFor(
|
||||||
|
height: 12,
|
||||||
|
width: 12,
|
||||||
|
),
|
||||||
onTap: (user) =>
|
onTap: (user) =>
|
||||||
widget.onUserTap(user, widget.userWidget),
|
widget.onUserTap(user, widget.userWidget),
|
||||||
onLongPress: widget.onUserLongPress,
|
onLongPress: widget.onUserLongPress,
|
||||||
|
|||||||
Reference in New Issue
Block a user