feat(ui, core): add remaining v4 list-views
Signed-off-by: xsahil03x <[email protected]>
This commit is contained in:
@@ -0,0 +1,297 @@
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stream_chat/stream_chat.dart';
|
||||
import 'package:stream_chat_flutter_core/src/paged_value_notifier.dart';
|
||||
|
||||
/// Signature for a function that creates a widget for a given index, e.g., in a
|
||||
/// [PagedValueListView].
|
||||
typedef PagedValueListViewIndexedWidgetBuilder<T> = Widget Function(
|
||||
BuildContext context,
|
||||
List<T> values,
|
||||
int index,
|
||||
);
|
||||
|
||||
/// Signature for the item builder that creates the children of the
|
||||
/// [PagedValueListView].
|
||||
typedef PagedValueListViewLoadMoreErrorBuilder = Widget Function(
|
||||
BuildContext context,
|
||||
StreamChatError error,
|
||||
);
|
||||
|
||||
/// A [ListView] that loads more pages when the user scrolls to the end of the
|
||||
/// list.
|
||||
///
|
||||
/// Use [loadMoreTriggerIndex] to set the index of the item that triggers the
|
||||
/// loading of the next page.
|
||||
class PagedValueListView<K, V> extends StatefulWidget {
|
||||
/// Creates a new instance of [PagedValueListView] widget.
|
||||
const PagedValueListView({
|
||||
Key? key,
|
||||
required this.controller,
|
||||
required this.itemBuilder,
|
||||
required this.separatorBuilder,
|
||||
required this.emptyBuilder,
|
||||
required this.loadMoreErrorBuilder,
|
||||
required this.loadMoreIndicatorBuilder,
|
||||
required this.loadingBuilder,
|
||||
required this.errorBuilder,
|
||||
this.loadMoreTriggerIndex = 3,
|
||||
this.padding,
|
||||
this.physics,
|
||||
this.reverse = false,
|
||||
this.scrollController,
|
||||
this.primary,
|
||||
this.scrollBehavior,
|
||||
this.shrinkWrap = false,
|
||||
this.cacheExtent,
|
||||
this.dragStartBehavior = DragStartBehavior.start,
|
||||
this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual,
|
||||
this.restorationId,
|
||||
}) : super(key: key);
|
||||
|
||||
/// The [PagedValueNotifier] used to control the list of items.
|
||||
final PagedValueNotifier<K, V> controller;
|
||||
|
||||
/// A builder that is called to build items in the [ListView].
|
||||
///
|
||||
/// The `value` parameter is the [V] at this position in the list.
|
||||
final PagedValueListViewIndexedWidgetBuilder<V> itemBuilder;
|
||||
|
||||
/// A builder that is called to build the list separator.
|
||||
final PagedValueListViewIndexedWidgetBuilder<V> separatorBuilder;
|
||||
|
||||
/// A builder that is called to build the empty state of the list.
|
||||
final WidgetBuilder emptyBuilder;
|
||||
|
||||
/// A builder that is called to build the load more error state of the list.
|
||||
final PagedValueListViewLoadMoreErrorBuilder loadMoreErrorBuilder;
|
||||
|
||||
/// A builder that is called to build the load more indicator of the list.
|
||||
final WidgetBuilder loadMoreIndicatorBuilder;
|
||||
|
||||
/// A builder that is called to build the loading state of the list.
|
||||
final WidgetBuilder loadingBuilder;
|
||||
|
||||
/// A builder that is called to build the error state of the list.
|
||||
final Widget Function(BuildContext, StreamChatError) errorBuilder;
|
||||
|
||||
/// The index to take into account when triggering [controller.loadMore].
|
||||
final int loadMoreTriggerIndex;
|
||||
|
||||
/// The amount of space by which to inset the children.
|
||||
final EdgeInsetsGeometry? padding;
|
||||
|
||||
/// {@template flutter.widgets.scroll_view.reverse}
|
||||
/// Whether the scroll view scrolls in the reading direction.
|
||||
///
|
||||
/// For example, if [scrollDirection] is [Axis.vertical], then the scroll view
|
||||
/// scrolls from top to bottom when [reverse] is false and from bottom to top
|
||||
/// when [reverse] is true.
|
||||
///
|
||||
/// Defaults to false.
|
||||
/// {@endtemplate}
|
||||
final bool reverse;
|
||||
|
||||
/// {@template flutter.widgets.scroll_view.controller}
|
||||
/// An object that can be used to control the position to which this scroll
|
||||
/// view is scrolled.
|
||||
///
|
||||
/// Must be null if [primary] is true.
|
||||
///
|
||||
/// A [ScrollController] serves several purposes. It can be used to control
|
||||
/// the initial scroll position (see [ScrollController.initialScrollOffset]).
|
||||
/// It can be used to control whether the scroll view should automatically
|
||||
/// save and restore its scroll position in the [PageStorage] (see
|
||||
/// [ScrollController.keepScrollOffset]). It can be used to read the current
|
||||
/// scroll position (see [ScrollController.offset]), or change it (see
|
||||
/// [ScrollController.animateTo]).
|
||||
/// {@endtemplate}
|
||||
final ScrollController? scrollController;
|
||||
|
||||
/// {@template flutter.widgets.scroll_view.primary}
|
||||
/// Whether this is the primary scroll view associated with the parent
|
||||
/// [PrimaryScrollController].
|
||||
///
|
||||
/// When this is true, the scroll view is scrollable even if it does not have
|
||||
/// sufficient content to actually scroll. Otherwise, by default the user can
|
||||
/// only scroll the view if it has sufficient content. See [physics].
|
||||
///
|
||||
/// Also when true, the scroll view is used for default [ScrollAction]s. If a
|
||||
/// ScrollAction is not handled by an otherwise focused part of the
|
||||
/// application, the ScrollAction will be evaluated using this scroll view,
|
||||
/// for example, when executing [Shortcuts] key events like page up and down.
|
||||
///
|
||||
/// On iOS, this also identifies the scroll view that will scroll to top in
|
||||
/// response to a tap in the status bar.
|
||||
/// {@endtemplate}
|
||||
///
|
||||
/// Defaults to true when [scrollController] is null.
|
||||
final bool? primary;
|
||||
|
||||
/// {@macro flutter.widgets.shadow.scrollBehavior}
|
||||
///
|
||||
/// [ScrollBehavior]s also provide [ScrollPhysics]. If an explicit
|
||||
/// [ScrollPhysics] is provided in [physics], it will take precedence,
|
||||
/// followed by [scrollBehavior], and then the inherited ancestor
|
||||
/// [ScrollBehavior].
|
||||
final ScrollBehavior? scrollBehavior;
|
||||
|
||||
/// {@template flutter.widgets.scroll_view.shrinkWrap}
|
||||
/// Whether the extent of the scroll view in the [scrollDirection] should be
|
||||
/// determined by the contents being viewed.
|
||||
///
|
||||
/// If the scroll view does not shrink wrap, then the scroll view will expand
|
||||
/// to the maximum allowed size in the [scrollDirection]. If the scroll view
|
||||
/// has unbounded constraints in the [scrollDirection], then [shrinkWrap] must
|
||||
/// be true.
|
||||
///
|
||||
/// Shrink wrapping the content of the scroll view is significantly more
|
||||
/// expensive than expanding to the maximum allowed size because the content
|
||||
/// can expand and contract during scrolling, which means the size of the
|
||||
/// scroll view needs to be recomputed whenever the scroll position changes.
|
||||
///
|
||||
/// Defaults to false.
|
||||
/// {@endtemplate}
|
||||
final bool shrinkWrap;
|
||||
|
||||
/// {@template flutter.widgets.scroll_view.physics}
|
||||
/// How the scroll view should respond to user input.
|
||||
///
|
||||
/// For example, determines how the scroll view continues to animate after the
|
||||
/// user stops dragging the scroll view.
|
||||
///
|
||||
/// Defaults to matching platform conventions. Furthermore, if [primary] is
|
||||
/// false, then the user cannot scroll if there is insufficient content to
|
||||
/// scroll, while if [primary] is true, they can always attempt to scroll.
|
||||
///
|
||||
/// To force the scroll view to always be scrollable even if there is
|
||||
/// insufficient content, as if [primary] was true but without necessarily
|
||||
/// setting it to true, provide an [AlwaysScrollableScrollPhysics] physics
|
||||
/// object, as in:
|
||||
///
|
||||
/// ```dart
|
||||
/// physics: const AlwaysScrollableScrollPhysics(),
|
||||
/// ```
|
||||
///
|
||||
/// To force the scroll view to use the default platform conventions and not
|
||||
/// be scrollable if there is insufficient content, regardless of the value of
|
||||
/// [primary], provide an explicit [ScrollPhysics] object, as in:
|
||||
///
|
||||
/// ```dart
|
||||
/// physics: const ScrollPhysics(),
|
||||
/// ```
|
||||
///
|
||||
/// The physics can be changed dynamically (by providing a new object in a
|
||||
/// subsequent build), but new physics will only take effect if the _class_ of
|
||||
/// the provided object changes. Merely constructing a new instance with a
|
||||
/// different configuration is insufficient to cause the physics to be
|
||||
/// reapplied. (This is because the final object used is generated
|
||||
/// dynamically, which can be relatively expensive, and it would be
|
||||
/// inefficient to speculatively create this object each frame to see if the
|
||||
/// physics should be updated.)
|
||||
/// {@endtemplate}
|
||||
///
|
||||
/// If an explicit [ScrollBehavior] is provided to [scrollBehavior], the
|
||||
/// [ScrollPhysics] provided by that behavior will take precedence after
|
||||
/// [physics].
|
||||
final ScrollPhysics? physics;
|
||||
|
||||
/// {@macro flutter.rendering.RenderViewportBase.cacheExtent}
|
||||
final double? cacheExtent;
|
||||
|
||||
/// {@macro flutter.widgets.scrollable.dragStartBehavior}
|
||||
final DragStartBehavior dragStartBehavior;
|
||||
|
||||
/// {@template flutter.widgets.scroll_view.keyboardDismissBehavior}
|
||||
/// [ScrollViewKeyboardDismissBehavior] the defines how this [ScrollView] will
|
||||
/// dismiss the keyboard automatically.
|
||||
/// {@endtemplate}
|
||||
final ScrollViewKeyboardDismissBehavior keyboardDismissBehavior;
|
||||
|
||||
/// {@macro flutter.widgets.scrollable.restorationId}
|
||||
final String? restorationId;
|
||||
|
||||
@override
|
||||
State<PagedValueListView<K, V>> createState() =>
|
||||
_PagedValueListViewState<K, V>();
|
||||
}
|
||||
|
||||
class _PagedValueListViewState<K, V> extends State<PagedValueListView<K, V>> {
|
||||
PagedValueNotifier<K, V> get _controller => widget.controller;
|
||||
|
||||
// Avoids duplicate requests on rebuilds.
|
||||
bool _hasRequestedNextPage = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller.doInitialLoad();
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant PagedValueListView<K, V> oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (_controller != oldWidget.controller) {
|
||||
// reset duplicate requests flag
|
||||
_hasRequestedNextPage = false;
|
||||
_controller.doInitialLoad();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => PagedValueListenableBuilder<K, V>(
|
||||
valueListenable: _controller,
|
||||
builder: (context, value, _) => value.when(
|
||||
(items, nextPageKey, error) {
|
||||
if (items.isEmpty) {
|
||||
return widget.emptyBuilder(context);
|
||||
}
|
||||
|
||||
return ListView.separated(
|
||||
padding: widget.padding,
|
||||
physics: widget.physics,
|
||||
reverse: widget.reverse,
|
||||
controller: widget.scrollController,
|
||||
primary: widget.primary,
|
||||
shrinkWrap: widget.shrinkWrap,
|
||||
keyboardDismissBehavior: widget.keyboardDismissBehavior,
|
||||
restorationId: widget.restorationId,
|
||||
dragStartBehavior: widget.dragStartBehavior,
|
||||
cacheExtent: widget.cacheExtent,
|
||||
itemCount: value.itemCount,
|
||||
separatorBuilder: (context, index) =>
|
||||
widget.separatorBuilder(context, items, index),
|
||||
itemBuilder: (context, index) {
|
||||
if (!_hasRequestedNextPage) {
|
||||
final newPageRequestTriggerIndex =
|
||||
items.length - widget.loadMoreTriggerIndex;
|
||||
final isBuildingTriggerIndexItem =
|
||||
index == newPageRequestTriggerIndex;
|
||||
if (nextPageKey != null && isBuildingTriggerIndexItem) {
|
||||
// Schedules the request for the end of this frame.
|
||||
WidgetsBinding.instance?.addPostFrameCallback((_) async {
|
||||
if (error == null) {
|
||||
await _controller.loadMore(nextPageKey);
|
||||
}
|
||||
_hasRequestedNextPage = false;
|
||||
});
|
||||
_hasRequestedNextPage = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (index == items.length) {
|
||||
if (error != null) {
|
||||
return widget.loadMoreErrorBuilder(context, error);
|
||||
}
|
||||
return widget.loadMoreIndicatorBuilder(context);
|
||||
}
|
||||
|
||||
return widget.itemBuilder(context, items, index);
|
||||
},
|
||||
);
|
||||
},
|
||||
loading: () => widget.loadingBuilder(context),
|
||||
error: (error) => widget.errorBuilder(context, error),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
import 'dart:async';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:stream_chat/stream_chat.dart' hide Success;
|
||||
import 'package:stream_chat_flutter_core/src/paged_value_notifier.dart';
|
||||
|
||||
/// The default channel page limit to load.
|
||||
const defaultMessageSearchPagedLimit = 10;
|
||||
|
||||
const _kDefaultBackendPaginationLimit = 30;
|
||||
|
||||
/// A controller for a user list.
|
||||
///
|
||||
/// This class lets you perform tasks such as:
|
||||
/// * Load initial data.
|
||||
/// * Load more data using [loadMore].
|
||||
/// * Replace the previously loaded users.
|
||||
class StreamMessageSearchListController
|
||||
extends PagedValueNotifier<String, GetMessageResponse> {
|
||||
/// Creates a Stream user list controller.
|
||||
///
|
||||
/// * `client` is the Stream chat client to use for the channels list.
|
||||
///
|
||||
/// * `filter` is the query filters to use.
|
||||
///
|
||||
/// * `sort` is the sorting used for the users matching the filters.
|
||||
///
|
||||
/// * `presence` sets whether you'll receive user presence updates via the
|
||||
/// websocket events.
|
||||
///
|
||||
/// * `limit` is the limit to apply to the user list.
|
||||
StreamMessageSearchListController({
|
||||
required this.client,
|
||||
required this.filter,
|
||||
this.messageFilter,
|
||||
this.searchQuery,
|
||||
this.sort,
|
||||
this.limit = defaultMessageSearchPagedLimit,
|
||||
}) : assert(
|
||||
messageFilter != null || searchQuery != null,
|
||||
'Either messageFilter or searchQuery must be provided',
|
||||
),
|
||||
assert(
|
||||
messageFilter == null || searchQuery == null,
|
||||
'Only one of messageFilter or searchQuery can be provided',
|
||||
),
|
||||
_activeFilter = filter,
|
||||
_activeMessageFilter = messageFilter,
|
||||
_activeSearchQuery = searchQuery,
|
||||
_activeSort = sort,
|
||||
super(const PagedValue.loading());
|
||||
|
||||
/// Creates a [StreamUserListController] from the passed [value].
|
||||
StreamMessageSearchListController.fromValue(
|
||||
PagedValue<String, GetMessageResponse> value, {
|
||||
required this.client,
|
||||
required this.filter,
|
||||
this.messageFilter,
|
||||
this.searchQuery,
|
||||
this.sort,
|
||||
this.limit = defaultMessageSearchPagedLimit,
|
||||
}) : assert(
|
||||
messageFilter != null || searchQuery != null,
|
||||
'Either messageFilter or searchQuery must be provided',
|
||||
),
|
||||
assert(
|
||||
messageFilter == null || searchQuery == null,
|
||||
'Only one of messageFilter or searchQuery can be provided',
|
||||
),
|
||||
_activeFilter = filter,
|
||||
_activeMessageFilter = messageFilter,
|
||||
_activeSearchQuery = searchQuery,
|
||||
_activeSort = sort,
|
||||
super(value);
|
||||
|
||||
/// The client to use for the channels list.
|
||||
final StreamChatClient client;
|
||||
|
||||
/// The query filters to use.
|
||||
///
|
||||
/// You can query on any of the custom fields you've defined on the [User].
|
||||
///
|
||||
/// You can also filter other built-in channel fields.
|
||||
final Filter filter;
|
||||
Filter _activeFilter;
|
||||
|
||||
/// The message query filters to use.
|
||||
///
|
||||
/// You can query on any of the custom fields you've defined on the [Channel].
|
||||
///
|
||||
/// You can also filter other built-in channel fields.
|
||||
final Filter? messageFilter;
|
||||
Filter? _activeMessageFilter;
|
||||
|
||||
/// Message String to search on.
|
||||
final String? searchQuery;
|
||||
String? _activeSearchQuery;
|
||||
|
||||
/// The sorting used for the users matching the filters.
|
||||
///
|
||||
/// Sorting is based on field and direction, multiple sorting options
|
||||
/// can be provided.
|
||||
///
|
||||
/// Direction can be ascending or descending.
|
||||
final List<SortOption>? sort;
|
||||
List<SortOption>? _activeSort;
|
||||
|
||||
/// The limit to apply to the user list. The default is set to
|
||||
/// [defaultUserPagedLimit].
|
||||
final int limit;
|
||||
|
||||
/// Allows for the change of filters used for user queries.
|
||||
///
|
||||
/// Use this if you need to support runtime filter changes,
|
||||
/// through custom filters UI.
|
||||
set filter(Filter value) => _activeFilter = value;
|
||||
|
||||
/// Allows for the change of message filters used for user queries.
|
||||
///
|
||||
/// Use this if you need to support runtime filter changes,
|
||||
/// through custom filters UI.
|
||||
set messageFilter(Filter? value) => _activeMessageFilter = value;
|
||||
|
||||
/// Allows for the change of filters used for user queries.
|
||||
///
|
||||
/// Use this if you need to support runtime filter changes,
|
||||
/// through custom filters UI.
|
||||
set searchQuery(String? value) => _activeSearchQuery = value;
|
||||
|
||||
/// Allows for the change of the query sort used for user queries.
|
||||
///
|
||||
/// Use this if you need to support runtime sort changes,
|
||||
/// through custom sort UI.
|
||||
set sort(List<SortOption>? value) => _activeSort = value;
|
||||
|
||||
@override
|
||||
Future<void> doInitialLoad() async {
|
||||
final limit = min(
|
||||
this.limit * defaultInitialPagedLimitMultiplier,
|
||||
_kDefaultBackendPaginationLimit,
|
||||
);
|
||||
try {
|
||||
final response = await client.search(
|
||||
_activeFilter,
|
||||
sort: _activeSort,
|
||||
query: _activeSearchQuery,
|
||||
messageFilters: _activeMessageFilter,
|
||||
paginationParams: PaginationParams(limit: limit),
|
||||
);
|
||||
|
||||
final results = response.results;
|
||||
final nextKey = response.next;
|
||||
value = PagedValue(
|
||||
items: results,
|
||||
nextPageKey: nextKey,
|
||||
);
|
||||
} on StreamChatError catch (error) {
|
||||
value = PagedValue.error(error);
|
||||
} catch (error) {
|
||||
final chatError = StreamChatError(error.toString());
|
||||
value = PagedValue.error(chatError);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> loadMore(String nextPageKey) async {
|
||||
final previousValue = value.asSuccess;
|
||||
|
||||
try {
|
||||
final response = await client.search(
|
||||
_activeFilter,
|
||||
sort: _activeSort,
|
||||
query: _activeSearchQuery,
|
||||
messageFilters: _activeMessageFilter,
|
||||
paginationParams: PaginationParams(limit: limit, next: nextPageKey),
|
||||
);
|
||||
|
||||
final results = response.results;
|
||||
final previousItems = previousValue.items;
|
||||
final newItems = previousItems + results;
|
||||
final next = response.next;
|
||||
final nextKey = next != null && next.isNotEmpty ? next : null;
|
||||
value = PagedValue(
|
||||
items: newItems,
|
||||
nextPageKey: nextKey,
|
||||
);
|
||||
} on StreamChatError catch (error) {
|
||||
value = previousValue.copyWith(error: error);
|
||||
} catch (error) {
|
||||
final chatError = StreamChatError(error.toString());
|
||||
value = previousValue.copyWith(error: chatError);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> refresh({bool resetValue = true}) {
|
||||
if (resetValue) {
|
||||
_activeFilter = filter;
|
||||
_activeMessageFilter = messageFilter;
|
||||
_activeSearchQuery = searchQuery;
|
||||
_activeSort = sort;
|
||||
}
|
||||
return super.refresh(resetValue: resetValue);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
import 'dart:async';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:stream_chat/stream_chat.dart' hide Success;
|
||||
import 'package:stream_chat_flutter_core/src/paged_value_notifier.dart';
|
||||
|
||||
/// The default channel page limit to load.
|
||||
const defaultUserPagedLimit = 10;
|
||||
|
||||
const _kDefaultBackendPaginationLimit = 30;
|
||||
|
||||
/// A controller for a user list.
|
||||
///
|
||||
/// This class lets you perform tasks such as:
|
||||
/// * Load initial data.
|
||||
/// * Load more data using [loadMore].
|
||||
/// * Replace the previously loaded users.
|
||||
class StreamUserListController extends PagedValueNotifier<int, User> {
|
||||
/// Creates a Stream user list controller.
|
||||
///
|
||||
/// * `client` is the Stream chat client to use for the channels list.
|
||||
///
|
||||
/// * `filter` is the query filters to use.
|
||||
///
|
||||
/// * `sort` is the sorting used for the users matching the filters.
|
||||
///
|
||||
/// * `presence` sets whether you'll receive user presence updates via the
|
||||
/// websocket events.
|
||||
///
|
||||
/// * `limit` is the limit to apply to the user list.
|
||||
StreamUserListController({
|
||||
required this.client,
|
||||
this.filter,
|
||||
this.sort,
|
||||
this.presence = true,
|
||||
this.limit = defaultUserPagedLimit,
|
||||
}) : _activeFilter = filter,
|
||||
_activeSort = sort,
|
||||
super(const PagedValue.loading());
|
||||
|
||||
/// Creates a [StreamUserListController] from the passed [value].
|
||||
StreamUserListController.fromValue(
|
||||
PagedValue<int, User> value, {
|
||||
required this.client,
|
||||
this.filter,
|
||||
this.sort,
|
||||
this.presence = true,
|
||||
this.limit = defaultUserPagedLimit,
|
||||
}) : _activeFilter = filter,
|
||||
_activeSort = sort,
|
||||
super(value);
|
||||
|
||||
/// The client to use for the channels list.
|
||||
final StreamChatClient client;
|
||||
|
||||
/// The query filters to use.
|
||||
///
|
||||
/// You can query on any of the custom fields you've defined on the [User].
|
||||
///
|
||||
/// You can also filter other built-in channel fields.
|
||||
final Filter? filter;
|
||||
Filter? _activeFilter;
|
||||
|
||||
/// The sorting used for the users matching the filters.
|
||||
///
|
||||
/// Sorting is based on field and direction, multiple sorting options
|
||||
/// can be provided.
|
||||
///
|
||||
/// Direction can be ascending or descending.
|
||||
final List<SortOption>? sort;
|
||||
List<SortOption>? _activeSort;
|
||||
|
||||
/// If true you’ll receive user presence updates via the websocket events
|
||||
final bool presence;
|
||||
|
||||
/// The limit to apply to the user list. The default is set to
|
||||
/// [defaultUserPagedLimit].
|
||||
final int limit;
|
||||
|
||||
/// Allows for the change of filters used for user queries.
|
||||
///
|
||||
/// Use this if you need to support runtime filter changes,
|
||||
/// through custom filters UI.
|
||||
set filter(Filter? value) => _activeFilter = value;
|
||||
|
||||
/// Allows for the change of the query sort used for user queries.
|
||||
///
|
||||
/// Use this if you need to support runtime sort changes,
|
||||
/// through custom sort UI.
|
||||
set sort(List<SortOption>? value) => _activeSort = value;
|
||||
|
||||
@override
|
||||
Future<void> doInitialLoad() async {
|
||||
final limit = min(
|
||||
this.limit * defaultInitialPagedLimitMultiplier,
|
||||
_kDefaultBackendPaginationLimit,
|
||||
);
|
||||
try {
|
||||
final userResponse = await client.queryUsers(
|
||||
filter: _activeFilter,
|
||||
sort: _activeSort,
|
||||
presence: presence,
|
||||
pagination: PaginationParams(limit: limit),
|
||||
);
|
||||
|
||||
final users = userResponse.users;
|
||||
final nextKey = users.length < limit ? null : users.length;
|
||||
value = PagedValue(
|
||||
items: users,
|
||||
nextPageKey: nextKey,
|
||||
);
|
||||
} on StreamChatError catch (error) {
|
||||
value = PagedValue.error(error);
|
||||
} catch (error) {
|
||||
final chatError = StreamChatError(error.toString());
|
||||
value = PagedValue.error(chatError);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> loadMore(int nextPageKey) async {
|
||||
final previousValue = value.asSuccess;
|
||||
|
||||
try {
|
||||
final userResponse = await client.queryUsers(
|
||||
filter: _activeFilter,
|
||||
sort: _activeSort,
|
||||
presence: presence,
|
||||
pagination: PaginationParams(limit: limit, offset: nextPageKey),
|
||||
);
|
||||
|
||||
final users = userResponse.users;
|
||||
final previousItems = previousValue.items;
|
||||
final newItems = previousItems + users;
|
||||
final nextKey = users.length < limit ? null : newItems.length;
|
||||
value = PagedValue(
|
||||
items: newItems,
|
||||
nextPageKey: nextKey,
|
||||
);
|
||||
} on StreamChatError catch (error) {
|
||||
value = previousValue.copyWith(error: error);
|
||||
} catch (error) {
|
||||
final chatError = StreamChatError(error.toString());
|
||||
value = previousValue.copyWith(error: chatError);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> refresh({bool resetValue = true}) {
|
||||
if (resetValue) {
|
||||
_activeFilter = filter;
|
||||
_activeSort = sort;
|
||||
}
|
||||
return super.refresh(resetValue: resetValue);
|
||||
}
|
||||
|
||||
/// Replaces the previously loaded users with [users] and updates
|
||||
/// the nextPageKey.
|
||||
set users(List<User> users) {
|
||||
value = PagedValue(
|
||||
items: users,
|
||||
nextPageKey: users.length,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -12,11 +12,14 @@ export 'src/message_list_core.dart' hide MessageListCoreState;
|
||||
export 'src/message_search_bloc.dart';
|
||||
export 'src/message_search_list_core.dart' hide MessageSearchListCoreState;
|
||||
export 'src/message_text_field_controller.dart';
|
||||
export 'src/paged_value_list_view.dart';
|
||||
export 'src/paged_value_notifier.dart' show PagedValueListenableBuilder;
|
||||
export 'src/stream_channel.dart';
|
||||
export 'src/stream_channel_list_controller.dart';
|
||||
export 'src/stream_channel_list_event_handler.dart';
|
||||
export 'src/stream_chat_core.dart';
|
||||
export 'src/stream_message_search_list_controller.dart';
|
||||
export 'src/stream_user_list_controller.dart';
|
||||
export 'src/typedef.dart';
|
||||
export 'src/user_list_core.dart' hide UserListCoreState;
|
||||
export 'src/users_bloc.dart';
|
||||
|
||||
Reference in New Issue
Block a user