feat(core, ui): added PagedValueGridView, added additional params in ListViews
Signed-off-by: xsahil03x <[email protected]>
This commit is contained in:
+66
-12
@@ -57,17 +57,21 @@ class StreamChannelListView extends StatelessWidget {
|
|||||||
this.onChannelTap,
|
this.onChannelTap,
|
||||||
this.onChannelLongPress,
|
this.onChannelLongPress,
|
||||||
this.loadMoreTriggerIndex = 3,
|
this.loadMoreTriggerIndex = 3,
|
||||||
this.padding,
|
this.scrollDirection = Axis.vertical,
|
||||||
this.physics,
|
|
||||||
this.reverse = false,
|
this.reverse = false,
|
||||||
this.scrollController,
|
this.scrollController,
|
||||||
this.primary,
|
this.primary,
|
||||||
this.scrollBehavior,
|
this.physics,
|
||||||
this.shrinkWrap = false,
|
this.shrinkWrap = false,
|
||||||
|
this.padding,
|
||||||
|
this.addAutomaticKeepAlives = true,
|
||||||
|
this.addRepaintBoundaries = true,
|
||||||
|
this.addSemanticIndexes = true,
|
||||||
this.cacheExtent,
|
this.cacheExtent,
|
||||||
this.dragStartBehavior = DragStartBehavior.start,
|
this.dragStartBehavior = DragStartBehavior.start,
|
||||||
this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual,
|
this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual,
|
||||||
this.restorationId,
|
this.restorationId,
|
||||||
|
this.clipBehavior = Clip.hardEdge,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
/// The [StreamChannelListController] used to control the list of channels.
|
/// The [StreamChannelListController] used to control the list of channels.
|
||||||
@@ -81,7 +85,7 @@ class StreamChannelListView extends StatelessWidget {
|
|||||||
final StreamChannelListViewIndexedWidgetBuilder? itemBuilder;
|
final StreamChannelListViewIndexedWidgetBuilder? itemBuilder;
|
||||||
|
|
||||||
/// A builder that is called to build the list separator.
|
/// A builder that is called to build the list separator.
|
||||||
final PagedValueListViewIndexedWidgetBuilder<Channel> separatorBuilder;
|
final PagedValueScrollViewIndexedWidgetBuilder<Channel> separatorBuilder;
|
||||||
|
|
||||||
/// A builder that is called to build the empty state of the list.
|
/// A builder that is called to build the empty state of the list.
|
||||||
///
|
///
|
||||||
@@ -107,9 +111,56 @@ class StreamChannelListView extends StatelessWidget {
|
|||||||
/// The index to take into account when triggering [controller.loadMore].
|
/// The index to take into account when triggering [controller.loadMore].
|
||||||
final int loadMoreTriggerIndex;
|
final int loadMoreTriggerIndex;
|
||||||
|
|
||||||
|
/// {@template flutter.widgets.scroll_view.scrollDirection}
|
||||||
|
/// The axis along which the scroll view scrolls.
|
||||||
|
///
|
||||||
|
/// Defaults to [Axis.vertical].
|
||||||
|
/// {@endtemplate}
|
||||||
|
final Axis scrollDirection;
|
||||||
|
|
||||||
/// The amount of space by which to inset the children.
|
/// The amount of space by which to inset the children.
|
||||||
final EdgeInsetsGeometry? padding;
|
final EdgeInsetsGeometry? padding;
|
||||||
|
|
||||||
|
/// Whether to wrap each child in an [AutomaticKeepAlive].
|
||||||
|
///
|
||||||
|
/// Typically, children in lazy list are wrapped in [AutomaticKeepAlive]
|
||||||
|
/// widgets so that children can use [KeepAliveNotification]s to preserve
|
||||||
|
/// their state when they would otherwise be garbage collected off-screen.
|
||||||
|
///
|
||||||
|
/// This feature (and [addRepaintBoundaries]) must be disabled if the children
|
||||||
|
/// are going to manually maintain their [KeepAlive] state. It may also be
|
||||||
|
/// more efficient to disable this feature if it is known ahead of time that
|
||||||
|
/// none of the children will ever try to keep themselves alive.
|
||||||
|
///
|
||||||
|
/// Defaults to true.
|
||||||
|
final bool addAutomaticKeepAlives;
|
||||||
|
|
||||||
|
/// Whether to wrap each child in a [RepaintBoundary].
|
||||||
|
///
|
||||||
|
/// Typically, children in a scrolling container are wrapped in repaint
|
||||||
|
/// boundaries so that they do not need to be repainted as the list scrolls.
|
||||||
|
/// If the children are easy to repaint (e.g., solid color blocks or a short
|
||||||
|
/// snippet of text), it might be more efficient to not add a repaint boundary
|
||||||
|
/// and simply repaint the children during scrolling.
|
||||||
|
///
|
||||||
|
/// Defaults to true.
|
||||||
|
final bool addRepaintBoundaries;
|
||||||
|
|
||||||
|
/// Whether to wrap each child in an [IndexedSemantics].
|
||||||
|
///
|
||||||
|
/// Typically, children in a scrolling container must be annotated with a
|
||||||
|
/// semantic index in order to generate the correct accessibility
|
||||||
|
/// announcements. This should only be set to false if the indexes have
|
||||||
|
/// already been provided by an [IndexedSemantics] widget.
|
||||||
|
///
|
||||||
|
/// Defaults to true.
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [IndexedSemantics], for an explanation of how to manually
|
||||||
|
/// provide semantic indexes.
|
||||||
|
final bool addSemanticIndexes;
|
||||||
|
|
||||||
/// {@template flutter.widgets.scroll_view.reverse}
|
/// {@template flutter.widgets.scroll_view.reverse}
|
||||||
/// Whether the scroll view scrolls in the reading direction.
|
/// Whether the scroll view scrolls in the reading direction.
|
||||||
///
|
///
|
||||||
@@ -157,14 +208,6 @@ class StreamChannelListView extends StatelessWidget {
|
|||||||
/// Defaults to true when [scrollController] is null.
|
/// Defaults to true when [scrollController] is null.
|
||||||
final bool? primary;
|
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}
|
/// {@template flutter.widgets.scroll_view.shrinkWrap}
|
||||||
/// Whether the extent of the scroll view in the [scrollDirection] should be
|
/// Whether the extent of the scroll view in the [scrollDirection] should be
|
||||||
/// determined by the contents being viewed.
|
/// determined by the contents being viewed.
|
||||||
@@ -240,18 +283,29 @@ class StreamChannelListView extends StatelessWidget {
|
|||||||
/// {@macro flutter.widgets.scrollable.restorationId}
|
/// {@macro flutter.widgets.scrollable.restorationId}
|
||||||
final String? restorationId;
|
final String? restorationId;
|
||||||
|
|
||||||
|
/// {@macro flutter.material.Material.clipBehavior}
|
||||||
|
///
|
||||||
|
/// Defaults to [Clip.hardEdge].
|
||||||
|
final Clip clipBehavior;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => PagedValueListView<int, Channel>(
|
Widget build(BuildContext context) => PagedValueListView<int, Channel>(
|
||||||
|
scrollDirection: scrollDirection,
|
||||||
padding: padding,
|
padding: padding,
|
||||||
physics: physics,
|
physics: physics,
|
||||||
reverse: reverse,
|
reverse: reverse,
|
||||||
controller: controller,
|
controller: controller,
|
||||||
|
scrollController: scrollController,
|
||||||
primary: primary,
|
primary: primary,
|
||||||
shrinkWrap: shrinkWrap,
|
shrinkWrap: shrinkWrap,
|
||||||
|
addAutomaticKeepAlives: addAutomaticKeepAlives,
|
||||||
|
addRepaintBoundaries: addRepaintBoundaries,
|
||||||
|
addSemanticIndexes: addSemanticIndexes,
|
||||||
keyboardDismissBehavior: keyboardDismissBehavior,
|
keyboardDismissBehavior: keyboardDismissBehavior,
|
||||||
restorationId: restorationId,
|
restorationId: restorationId,
|
||||||
dragStartBehavior: dragStartBehavior,
|
dragStartBehavior: dragStartBehavior,
|
||||||
cacheExtent: cacheExtent,
|
cacheExtent: cacheExtent,
|
||||||
|
clipBehavior: clipBehavior,
|
||||||
loadMoreTriggerIndex: loadMoreTriggerIndex,
|
loadMoreTriggerIndex: loadMoreTriggerIndex,
|
||||||
separatorBuilder: separatorBuilder,
|
separatorBuilder: separatorBuilder,
|
||||||
itemBuilder: (context, channels, index) {
|
itemBuilder: (context, channels, index) {
|
||||||
|
|||||||
+67
-13
@@ -1,7 +1,6 @@
|
|||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:stream_chat_flutter/src/extension.dart';
|
import 'package:stream_chat_flutter/src/extension.dart';
|
||||||
import 'package:stream_chat_flutter/src/v4/stream_list_view_indexed_widget_builder.dart';
|
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
/// Default separator builder for [StreamMessageSearchListView].
|
/// Default separator builder for [StreamMessageSearchListView].
|
||||||
@@ -54,17 +53,21 @@ class StreamMessageSearchListView extends StatelessWidget {
|
|||||||
this.onMessageTap,
|
this.onMessageTap,
|
||||||
this.onMessageLongPress,
|
this.onMessageLongPress,
|
||||||
this.loadMoreTriggerIndex = 3,
|
this.loadMoreTriggerIndex = 3,
|
||||||
this.padding,
|
this.scrollDirection = Axis.vertical,
|
||||||
this.physics,
|
|
||||||
this.reverse = false,
|
this.reverse = false,
|
||||||
this.scrollController,
|
this.scrollController,
|
||||||
this.primary,
|
this.primary,
|
||||||
this.scrollBehavior,
|
this.physics,
|
||||||
this.shrinkWrap = false,
|
this.shrinkWrap = false,
|
||||||
|
this.padding,
|
||||||
|
this.addAutomaticKeepAlives = true,
|
||||||
|
this.addRepaintBoundaries = true,
|
||||||
|
this.addSemanticIndexes = true,
|
||||||
this.cacheExtent,
|
this.cacheExtent,
|
||||||
this.dragStartBehavior = DragStartBehavior.start,
|
this.dragStartBehavior = DragStartBehavior.start,
|
||||||
this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual,
|
this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual,
|
||||||
this.restorationId,
|
this.restorationId,
|
||||||
|
this.clipBehavior = Clip.hardEdge,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
/// The [StreamUserListController] used to control the list of
|
/// The [StreamUserListController] used to control the list of
|
||||||
@@ -79,7 +82,7 @@ class StreamMessageSearchListView extends StatelessWidget {
|
|||||||
final StreamMessageSearchListViewIndexedWidgetBuilder? itemBuilder;
|
final StreamMessageSearchListViewIndexedWidgetBuilder? itemBuilder;
|
||||||
|
|
||||||
/// A builder that is called to build the list separator.
|
/// A builder that is called to build the list separator.
|
||||||
final PagedValueListViewIndexedWidgetBuilder<GetMessageResponse>
|
final PagedValueScrollViewIndexedWidgetBuilder<GetMessageResponse>
|
||||||
separatorBuilder;
|
separatorBuilder;
|
||||||
|
|
||||||
/// A builder that is called to build the empty state of the list.
|
/// A builder that is called to build the empty state of the list.
|
||||||
@@ -106,9 +109,56 @@ class StreamMessageSearchListView extends StatelessWidget {
|
|||||||
/// The index to take into account when triggering [controller.loadMore].
|
/// The index to take into account when triggering [controller.loadMore].
|
||||||
final int loadMoreTriggerIndex;
|
final int loadMoreTriggerIndex;
|
||||||
|
|
||||||
|
/// {@template flutter.widgets.scroll_view.scrollDirection}
|
||||||
|
/// The axis along which the scroll view scrolls.
|
||||||
|
///
|
||||||
|
/// Defaults to [Axis.vertical].
|
||||||
|
/// {@endtemplate}
|
||||||
|
final Axis scrollDirection;
|
||||||
|
|
||||||
/// The amount of space by which to inset the children.
|
/// The amount of space by which to inset the children.
|
||||||
final EdgeInsetsGeometry? padding;
|
final EdgeInsetsGeometry? padding;
|
||||||
|
|
||||||
|
/// Whether to wrap each child in an [AutomaticKeepAlive].
|
||||||
|
///
|
||||||
|
/// Typically, children in lazy list are wrapped in [AutomaticKeepAlive]
|
||||||
|
/// widgets so that children can use [KeepAliveNotification]s to preserve
|
||||||
|
/// their state when they would otherwise be garbage collected off-screen.
|
||||||
|
///
|
||||||
|
/// This feature (and [addRepaintBoundaries]) must be disabled if the children
|
||||||
|
/// are going to manually maintain their [KeepAlive] state. It may also be
|
||||||
|
/// more efficient to disable this feature if it is known ahead of time that
|
||||||
|
/// none of the children will ever try to keep themselves alive.
|
||||||
|
///
|
||||||
|
/// Defaults to true.
|
||||||
|
final bool addAutomaticKeepAlives;
|
||||||
|
|
||||||
|
/// Whether to wrap each child in a [RepaintBoundary].
|
||||||
|
///
|
||||||
|
/// Typically, children in a scrolling container are wrapped in repaint
|
||||||
|
/// boundaries so that they do not need to be repainted as the list scrolls.
|
||||||
|
/// If the children are easy to repaint (e.g., solid color blocks or a short
|
||||||
|
/// snippet of text), it might be more efficient to not add a repaint boundary
|
||||||
|
/// and simply repaint the children during scrolling.
|
||||||
|
///
|
||||||
|
/// Defaults to true.
|
||||||
|
final bool addRepaintBoundaries;
|
||||||
|
|
||||||
|
/// Whether to wrap each child in an [IndexedSemantics].
|
||||||
|
///
|
||||||
|
/// Typically, children in a scrolling container must be annotated with a
|
||||||
|
/// semantic index in order to generate the correct accessibility
|
||||||
|
/// announcements. This should only be set to false if the indexes have
|
||||||
|
/// already been provided by an [IndexedSemantics] widget.
|
||||||
|
///
|
||||||
|
/// Defaults to true.
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [IndexedSemantics], for an explanation of how to manually
|
||||||
|
/// provide semantic indexes.
|
||||||
|
final bool addSemanticIndexes;
|
||||||
|
|
||||||
/// {@template flutter.widgets.scroll_view.reverse}
|
/// {@template flutter.widgets.scroll_view.reverse}
|
||||||
/// Whether the scroll view scrolls in the reading direction.
|
/// Whether the scroll view scrolls in the reading direction.
|
||||||
///
|
///
|
||||||
@@ -156,14 +206,6 @@ class StreamMessageSearchListView extends StatelessWidget {
|
|||||||
/// Defaults to true when [scrollController] is null.
|
/// Defaults to true when [scrollController] is null.
|
||||||
final bool? primary;
|
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}
|
/// {@template flutter.widgets.scroll_view.shrinkWrap}
|
||||||
/// Whether the extent of the scroll view in the [scrollDirection] should be
|
/// Whether the extent of the scroll view in the [scrollDirection] should be
|
||||||
/// determined by the contents being viewed.
|
/// determined by the contents being viewed.
|
||||||
@@ -239,19 +281,31 @@ class StreamMessageSearchListView extends StatelessWidget {
|
|||||||
/// {@macro flutter.widgets.scrollable.restorationId}
|
/// {@macro flutter.widgets.scrollable.restorationId}
|
||||||
final String? restorationId;
|
final String? restorationId;
|
||||||
|
|
||||||
|
/// {@macro flutter.material.Material.clipBehavior}
|
||||||
|
///
|
||||||
|
/// Defaults to [Clip.hardEdge].
|
||||||
|
final Clip clipBehavior;
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) =>
|
Widget build(BuildContext context) =>
|
||||||
PagedValueListView<String, GetMessageResponse>(
|
PagedValueListView<String, GetMessageResponse>(
|
||||||
|
scrollDirection: scrollDirection,
|
||||||
padding: padding,
|
padding: padding,
|
||||||
physics: physics,
|
physics: physics,
|
||||||
reverse: reverse,
|
reverse: reverse,
|
||||||
controller: controller,
|
controller: controller,
|
||||||
|
scrollController: scrollController,
|
||||||
primary: primary,
|
primary: primary,
|
||||||
shrinkWrap: shrinkWrap,
|
shrinkWrap: shrinkWrap,
|
||||||
|
addAutomaticKeepAlives: addAutomaticKeepAlives,
|
||||||
|
addRepaintBoundaries: addRepaintBoundaries,
|
||||||
|
addSemanticIndexes: addSemanticIndexes,
|
||||||
keyboardDismissBehavior: keyboardDismissBehavior,
|
keyboardDismissBehavior: keyboardDismissBehavior,
|
||||||
restorationId: restorationId,
|
restorationId: restorationId,
|
||||||
dragStartBehavior: dragStartBehavior,
|
dragStartBehavior: dragStartBehavior,
|
||||||
cacheExtent: cacheExtent,
|
cacheExtent: cacheExtent,
|
||||||
|
clipBehavior: clipBehavior,
|
||||||
loadMoreTriggerIndex: loadMoreTriggerIndex,
|
loadMoreTriggerIndex: loadMoreTriggerIndex,
|
||||||
separatorBuilder: separatorBuilder,
|
separatorBuilder: separatorBuilder,
|
||||||
itemBuilder: (context, messageResponses, index) {
|
itemBuilder: (context, messageResponses, index) {
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:stream_chat_flutter/src/extension.dart';
|
import 'package:stream_chat_flutter/src/extension.dart';
|
||||||
import 'package:stream_chat_flutter/src/v4/stream_list_view_indexed_widget_builder.dart';
|
|
||||||
import 'package:stream_chat_flutter/src/v4/user_list_view/stream_user_list_tile.dart';
|
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
/// Default separator builder for [StreamUserListView].
|
/// Default separator builder for [StreamUserListView].
|
||||||
@@ -54,17 +52,21 @@ class StreamUserListView extends StatelessWidget {
|
|||||||
this.onUserTap,
|
this.onUserTap,
|
||||||
this.onUserLongPress,
|
this.onUserLongPress,
|
||||||
this.loadMoreTriggerIndex = 3,
|
this.loadMoreTriggerIndex = 3,
|
||||||
this.padding,
|
this.scrollDirection = Axis.vertical,
|
||||||
this.physics,
|
|
||||||
this.reverse = false,
|
this.reverse = false,
|
||||||
this.scrollController,
|
this.scrollController,
|
||||||
this.primary,
|
this.primary,
|
||||||
this.scrollBehavior,
|
this.physics,
|
||||||
this.shrinkWrap = false,
|
this.shrinkWrap = false,
|
||||||
|
this.padding,
|
||||||
|
this.addAutomaticKeepAlives = true,
|
||||||
|
this.addRepaintBoundaries = true,
|
||||||
|
this.addSemanticIndexes = true,
|
||||||
this.cacheExtent,
|
this.cacheExtent,
|
||||||
this.dragStartBehavior = DragStartBehavior.start,
|
this.dragStartBehavior = DragStartBehavior.start,
|
||||||
this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual,
|
this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual,
|
||||||
this.restorationId,
|
this.restorationId,
|
||||||
|
this.clipBehavior = Clip.hardEdge,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
/// The [StreamUserListController] used to control the list of users.
|
/// The [StreamUserListController] used to control the list of users.
|
||||||
@@ -78,7 +80,7 @@ class StreamUserListView extends StatelessWidget {
|
|||||||
final StreamUserListViewIndexedWidgetBuilder? itemBuilder;
|
final StreamUserListViewIndexedWidgetBuilder? itemBuilder;
|
||||||
|
|
||||||
/// A builder that is called to build the list separator.
|
/// A builder that is called to build the list separator.
|
||||||
final PagedValueListViewIndexedWidgetBuilder<User> separatorBuilder;
|
final PagedValueScrollViewIndexedWidgetBuilder<User> separatorBuilder;
|
||||||
|
|
||||||
/// A builder that is called to build the empty state of the list.
|
/// A builder that is called to build the empty state of the list.
|
||||||
///
|
///
|
||||||
@@ -104,9 +106,56 @@ class StreamUserListView extends StatelessWidget {
|
|||||||
/// The index to take into account when triggering [controller.loadMore].
|
/// The index to take into account when triggering [controller.loadMore].
|
||||||
final int loadMoreTriggerIndex;
|
final int loadMoreTriggerIndex;
|
||||||
|
|
||||||
|
/// {@template flutter.widgets.scroll_view.scrollDirection}
|
||||||
|
/// The axis along which the scroll view scrolls.
|
||||||
|
///
|
||||||
|
/// Defaults to [Axis.vertical].
|
||||||
|
/// {@endtemplate}
|
||||||
|
final Axis scrollDirection;
|
||||||
|
|
||||||
/// The amount of space by which to inset the children.
|
/// The amount of space by which to inset the children.
|
||||||
final EdgeInsetsGeometry? padding;
|
final EdgeInsetsGeometry? padding;
|
||||||
|
|
||||||
|
/// Whether to wrap each child in an [AutomaticKeepAlive].
|
||||||
|
///
|
||||||
|
/// Typically, children in lazy list are wrapped in [AutomaticKeepAlive]
|
||||||
|
/// widgets so that children can use [KeepAliveNotification]s to preserve
|
||||||
|
/// their state when they would otherwise be garbage collected off-screen.
|
||||||
|
///
|
||||||
|
/// This feature (and [addRepaintBoundaries]) must be disabled if the children
|
||||||
|
/// are going to manually maintain their [KeepAlive] state. It may also be
|
||||||
|
/// more efficient to disable this feature if it is known ahead of time that
|
||||||
|
/// none of the children will ever try to keep themselves alive.
|
||||||
|
///
|
||||||
|
/// Defaults to true.
|
||||||
|
final bool addAutomaticKeepAlives;
|
||||||
|
|
||||||
|
/// Whether to wrap each child in a [RepaintBoundary].
|
||||||
|
///
|
||||||
|
/// Typically, children in a scrolling container are wrapped in repaint
|
||||||
|
/// boundaries so that they do not need to be repainted as the list scrolls.
|
||||||
|
/// If the children are easy to repaint (e.g., solid color blocks or a short
|
||||||
|
/// snippet of text), it might be more efficient to not add a repaint boundary
|
||||||
|
/// and simply repaint the children during scrolling.
|
||||||
|
///
|
||||||
|
/// Defaults to true.
|
||||||
|
final bool addRepaintBoundaries;
|
||||||
|
|
||||||
|
/// Whether to wrap each child in an [IndexedSemantics].
|
||||||
|
///
|
||||||
|
/// Typically, children in a scrolling container must be annotated with a
|
||||||
|
/// semantic index in order to generate the correct accessibility
|
||||||
|
/// announcements. This should only be set to false if the indexes have
|
||||||
|
/// already been provided by an [IndexedSemantics] widget.
|
||||||
|
///
|
||||||
|
/// Defaults to true.
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [IndexedSemantics], for an explanation of how to manually
|
||||||
|
/// provide semantic indexes.
|
||||||
|
final bool addSemanticIndexes;
|
||||||
|
|
||||||
/// {@template flutter.widgets.scroll_view.reverse}
|
/// {@template flutter.widgets.scroll_view.reverse}
|
||||||
/// Whether the scroll view scrolls in the reading direction.
|
/// Whether the scroll view scrolls in the reading direction.
|
||||||
///
|
///
|
||||||
@@ -154,14 +203,6 @@ class StreamUserListView extends StatelessWidget {
|
|||||||
/// Defaults to true when [scrollController] is null.
|
/// Defaults to true when [scrollController] is null.
|
||||||
final bool? primary;
|
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}
|
/// {@template flutter.widgets.scroll_view.shrinkWrap}
|
||||||
/// Whether the extent of the scroll view in the [scrollDirection] should be
|
/// Whether the extent of the scroll view in the [scrollDirection] should be
|
||||||
/// determined by the contents being viewed.
|
/// determined by the contents being viewed.
|
||||||
@@ -237,18 +278,29 @@ class StreamUserListView extends StatelessWidget {
|
|||||||
/// {@macro flutter.widgets.scrollable.restorationId}
|
/// {@macro flutter.widgets.scrollable.restorationId}
|
||||||
final String? restorationId;
|
final String? restorationId;
|
||||||
|
|
||||||
|
/// {@macro flutter.material.Material.clipBehavior}
|
||||||
|
///
|
||||||
|
/// Defaults to [Clip.hardEdge].
|
||||||
|
final Clip clipBehavior;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => PagedValueListView<int, User>(
|
Widget build(BuildContext context) => PagedValueListView<int, User>(
|
||||||
|
scrollDirection: scrollDirection,
|
||||||
padding: padding,
|
padding: padding,
|
||||||
physics: physics,
|
physics: physics,
|
||||||
reverse: reverse,
|
reverse: reverse,
|
||||||
controller: controller,
|
controller: controller,
|
||||||
|
scrollController: scrollController,
|
||||||
primary: primary,
|
primary: primary,
|
||||||
shrinkWrap: shrinkWrap,
|
shrinkWrap: shrinkWrap,
|
||||||
|
addAutomaticKeepAlives: addAutomaticKeepAlives,
|
||||||
|
addRepaintBoundaries: addRepaintBoundaries,
|
||||||
|
addSemanticIndexes: addSemanticIndexes,
|
||||||
keyboardDismissBehavior: keyboardDismissBehavior,
|
keyboardDismissBehavior: keyboardDismissBehavior,
|
||||||
restorationId: restorationId,
|
restorationId: restorationId,
|
||||||
dragStartBehavior: dragStartBehavior,
|
dragStartBehavior: dragStartBehavior,
|
||||||
cacheExtent: cacheExtent,
|
cacheExtent: cacheExtent,
|
||||||
|
clipBehavior: clipBehavior,
|
||||||
loadMoreTriggerIndex: loadMoreTriggerIndex,
|
loadMoreTriggerIndex: loadMoreTriggerIndex,
|
||||||
separatorBuilder: separatorBuilder,
|
separatorBuilder: separatorBuilder,
|
||||||
itemBuilder: (context, users, index) {
|
itemBuilder: (context, users, index) {
|
||||||
|
|||||||
@@ -1,297 +0,0 @@
|
|||||||
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,707 @@
|
|||||||
|
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] and [PagedValueGridView].
|
||||||
|
typedef PagedValueScrollViewIndexedWidgetBuilder<T> = Widget Function(
|
||||||
|
BuildContext context,
|
||||||
|
List<T> values,
|
||||||
|
int index,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Signature for the item builder that creates the children of the
|
||||||
|
/// [PagedValueListView] and [PagedValueGridView].
|
||||||
|
typedef PagedValueScrollViewLoadMoreErrorBuilder = 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.scrollDirection = Axis.vertical,
|
||||||
|
this.reverse = false,
|
||||||
|
this.scrollController,
|
||||||
|
this.primary,
|
||||||
|
this.physics,
|
||||||
|
this.shrinkWrap = false,
|
||||||
|
this.padding,
|
||||||
|
this.addAutomaticKeepAlives = true,
|
||||||
|
this.addRepaintBoundaries = true,
|
||||||
|
this.addSemanticIndexes = true,
|
||||||
|
this.cacheExtent,
|
||||||
|
this.dragStartBehavior = DragStartBehavior.start,
|
||||||
|
this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual,
|
||||||
|
this.restorationId,
|
||||||
|
this.clipBehavior = Clip.hardEdge,
|
||||||
|
}) : 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 PagedValueScrollViewIndexedWidgetBuilder<V> itemBuilder;
|
||||||
|
|
||||||
|
/// A builder that is called to build the list separator.
|
||||||
|
final PagedValueScrollViewIndexedWidgetBuilder<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 PagedValueScrollViewLoadMoreErrorBuilder 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;
|
||||||
|
|
||||||
|
/// {@template flutter.widgets.scroll_view.scrollDirection}
|
||||||
|
/// The axis along which the scroll view scrolls.
|
||||||
|
///
|
||||||
|
/// Defaults to [Axis.vertical].
|
||||||
|
/// {@endtemplate}
|
||||||
|
final Axis scrollDirection;
|
||||||
|
|
||||||
|
/// The amount of space by which to inset the children.
|
||||||
|
final EdgeInsetsGeometry? padding;
|
||||||
|
|
||||||
|
/// Whether to wrap each child in an [AutomaticKeepAlive].
|
||||||
|
///
|
||||||
|
/// Typically, children in lazy list are wrapped in [AutomaticKeepAlive]
|
||||||
|
/// widgets so that children can use [KeepAliveNotification]s to preserve
|
||||||
|
/// their state when they would otherwise be garbage collected off-screen.
|
||||||
|
///
|
||||||
|
/// This feature (and [addRepaintBoundaries]) must be disabled if the children
|
||||||
|
/// are going to manually maintain their [KeepAlive] state. It may also be
|
||||||
|
/// more efficient to disable this feature if it is known ahead of time that
|
||||||
|
/// none of the children will ever try to keep themselves alive.
|
||||||
|
///
|
||||||
|
/// Defaults to true.
|
||||||
|
final bool addAutomaticKeepAlives;
|
||||||
|
|
||||||
|
/// Whether to wrap each child in a [RepaintBoundary].
|
||||||
|
///
|
||||||
|
/// Typically, children in a scrolling container are wrapped in repaint
|
||||||
|
/// boundaries so that they do not need to be repainted as the list scrolls.
|
||||||
|
/// If the children are easy to repaint (e.g., solid color blocks or a short
|
||||||
|
/// snippet of text), it might be more efficient to not add a repaint boundary
|
||||||
|
/// and simply repaint the children during scrolling.
|
||||||
|
///
|
||||||
|
/// Defaults to true.
|
||||||
|
final bool addRepaintBoundaries;
|
||||||
|
|
||||||
|
/// Whether to wrap each child in an [IndexedSemantics].
|
||||||
|
///
|
||||||
|
/// Typically, children in a scrolling container must be annotated with a
|
||||||
|
/// semantic index in order to generate the correct accessibility
|
||||||
|
/// announcements. This should only be set to false if the indexes have
|
||||||
|
/// already been provided by an [IndexedSemantics] widget.
|
||||||
|
///
|
||||||
|
/// Defaults to true.
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [IndexedSemantics], for an explanation of how to manually
|
||||||
|
/// provide semantic indexes.
|
||||||
|
final bool addSemanticIndexes;
|
||||||
|
|
||||||
|
/// {@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;
|
||||||
|
|
||||||
|
/// {@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;
|
||||||
|
|
||||||
|
/// {@macro flutter.material.Material.clipBehavior}
|
||||||
|
///
|
||||||
|
/// Defaults to [Clip.hardEdge].
|
||||||
|
final Clip clipBehavior;
|
||||||
|
|
||||||
|
@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(
|
||||||
|
scrollDirection: widget.scrollDirection,
|
||||||
|
padding: widget.padding,
|
||||||
|
physics: widget.physics,
|
||||||
|
reverse: widget.reverse,
|
||||||
|
controller: widget.scrollController,
|
||||||
|
primary: widget.primary,
|
||||||
|
shrinkWrap: widget.shrinkWrap,
|
||||||
|
addAutomaticKeepAlives: widget.addAutomaticKeepAlives,
|
||||||
|
addRepaintBoundaries: widget.addRepaintBoundaries,
|
||||||
|
addSemanticIndexes: widget.addSemanticIndexes,
|
||||||
|
keyboardDismissBehavior: widget.keyboardDismissBehavior,
|
||||||
|
restorationId: widget.restorationId,
|
||||||
|
dragStartBehavior: widget.dragStartBehavior,
|
||||||
|
cacheExtent: widget.cacheExtent,
|
||||||
|
clipBehavior: widget.clipBehavior,
|
||||||
|
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),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A [GridView] that loads more pages when the user scrolls to the end of the
|
||||||
|
/// grid.
|
||||||
|
///
|
||||||
|
/// Use [loadMoreTriggerIndex] to set the index of the item that triggers the
|
||||||
|
/// loading of the next page.
|
||||||
|
class PagedValueGridView<K, V> extends StatefulWidget {
|
||||||
|
/// Creates a new instance of [PagedValueGridView] widget.
|
||||||
|
const PagedValueGridView({
|
||||||
|
Key? key,
|
||||||
|
required this.controller,
|
||||||
|
required this.gridDelegate,
|
||||||
|
required this.itemBuilder,
|
||||||
|
required this.emptyBuilder,
|
||||||
|
required this.loadMoreErrorBuilder,
|
||||||
|
required this.loadMoreIndicatorBuilder,
|
||||||
|
required this.loadingBuilder,
|
||||||
|
required this.errorBuilder,
|
||||||
|
this.loadMoreTriggerIndex = 3,
|
||||||
|
this.scrollDirection = Axis.vertical,
|
||||||
|
this.reverse = false,
|
||||||
|
this.scrollController,
|
||||||
|
this.primary,
|
||||||
|
this.physics,
|
||||||
|
this.shrinkWrap = false,
|
||||||
|
this.padding,
|
||||||
|
this.addAutomaticKeepAlives = true,
|
||||||
|
this.addRepaintBoundaries = true,
|
||||||
|
this.addSemanticIndexes = true,
|
||||||
|
this.cacheExtent,
|
||||||
|
this.semanticChildCount,
|
||||||
|
this.dragStartBehavior = DragStartBehavior.start,
|
||||||
|
this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual,
|
||||||
|
this.restorationId,
|
||||||
|
this.clipBehavior = Clip.hardEdge,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
/// The [PagedValueNotifier] used to control the list of items.
|
||||||
|
final PagedValueNotifier<K, V> controller;
|
||||||
|
|
||||||
|
/// A delegate that controls the layout of the children within
|
||||||
|
/// the [PagedValueGridView].
|
||||||
|
final SliverGridDelegate gridDelegate;
|
||||||
|
|
||||||
|
/// A builder that is called to build items in the [PagedValueGridView].
|
||||||
|
///
|
||||||
|
/// The `value` parameter is the [V] at this position in the list.
|
||||||
|
final PagedValueScrollViewIndexedWidgetBuilder<V> itemBuilder;
|
||||||
|
|
||||||
|
/// 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 PagedValueScrollViewLoadMoreErrorBuilder 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;
|
||||||
|
|
||||||
|
/// {@template flutter.widgets.scroll_view.scrollDirection}
|
||||||
|
/// The axis along which the scroll view scrolls.
|
||||||
|
///
|
||||||
|
/// Defaults to [Axis.vertical].
|
||||||
|
/// {@endtemplate}
|
||||||
|
final Axis scrollDirection;
|
||||||
|
|
||||||
|
/// {@template flutter.widgets.scroll_view.reverse}
|
||||||
|
/// Whether the scroll view scrolls in the reading direction.
|
||||||
|
///
|
||||||
|
/// For example, if the reading direction is left-to-right and
|
||||||
|
/// [scrollDirection] is [Axis.horizontal], then the scroll view scrolls from
|
||||||
|
/// left to right when [reverse] is false and from right to left when
|
||||||
|
/// [reverse] is true.
|
||||||
|
///
|
||||||
|
/// Similarly, 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 [scrollDirection] is [Axis.vertical] and
|
||||||
|
/// [controller] is null.
|
||||||
|
final bool? primary;
|
||||||
|
|
||||||
|
/// {@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;
|
||||||
|
|
||||||
|
/// {@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;
|
||||||
|
|
||||||
|
/// The amount of space by which to inset the children.
|
||||||
|
final EdgeInsetsGeometry? padding;
|
||||||
|
|
||||||
|
/// Whether to wrap each child in an [AutomaticKeepAlive].
|
||||||
|
///
|
||||||
|
/// Typically, children in lazy list are wrapped in [AutomaticKeepAlive]
|
||||||
|
/// widgets so that children can use [KeepAliveNotification]s to preserve
|
||||||
|
/// their state when they would otherwise be garbage collected off-screen.
|
||||||
|
///
|
||||||
|
/// This feature (and [addRepaintBoundaries]) must be disabled if the children
|
||||||
|
/// are going to manually maintain their [KeepAlive] state. It may also be
|
||||||
|
/// more efficient to disable this feature if it is known ahead of time that
|
||||||
|
/// none of the children will ever try to keep themselves alive.
|
||||||
|
///
|
||||||
|
/// Defaults to true.
|
||||||
|
final bool addAutomaticKeepAlives;
|
||||||
|
|
||||||
|
/// Whether to wrap each child in a [RepaintBoundary].
|
||||||
|
///
|
||||||
|
/// Typically, children in a scrolling container are wrapped in repaint
|
||||||
|
/// boundaries so that they do not need to be repainted as the list scrolls.
|
||||||
|
/// If the children are easy to repaint (e.g., solid color blocks or a short
|
||||||
|
/// snippet of text), it might be more efficient to not add a repaint boundary
|
||||||
|
/// and simply repaint the children during scrolling.
|
||||||
|
///
|
||||||
|
/// Defaults to true.
|
||||||
|
final bool addRepaintBoundaries;
|
||||||
|
|
||||||
|
/// Whether to wrap each child in an [IndexedSemantics].
|
||||||
|
///
|
||||||
|
/// Typically, children in a scrolling container must be annotated with a
|
||||||
|
/// semantic index in order to generate the correct accessibility
|
||||||
|
/// announcements. This should only be set to false if the indexes have
|
||||||
|
/// already been provided by an [IndexedSemantics] widget.
|
||||||
|
///
|
||||||
|
/// Defaults to true.
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [IndexedSemantics], for an explanation of how to manually
|
||||||
|
/// provide semantic indexes.
|
||||||
|
final bool addSemanticIndexes;
|
||||||
|
|
||||||
|
/// {@macro flutter.rendering.RenderViewportBase.cacheExtent}
|
||||||
|
final double? cacheExtent;
|
||||||
|
|
||||||
|
/// The number of children that will contribute semantic information.
|
||||||
|
///
|
||||||
|
/// Some subtypes of [ScrollView] can infer this value automatically. For
|
||||||
|
/// example [ListView] will use the number of widgets in the child list,
|
||||||
|
/// while the [ListView.separated] constructor will use half that amount.
|
||||||
|
///
|
||||||
|
/// For [CustomScrollView] and other types which do not receive a builder
|
||||||
|
/// or list of widgets, the child count must be explicitly provided. If the
|
||||||
|
/// number is unknown or unbounded this should be left unset or set to null.
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [SemanticsConfiguration.scrollChildCount], the corresponding semantics property.
|
||||||
|
final int? semanticChildCount;
|
||||||
|
|
||||||
|
/// {@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;
|
||||||
|
|
||||||
|
/// {@macro flutter.material.Material.clipBehavior}
|
||||||
|
///
|
||||||
|
/// Defaults to [Clip.hardEdge].
|
||||||
|
final Clip clipBehavior;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<PagedValueGridView<K, V>> createState() =>
|
||||||
|
_PagedValueGridViewState<K, V>();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PagedValueGridViewState<K, V> extends State<PagedValueGridView<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 PagedValueGridView<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 GridView.builder(
|
||||||
|
scrollDirection: widget.scrollDirection,
|
||||||
|
reverse: widget.reverse,
|
||||||
|
controller: widget.scrollController,
|
||||||
|
primary: widget.primary,
|
||||||
|
physics: widget.physics,
|
||||||
|
shrinkWrap: widget.shrinkWrap,
|
||||||
|
padding: widget.padding,
|
||||||
|
addAutomaticKeepAlives: widget.addAutomaticKeepAlives,
|
||||||
|
addRepaintBoundaries: widget.addRepaintBoundaries,
|
||||||
|
addSemanticIndexes: widget.addSemanticIndexes,
|
||||||
|
cacheExtent: widget.cacheExtent,
|
||||||
|
semanticChildCount: widget.semanticChildCount,
|
||||||
|
dragStartBehavior: widget.dragStartBehavior,
|
||||||
|
keyboardDismissBehavior: widget.keyboardDismissBehavior,
|
||||||
|
restorationId: widget.restorationId,
|
||||||
|
clipBehavior: widget.clipBehavior,
|
||||||
|
itemCount: value.itemCount,
|
||||||
|
gridDelegate: widget.gridDelegate,
|
||||||
|
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);
|
||||||
|
},
|
||||||
|
// gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
|
// crossAxisCount: widget.crossAxisCount,
|
||||||
|
// ),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
loading: () => widget.loadingBuilder(context),
|
||||||
|
error: (error) => widget.errorBuilder(context, error),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -12,8 +12,8 @@ export 'src/message_list_core.dart' hide MessageListCoreState;
|
|||||||
export 'src/message_search_bloc.dart';
|
export 'src/message_search_bloc.dart';
|
||||||
export 'src/message_search_list_core.dart' hide MessageSearchListCoreState;
|
export 'src/message_search_list_core.dart' hide MessageSearchListCoreState;
|
||||||
export 'src/message_text_field_controller.dart';
|
export 'src/message_text_field_controller.dart';
|
||||||
export 'src/paged_value_list_view.dart';
|
|
||||||
export 'src/paged_value_notifier.dart' show PagedValueListenableBuilder;
|
export 'src/paged_value_notifier.dart' show PagedValueListenableBuilder;
|
||||||
|
export 'src/paged_value_scroll_view.dart';
|
||||||
export 'src/stream_channel.dart';
|
export 'src/stream_channel.dart';
|
||||||
export 'src/stream_channel_list_controller.dart';
|
export 'src/stream_channel_list_controller.dart';
|
||||||
export 'src/stream_channel_list_event_handler.dart';
|
export 'src/stream_channel_list_event_handler.dart';
|
||||||
|
|||||||
Reference in New Issue
Block a user