Scroll Infinity
Installation
Run this command:
flutter pub add scroll_infinity
Usage Example
Here are some examples of how to use the package to create a list with infinite scrolling:
Vertical:
import 'package:flutter/material.dart';
import 'package:scroll_infinity/scroll_infinity.dart';
class Example extends StatefulWidget {
const Example({super.key});
@override
State<Example> createState() => _ExampleState();
}
class _ExampleState extends State<Example> {
static const _maxItems = 20;
@override
Widget build(BuildContext context) {
return Scaffold(
body: ScrollInfinity(
maxItems: _maxItems,
loadData: (pageKey) async {
await Future.delayed(
const Duration(
seconds: 2,
),
);
return List.generate(_maxItems, (index) {
return _maxItems * pageKey + index + 1;
});
},
itemBuilder: (value, index) {
return ListTile(
title: Text('Item $value'),
subtitle: const Text('Subtitle'),
trailing: const Icon(
Icons.keyboard_arrow_right_rounded,
),
);
},
),
);
}
}
Horizontal:
import 'package:flutter/material.dart';
import 'package:scroll_infinity/scroll_infinity.dart';
class Example extends StatefulWidget {
const Example({super.key});
@override
State<Example> createState() => _ExampleState();
}
class _ExampleState extends State<Example> {
static const _maxItems = 6;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: SizedBox(
height: 64.0,
child: ScrollInfinity(
scrollDirection: Axis.horizontal,
maxItems: _maxItems,
loadData: (pageKey) async {
await Future.delayed(
const Duration(
seconds: 2,
),
);
return List.generate(_maxItems, (index) {
return _maxItems * pageKey + index + 1;
});
},
itemBuilder: (value, index) {
return Center(
child: SizedBox(
width: MediaQuery.sizeOf(context).width * 0.5,
child: ListTile(
onTap: () {},
title: Text('Item $value'),
subtitle: const Text('Subtitle'),
trailing: const Icon(
Icons.keyboard_arrow_right_rounded,
),
),
),
);
},
),
),
),
);
}
}
With Interval
Intervals are identified when the value
of itemBuilder
is null
.
To allow the value
of itemBuilder
to be nullable, facilitating the verification of the value
, use ScrollInfinity
with the expected value type followed by ?
.
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:scroll_infinity/scroll_infinity.dart';
class Example extends StatefulWidget {
const Example({super.key});
@override
State<Example> createState() => _ExampleState();
}
class _ExampleState extends State<Example> {
static const _maxItems = 20;
final _random = Random();
@override
Widget build(BuildContext context) {
return Scaffold(
body: ScrollInfinity<int?>(
maxItems: _maxItems,
interval: 2,
loadData: (pageKey) async {
await Future.delayed(
const Duration(
seconds: 2,
),
);
if (_random.nextInt(4) == 0) {
return null;
}
return List.generate(_maxItems, (index) {
return _maxItems * pageKey + index + 1;
});
},
itemBuilder: (value, index) {
if (value == null) {
return const SizedBox(
height: 60.0,
child: Placeholder(),
);
}
return ListTile(
title: Text('Item $value'),
subtitle: const Text('Subtitle'),
trailing: const Icon(
Icons.keyboard_arrow_right_rounded,
),
);
},
),
);
}
}
With Loader
Add custom loading for initial items.
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:scroll_infinity/scroll_infinity.dart';
class Example extends StatefulWidget {
const Example({super.key});
@override
State<Example> createState() => _ExampleState();
}
class _ExampleState extends State<Example> {
final _notifier = ScrollInfinityInitialItemsNotifier<int?>(null);
static const _maxItems = 20;
final _random = Random();
Future<void> _initLoader() async {
_notifier.value = await _loadData(0);
}
Future<List<int>?> _loadData(int pageIndex) async {
await Future.delayed(
const Duration(
seconds: 2,
),
);
if (_random.nextInt(4) == 0) {
return null;
}
return List.generate(_maxItems, (index) {
return _maxItems * pageIndex + index + 1;
});
}
@override
void initState() {
_initLoader();
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: ScrollInfinityLoader(
notifier: _notifier,
scrollInfinityBuilder: (items) {
return ScrollInfinity<int?>(
maxItems: _maxItems,
initialPageIndex: 1,
initialItems: items,
interval: 2,
loadData: _loadData,
itemBuilder: (value, index) {
if (value == null) {
return const SizedBox(
height: 60.0,
child: Placeholder(),
);
}
return ListTile(
title: Text('Item $value'),
subtitle: const Text('Subtitle'),
trailing: const Icon(
Icons.keyboard_arrow_right_rounded,
),
);
},
);
},
),
);
}
}
Properties
- scrollDirection: Defines the scrolling direction of the list. Can be
Axis.vertical
orAxis.horizontal
.
scrollDirection: Axis.vertical,
- scrollbars: Shows scrollbars if
true
. Default isfalse
.
scrollbars: true,
- padding: Specifies the internal padding of the list.
padding: EdgeInsets.all(8.0),
- header: Listing header.
header: HeaderWidget(),
- initialPageIndex: Initial page index. Default is
0
.
initialPageIndex: 1,
- enableRetryOnError: Determines if retrying to load data after an error is enabled. Default is
true
.
enableRetryOnError: false,
- empty: Widget used to display custom content when the list is empty.
empty: Text('No items available.'),
- reset: Widget used to display custom content during a reset.
reset: Text('Reseting...'),
- error: Widget used to display custom content when an error occurs.
error: Text('Error occurred.'),
- initialItems: Specifies the initial items to be displayed in the list.
initialItems: <Widget>[
// items
],
- interval: Specifies the range in which the
null
value is passed.
interval: 20,
- loading: Allows passing a custom loading component.
loading: LoadingWidget(),
- loadingStyle: Defines the style of the
CircularProgressIndicator
.
loadingStyle: CircularProgressIndicator(
color: Colors.blue,
strokeWidth: 8.0,
),
- tryAgainButtonBuilder: Allows passing a custom retry button component, triggered by a callback.
tryAgainButtonBuilder: (action) {
return ElevatedButton(
onPressed: action,
child: Text('Retry'),
);
},
- maxItems: Specifies the maximum number of items per request. This will be used to determine when the list reaches the end.
maxItems: 20,
- loadData: Function responsible for loading the data. It should return a list of items.
loadData: (pageIndex) async {
// Logic to load the data
},
- separatorBuilder: Builds the separator component between the items in the list.
separatorBuilder: (context, index) {
return Divider(
color: Colors.grey,
height: 1.0,
);
},
- itemBuilder: Builds the items in the list. This function should return the widget that represents each item in the list.
itemBuilder: (value, index) {
final item = items[index];
return ListTile(
title: Text(item.title),
subtitle: Text(item.subtitle),
);
},
Author
This Flutter package was developed by Dário Matias.
Donations
Help maintain the project with donations.