diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
index 1d526a1..919434a 100644
--- a/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
+++ b/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -2,6 +2,6 @@
+ location = "self:">
diff --git a/example/lib/main.dart b/example/lib/main.dart
index c6546be..c86f9c6 100644
--- a/example/lib/main.dart
+++ b/example/lib/main.dart
@@ -105,7 +105,7 @@ class _CustomInfiniteList extends StatelessWidget {
},
),
onError: (context, retry, error) {
- Scaffold.of(context)
+ ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(SnackBar(
content: Text(error.toString()),
@@ -119,7 +119,7 @@ class _CustomInfiniteList extends StatelessWidget {
}
}
-Future> _itemLoader(int limit, {int start = 0}) async {
+Future?> _itemLoader(int limit, {int start = 0}) async {
await Future.delayed(const Duration(seconds: 1));
if (start >= 100) return null;
if (Random().nextInt(2) == 0) throw Exception('Oops!');
@@ -136,9 +136,9 @@ class _Loading extends StatelessWidget {
class _Error extends StatelessWidget {
const _Error({
- Key key,
- this.error,
- this.retry,
+ Key? key,
+ required this.error,
+ required this.retry,
}) : super(key: key);
final Object error;
@@ -153,7 +153,7 @@ class _Error extends StatelessWidget {
children: [
Text(
error.toString(),
- style: theme.textTheme.headline4.copyWith(color: theme.errorColor),
+ style: theme.textTheme.headline4?.copyWith(color: theme.errorColor),
textAlign: TextAlign.center,
),
const SizedBox(height: 16),
@@ -169,7 +169,7 @@ class _Error extends StatelessWidget {
}
class _ErrorLoader extends StatelessWidget {
- const _ErrorLoader({Key key, @required this.retry}) : super(key: key);
+ const _ErrorLoader({Key? key, required this.retry}) : super(key: key);
final VoidCallback retry;
@override
diff --git a/example/pubspec.yaml b/example/pubspec.yaml
index 419028c..52b9165 100644
--- a/example/pubspec.yaml
+++ b/example/pubspec.yaml
@@ -5,7 +5,7 @@ publish_to: none
version: 1.0.0+1
environment:
- sdk: ">=2.7.0 <3.0.0"
+ sdk: ">=2.12.0 <3.0.0"
dependencies:
flutter:
@@ -14,7 +14,7 @@ dependencies:
path: ../
dev_dependencies:
- very_good_analysis: ^1.0.4
+ very_good_analysis: ^2.0.0
flutter:
uses-material-design: true
diff --git a/lib/very_good_infinite_list.dart b/lib/very_good_infinite_list.dart
index d34c7b5..9e7cdbc 100644
--- a/lib/very_good_infinite_list.dart
+++ b/lib/very_good_infinite_list.dart
@@ -19,7 +19,7 @@ class InfiniteListException implements Exception {}
///
/// * [limit] is the number of items you'd like to fetch.
/// * [start] is an optional offset which defaults to 0.
-typedef ItemLoader = Future> Function(int limit, {int start});
+typedef ItemLoader = Future?> Function(int limit, {int start});
/// Function which returns a [Widget] given a [context], [retry], and [error].
/// Used by [InfiniteList] to render widgets in response to exceptions thrown
@@ -48,15 +48,15 @@ typedef OnError = void Function(
class InfiniteListBuilder {
/// {@macro infinite_list_builder}
const InfiniteListBuilder({
- @required this.success,
- WidgetBuilder loading,
- ErrorBuilder error,
- WidgetBuilder empty,
+ required this.success,
+ WidgetBuilder? loading,
+ ErrorBuilder? error,
+ WidgetBuilder? empty,
}) : _loading = loading,
_error = error,
_empty = empty;
- final WidgetBuilder _loading;
+ final WidgetBuilder? _loading;
/// [WidgetBuilder] which is invoked when the [InfiniteList]
/// is rendered while content is being fetched by the [ItemLoader].
@@ -73,7 +73,7 @@ class InfiniteListBuilder {
/// retrieved from the [ItemLoader].
final Widget Function(BuildContext, T) success;
- final ErrorBuilder _error;
+ final ErrorBuilder? _error;
/// [WidgetBuilder] which is invoked when the [InfiniteList]
/// is rendered and an [InfiniteListException]
@@ -89,7 +89,7 @@ class InfiniteListBuilder {
};
}
- final WidgetBuilder _empty;
+ final WidgetBuilder? _empty;
/// [WidgetBuilder] which is invoked when the [InfiniteList]
/// is rendered and the [ItemLoader] has returned an empty list.
@@ -109,20 +109,18 @@ class InfiniteListBuilder {
class InfiniteList extends StatefulWidget {
/// {@macro infinite_list}
const InfiniteList({
- Key key,
- @required this.itemLoader,
- @required this.builder,
- WidgetBuilder bottomLoader,
- ErrorBuilder errorLoader,
+ Key? key,
+ required this.itemLoader,
+ required this.builder,
+ WidgetBuilder? bottomLoader,
+ ErrorBuilder? errorLoader,
+ ScrollController? scrollController,
this.debounceDuration,
this.reverse = false,
this.onError,
this.padding,
- ScrollController scrollController,
- double scrollOffsetThreshold,
- }) : assert(itemLoader != null),
- assert(builder != null),
- _bottomLoader = bottomLoader,
+ double? scrollOffsetThreshold,
+ }) : _bottomLoader = bottomLoader,
_errorLoader = errorLoader,
_scrollController = scrollController,
_scrollOffsetThreshold =
@@ -130,7 +128,7 @@ class InfiniteList extends StatefulWidget {
super(key: key);
/// The amount of space by which to inset the children of the [builder].
- final EdgeInsetsGeometry padding;
+ final EdgeInsetsGeometry? padding;
/// {@macro infinite_list_builder}
final InfiniteListBuilder builder;
@@ -139,7 +137,7 @@ class InfiniteList extends StatefulWidget {
/// to lazily fetch content.
final ItemLoader itemLoader;
- final WidgetBuilder _bottomLoader;
+ final WidgetBuilder? _bottomLoader;
/// [WidgetBuilder] which is responsible for rendering the bottom loader
/// widget which is rendered when the user scrolls to the bottom of the list
@@ -152,7 +150,7 @@ class InfiniteList extends StatefulWidget {
);
}
- final ErrorBuilder _errorLoader;
+ final ErrorBuilder? _errorLoader;
/// [WidgetBuilder] which is responsible for rendering the bottom loader
/// widget which is rendered when additional content is unable to be loaded
@@ -167,13 +165,13 @@ class InfiniteList extends StatefulWidget {
);
/// {@macro on_error}
- final OnError onError;
+ final OnError? onError;
- final ScrollController _scrollController;
+ final ScrollController? _scrollController;
/// Debounce duration for the [itemLoader].
/// Defaults to `const Duration(milliseconds: 100)`.
- final Duration debounceDuration;
+ final Duration? debounceDuration;
/// Whether the scroll view scrolls in the reading direction.
///
@@ -196,9 +194,9 @@ class InfiniteList extends StatefulWidget {
}
class _InfiniteListState extends State> {
- ScrollController _scrollController;
- _ListController _controller;
- _Debouncer _debouncer;
+ late ScrollController _scrollController;
+ late _ListController _controller;
+ late _Debouncer _debouncer;
void _onListStateChanged() {
final state = _controller.value;
@@ -225,7 +223,7 @@ class _InfiniteListState extends State> {
@override
void dispose() {
- _debouncer?.dispose();
+ _debouncer.dispose();
_controller
..removeListener(_onListStateChanged)
..dispose();
@@ -250,7 +248,7 @@ class _InfiniteListState extends State> {
Widget build(BuildContext context) {
return ValueListenableBuilder(
valueListenable: _controller,
- builder: (context, state, child) {
+ builder: (context, _ListState state, child) {
final itemCount = state.hasReachedMax == false
? state.items.length + 1
: state.items.length;
@@ -314,9 +312,9 @@ class _InfiniteListState extends State> {
class _DefaultError extends StatelessWidget {
const _DefaultError({
- Key key,
- this.error,
- this.retry,
+ Key? key,
+ required this.error,
+ required this.retry,
}) : super(key: key);
final Object error;
@@ -331,7 +329,7 @@ class _DefaultError extends StatelessWidget {
children: [
Text(
'$error',
- style: theme.textTheme.headline4.copyWith(color: theme.errorColor),
+ style: theme.textTheme.headline4?.copyWith(color: theme.errorColor),
textAlign: TextAlign.center,
),
const SizedBox(height: 16),
@@ -346,7 +344,7 @@ class _DefaultError extends StatelessWidget {
}
class _DefaultErrorLoader extends StatelessWidget {
- const _DefaultErrorLoader({Key key, @required this.retry}) : super(key: key);
+ const _DefaultErrorLoader({Key? key, required this.retry}) : super(key: key);
final VoidCallback retry;
@override
@@ -368,7 +366,7 @@ class _ListException implements Exception {
final Object value;
- static const none = _ListException(null);
+ static const none = _ListException(Object());
}
class _ListState {
@@ -387,11 +385,11 @@ class _ListState {
final _ListException exception;
_ListState copyWith({
- @required _ListStatus status,
- int currentIndex,
- List items,
- bool hasReachedMax,
- _ListException exception,
+ required _ListStatus status,
+ int? currentIndex,
+ List? items,
+ bool? hasReachedMax,
+ _ListException? exception,
}) {
return _ListState(
currentIndex: currentIndex ?? this.currentIndex,
@@ -455,10 +453,10 @@ class _ListController extends ValueNotifier<_ListState> {
}
class _Debouncer {
- _Debouncer({Duration delay}) : _delay = delay ?? _kDebounceDuration;
+ _Debouncer({Duration? delay}) : _delay = delay ?? _kDebounceDuration;
final Duration _delay;
- Timer _timer;
+ Timer? _timer;
void call(void Function() action) {
_timer?.cancel();
diff --git a/pubspec.yaml b/pubspec.yaml
index b9ee70c..59e6563 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -2,15 +2,15 @@ name: very_good_infinite_list
description: >-
A Very Good Infinite List Widget created by Very Good Ventures.
Comes in handy when making activity feeds, news feeds, etc.
-version: 0.2.1
+version: 0.3.0
repository: https://github.com/VeryGoodOpenSource/very_good_infinite_list
issue_tracker: https://github.com/VeryGoodOpenSource/very_good_infinite_list/issues
homepage: https://github.com/VeryGoodOpenSource/very_good_infinite_list
documentation: https://github.com/VeryGoodOpenSource/very_good_infinite_list
environment:
- sdk: ">=2.7.0 <3.0.0"
- flutter: ">=1.17.0"
+ sdk: ">=2.12.0 <3.0.0"
+ flutter: ">=2.0.0"
dependencies:
flutter:
@@ -19,4 +19,4 @@ dependencies:
dev_dependencies:
flutter_test:
sdk: flutter
- very_good_analysis: ^1.0.4
+ very_good_analysis: ^2.0.0
diff --git a/test/very_good_infinite_list_test.dart b/test/very_good_infinite_list_test.dart
index 0b13ffa..5d60db9 100644
--- a/test/very_good_infinite_list_test.dart
+++ b/test/very_good_infinite_list_test.dart
@@ -4,31 +4,9 @@ import 'package:very_good_infinite_list/very_good_infinite_list.dart';
void main() {
group('InfiniteList', () {
- group('constructor', () {
- test('throws AssertionError when itemLoader is null', () {
- expect(
- () => InfiniteList(
- itemLoader: null,
- builder: InfiniteListBuilder(success: (_, __) => const SizedBox()),
- ),
- throwsAssertionError,
- );
- });
-
- test('throws AssertionError when builder is null', () {
- expect(
- () => InfiniteList(
- itemLoader: (int limit, {int start}) async => [],
- builder: null,
- ),
- throwsAssertionError,
- );
- });
- });
-
testWidgets('updates scroll controller when changed', (tester) async {
var itemLoaderCallCount = 0;
- final itemLoader = (int limit, {int start}) async {
+ final itemLoader = (int limit, {int? start}) async {
itemLoaderCallCount++;
return List.generate(15, (i) => i);
};
@@ -91,7 +69,7 @@ void main() {
});
testWidgets('reverse updates list view', (tester) async {
- final itemLoader = (int limit, {int start}) async {
+ final itemLoader = (int limit, {int? start}) async {
return List.generate(15, (i) => i);
};
@@ -110,7 +88,7 @@ void main() {
});
testWidgets('list view is not reversed by default', (tester) async {
- final itemLoader = (int limit, {int start}) async {
+ final itemLoader = (int limit, {int? start}) async {
return List.generate(15, (i) => i);
};
@@ -128,7 +106,7 @@ void main() {
});
testWidgets('list view supports custom padding', (tester) async {
- final itemLoader = (int limit, {int start}) async {
+ final itemLoader = (int limit, {int? start}) async {
return List.generate(15, (i) => i);
};
const padding = EdgeInsets.all(16);
@@ -148,7 +126,7 @@ void main() {
testWidgets('invokes itemLoader immediately', (tester) async {
var itemLoaderCallCount = 0;
- final itemLoader = (int limit, {int start}) {
+ final itemLoader = (int limit, {int? start}) async {
itemLoaderCallCount++;
};
@@ -162,7 +140,7 @@ void main() {
testWidgets('renders default loading widget by default', (tester) async {
await tester.pumpApp(InfiniteList(
- itemLoader: (int limit, {int start}) async => [],
+ itemLoader: (int limit, {int? start}) async => [],
builder: InfiniteListBuilder(success: (_, __) => const SizedBox()),
));
@@ -172,7 +150,7 @@ void main() {
testWidgets('renders default bottom loader widget by default',
(tester) async {
await tester.pumpApp(InfiniteList(
- itemLoader: (int limit, {int start}) async {
+ itemLoader: (int limit, {int? start}) async {
return List.generate(1, (i) => i);
},
builder: InfiniteListBuilder(success: (_, __) => const SizedBox()),
@@ -190,15 +168,15 @@ void main() {
(tester) async {
var itemLoaderCallCount = 0;
final itemLoaderResults = [
- (int limit, {int start}) async {
+ (int limit, {int? start}) async {
return List.generate(15, (i) => i);
},
- (int limit, {int start}) async {
+ (int limit, {int? start}) async {
throw Exception('oops');
},
];
await tester.pumpApp(InfiniteList(
- itemLoader: (int limit, {int start}) async {
+ itemLoader: (int limit, {int? start}) async {
itemLoaderCallCount++;
return itemLoaderResults.removeAt(0).call(limit, start: start);
},
@@ -240,15 +218,15 @@ void main() {
testWidgets('renders default error widget by default', (tester) async {
var itemLoaderCallCount = 0;
final itemLoaderResults = [
- (int limit, {int start}) async {
+ (int limit, {int? start}) async {
return List.generate(15, (i) => i);
},
- (int limit, {int start}) async {
+ (int limit, {int? start}) async {
throw InfiniteListException();
},
];
await tester.pumpApp(InfiniteList(
- itemLoader: (int limit, {int start}) async {
+ itemLoader: (int limit, {int? start}) async {
itemLoaderCallCount++;
return itemLoaderResults.removeAt(0).call(limit, start: start);
},
@@ -290,15 +268,15 @@ void main() {
testWidgets('retry from first time failure', (tester) async {
var itemLoaderCallCount = 0;
final itemLoaderResults = [
- (int limit, {int start}) async {
+ (int limit, {int? start}) async {
throw InfiniteListException();
},
- (int limit, {int start}) async {
+ (int limit, {int? start}) async {
return List.generate(15, (i) => i);
},
];
await tester.pumpApp(InfiniteList(
- itemLoader: (int limit, {int start}) async {
+ itemLoader: (int limit, {int? start}) async {
itemLoaderCallCount++;
return itemLoaderResults.removeAt(0).call(limit, start: start);
},
@@ -326,18 +304,18 @@ void main() {
testWidgets('retry from subsequent failure (critical)', (tester) async {
var itemLoaderCallCount = 0;
final itemLoaderResults = [
- (int limit, {int start}) async {
+ (int limit, {int? start}) async {
return List.generate(15, (i) => i);
},
- (int limit, {int start}) async {
+ (int limit, {int? start}) async {
throw InfiniteListException();
},
- (int limit, {int start}) async {
- return List.generate(15, (i) => i + start);
+ (int limit, {int? start}) async {
+ return List.generate(15, (i) => i + start!);
},
];
await tester.pumpApp(InfiniteList(
- itemLoader: (int limit, {int start}) async {
+ itemLoader: (int limit, {int? start}) async {
itemLoaderCallCount++;
return itemLoaderResults.removeAt(0).call(limit, start: start);
},
@@ -384,18 +362,18 @@ void main() {
testWidgets('retry from subsequent failure', (tester) async {
var itemLoaderCallCount = 0;
final itemLoaderResults = [
- (int limit, {int start}) async {
+ (int limit, {int? start}) async {
return List.generate(15, (i) => i);
},
- (int limit, {int start}) async {
+ (int limit, {int? start}) async {
throw Exception('oops');
},
- (int limit, {int start}) async {
- return List.generate(15, (i) => i + start);
+ (int limit, {int? start}) async {
+ return List.generate(15, (i) => i + start!);
},
];
await tester.pumpApp(InfiniteList(
- itemLoader: (int limit, {int start}) async {
+ itemLoader: (int limit, {int? start}) async {
itemLoaderCallCount++;
return itemLoaderResults.removeAt(0).call(limit, start: start);
},
@@ -443,13 +421,13 @@ void main() {
(tester) async {
var itemLoaderCallCount = 0;
final itemLoaderResults = [
- (int limit, {int start}) async {
+ (int limit, {int? start}) async {
return List.generate(15, (i) => i);
},
- (int limit, {int start}) async => [],
+ (int limit, {int? start}) async => [],
];
await tester.pumpApp(InfiniteList(
- itemLoader: (int limit, {int start}) async {
+ itemLoader: (int limit, {int? start}) async {
itemLoaderCallCount++;
return itemLoaderResults.removeAt(0).call(limit, start: start);
},
@@ -494,7 +472,7 @@ void main() {
testWidgets('renders default empty widget by default', (tester) async {
await tester.pumpApp(InfiniteList(
- itemLoader: (int limit, {int start}) async => [],
+ itemLoader: (int limit, {int? start}) async => [],
builder: InfiniteListBuilder(success: (_, __) => const SizedBox()),
));
@@ -505,7 +483,7 @@ void main() {
testWidgets('renders default error widget by default (generic)',
(tester) async {
await tester.pumpApp(InfiniteList(
- itemLoader: (int limit, {int start}) async {
+ itemLoader: (int limit, {int? start}) async {
throw Exception('oops');
},
builder: InfiniteListBuilder(success: (_, __) => const SizedBox()),
@@ -518,7 +496,7 @@ void main() {
testWidgets('renders default error widget by default (specific)',
(tester) async {
await tester.pumpApp(InfiniteList(
- itemLoader: (int limit, {int start}) async {
+ itemLoader: (int limit, {int? start}) async {
throw InfiniteListException();
},
builder: InfiniteListBuilder(success: (_, __) => const SizedBox()),