elektronika/lib/library/pagewise/flutter_pagewise.dart

837 lines
30 KiB
Dart

library flutter_pagewise;
import 'package:flutter/material.dart';
import 'helpers/grid_helpers.dart';
typedef Widget ItemBuilder<T>(BuildContext context, T entry, int index);
typedef Future<List<T>> PageFuture<T>(int? pageIndex);
typedef Widget ErrorBuilder(BuildContext context, Object? error);
typedef Widget LoadingBuilder(BuildContext context);
typedef Widget NoItemsFoundBuilder(BuildContext context);
typedef Widget RetryBuilder(BuildContext context, RetryCallback retryCallback);
typedef void RetryCallback();
typedef Widget PagewiseBuilder<T>(PagewiseState<T> state);
/// An abstract base class for widgets that fetch their content one page at a
/// time.
///
/// The widget fetches the page when we scroll down to it, and then keeps it in
/// memory
///
/// You can build your own Pagewise widgets by extending this class and
/// returning your builder in the [builder] function which provides you with the
/// Pagewise state. Look [PagewiseListView] and [PagewiseGridView] for examples.
///
/// See also:
///
/// * [PagewiseGridView], a [Pagewise] implementation of [GridView](https://docs.flutter.io/flutter/widgets/GridView-class.html)
/// * [PagewiseListView], a [Pagewise] implementation of [ListView](https://docs.flutter.io/flutter/widgets/ListView-class.html)
abstract class Pagewise<T> extends StatefulWidget {
/// The number of entries per page
final int? pageSize;
/// Called whenever a new page (or batch) is to be fetched
///
/// It is provided with the page index, and expected to return a [Future](https://api.dartlang.org/stable/1.24.3/dart-async/Future-class.html) that
/// resolves to a list of entries. Please make sure to return only [pageSize]
/// or less entries (in the case of the last page) for each page.
final PageFuture<T>? pageFuture;
/// Called when loading each page.
///
/// It is expected to return a widget to display while the page is loading.
/// For example:
/// ```dart
/// (BuildContext context) {
/// return Text('Loading...');
/// }
/// ```
///
/// If not specified, a [CircularProgressIndicator](https://docs.flutter.io/flutter/material/CircularProgressIndicator-class.html) will be shown
final LoadingBuilder? loadingBuilder;
/// Called with an error object if an error occurs when loading the page
///
/// It is expected to return a widget to display in place of the page that
/// failed to load. For example:
/// ```dart
/// (BuildContext context, Object error) {
/// return Text('Failed to load page: $error');
/// }
/// ```
/// If not specified, a [Text] containing the error will be displayed
final ErrorBuilder? errorBuilder;
/// Whether to show a retry button when page fails to load.
///
/// If set to true, [retryBuilder] is called to show appropriate retry button.
///
/// If set to false, [errorBuilder] is called instead to show appropriate
/// error.
final bool showRetry;
/// Called when a page fails to load and [showRetry] is set to true.
///
/// It is expected to return a widget that gives the user the idea that retry
/// is possible. The builder is provided with a [RetryCallback] that must be
/// called for the retry to happen.
///
/// For example:
/// ```dart
/// (context, retryCallback) {
/// return FloatingActionButton(
/// onPressed: retryCallback,
/// backgroundColor: Colors.red,
/// child: Icon(Icons.refresh),
/// );
/// }
/// ```
///
/// In the code above, when the button is pressed, retryCallback is called,
/// which will retry to fetch the page.
///
/// If not specified, a simple retry button will be shown
final RetryBuilder? retryBuilder;
/// Called when no items are found
///
/// It is expected to return a widget that gives the user the idea that no
/// items exist in the list
/// For example:
/// ```dart
/// (BuildContext context) {
/// return Text('No Items Found!');
/// }
/// ```
final NoItemsFoundBuilder? noItemsFoundBuilder;
/// Called to build each entry in the view.
///
/// It is called for each of the entries fetched by [pageFuture] and provided
/// with the [BuildContext](https://docs.flutter.io/flutter/widgets/BuildContext-class.html) and the entry. It is expected to return the widget
/// that we want to display for each entry
///
/// For example, the [pageFuture] might return a list that looks like:
/// ```dart
///[
/// {
/// 'name': 'product1',
/// 'price': 10
/// },
/// {
/// 'name': 'product2',
/// 'price': 15
/// },
///]
/// ```
/// Then itemBuilder will be called twice, once for each entry. We can for
/// example do:
/// ```dart
/// (BuildContext context, dynamic entry) {
/// return Text(entry['name'] + ' - ' + entry['price']);
/// }
/// ```
final ItemBuilder<T> itemBuilder;
/// The actual builder that builds the Pagewise widget. It is called and
/// provided the PagewiseState. This function is important only for classes
/// extending Pagewise. See [PagewiseListView] and [PagewiseGridView] for
/// examples.
final PagewiseBuilder<T> builder;
/// The controller that controls the loading of pages.
///
/// You don't have to provide this parameter unless you want to control or
/// listen to the data that Pagewise fetches. Review the documentation of
/// [PagewiseLoadController] for more details
final PagewiseLoadController<T>? pageLoadController;
/// Creates a pagewise widget.
///
/// This is an abstract class, this constructor should only be called from
/// constructors of widgets that extend this class
Pagewise(
{this.pageSize,
this.pageFuture,
Key? key,
this.pageLoadController,
this.loadingBuilder,
this.retryBuilder,
this.noItemsFoundBuilder,
this.showRetry: true,
required this.itemBuilder,
this.errorBuilder,
required this.builder})
: assert(
(pageLoadController == null && pageSize != null && pageFuture != null) || (pageLoadController != null && pageSize == null && pageFuture == null)),
assert(showRetry == false || errorBuilder == null, 'Cannot specify showRetry and errorBuilder at the same time'),
assert(showRetry == true || retryBuilder == null, 'Cannot specify retryBuilder when showRetry is set to false'),
super(key: key);
@override
PagewiseState<T> createState() => PagewiseState<T>();
}
class PagewiseState<T> extends State<Pagewise<T>> {
PagewiseLoadController<T>? _controller;
PagewiseLoadController<T>? get _effectiveController => widget.pageLoadController ?? this._controller;
late VoidCallback _controllerListener;
@override
void initState() {
super.initState();
if (widget.pageLoadController == null) {
this._controller = PagewiseLoadController<T>(pageFuture: widget.pageFuture, pageSize: widget.pageSize);
}
this._effectiveController!.init();
this._controllerListener = () {
setState(() {});
};
this._effectiveController!.addListener(this._controllerListener);
}
@override
void dispose() {
super.dispose();
this._effectiveController!.removeListener(this._controllerListener);
}
@override
void didUpdateWidget(Pagewise<T> oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.pageLoadController == null && oldWidget.pageLoadController != null) {
oldWidget.pageLoadController!.removeListener(this._controllerListener);
this._controller = PagewiseLoadController<T>(pageFuture: oldWidget.pageLoadController!.pageFuture, pageSize: oldWidget.pageLoadController!.pageSize);
this._effectiveController!.addListener(this._controllerListener);
this._effectiveController!.init();
} else if (widget.pageLoadController != null && oldWidget.pageLoadController == null) {
this._controller!.removeListener(this._controllerListener);
this._controller = null;
this._effectiveController!.addListener(this._controllerListener);
this._effectiveController!.init();
} else if (widget.pageLoadController != null && (widget.pageLoadController != oldWidget.pageLoadController)) {
oldWidget.pageLoadController!.removeListener(this._controllerListener);
this._effectiveController!.addListener(this._controllerListener);
this._effectiveController!.init();
}
}
int get _itemCount => this._effectiveController!.loadedItems!.length + 1;
@override
Widget build(BuildContext context) {
return widget.builder(this);
}
Widget _itemBuilder(BuildContext context, int index) {
// The total number of widgets, is the number of loaded items, plus the
// number of items that we appended to make all pages the same size,
// plus 1 for the loader
final total = this._effectiveController!.loadedItems!.length + this._effectiveController!._appendedItems.length + 1;
if (index >= total) return SizedBox.shrink();
if (index == total - 1) {
if (this._effectiveController!.noItemsFound) {
return this._getNoItemsFoundWidget();
}
if (this._effectiveController!.error != null) {
if (widget.showRetry) {
return this._getRetryWidget();
} else {
return this._getErrorWidget(this._effectiveController!.error);
}
}
if (this._effectiveController!.hasMoreItems!) {
this._effectiveController!.fetchNewPage();
return this._getLoadingWidget();
} else {
return Container();
}
} else {
if (index >= this._effectiveController!.loadedItems!.length) {
// this means that the function is asking for an element from the
// appended items, so we return an empty container
return Container();
}
// Otherwise, we return the actual item
return widget.itemBuilder(context, this._effectiveController!.loadedItems![index], index);
}
}
Widget _getLoadingWidget() {
return this._getStandardContainer(child: widget.loadingBuilder != null ? widget.loadingBuilder!(context) : CircularProgressIndicator());
}
Widget _getNoItemsFoundWidget() {
return this._getStandardContainer(child: widget.noItemsFoundBuilder != null ? widget.noItemsFoundBuilder!(context) : Container());
}
Widget _getErrorWidget(Object? error) {
return this._getStandardContainer(
child: widget.errorBuilder != null
? widget.errorBuilder!(context, this._effectiveController!.error)
: Text('Error: $error', style: TextStyle(color: Theme.of(context).disabledColor, fontStyle: FontStyle.italic)));
}
Widget _getRetryWidget() {
var defaultRetryButton = TextButton(
style: TextButton.styleFrom(
backgroundColor: Colors.grey[300],
shape: CircleBorder(),
),
child: Icon(
Icons.refresh,
color: Colors.white,
),
onPressed: this._effectiveController!.retry,
);
return this
._getStandardContainer(child: widget.retryBuilder != null ? widget.retryBuilder!(context, this._effectiveController!.retry) : defaultRetryButton);
}
Widget _getStandardContainer({Widget? child}) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Align(
alignment: Alignment.topCenter,
child: child,
));
}
}
/// The controller responsible for managing page loading in Pagewise
///
/// You don't have to provide a controller yourself when creating a Pagewise
/// widget. The widget will create one for you. However you might wish to create
/// one yourself in order to achieve some effects.
///
/// Notice though that if you provide a controller yourself, you should provide
/// the [pageFuture] and [pageSize] parameters to the *controller* instead of
/// the widget.
///
/// A possible use case of the controller is to force a reset of the loaded
/// pages using a [RefreshIndicator](https://docs.flutter.io/flutter/material/RefreshIndicator-class.html).
/// you could achieve that as follows:
///
/// ```dart
/// final _pageLoadController = PagewiseLoadController(
/// pageSize: 6,
/// pageFuture: BackendService.getPage
/// );
///
/// @override
/// Widget build(BuildContext context) {
/// return RefreshIndicator(
/// onRefresh: () async {
/// await this._pageLoadController.reset();
/// },
/// child: PagewiseListView(
/// itemBuilder: this._itemBuilder,
/// pageLoadController: this._pageLoadController,
/// ),
/// );
/// }
/// ```
///
/// Another use case for creating the controller yourself is if you want to
/// listen to the state of Pagewise and act accordingly.
/// For example, you might want to show a specific widget when the list is empty
/// In that case, you could do:
/// ```dart
/// final _pageLoadController = PagewiseLoadController(
/// pageSize: 6,
/// pageFuture: BackendService.getPage
/// );
///
/// bool _empty = false;
///
/// @override
/// void initState() {
/// super.initState();
///
/// this._pageLoadController.addListener(() {
/// if (this._pageLoadController.noItemsFound) {
/// setState(() {
/// this._empty = this._pageLoadController.noItemsFound;
/// });
/// }
/// });
/// }
/// ```
///
/// And then in your `build` function you do:
/// ```dart
/// if (this._empty) {
/// return Text('NO ITEMS FOUND');
/// }
/// ```
class PagewiseLoadController<T> extends ChangeNotifier {
List<T>? _loadedItems;
late List _appendedItems;
int _numberOfLoadedPages = 0;
bool? _hasMoreItems;
Object? _error;
late bool _isFetching;
/// Called whenever a new page (or batch) is to be fetched
///
/// It is provided with the page index, and expected to return a [Future](https://api.dartlang.org/stable/1.24.3/dart-async/Future-class.html) that
/// resolves to a list of entries. Please make sure to return only [pageSize]
/// or less entries (in the case of the last page) for each page.
final PageFuture<T>? pageFuture;
/// The number of entries per page
final int? pageSize;
/// Creates a PagewiseLoadController.
///
/// You must provide both the [pageFuture] and the [pageSize]
PagewiseLoadController({required this.pageFuture, required this.pageSize});
/// The list of items that have already been loaded
List<T>? get loadedItems => this._loadedItems;
/// The number of pages that have already been loaded
int? get numberOfLoadedPages => this._numberOfLoadedPages;
/// Whether there are still more items to load
bool? get hasMoreItems => this._hasMoreItems;
/// The latest error that has been faced when trying to load a page
Object? get error => this._error;
/// set to true if no data was found
bool get noItemsFound => this._loadedItems!.length == 0 && this.hasMoreItems == false;
/// Called to initialize the controller. Same as [reset]
void init() {
this.reset();
}
/// Resets all the information of the controller
void reset() {
this._appendedItems = [];
this._loadedItems = [];
this._numberOfLoadedPages = 0;
this._hasMoreItems = true;
this._error = null;
this._isFetching = false;
this.notifyListeners();
}
/// Fetches a new page by calling [pageFuture]
Future<void> fetchNewPage() async {
if (!this._isFetching) {
this._isFetching = true;
List<T> page;
try {
page = await this.pageFuture!(this._numberOfLoadedPages);
this._numberOfLoadedPages++;
} catch (error) {
this._error = error;
this._isFetching = false;
this.notifyListeners();
return;
}
// Get length accounting for possible null Future return. We'l treat a null Future as an empty return
final int length = (page.length);
if (length > this.pageSize!) {
this._isFetching = false;
throw ('Page length ($length) is greater than the maximum size (${this.pageSize})');
}
if (length > 0 && length < this.pageSize!) {
// This should only happen when loading the last page.
// In that case, we append the last page with a few items to make its size
// similar to normal pages. This is useful especially with GridView,
// because we want the loading to show on a new line on its own
this._appendedItems = List.generate(this.pageSize! - length, (_) => {});
}
if (length == 0) {
this._hasMoreItems = false;
} else {
this._loadedItems!.addAll(page);
}
this._isFetching = false;
notifyListeners();
}
}
/// Attempts to retry in case an error occurred
void retry() {
this._error = null;
this.notifyListeners();
}
void removeItem(bool Function(T item) test) {
this._loadedItems?.removeWhere(test);
this.notifyListeners();
}
}
class PagewiseListView<T> extends Pagewise<T> {
/// Creates a Pagewise ListView.
///
/// All the properties are either those documented for normal [ListViews](https://docs.flutter.io/flutter/widgets/ListView-class.html),
/// or those inherited from [Pagewise]
PagewiseListView(
{Key? key,
EdgeInsetsGeometry? padding,
bool? primary,
bool addSemanticIndexes = true,
int? semanticChildCount,
bool shrinkWrap: false,
ScrollController? controller,
PagewiseLoadController<T>? pageLoadController,
double? itemExtent,
bool addAutomaticKeepAlives: true,
Axis scrollDirection: Axis.vertical,
bool addRepaintBoundaries: true,
double? cacheExtent,
ScrollPhysics? physics,
bool reverse: false,
int? pageSize,
PageFuture<T>? pageFuture,
LoadingBuilder? loadingBuilder,
RetryBuilder? retryBuilder,
NoItemsFoundBuilder? noItemsFoundBuilder,
bool showRetry: true,
required ItemBuilder<T> itemBuilder,
ErrorBuilder? errorBuilder})
: super(
pageSize: pageSize,
pageFuture: pageFuture,
pageLoadController: pageLoadController,
key: key,
loadingBuilder: loadingBuilder,
retryBuilder: retryBuilder,
showRetry: showRetry,
itemBuilder: itemBuilder,
errorBuilder: errorBuilder,
noItemsFoundBuilder: noItemsFoundBuilder,
builder: (PagewiseState<T> state) {
return ListView.builder(
itemExtent: itemExtent,
addAutomaticKeepAlives: addAutomaticKeepAlives,
scrollDirection: scrollDirection,
addRepaintBoundaries: addRepaintBoundaries,
cacheExtent: cacheExtent,
physics: physics,
reverse: reverse,
padding: padding,
addSemanticIndexes: addSemanticIndexes,
semanticChildCount: semanticChildCount,
shrinkWrap: shrinkWrap,
primary: primary,
controller: controller,
itemCount: state._itemCount,
itemBuilder: state._itemBuilder);
});
}
class PagewiseGridView<T> extends Pagewise<T> {
/// Creates a Pagewise GridView with a crossAxisCount.
///
/// All the properties are either those documented for normal [GridViews](https://docs.flutter.io/flutter/widgets/GridView-class.html)
/// or those inherited from [Pagewise]
PagewiseGridView.count(
{Key? key,
EdgeInsetsGeometry? padding,
required int crossAxisCount,
required double height,
double crossAxisSpacing = 0.0,
double mainAxisSpacing = 0.0,
bool addSemanticIndexes = true,
int? semanticChildCount,
bool? primary,
bool shrinkWrap: false,
ScrollController? controller,
PagewiseLoadController<T>? pageLoadController,
bool addAutomaticKeepAlives: true,
Axis scrollDirection: Axis.vertical,
bool addRepaintBoundaries: true,
double? cacheExtent,
ScrollPhysics? physics,
bool reverse: false,
int? pageSize,
PageFuture<T>? pageFuture,
LoadingBuilder? loadingBuilder,
RetryBuilder? retryBuilder,
NoItemsFoundBuilder? noItemsFoundBuilder,
bool showRetry: true,
required ItemBuilder<T> itemBuilder,
ErrorBuilder? errorBuilder})
: super(
pageSize: pageSize,
pageFuture: pageFuture,
pageLoadController: pageLoadController,
key: key,
loadingBuilder: loadingBuilder,
retryBuilder: retryBuilder,
showRetry: showRetry,
itemBuilder: itemBuilder,
errorBuilder: errorBuilder,
noItemsFoundBuilder: noItemsFoundBuilder,
builder: (PagewiseState<T> state) {
return GridView.builder(
reverse: reverse,
physics: physics,
cacheExtent: cacheExtent,
addRepaintBoundaries: addRepaintBoundaries,
scrollDirection: scrollDirection,
addAutomaticKeepAlives: addAutomaticKeepAlives,
controller: controller,
primary: primary,
shrinkWrap: shrinkWrap,
padding: padding,
addSemanticIndexes: addSemanticIndexes,
semanticChildCount: semanticChildCount,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCountAndLoading(
crossAxisCount: crossAxisCount,
// childAspectRatio: childAspectRatio,
height: height,
crossAxisSpacing: crossAxisSpacing,
mainAxisSpacing: mainAxisSpacing,
itemCount: state._itemCount,
),
itemCount: state._itemCount,
itemBuilder: state._itemBuilder);
});
/// Creates a Pagewise GridView with a maxCrossAxisExtent.
///
/// All the properties are either those documented for normal [GridViews](https://docs.flutter.io/flutter/widgets/GridView-class.html)
/// or those inherited from [Pagewise]
PagewiseGridView.extent(
{Key? key,
EdgeInsetsGeometry? padding,
required double maxCrossAxisExtent,
double childAspectRatio = 1.0,
double crossAxisSpacing = 0.0,
double mainAxisSpacing = 0.0,
bool addSemanticIndexes = true,
int? semanticChildCount,
bool? primary,
bool shrinkWrap: false,
ScrollController? controller,
PagewiseLoadController<T>? pageLoadController,
bool addAutomaticKeepAlives: true,
Axis scrollDirection: Axis.vertical,
bool addRepaintBoundaries: true,
double? cacheExtent,
ScrollPhysics? physics,
bool reverse: false,
int? pageSize,
PageFuture<T>? pageFuture,
LoadingBuilder? loadingBuilder,
RetryBuilder? retryBuilder,
NoItemsFoundBuilder? noItemsFoundBuilder,
bool showRetry: true,
required ItemBuilder<T> itemBuilder,
ErrorBuilder? errorBuilder})
: super(
pageSize: pageSize,
pageFuture: pageFuture,
pageLoadController: pageLoadController,
key: key,
loadingBuilder: loadingBuilder,
retryBuilder: retryBuilder,
showRetry: showRetry,
itemBuilder: itemBuilder,
errorBuilder: errorBuilder,
noItemsFoundBuilder: noItemsFoundBuilder,
builder: (PagewiseState<T> state) {
return GridView.builder(
reverse: reverse,
physics: physics,
cacheExtent: cacheExtent,
addRepaintBoundaries: addRepaintBoundaries,
scrollDirection: scrollDirection,
addAutomaticKeepAlives: addAutomaticKeepAlives,
addSemanticIndexes: addSemanticIndexes,
semanticChildCount: semanticChildCount,
controller: controller,
primary: primary,
shrinkWrap: shrinkWrap,
padding: padding,
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtentAndLoading(
maxCrossAxisExtent: maxCrossAxisExtent,
childAspectRatio: childAspectRatio,
crossAxisSpacing: crossAxisSpacing,
mainAxisSpacing: mainAxisSpacing,
itemCount: state._itemCount),
itemCount: state._itemCount,
itemBuilder: state._itemBuilder);
});
}
int _kDefaultSemanticIndexCallback(Widget _, int localIndex) => localIndex;
class PagewiseSliverList<T> extends Pagewise<T> {
/// Creates a Pagewise SliverList.
///
/// All the properties are either those documented for normal [SliverList](https://docs.flutter.io/flutter/widgets/SliverList-class.html)
/// or those inherited from [Pagewise]
PagewiseSliverList(
{Key? key,
bool addSemanticIndexes = true,
bool addAutomaticKeepAlives: true,
bool addRepaintBoundaries: true,
SemanticIndexCallback semanticIndexCallback = _kDefaultSemanticIndexCallback,
int semanticIndexOffset = 0,
PagewiseLoadController<T>? pageLoadController,
int? pageSize,
PageFuture<T>? pageFuture,
LoadingBuilder? loadingBuilder,
RetryBuilder? retryBuilder,
NoItemsFoundBuilder? noItemsFoundBuilder,
bool showRetry: true,
required ItemBuilder<T> itemBuilder,
ErrorBuilder? errorBuilder})
: super(
pageSize: pageSize,
pageFuture: pageFuture,
pageLoadController: pageLoadController,
key: key,
loadingBuilder: loadingBuilder,
retryBuilder: retryBuilder,
showRetry: showRetry,
itemBuilder: itemBuilder,
errorBuilder: errorBuilder,
noItemsFoundBuilder: noItemsFoundBuilder,
builder: (PagewiseState<T> state) {
return SliverList(
delegate: SliverChildBuilderDelegate(state._itemBuilder,
addAutomaticKeepAlives: addAutomaticKeepAlives,
addRepaintBoundaries: addRepaintBoundaries,
addSemanticIndexes: addSemanticIndexes,
semanticIndexCallback: semanticIndexCallback,
semanticIndexOffset: semanticIndexOffset,
childCount: state._itemCount),
);
});
}
class PagewiseSliverGrid<T> extends Pagewise<T> {
/// Creates a Pagewise SliverGrid with a crossAxisCount.
///
/// All the properties are either those documented for normal [SliverGrid](https://docs.flutter.io/flutter/widgets/SliverGrid-class.html)
/// or those inherited from [Pagewise]
PagewiseSliverGrid.count(
{Key? key,
bool addSemanticIndexes = true,
bool addAutomaticKeepAlives: true,
bool addRepaintBoundaries: true,
SemanticIndexCallback semanticIndexCallback = _kDefaultSemanticIndexCallback,
int semanticIndexOffset = 0,
required int crossAxisCount,
required double height,
double crossAxisSpacing = 0.0,
double mainAxisSpacing = 0.0,
PagewiseLoadController<T>? pageLoadController,
int? pageSize,
PageFuture<T>? pageFuture,
LoadingBuilder? loadingBuilder,
RetryBuilder? retryBuilder,
NoItemsFoundBuilder? noItemsFoundBuilder,
bool showRetry: true,
required ItemBuilder<T> itemBuilder,
ErrorBuilder? errorBuilder})
: super(
pageSize: pageSize,
pageFuture: pageFuture,
pageLoadController: pageLoadController,
key: key,
loadingBuilder: loadingBuilder,
retryBuilder: retryBuilder,
showRetry: showRetry,
itemBuilder: itemBuilder,
errorBuilder: errorBuilder,
noItemsFoundBuilder: noItemsFoundBuilder,
builder: (PagewiseState<T> state) {
return SliverGrid(
delegate: SliverChildBuilderDelegate(state._itemBuilder,
addAutomaticKeepAlives: addAutomaticKeepAlives,
addRepaintBoundaries: addRepaintBoundaries,
addSemanticIndexes: addSemanticIndexes,
semanticIndexCallback: semanticIndexCallback,
semanticIndexOffset: semanticIndexOffset,
childCount: state._itemCount),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCountAndLoading(
crossAxisCount: crossAxisCount,
height: height,
crossAxisSpacing: crossAxisSpacing,
mainAxisSpacing: mainAxisSpacing,
itemCount: state._itemCount,
),
);
});
/// Creates a Pagewise SliverGrid with a maxCrossAxisExtent.
///
/// All the properties are either those documented for normal [SliverGrid](https://docs.flutter.io/flutter/widgets/SliverGrid-class.html)
/// or those inherited from [Pagewise]
PagewiseSliverGrid.extent(
{Key? key,
bool addSemanticIndexes = true,
bool addAutomaticKeepAlives: true,
bool addRepaintBoundaries: true,
SemanticIndexCallback semanticIndexCallback = _kDefaultSemanticIndexCallback,
int semanticIndexOffset = 0,
required double maxCrossAxisExtent,
double childAspectRatio = 1.0,
double crossAxisSpacing = 0.0,
double mainAxisSpacing = 0.0,
PagewiseLoadController<T>? pageLoadController,
int? pageSize,
PageFuture<T>? pageFuture,
LoadingBuilder? loadingBuilder,
RetryBuilder? retryBuilder,
NoItemsFoundBuilder? noItemsFoundBuilder,
bool showRetry: true,
required ItemBuilder<T> itemBuilder,
ErrorBuilder? errorBuilder})
: super(
pageSize: pageSize,
pageFuture: pageFuture,
pageLoadController: pageLoadController,
key: key,
loadingBuilder: loadingBuilder,
noItemsFoundBuilder: noItemsFoundBuilder,
retryBuilder: retryBuilder,
showRetry: showRetry,
itemBuilder: itemBuilder,
errorBuilder: errorBuilder,
builder: (PagewiseState<T> state) {
return SliverGrid(
delegate: SliverChildBuilderDelegate(state._itemBuilder,
addAutomaticKeepAlives: addAutomaticKeepAlives,
addRepaintBoundaries: addRepaintBoundaries,
addSemanticIndexes: addSemanticIndexes,
semanticIndexCallback: semanticIndexCallback,
semanticIndexOffset: semanticIndexOffset,
childCount: state._itemCount),
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtentAndLoading(
maxCrossAxisExtent: maxCrossAxisExtent,
childAspectRatio: childAspectRatio,
crossAxisSpacing: crossAxisSpacing,
mainAxisSpacing: mainAxisSpacing,
itemCount: state._itemCount),
);
});
}