This commit is contained in:
Deven Joshi
2021-05-05 17:35:41 +05:30
parent 8a28f8c2b3
commit dca6b10c41
@@ -36,10 +36,13 @@ typedef UserItemBuilder = Widget Function(BuildContext, User, bool);
/// ``` /// ```
/// ///
/// ///
/// Make sure to have a [UsersBloc] ancestor in order to provide the information about the users. /// Make sure to have a [UsersBloc] ancestor in order to provide the
/// The widget uses a [ListView.separated], [GridView.builder] to render the list, grid of channels. /// information about the users.
/// The widget uses a [ListView.separated], [GridView.builder] to render the
/// list, grid of channels.
/// ///
/// The widget components render the ui based on the first ancestor of type [StreamChatTheme]. /// The widget components render the ui based on the first ancestor of
/// type [StreamChatTheme].
/// Modify it to change the widget appearance. /// Modify it to change the widget appearance.
class UserListView extends StatefulWidget { class UserListView extends StatefulWidget {
/// Instantiate a new UserListView /// Instantiate a new UserListView
@@ -81,8 +84,10 @@ class UserListView extends StatefulWidget {
final Map<String, dynamic>? options; final Map<String, dynamic>? options;
/// The sorting used for the channels matching the filters. /// The sorting used for the channels matching the filters.
/// Sorting is based on field and direction, multiple sorting options can be provided. /// Sorting is based on field and direction, multiple sorting options can
/// You can sort based on last_updated, last_message_at, updated_at, created_at or member_count. /// be provided.
/// You can sort based on last_updated, last_message_at, updated_at, created_
/// at or member_count.
/// Direction can be ascending or descending. /// Direction can be ascending or descending.
final List<SortOption>? sort; final List<SortOption>? sort;
@@ -115,7 +120,8 @@ class UserListView extends StatefulWidget {
/// Set it to false to disable the pull-to-refresh widget /// Set it to false to disable the pull-to-refresh widget
final bool pullToRefresh; final bool pullToRefresh;
/// Sets a blue trailing checkMark in [ListUserItem] for all the [selectedUsers] /// Sets a blue trailing checkMark in [ListUserItem] for all the
/// [selectedUsers]
final Set<User>? selectedUsers; final Set<User>? selectedUsers;
/// Set it to true to group users by their first character /// Set it to true to group users by their first character
@@ -153,35 +159,25 @@ class _UserListViewState extends State<UserListView>
Widget build(BuildContext context) { Widget build(BuildContext context) {
final child = UserListCore( final child = UserListCore(
errorBuilder: widget.errorBuilder as Widget Function(Object)? ?? errorBuilder: widget.errorBuilder as Widget Function(Object)? ??
(err) { (err) => _buildError(err as Error),
return _buildError(err as Error); emptyBuilder: widget.emptyBuilder ?? (context) => _buildEmpty(),
},
emptyBuilder: widget.emptyBuilder ??
(context) {
return _buildEmpty();
},
loadingBuilder: widget.loadingBuilder ?? loadingBuilder: widget.loadingBuilder ??
(context) { (context) => LayoutBuilder(
return LayoutBuilder( builder: (context, viewportConstraints) =>
builder: (context, viewportConstraints) { SingleChildScrollView(
return SingleChildScrollView( physics: const AlwaysScrollableScrollPhysics(),
physics: AlwaysScrollableScrollPhysics(),
child: ConstrainedBox( child: ConstrainedBox(
constraints: BoxConstraints( constraints: BoxConstraints(
minHeight: viewportConstraints.maxHeight, minHeight: viewportConstraints.maxHeight,
), ),
child: Center( child: const Center(
child: CircularProgressIndicator(), child: CircularProgressIndicator(),
), ),
), ),
); ),
}, ),
); listBuilder:
}, widget.listBuilder ?? (context, list) => _buildListView(list),
listBuilder: widget.listBuilder ??
(context, list) {
return _buildListView(list);
},
pagination: widget.pagination, pagination: widget.pagination,
options: widget.options, options: widget.options,
sort: widget.sort, sort: widget.sort,
@@ -204,7 +200,7 @@ class _UserListViewState extends State<UserListView>
widget.sort?.any((e) => e.field == 'name' && e.direction == 1) ?? false; widget.sort?.any((e) => e.field == 'name' && e.direction == 1) ?? false;
Widget _buildError(Error error) { Widget _buildError(Error error) {
print((error).stackTrace); print(error.stackTrace);
var message = error.toString(); var message = error.toString();
if (error is DioError) { if (error is DioError) {
@@ -220,11 +216,11 @@ class _UserListViewState extends State<UserListView>
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[ children: <Widget>[
Text.rich( Text.rich(
TextSpan( const TextSpan(
children: [ children: [
WidgetSpan( WidgetSpan(
child: Padding( child: Padding(
padding: const EdgeInsets.only( padding: EdgeInsets.only(
right: 2, right: 2,
), ),
child: Icon(Icons.error_outline), child: Icon(Icons.error_outline),
@@ -243,37 +239,33 @@ class _UserListViewState extends State<UserListView>
), ),
TextButton( TextButton(
onPressed: () => _userListController.loadData!(), onPressed: () => _userListController.loadData!(),
child: Text('Retry'), child: const Text('Retry'),
), ),
], ],
), ),
); );
} }
Widget _buildEmpty() { Widget _buildEmpty() => LayoutBuilder(
return LayoutBuilder( builder: (context, viewportConstraints) => SingleChildScrollView(
builder: (context, viewportConstraints) { physics: const AlwaysScrollableScrollPhysics(),
return SingleChildScrollView(
physics: AlwaysScrollableScrollPhysics(),
child: ConstrainedBox( child: ConstrainedBox(
constraints: BoxConstraints( constraints: BoxConstraints(
minHeight: viewportConstraints.maxHeight, minHeight: viewportConstraints.maxHeight,
), ),
child: Center( child: const Center(
child: Text('There are no users currently'), child: Text('There are no users currently'),
), ),
), ),
); ),
}, );
);
}
Widget _buildListView( Widget _buildListView(
List<ListItem> items, List<ListItem> items,
) { ) {
final child = _isListView final child = _isListView
? ListView.separated( ? ListView.separated(
physics: AlwaysScrollableScrollPhysics(), physics: const AlwaysScrollableScrollPhysics(),
itemCount: items.isNotEmpty ? items.length + 1 : items.length, itemCount: items.isNotEmpty ? items.length + 1 : items.length,
separatorBuilder: (_, index) { separatorBuilder: (_, index) {
if (widget.separatorBuilder != null) { if (widget.separatorBuilder != null) {
@@ -281,19 +273,17 @@ class _UserListViewState extends State<UserListView>
} }
return _separatorBuilder(context, index); return _separatorBuilder(context, index);
}, },
itemBuilder: (context, index) { itemBuilder: (context, index) =>
return _listItemBuilder(context, index, items); _listItemBuilder(context, index, items),
},
) )
: GridView.builder( : GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: widget.crossAxisCount, crossAxisCount: widget.crossAxisCount,
), ),
itemCount: items.isNotEmpty ? items.length + 1 : items.length, itemCount: items.isNotEmpty ? items.length + 1 : items.length,
physics: AlwaysScrollableScrollPhysics(), physics: const AlwaysScrollableScrollPhysics(),
itemBuilder: (context, index) { itemBuilder: (context, index) =>
return _gridItemBuilder(context, index, items); _gridItemBuilder(context, index, items),
},
); );
return LazyLoadScrollView( return LazyLoadScrollView(
@@ -307,24 +297,21 @@ class _UserListViewState extends State<UserListView>
if (i < items.length) { if (i < items.length) {
final item = items[i]; final item = items[i];
return item.when( return item.when(
headerItem: (header) { headerItem: (header) => Container(
return Container( key: ValueKey<String>('HEADER-$header'),
key: ValueKey<String>('HEADER-$header'), color: StreamChatTheme.of(context).colorTheme.black.withOpacity(0.05),
color: child: Padding(
StreamChatTheme.of(context).colorTheme.black.withOpacity(0.05), padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
child: Padding( child: Text(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6), header,
child: Text( style: TextStyle(
header, fontWeight: FontWeight.bold,
style: TextStyle( fontSize: 14.5,
fontWeight: FontWeight.bold, color: StreamChatTheme.of(context).colorTheme.grey,
fontSize: 14.5,
color: StreamChatTheme.of(context).colorTheme.grey,
),
), ),
), ),
); ),
}, ),
userItem: (user) { userItem: (user) {
final selected = widget.selectedUsers?.contains(user) ?? false; final selected = widget.selectedUsers?.contains(user) ?? false;
return Container( return Container(
@@ -351,7 +338,7 @@ class _UserListViewState extends State<UserListView>
if (i < items.length) { if (i < items.length) {
final item = items[i]; final item = items[i];
return item.when( return item.when(
headerItem: (_) => Offstage(), headerItem: (_) => const Offstage(),
userItem: (user) { userItem: (user) {
final selected = widget.selectedUsers?.contains(user) ?? false; final selected = widget.selectedUsers?.contains(user) ?? false;
return Container( return Container(
@@ -365,11 +352,12 @@ class _UserListViewState extends State<UserListView>
user: user, user: user,
borderRadius: BorderRadius.circular(32), borderRadius: BorderRadius.circular(32),
selected: selected, selected: selected,
constraints: BoxConstraints.tightFor( constraints: const BoxConstraints.tightFor(
height: 64, height: 64,
width: 64, width: 64,
), ),
onlineIndicatorConstraints: BoxConstraints.tightFor( onlineIndicatorConstraints:
const BoxConstraints.tightFor(
height: 12, height: 12,
width: 12, width: 12,
), ),
@@ -377,7 +365,7 @@ class _UserListViewState extends State<UserListView>
widget.onUserTap!(user, widget.userWidget), widget.onUserTap!(user, widget.userWidget),
onLongPress: widget.onUserLongPress, onLongPress: widget.onUserLongPress,
), ),
SizedBox(height: 4), const SizedBox(height: 4),
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 8), padding: const EdgeInsets.symmetric(horizontal: 8),
child: Text( child: Text(
@@ -385,7 +373,7 @@ class _UserListViewState extends State<UserListView>
textAlign: TextAlign.center, textAlign: TextAlign.center,
maxLines: 2, maxLines: 2,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: TextStyle( style: const TextStyle(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
fontSize: 12, fontSize: 12,
), ),
@@ -401,39 +389,38 @@ class _UserListViewState extends State<UserListView>
} }
} }
Widget _buildQueryProgressIndicator(context, UsersBlocState usersProvider) { Widget _buildQueryProgressIndicator(context, UsersBlocState usersProvider) =>
return StreamBuilder<bool>( StreamBuilder<bool>(
stream: usersProvider.queryUsersLoading, stream: usersProvider.queryUsersLoading,
initialData: false, initialData: false,
builder: (context, snapshot) { builder: (context, snapshot) {
if (snapshot.hasError) { if (snapshot.hasError) {
return Container( return Container(
color: StreamChatTheme.of(context) color: StreamChatTheme.of(context)
.colorTheme .colorTheme
.accentRed .accentRed
.withOpacity(.2), .withOpacity(.2),
child: Padding( child: const Padding(
padding: const EdgeInsets.symmetric(vertical: 16), padding: EdgeInsets.symmetric(vertical: 16),
child: Center( child: Center(
child: Text('Error loading users'), child: Text('Error loading users'),
),
), ),
);
}
return Container(
height: 100,
padding: const EdgeInsets.all(32),
child: Center(
child: snapshot.data!
? const CircularProgressIndicator()
: Container(),
), ),
); );
} });
return Container(
height: 100,
padding: EdgeInsets.all(32),
child: Center(
child: snapshot.data! ? CircularProgressIndicator() : Container(),
),
);
});
}
Widget _separatorBuilder(context, i) { Widget _separatorBuilder(context, i) => Container(
return Container( height: 1,
height: 1, color: StreamChatTheme.of(context).colorTheme.greyWhisper,
color: StreamChatTheme.of(context).colorTheme.greyWhisper, );
);
}
} }