#flutter #flutter-hooks
#flutter #flutter-крючки
Вопрос:
Я создал виджет с именем InfiniteScroll
, который обрабатывает асинхронно загруженные данные и отображает их ListView.builder
. Однако у меня возникли проблемы с созданием контроллера для него (например, для очистки всех загруженных данных). Я прочитал реализацию существующих контроллеров, таких как TextEditingController
, но, похоже, я не могу понять это. Вот пример того, чего я пытаюсь достичь:
// 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();
Как создать такой контроллер? Я использую flutter_hooks
для локального управления состоянием, если это имеет значение.
Комментарии:
1. Я не понимаю, как это связано с моей проблемой. Я ищу способ создания контроллера, сам виджет — всего лишь пример.
Ответ №1:
Я не думаю, что ответ @ValdaXD описывает хорошее решение.
Обычно это решается также в собственных виджетах Flutter, таких как TextField
или ScrollController
— это класс контроллера, который расширяется ChangeNotifier
.
Контроллер будет обрабатывать элементы и предоставлять общедоступный API для их очистки:
class InfiniteScrollController extends ChangeNotifier {
List<Widget> items = [];
void clearItems() {
items.clear();
notifyListeners();
}
}
Затем виджет может отображать элементы с помощью введенного контроллера:
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,
);
}
}
Я создал сообщение в блоге с другим примером, но на ту же тему: контроллер для пользовательского виджета: https://www.flutterclutter.dev/flutter/tutorials/create-a-controller-for-a-custom-widget/2021/2149 /
Комментарии:
1. Этот ответ является лучшим решением. Принятый ответ выходит за рамки того, как собственные виджеты решают проблему. Сообщение в блоге от @Schnodderbalken — отличное руководство по решению этой проблемы.
2. Спасибо за ваш ответ, я пометил его как принятый.
3. ваша статья в блоге очень полезна.
4. Хороший ответ, но у меня есть одно сомнение по этому поводу. Я считаю, что это решение подразумевает сохранение состояния в контроллере. Что, если вы хотите, чтобы управляемый виджет содержал все состояния, а не разделял его внутри контроллера? Разве решение ValdaXDs не было бы лучше?
Ответ №2:
Я просто передаю функции, которые я хочу предоставить контроллеру.
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();
}
}
Тогда использование будет простым
//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();
...
Необходимо проделать небольшую работу, чтобы избежать исключений null, например, мы могли бы проверить, являются ли ссылки на контроллеры null перед вызовом функций, и выдать ошибку, но это решать вам.
Ответ №3:
Вы могли бы просто использовать GlobalKey. Я действительно не знаю, как создавать контроллеры, но это было бы моим решением. Это действительно похоже на GlobalKey, но я надеюсь, что это сработает для вас. Но я ничего не нашел о том, как делать контроллеры тааак … да.
class InfiniteScroll extends StatefulWidget {
InfiniteScrollController controller;
InfiniteScroll({@required this.controller}) {}
InfiniteScrollState createState() => InfiniteScrollState(controller);
}
class InfiniteScrollState extends State<InfiniteScroll> {
InfiniteScrollController controller;
InfiniteScrollState(this.controller) {
this.controller.setParent(this);
}
Widget build(BuildContext context) {}
}
class InfiniteScrollController {
InfiniteScrollState infiniteScroll;
void setParent(InfiniteScrollState infiniteScroll) {
this.infiniteScroll = infiniteScroll;
}
void clearItems() {
infiniteScroll.setState(() {
//clear Items
});
}
}
Комментарии:
1. Спасибо за ваш ответ, это похоже на хороший обходной путь, хотя принятый ответ более чистый