I created a widget named InfiniteScroll
which handles asynchronously loaded data and renders it with ListView.builder
. However I am having trouble creating a controller for it (for example for clearing all the loaded data). I read through the implementation of existing controllers such as TextEditingController
but I can't seem to wrap my head around it. Here's an example of what I'm trying to achieve:
// I have
InfiniteScroll(
fetchMore: () async {}, // fetching more data
builder: (data) {}, // building an item
)
// need
InfiniteScroll(
controller: _infiniteScrollController,
fetchMore: () async {}, // fetching more data
builder: (data) {} // building an item
)
// later
_infiniteScrollController.clearItems();
How to create such a controller? I am using flutter_hooks
for local state management if that matters.
I don't think the answer of @ValdaXD describes a good solution.
The way this is usually solved, also in native Flutter widgets like TextField
or ScrollController
is a controller class that extends ChangeNotifier
.
The controller would handle the items and provide a public API to clear them:
class InfiniteScrollController extends ChangeNotifier {
List<Widget> items = [];
void clearItems() {
items.clear();
notifyListeners();
}
}
The widget could then display the items via the injected controller:
class InfiniteScroll extends StatefulWidget {
InfiniteScroll({
required this.controller
});
final InfiniteScrollController controller;
@override
State<StatefulWidget> createState() {
return _InfiniteScrollState();
}
}
class _InfiniteScrollState extends State<InfiniteScroll> {
@override
Widget build(BuildContext context) {
return ListView(
children: widget.controller.items,
);
}
}
I have created a blog post with a different example but the same topic: a controller for a custom widget: https://www.flutterclutter.dev/flutter/tutorials/create-a-controller-for-a-custom-widget/2021/2149/
I just pass the functions that i want to expose to the controller.
typedef MyTypedef(int value);
class MyController {
VoidCallback myFunction;
VoidCallback mySecondFunction;
MyTypedef functionThatReturns;
void dispose() {
//Remove any data that's will cause a memory leak/render errors in here
myFunction = null;
mySecondFunction = null;
functionThatReturns = null;
}
}
class MyWidget extends StatefulWidget {
const MyWidget({this.controller});
final MyController controller;
@override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
@override
void initState() {
super.initState();
MyController _controller = widget.controller;
if (_controller != null) {
_controller.myFunction = firstFunction;
_controller.mySecondFunction = secondFunction;
_controller.functionThatReturns = functionWithInt;
}
}
void firstFunction() {
print('Calling first function');
}
void secondFunction() {
print('Calling second function');
}
void functionWithInt(int value) {
print('Calling third function with value $value');
}
@override
Widget build(BuildContext context) {
return Container();
}
}
Then the usage is easy
//We create a variable somewhere
...
MyController controller;
...
//We initialize it
...
controller = MyController();
...
//We assign it
@override
Widget build(BuildContext context) {
return MyWidget(controller: controller);
}
}
//When we cant to call a function
...
controller.myFunction();
...
//When we want to dispose it
...
controller.dispose();
...
There is a little work to be done to avoid null exceptions , per example we could check if the controller references are null before calling the functions, and throw an error, but that's up to you to decide.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With