feat: Added fixes and user_list_view.dart
This commit is contained in:
@@ -135,287 +135,284 @@ class _NewChatScreenState extends State<NewChatScreen> {
|
|||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
),
|
),
|
||||||
body: ValueListenableBuilder<ConnectionStatus>(
|
body: ValueListenableBuilder<ConnectionStatus>(
|
||||||
valueListenable: StreamChat.of(context).client.wsConnectionStatus,
|
valueListenable: StreamChat.of(context).client.wsConnectionStatus,
|
||||||
builder: (context, status, _) {
|
builder: (context, status, _) {
|
||||||
String statusString = '';
|
String statusString = '';
|
||||||
bool showStatus = true;
|
bool showStatus = true;
|
||||||
|
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case ConnectionStatus.connected:
|
case ConnectionStatus.connected:
|
||||||
statusString = 'Connected';
|
statusString = 'Connected';
|
||||||
showStatus = false;
|
showStatus = false;
|
||||||
break;
|
break;
|
||||||
case ConnectionStatus.connecting:
|
case ConnectionStatus.connecting:
|
||||||
statusString = 'Reconnecting...';
|
statusString = 'Reconnecting...';
|
||||||
break;
|
break;
|
||||||
case ConnectionStatus.disconnected:
|
case ConnectionStatus.disconnected:
|
||||||
statusString = 'Disconnected';
|
statusString = 'Disconnected';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return InfoTile(
|
return InfoTile(
|
||||||
showMessage: showStatus,
|
showMessage: showStatus,
|
||||||
tileAnchor: Alignment.topCenter,
|
tileAnchor: Alignment.topCenter,
|
||||||
childAnchor: Alignment.topCenter,
|
childAnchor: Alignment.topCenter,
|
||||||
message: statusString,
|
message: statusString,
|
||||||
child: StreamChannel(
|
child: StreamChannel(
|
||||||
showLoading: false,
|
showLoading: false,
|
||||||
channel: channel,
|
channel: channel,
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
ChipsInputTextField<User>(
|
ChipsInputTextField<User>(
|
||||||
key: _chipInputTextFieldStateKey,
|
key: _chipInputTextFieldStateKey,
|
||||||
controller: _controller,
|
controller: _controller,
|
||||||
focusNode: _searchFocusNode,
|
focusNode: _searchFocusNode,
|
||||||
chipBuilder: (context, user) {
|
chipBuilder: (context, user) {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
_chipInputTextFieldState.removeItem(user);
|
_chipInputTextFieldState.removeItem(user);
|
||||||
_searchFocusNode.requestFocus();
|
_searchFocusNode.requestFocus();
|
||||||
},
|
},
|
||||||
child: Stack(
|
child: Stack(
|
||||||
alignment: AlignmentDirectional.centerStart,
|
alignment: AlignmentDirectional.centerStart,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: StreamChatTheme.of(context)
|
||||||
|
.colorTheme
|
||||||
|
.greyGainsboro,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.only(left: 24),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(8, 4, 12, 4),
|
||||||
|
child: Text(
|
||||||
|
user.name,
|
||||||
|
maxLines: 1,
|
||||||
|
style: TextStyle(
|
||||||
|
color: StreamChatTheme.of(context)
|
||||||
|
.colorTheme
|
||||||
|
.black,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
foregroundDecoration: BoxDecoration(
|
||||||
|
color: StreamChatTheme.of(context)
|
||||||
|
.colorTheme
|
||||||
|
.overlay,
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
),
|
||||||
|
child: UserAvatar(
|
||||||
|
showOnlineStatus: false,
|
||||||
|
user: user,
|
||||||
|
constraints: BoxConstraints.tightFor(
|
||||||
|
height: 24,
|
||||||
|
width: 24,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
StreamSvgIcon.close(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onChipAdded: (user) {
|
||||||
|
setState(() => _selectedUsers.add(user));
|
||||||
|
},
|
||||||
|
onChipRemoved: (user) {
|
||||||
|
setState(() => _selectedUsers.remove(user));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
if (!_isSearchActive && !_selectedUsers.isNotEmpty)
|
||||||
|
Container(
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.pushNamed(
|
||||||
|
context,
|
||||||
|
Routes.NEW_GROUP_CHAT,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||||
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Container(
|
StreamNeumorphicButton(
|
||||||
decoration: BoxDecoration(
|
child: Center(
|
||||||
color: StreamChatTheme.of(context)
|
child: StreamSvgIcon.contacts(
|
||||||
.colorTheme
|
color: StreamChatTheme.of(context)
|
||||||
.greyGainsboro,
|
.colorTheme
|
||||||
borderRadius: BorderRadius.circular(12),
|
.accentBlue,
|
||||||
),
|
size: 24,
|
||||||
padding: const EdgeInsets.only(left: 24),
|
|
||||||
child: Padding(
|
|
||||||
padding:
|
|
||||||
const EdgeInsets.fromLTRB(8, 4, 12, 4),
|
|
||||||
child: Text(
|
|
||||||
user.name,
|
|
||||||
maxLines: 1,
|
|
||||||
style: TextStyle(
|
|
||||||
color: StreamChatTheme.of(context)
|
|
||||||
.colorTheme
|
|
||||||
.black,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Container(
|
SizedBox(width: 8),
|
||||||
foregroundDecoration: BoxDecoration(
|
Text(
|
||||||
color: StreamChatTheme.of(context)
|
'Create a Group',
|
||||||
.colorTheme
|
style: StreamChatTheme.of(context)
|
||||||
.overlay,
|
.textTheme
|
||||||
shape: BoxShape.circle,
|
.bodyBold,
|
||||||
),
|
|
||||||
child: UserAvatar(
|
|
||||||
showOnlineStatus: false,
|
|
||||||
user: user,
|
|
||||||
constraints: BoxConstraints.tightFor(
|
|
||||||
height: 24,
|
|
||||||
width: 24,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
StreamSvgIcon.close(),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
},
|
),
|
||||||
onChipAdded: (user) {
|
|
||||||
setState(() => _selectedUsers.add(user));
|
|
||||||
},
|
|
||||||
onChipRemoved: (user) {
|
|
||||||
setState(() => _selectedUsers.remove(user));
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
if (!_isSearchActive && !_selectedUsers.isNotEmpty)
|
if (_showUserList)
|
||||||
Container(
|
Container(
|
||||||
child: InkWell(
|
width: double.maxFinite,
|
||||||
onTap: () {
|
decoration: BoxDecoration(
|
||||||
Navigator.pushNamed(
|
gradient:
|
||||||
context,
|
StreamChatTheme.of(context).colorTheme.bgGradient,
|
||||||
Routes.NEW_GROUP_CHAT,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
StreamNeumorphicButton(
|
|
||||||
child: Center(
|
|
||||||
child: StreamSvgIcon.contacts(
|
|
||||||
color: StreamChatTheme.of(context)
|
|
||||||
.colorTheme
|
|
||||||
.accentBlue,
|
|
||||||
size: 24,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(width: 8),
|
|
||||||
Text(
|
|
||||||
'Create a Group',
|
|
||||||
style: StreamChatTheme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.bodyBold,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
if (_showUserList)
|
child: Padding(
|
||||||
Container(
|
padding: const EdgeInsets.symmetric(
|
||||||
width: double.maxFinite,
|
vertical: 8,
|
||||||
decoration: BoxDecoration(
|
horizontal: 8,
|
||||||
gradient:
|
|
||||||
StreamChatTheme.of(context).colorTheme.bgGradient,
|
|
||||||
),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
vertical: 8,
|
|
||||||
horizontal: 8,
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
_isSearchActive
|
|
||||||
? "Matches for \"$_userNameQuery\""
|
|
||||||
: 'On the platform',
|
|
||||||
style: StreamChatTheme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.footnote
|
|
||||||
.copyWith(
|
|
||||||
color: StreamChatTheme.of(context)
|
|
||||||
.colorTheme
|
|
||||||
.black
|
|
||||||
.withOpacity(.5))),
|
|
||||||
),
|
),
|
||||||
|
child: Text(
|
||||||
|
_isSearchActive
|
||||||
|
? "Matches for \"$_userNameQuery\""
|
||||||
|
: 'On the platform',
|
||||||
|
style: StreamChatTheme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.footnote
|
||||||
|
.copyWith(
|
||||||
|
color: StreamChatTheme.of(context)
|
||||||
|
.colorTheme
|
||||||
|
.black
|
||||||
|
.withOpacity(.5))),
|
||||||
),
|
),
|
||||||
Expanded(
|
),
|
||||||
child: _showUserList
|
Expanded(
|
||||||
? GestureDetector(
|
child: _showUserList
|
||||||
behavior: HitTestBehavior.opaque,
|
? GestureDetector(
|
||||||
onPanDown: (_) =>
|
behavior: HitTestBehavior.opaque,
|
||||||
FocusScope.of(context).unfocus(),
|
onPanDown: (_) => FocusScope.of(context).unfocus(),
|
||||||
child: UsersBloc(
|
child: UsersBloc(
|
||||||
child: UserListView(
|
child: UserListView(
|
||||||
selectedUsers: _selectedUsers,
|
selectedUsers: _selectedUsers,
|
||||||
groupAlphabetically:
|
groupAlphabetically:
|
||||||
_isSearchActive ? false : true,
|
_isSearchActive ? false : true,
|
||||||
onUserTap: (user, _) {
|
onUserTap: (user, _) {
|
||||||
_controller.clear();
|
_controller.clear();
|
||||||
if (!_selectedUsers.contains(user)) {
|
if (!_selectedUsers.contains(user)) {
|
||||||
_chipInputTextFieldState
|
_chipInputTextFieldState
|
||||||
..addItem(user)
|
..addItem(user)
|
||||||
..pauseItemAddition();
|
..pauseItemAddition();
|
||||||
} else {
|
} else {
|
||||||
_chipInputTextFieldState.removeItem(user);
|
_chipInputTextFieldState.removeItem(user);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
pagination: PaginationParams(
|
pagination: PaginationParams(
|
||||||
limit: 25,
|
limit: 25,
|
||||||
),
|
),
|
||||||
filter: {
|
filter: {
|
||||||
if (_userNameQuery.isNotEmpty)
|
if (_userNameQuery.isNotEmpty)
|
||||||
'name': {
|
'name': {
|
||||||
r'$autocomplete': _userNameQuery,
|
r'$autocomplete': _userNameQuery,
|
||||||
},
|
|
||||||
'id': {
|
|
||||||
r'$ne': StreamChat.of(context).user.id,
|
|
||||||
},
|
},
|
||||||
|
'id': {
|
||||||
|
r'$ne': StreamChat.of(context).user.id,
|
||||||
},
|
},
|
||||||
sort: [
|
},
|
||||||
SortOption(
|
sort: [
|
||||||
'name',
|
SortOption(
|
||||||
direction: 1,
|
'name',
|
||||||
),
|
direction: 1,
|
||||||
],
|
),
|
||||||
emptyBuilder: (_) {
|
],
|
||||||
return LayoutBuilder(
|
emptyBuilder: (_) {
|
||||||
builder: (context, viewportConstraints) {
|
return LayoutBuilder(
|
||||||
return SingleChildScrollView(
|
builder: (context, viewportConstraints) {
|
||||||
physics:
|
return SingleChildScrollView(
|
||||||
AlwaysScrollableScrollPhysics(),
|
physics:
|
||||||
child: ConstrainedBox(
|
AlwaysScrollableScrollPhysics(),
|
||||||
constraints: BoxConstraints(
|
child: ConstrainedBox(
|
||||||
minHeight:
|
constraints: BoxConstraints(
|
||||||
viewportConstraints.maxHeight,
|
minHeight:
|
||||||
),
|
viewportConstraints.maxHeight,
|
||||||
child: Center(
|
),
|
||||||
child: Column(
|
child: Center(
|
||||||
children: [
|
child: Column(
|
||||||
Padding(
|
children: [
|
||||||
padding:
|
Padding(
|
||||||
const EdgeInsets.all(
|
padding:
|
||||||
24),
|
const EdgeInsets.all(24),
|
||||||
child: StreamSvgIcon.search(
|
child: StreamSvgIcon.search(
|
||||||
size: 96,
|
size: 96,
|
||||||
color: Colors.grey,
|
color: Colors.grey,
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Text(
|
),
|
||||||
'No user matches these keywords...',
|
Text(
|
||||||
style: StreamChatTheme.of(
|
'No user matches these keywords...',
|
||||||
context)
|
style: StreamChatTheme.of(
|
||||||
.textTheme
|
context)
|
||||||
.footnote
|
.textTheme
|
||||||
.copyWith(
|
.footnote
|
||||||
color: StreamChatTheme
|
.copyWith(
|
||||||
.of(context)
|
color: StreamChatTheme
|
||||||
.colorTheme
|
.of(context)
|
||||||
.black
|
.colorTheme
|
||||||
.withOpacity(
|
.black
|
||||||
.5)),
|
.withOpacity(.5)),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
},
|
);
|
||||||
);
|
},
|
||||||
},
|
);
|
||||||
),
|
},
|
||||||
),
|
),
|
||||||
)
|
|
||||||
: FutureBuilder<bool>(
|
|
||||||
future: channel.initialized,
|
|
||||||
builder: (context, snapshot) {
|
|
||||||
if (snapshot.data == true) {
|
|
||||||
return MessageListView();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Center(
|
|
||||||
child: Text(
|
|
||||||
'No chats here yet...',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
color: StreamChatTheme.of(context)
|
|
||||||
.colorTheme
|
|
||||||
.black
|
|
||||||
.withOpacity(.5),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
)
|
||||||
MessageInput(
|
: FutureBuilder<bool>(
|
||||||
focusNode: _messageInputFocusNode,
|
future: channel.initialized,
|
||||||
preMessageSending: (message) async {
|
builder: (context, snapshot) {
|
||||||
await channel.watch();
|
if (snapshot.data == true) {
|
||||||
return message;
|
return MessageListView();
|
||||||
},
|
}
|
||||||
onMessageSent: (m) {
|
|
||||||
Navigator.pushNamedAndRemoveUntil(
|
return Center(
|
||||||
context,
|
child: Text(
|
||||||
Routes.CHANNEL_PAGE,
|
'No chats here yet...',
|
||||||
ModalRoute.withName(Routes.HOME),
|
style: TextStyle(
|
||||||
arguments: ChannelPageArgs(channel: channel),
|
fontSize: 12,
|
||||||
);
|
color: StreamChatTheme.of(context)
|
||||||
},
|
.colorTheme
|
||||||
),
|
.black
|
||||||
],
|
.withOpacity(.5),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
MessageInput(
|
||||||
|
focusNode: _messageInputFocusNode,
|
||||||
|
preMessageSending: (message) async {
|
||||||
|
await channel.watch();
|
||||||
|
return message;
|
||||||
|
},
|
||||||
|
onMessageSent: (m) {
|
||||||
|
Navigator.pushNamedAndRemoveUntil(
|
||||||
|
context,
|
||||||
|
Routes.CHANNEL_PAGE,
|
||||||
|
ModalRoute.withName(Routes.HOME),
|
||||||
|
arguments: ChannelPageArgs(channel: channel),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
}),
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -159,34 +159,36 @@ class _ChannelListViewState extends State<ChannelListView>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (!widget.pullToRefresh) {
|
var child = ChannelListCore(
|
||||||
_channelListController.loadData();
|
channelListController: _channelListController,
|
||||||
}
|
listBuilder: (context, list) {
|
||||||
|
return _buildListView(list);
|
||||||
return RefreshIndicator(
|
|
||||||
onRefresh: () async {
|
|
||||||
_channelListController.loadData();
|
|
||||||
},
|
},
|
||||||
child: ChannelListCore(
|
emptyBuilder: (BuildContext context) {
|
||||||
channelListController: _channelListController,
|
return _buildEmptyWidget();
|
||||||
listBuilder: (context, list) {
|
},
|
||||||
return _buildListView(list);
|
errorBuilder: (Error error) {
|
||||||
},
|
return _buildErrorWidget(context);
|
||||||
emptyBuilder: (BuildContext context) {
|
},
|
||||||
return _buildEmptyWidget();
|
loadingBuilder: (BuildContext context) {
|
||||||
},
|
return _buildLoadingWidget();
|
||||||
errorBuilder: (Error error) {
|
},
|
||||||
return _buildErrorWidget(context);
|
pagination: widget.pagination,
|
||||||
},
|
options: widget.options,
|
||||||
loadingBuilder: (BuildContext context) {
|
sort: widget.sort,
|
||||||
return _buildLoadingWidget();
|
filter: widget.filter,
|
||||||
},
|
|
||||||
pagination: widget.pagination,
|
|
||||||
options: widget.options,
|
|
||||||
sort: widget.sort,
|
|
||||||
filter: widget.filter,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (!widget.pullToRefresh) {
|
||||||
|
return child;
|
||||||
|
} else {
|
||||||
|
return RefreshIndicator(
|
||||||
|
onRefresh: () async {
|
||||||
|
_channelListController.loadData();
|
||||||
|
},
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildListView(
|
Widget _buildListView(
|
||||||
|
|||||||
@@ -140,209 +140,170 @@ class _UserListViewState extends State<UserListView>
|
|||||||
with WidgetsBindingObserver {
|
with WidgetsBindingObserver {
|
||||||
bool get _isListView => widget.crossAxisCount == 1;
|
bool get _isListView => widget.crossAxisCount == 1;
|
||||||
|
|
||||||
@override
|
UserListController _userListController = UserListController();
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
final usersBloc = UsersBloc.of(context);
|
|
||||||
usersBloc.queryUsers(
|
|
||||||
filter: widget.filter,
|
|
||||||
sort: widget.sort,
|
|
||||||
pagination: widget.pagination,
|
|
||||||
options: widget.options,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final usersBloc = UsersBloc.of(context);
|
var child = UserListCore(
|
||||||
|
errorBuilder: (err) {
|
||||||
if (!widget.pullToRefresh) {
|
return _buildError(err);
|
||||||
return _buildListView(usersBloc);
|
},
|
||||||
}
|
emptyBuilder: (context) {
|
||||||
|
return _buildEmpty();
|
||||||
return RefreshIndicator(
|
},
|
||||||
onRefresh: () async {
|
loadingBuilder: (context) {
|
||||||
return usersBloc.queryUsers(
|
return LayoutBuilder(
|
||||||
filter: widget.filter,
|
builder: (context, viewportConstraints) {
|
||||||
sort: widget.sort,
|
return SingleChildScrollView(
|
||||||
options: widget.options,
|
physics: AlwaysScrollableScrollPhysics(),
|
||||||
pagination: widget.pagination,
|
child: ConstrainedBox(
|
||||||
|
constraints: BoxConstraints(
|
||||||
|
minHeight: viewportConstraints.maxHeight,
|
||||||
|
),
|
||||||
|
child: Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
child: _buildListView(usersBloc),
|
listBuilder: (context, list) {
|
||||||
|
return _buildListView(list);
|
||||||
|
},
|
||||||
|
pagination: widget.pagination,
|
||||||
|
options: widget.options,
|
||||||
|
sort: widget.sort,
|
||||||
|
filter: widget.filter,
|
||||||
|
groupAlphabetically: widget.groupAlphabetically,
|
||||||
|
userListController: _userListController,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (!widget.pullToRefresh) {
|
||||||
|
return child;
|
||||||
|
} else {
|
||||||
|
return RefreshIndicator(
|
||||||
|
onRefresh: () async {
|
||||||
|
_userListController.loadData();
|
||||||
|
},
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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(
|
Widget _buildError(Error error) {
|
||||||
UsersBlocState usersBlocState,
|
print((error).stackTrace);
|
||||||
) {
|
|
||||||
return usersBlocState.usersStream.map(
|
if (widget.errorBuilder != null) {
|
||||||
(users) {
|
return widget.errorBuilder(error);
|
||||||
if (widget.groupAlphabetically) {
|
}
|
||||||
var temp = users;
|
|
||||||
if (!isListAlreadySorted) {
|
var message = error.toString();
|
||||||
temp = users..sort((curr, next) => curr.name.compareTo(next.name));
|
if (error is DioError) {
|
||||||
}
|
final dioError = error as DioError;
|
||||||
final groupedUsers = <String, List<User>>{};
|
if (dioError.type == DioErrorType.RESPONSE) {
|
||||||
for (var e in temp) {
|
message = dioError.message;
|
||||||
final alphabet = e.name[0]?.toUpperCase();
|
} else {
|
||||||
groupedUsers[alphabet] = [...groupedUsers[alphabet] ?? [], e];
|
message = 'Check your connection and retry';
|
||||||
}
|
}
|
||||||
final items = <ListItem>[];
|
}
|
||||||
for (var key in groupedUsers.keys) {
|
return Center(
|
||||||
items.add(ListHeaderItem(key));
|
child: Column(
|
||||||
items.addAll(groupedUsers[key].map((e) => ListUserItem(e)));
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
}
|
children: <Widget>[
|
||||||
return items;
|
Text.rich(
|
||||||
}
|
TextSpan(
|
||||||
return users.map((e) => ListUserItem(e)).toList();
|
children: [
|
||||||
},
|
WidgetSpan(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
right: 2.0,
|
||||||
|
),
|
||||||
|
child: Icon(Icons.error_outline),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextSpan(text: 'Error loading channels'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
style: Theme.of(context).textTheme.headline6,
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
top: 16.0,
|
||||||
|
),
|
||||||
|
child: Text(message),
|
||||||
|
),
|
||||||
|
FlatButton(
|
||||||
|
onPressed: () {
|
||||||
|
_userListController.loadData();
|
||||||
|
},
|
||||||
|
child: Text('Retry'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
StreamBuilder<List<ListItem>> _buildListView(
|
Widget _buildEmpty() {
|
||||||
UsersBlocState usersBlocState,
|
if (widget.emptyBuilder != null) {
|
||||||
) {
|
return widget.emptyBuilder(context);
|
||||||
return StreamBuilder(
|
}
|
||||||
stream: _buildUserStream(usersBlocState),
|
|
||||||
builder: (context, snapshot) {
|
|
||||||
if (snapshot.hasError) {
|
|
||||||
if (snapshot.error is Error) {
|
|
||||||
print((snapshot.error as Error).stackTrace);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (widget.errorBuilder != null) {
|
if (widget.emptyBuilder == null) {
|
||||||
return widget.errorBuilder(snapshot.error);
|
return LayoutBuilder(
|
||||||
}
|
builder: (context, viewportConstraints) {
|
||||||
|
return SingleChildScrollView(
|
||||||
var message = snapshot.error.toString();
|
physics: AlwaysScrollableScrollPhysics(),
|
||||||
if (snapshot.error is DioError) {
|
child: ConstrainedBox(
|
||||||
final dioError = snapshot.error as DioError;
|
constraints: BoxConstraints(
|
||||||
if (dioError.type == DioErrorType.RESPONSE) {
|
minHeight: viewportConstraints.maxHeight,
|
||||||
message = dioError.message;
|
),
|
||||||
} else {
|
child: Center(
|
||||||
message = 'Check your connection and retry';
|
child: Text('There are no users currently'),
|
||||||
}
|
),
|
||||||
}
|
|
||||||
return Center(
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: <Widget>[
|
|
||||||
Text.rich(
|
|
||||||
TextSpan(
|
|
||||||
children: [
|
|
||||||
WidgetSpan(
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
right: 2.0,
|
|
||||||
),
|
|
||||||
child: Icon(Icons.error_outline),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
TextSpan(text: 'Error loading channels'),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
style: Theme.of(context).textTheme.headline6,
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
top: 16.0,
|
|
||||||
),
|
|
||||||
child: Text(message),
|
|
||||||
),
|
|
||||||
FlatButton(
|
|
||||||
onPressed: () {
|
|
||||||
usersBlocState.queryUsers(
|
|
||||||
filter: widget.filter,
|
|
||||||
sort: widget.sort,
|
|
||||||
pagination: widget.pagination,
|
|
||||||
options: widget.options,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: Text('Retry'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!snapshot.hasData) {
|
Widget _buildListView(
|
||||||
return LayoutBuilder(
|
List<ListItem> items,
|
||||||
builder: (context, viewportConstraints) {
|
) {
|
||||||
return SingleChildScrollView(
|
final child = _isListView
|
||||||
physics: AlwaysScrollableScrollPhysics(),
|
? ListView.separated(
|
||||||
child: ConstrainedBox(
|
physics: AlwaysScrollableScrollPhysics(),
|
||||||
constraints: BoxConstraints(
|
itemCount: items.isNotEmpty ? items.length + 1 : items.length,
|
||||||
minHeight: viewportConstraints.maxHeight,
|
separatorBuilder: (_, index) {
|
||||||
),
|
if (widget.separatorBuilder != null) {
|
||||||
child: Center(
|
return widget.separatorBuilder(context, index);
|
||||||
child: CircularProgressIndicator(),
|
}
|
||||||
),
|
return _separatorBuilder(context, index);
|
||||||
),
|
},
|
||||||
);
|
itemBuilder: (context, index) {
|
||||||
|
return _listItemBuilder(context, index, items);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
: GridView.builder(
|
||||||
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
|
crossAxisCount: widget.crossAxisCount,
|
||||||
|
),
|
||||||
|
itemCount: items.isNotEmpty ? items.length + 1 : items.length,
|
||||||
|
physics: AlwaysScrollableScrollPhysics(),
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
return _gridItemBuilder(context, index, items);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
final items = snapshot.data;
|
return LazyLoadScrollView(
|
||||||
|
onEndOfPage: () async {
|
||||||
if (items.isEmpty && widget.emptyBuilder != null) {
|
return _userListController.paginateData();
|
||||||
return widget.emptyBuilder(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (items.isEmpty && widget.emptyBuilder == null) {
|
|
||||||
return LayoutBuilder(
|
|
||||||
builder: (context, viewportConstraints) {
|
|
||||||
return SingleChildScrollView(
|
|
||||||
physics: AlwaysScrollableScrollPhysics(),
|
|
||||||
child: ConstrainedBox(
|
|
||||||
constraints: BoxConstraints(
|
|
||||||
minHeight: viewportConstraints.maxHeight,
|
|
||||||
),
|
|
||||||
child: Center(
|
|
||||||
child: Text('There are no users currently'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final child = _isListView
|
|
||||||
? ListView.separated(
|
|
||||||
physics: AlwaysScrollableScrollPhysics(),
|
|
||||||
itemCount: items.isNotEmpty ? items.length + 1 : items.length,
|
|
||||||
separatorBuilder: (_, index) {
|
|
||||||
if (widget.separatorBuilder != null) {
|
|
||||||
return widget.separatorBuilder(context, index);
|
|
||||||
}
|
|
||||||
return _separatorBuilder(context, index);
|
|
||||||
},
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
return _listItemBuilder(context, index, items);
|
|
||||||
},
|
|
||||||
)
|
|
||||||
: GridView.builder(
|
|
||||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
|
||||||
crossAxisCount: widget.crossAxisCount,
|
|
||||||
),
|
|
||||||
itemCount: items.isNotEmpty ? items.length + 1 : items.length,
|
|
||||||
physics: AlwaysScrollableScrollPhysics(),
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
return _gridItemBuilder(context, index, items);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
return LazyLoadScrollView(
|
|
||||||
onEndOfPage: () async {
|
|
||||||
return _listenUserPagination(usersBlocState);
|
|
||||||
},
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
|
child: child,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -481,33 +442,4 @@ class _UserListViewState extends State<UserListView>
|
|||||||
color: StreamChatTheme.of(context).colorTheme.greyWhisper,
|
color: StreamChatTheme.of(context).colorTheme.greyWhisper,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _listenUserPagination(UsersBlocState usersProvider) {
|
|
||||||
usersProvider.queryUsers(
|
|
||||||
filter: widget.filter,
|
|
||||||
sort: widget.sort,
|
|
||||||
pagination: widget.pagination.copyWith(
|
|
||||||
offset: usersProvider.users?.length ?? 0,
|
|
||||||
),
|
|
||||||
options: widget.options,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void didUpdateWidget(UserListView oldWidget) {
|
|
||||||
super.didUpdateWidget(oldWidget);
|
|
||||||
if (widget.filter?.toString() != oldWidget.filter?.toString() ||
|
|
||||||
jsonEncode(widget.sort) != jsonEncode(oldWidget.sort) ||
|
|
||||||
widget.pagination?.toJson()?.toString() !=
|
|
||||||
oldWidget.pagination?.toJson()?.toString() ||
|
|
||||||
widget.options?.toString() != oldWidget.options?.toString()) {
|
|
||||||
final usersBloc = UsersBloc.of(context);
|
|
||||||
usersBloc.queryUsers(
|
|
||||||
filter: widget.filter,
|
|
||||||
sort: widget.sort,
|
|
||||||
pagination: widget.pagination,
|
|
||||||
options: widget.options,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ class UserListCore extends StatefulWidget {
|
|||||||
this.options,
|
this.options,
|
||||||
this.sort,
|
this.sort,
|
||||||
this.pagination,
|
this.pagination,
|
||||||
this.pullToRefresh = true,
|
|
||||||
this.groupAlphabetically = false,
|
this.groupAlphabetically = false,
|
||||||
@required this.errorBuilder,
|
@required this.errorBuilder,
|
||||||
@required this.emptyBuilder,
|
@required this.emptyBuilder,
|
||||||
@@ -89,9 +88,6 @@ class UserListCore extends StatefulWidget {
|
|||||||
/// message_limit: how many messages should be included to each channel
|
/// message_limit: how many messages should be included to each channel
|
||||||
final PaginationParams pagination;
|
final PaginationParams pagination;
|
||||||
|
|
||||||
/// Set it to false to disable the pull-to-refresh widget
|
|
||||||
final bool pullToRefresh;
|
|
||||||
|
|
||||||
/// Set it to true to group users by their first character
|
/// Set it to true to group users by their first character
|
||||||
///
|
///
|
||||||
/// defaults to false
|
/// defaults to false
|
||||||
@@ -124,21 +120,7 @@ class _UserListCoreState extends State<UserListCore>
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final usersBloc = UsersBloc.of(context);
|
final usersBloc = UsersBloc.of(context);
|
||||||
|
|
||||||
if (!widget.pullToRefresh) {
|
return _buildListView(usersBloc);
|
||||||
return _buildListView(usersBloc);
|
|
||||||
}
|
|
||||||
|
|
||||||
return RefreshIndicator(
|
|
||||||
onRefresh: () async {
|
|
||||||
return usersBloc.queryUsers(
|
|
||||||
filter: widget.filter,
|
|
||||||
sort: widget.sort,
|
|
||||||
options: widget.options,
|
|
||||||
pagination: widget.pagination,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: _buildListView(usersBloc),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get isListAlreadySorted =>
|
bool get isListAlreadySorted =>
|
||||||
|
|||||||
Reference in New Issue
Block a user