Merge branch 'feature/new-ui' into feature/mono-repo

This commit is contained in:
Salvatore Giordano
2021-01-15 14:36:31 +01:00
11 changed files with 352 additions and 338 deletions
@@ -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: Container(
height: 36,
child: TextField( child: TextField(
controller: _searchController, controller: _searchController,
cursorColor: StreamChatTheme.of(context).colorTheme.black, cursorColor: theme.colorTheme.black,
autofocus: true, autofocus: true,
decoration: InputDecoration( decoration: InputDecoration(
isDense: true,
prefixIconConstraints: BoxConstraints.tight(Size(36.0, 44.0)),
prefixIcon: Padding(
padding: const EdgeInsets.symmetric(
vertical: 2.0, horizontal: 6.0),
child: StreamSvgIcon.search(
color: StreamChatTheme.of(context).colorTheme.black,
),
),
hintText: 'Search', hintText: 'Search',
border: OutlineInputBorder( hintStyle: theme.textTheme.body.copyWith(
borderRadius: BorderRadius.circular(32.0), color: theme.colorTheme.grey,
),
prefixIconConstraints: BoxConstraints.tight(Size(40, 24)),
prefixIcon: StreamSvgIcon.search(
color: theme.colorTheme.black,
size: 24,
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(24.0),
borderSide: BorderSide( borderSide: BorderSide(
color: StreamChatTheme.of(context) color: theme.colorTheme.greyWhisper,
.colorTheme ),
.black
.withOpacity(0.08)),
), ),
focusedBorder: OutlineInputBorder( focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(32.0), borderRadius: BorderRadius.circular(24.0),
borderSide: BorderSide( borderSide: BorderSide(
color: StreamChatTheme.of(context) color: theme.colorTheme.greyWhisper,
.colorTheme )),
.black contentPadding: const EdgeInsets.all(0),
.withOpacity(0.08)),
),
contentPadding: EdgeInsets.zero,
), ),
), ),
), ),
SizedBox(
width: 8.0,
), ),
SizedBox(width: 16.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),
) )
], ],
), ),
+10 -11
View File
@@ -457,14 +457,17 @@ class _ChannelListPageState extends State<ChannelListPage> {
}, },
child: ChannelsBloc( child: ChannelsBloc(
child: MessageSearchBloc( child: MessageSearchBloc(
child: Column( child: NestedScrollView(
children: [ floatHeaderSlivers: true,
SearchTextField( headerSliverBuilder: (_, __) => [
SliverToBoxAdapter(
child: SearchTextField(
controller: _controller, controller: _controller,
showCloseButton: _isSearchActive, showCloseButton: _isSearchActive,
), ),
Expanded( ),
child: AnimatedSwitcher( ],
body: AnimatedSwitcher(
duration: const Duration(milliseconds: 350), duration: const Duration(milliseconds: 350),
child: GestureDetector( child: GestureDetector(
behavior: HitTestBehavior.opaque, behavior: HitTestBehavior.opaque,
@@ -492,8 +495,7 @@ class _ChannelListPageState extends State<ChannelListPage> {
physics: AlwaysScrollableScrollPhysics(), physics: AlwaysScrollableScrollPhysics(),
child: ConstrainedBox( child: ConstrainedBox(
constraints: BoxConstraints( constraints: BoxConstraints(
minHeight: minHeight: viewportConstraints.maxHeight,
viewportConstraints.maxHeight,
), ),
child: Center( child: Center(
child: Column( child: Column(
@@ -554,8 +556,7 @@ class _ChannelListPageState extends State<ChannelListPage> {
), ),
channelWidget: ChannelPage(), channelWidget: ChannelPage(),
onViewInfoTap: (channel) { onViewInfoTap: (channel) {
if (channel.memberCount == 2 && if (channel.memberCount == 2 && channel.isDistinct) {
channel.isDistinct) {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
@@ -583,8 +584,6 @@ class _ChannelListPageState extends State<ChannelListPage> {
), ),
), ),
), ),
],
),
), ),
), ),
); );
@@ -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(
sigmaX: effect.sigmaX,
sigmaY: effect.sigmaY,
),
child: Container(
height: 1, height: 1,
color: effect.color.withOpacity(0.08), color: effect.color.withOpacity(effect.alpha ?? 1.0),
),
); );
} }
@@ -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,29 +174,40 @@ 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;
final noOfRowToShowInitially =
widget.mediaAttachments.length > crossAxisCount ? 2 : 1;
final size = MediaQuery.of(context).size;
final initialChildSize =
48 + (size.width * noOfRowToShowInitially) / crossAxisCount;
return DraggableScrollableSheet(
expand: false,
initialChildSize: initialChildSize / size.height,
builder: (context, scrollController) {
return SingleChildScrollView(
controller: scrollController,
child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Container( Stack(
decoration: BoxDecoration(
color: StreamChatTheme.of(context).colorTheme.white,
borderRadius: BorderRadius.only(
topRight: Radius.circular(16.0),
topLeft: Radius.circular(16.0),
)),
child: Stack(
children: [ children: [
Center( Center(
child: Padding( child: Padding(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: Text( child: Text(
'Photos', 'Photos',
style: style: StreamChatTheme.of(context)
StreamChatTheme.of(context).textTheme.headlineBold, .textTheme
.headlineBold,
), ),
), ),
), ),
@@ -211,24 +222,27 @@ class _ImageFooterState extends State<ImageFooter> {
), ),
], ],
), ),
), Flexible(
Container(
color: StreamChatTheme.of(context).colorTheme.white,
child: GridView.builder( child: GridView.builder(
shrinkWrap: true, shrinkWrap: true,
physics: NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, position) { itemCount: widget.mediaAttachments.length,
padding: const EdgeInsets.all(1),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: crossAxisCount,
mainAxisSpacing: 2.0,
crossAxisSpacing: 2.0,
),
itemBuilder: (context, index) {
Widget media; Widget media;
final attachment = widget.mediaAttachments[index];
if (widget.mediaAttachments[position].type == 'video') { if (attachment.type == 'video') {
var controllerPackage = widget.videoPackages[ var controllerPackage = widget.videoPackages[
videoAttachments videoAttachments.indexOf(attachment)];
.indexOf(widget.mediaAttachments[position])];
media = InkWell( media = InkWell(
onTap: () { onTap: () => widget.mediaSelectedCallBack(index),
widget.mediaSelectedCallBack(position);
},
child: FittedBox( child: FittedBox(
fit: BoxFit.cover, fit: BoxFit.cover,
child: Chewie( child: Chewie(
@@ -238,22 +252,16 @@ class _ImageFooterState extends State<ImageFooter> {
); );
} else { } else {
media = InkWell( media = InkWell(
onTap: () { onTap: () => widget.mediaSelectedCallBack(index),
widget.mediaSelectedCallBack(position);
},
child: Padding(
padding: const EdgeInsets.all(1.0),
child: AspectRatio( child: AspectRatio(
child: CachedNetworkImage( child: CachedNetworkImage(
imageUrl: widget imageUrl: attachment.imageUrl ??
.mediaAttachments[position].imageUrl ?? attachment.assetUrl ??
widget.mediaAttachments[position].assetUrl ?? attachment.thumbUrl,
widget.mediaAttachments[position].thumbUrl,
fit: BoxFit.cover, fit: BoxFit.cover,
), ),
aspectRatio: 1.0, aspectRatio: 1.0,
), ),
),
); );
} }
@@ -262,16 +270,29 @@ class _ImageFooterState extends State<ImageFooter> {
media, media,
Padding( Padding(
padding: EdgeInsets.all(8.0), padding: EdgeInsets.all(8.0),
child: Align( child: Container(
alignment: Alignment.topLeft, 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( child: UserAvatar(
user: widget.message.user, user: widget.message.user,
constraints: BoxConstraints.tight( constraints:
Size( BoxConstraints.tight(Size(24, 24)),
24,
24,
),
),
showOnlineStatus: false, showOnlineStatus: false,
), ),
), ),
@@ -279,12 +300,12 @@ class _ImageFooterState extends State<ImageFooter> {
], ],
); );
}, },
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,32 +402,22 @@ class _MessageWidgetState extends State<MessageWidget> {
BorderRadius.zero, BorderRadius.zero,
), ),
color: _getBackgroundColor(), color: _getBackgroundColor(),
child: Padding(
padding: EdgeInsets.all(
hasFiles ? 2.0 : 0.0),
child: Column( child: Column(
crossAxisAlignment: crossAxisAlignment:
CrossAxisAlignment CrossAxisAlignment.end,
.end,
mainAxisSize: mainAxisSize:
MainAxisSize.min, MainAxisSize.min,
children: <Widget>[ children: <Widget>[
if (hasQuotedMessage) if (hasQuotedMessage)
_buildQuotedMessage(), _buildQuotedMessage(),
if (hasNonUrlAttachments) if (hasNonUrlAttachments)
_parseAttachments( _parseAttachments(),
context), if (!isGiphy)
if (widget.message.text _buildTextBubble(),
.trim()
.isNotEmpty &&
!isGiphy)
_buildTextBubble(
context),
], ],
), ),
), ),
), ),
),
if (widget.showReactionPickerIndicator) if (widget.showReactionPickerIndicator)
Positioned( Positioned(
right: 4, right: 4,
@@ -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,