Вопрос или проблема
Я новичок в создании пользовательских контроллеров, но я пытаюсь динамически создавать/удалять контейнерные виджеты, при этом добавляя данные в каждый из них независимо друг от друга. Когда я удаляю виджеты, я пытаюсь конкретно выбрать, какой из них удалить, и исследования показали, что создание индексированных контроллеров — это то, что мне нужно, но у меня возникают проблемы с реализацией этого.
Пользовательский контроллер выглядит так:
class WidgetController extends ChangeNotifier {
void removeWidget(controller, index) {
controller[index].dispose();
controller.removeAt(index);
}
void addWidget(controller) {
controller.add(WidgetController());
}
}
Динамический контейнерный виджет, который необходимо создать/удалить, выглядит так:
class DynamicWidget extends StatefulWidget {
const DynamicWidget({
required this.controller,
required this.index,
super.key,
});
final List<WidgetController> controller;
final int index;
@override
State<DynamicWidget> createState() => _DynamicWidget();
}
class _DynamicWidget extends State<DynamicWidget> {
_DynamicWidget();
int num = 1;
@override
Widget build(BuildContext context) {
return Expanded(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
color: Colors.redAccent.shade100,
),
child: Row(
children: [
Text(num.toString()),
IconButton(
onPressed: (() {
setState(() {
num = num + 1;
});
}),
icon: const Icon(Icons.add),
),
IconButton(
onPressed: () {
setState(() { // УДАЛЕНИЕ ВИДЖЕТА
WidgetController().removeWidget(widget.controller, widget.index);
});
},
icon: const Icon(Icons.close),
),
],
),
),
);
}
}
И код, который вызывает этот виджет, выглядит так:
class Test extends StatefulWidget {
const Test({super.key});
@override
State<Test> createState() => _Test();
}
class _Test extends State<Test> {
List<WidgetController> controller = [WidgetController()];
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ListView.builder(
padding: const EdgeInsets.symmetric(horizontal: 15),
shrinkWrap: true,
itemCount: controller.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.only(top: 15),
child: Row(
children: [
DynamicWidget(controller: controller, index: index,),
],
),
);
},
),
const SizedBox(
height: 50,
),
Center(
child: TextButton(
onPressed: () {
setState(() { // ДОБАВЛЕНИЕ ВИДЖЕТА
WidgetController().addWidget(controller);
});
},
child: const Text('Добавить еще'),
),
),
],
),
),
),
);
}
}
Проблемы, с которыми я сталкиваюсь:
- Я могу добавлять виджеты нормально, но не могу визуально их удалять (виджеты удаляются за кулисами, но не обновляются в интерфейсе приложения, пока я не обновлю, добавив другой виджет)
- Я не уверен, что индексация работает правильно. Я думаю, что удаляется только последний элемент списка виджетов, а не конкретный виджет, который я пытаюсь удалить
Если кто-то сможет изменить мой код выше, чтобы исправить эти проблемы, я буду очень признателен. Я пытался использовать отображение, но обнаружил, что при добавлении чего-либо в один из созданных виджетов оно добавляется ко всем из них.
Пожалуйста, обратите внимание, что это упрощенная версия исходного кода.
Ответ или решение
Для решения вашей задачи по динамическому добавлению и удалению контейнерных виджетов с использованием кастомного контроллера в Flutter, необходимо внести несколько изменений в ваш текущий код. В данной статье мы подробно рассмотрим шага за шагом решение ваших проблем.
Проблемы:
-
Необновление UI при удалении виджетов: После удаления виджетов состояние не обновляется, и приложение не отображает изменения до тех пор, пока вы не добавите новый виджет.
-
Индексация виджетов: Индексация виджетов работает некорректно, так как, похоже, удаляется только последний виджет в списке.
Решение:
1. Обновление UI
Для того чтобы обновить интерфейс после удаления виджета, необходимо использовать метод notifyListeners()
из класса ChangeNotifier
. Он оповестит виджеты о том, что данные изменились, и что нужно обновить интерфейс.
2. Индексация
Ваша реализация удаления виджетов через управление индексом выглядит правильно, однако необходимо убедиться, что вы корректно управляете состоянием.
Исправленный код
Вот исправленный код, который учитывает описанные выше проблемы:
// Класс контроллера
class WidgetController extends ChangeNotifier {
List<DynamicWidgetData> widgetsData = [];
void removeWidget(int index) {
if (index < widgetsData.length) {
widgetsData.removeAt(index);
notifyListeners(); // Оповещаем слушателей об изменениях
}
}
void addWidget() {
widgetsData.add(DynamicWidgetData()); // Или используйте вашу логику для добавления данных
notifyListeners();
}
// Доступ к данным
DynamicWidgetData getWidgetData(int index) {
return widgetsData[index];
}
int get widgetsCount => widgetsData.length;
}
// Данные для динамического виджета
class DynamicWidgetData {
int num = 1;
}
// Динамический виджет
class DynamicWidget extends StatelessWidget {
final WidgetController controller;
final int index;
const DynamicWidget({required this.controller, required this.index, Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final data = controller.getWidgetData(index);
return Expanded(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
color: Colors.redAccent.shade100,
),
child: Row(
children: [
Text(data.num.toString()),
IconButton(
onPressed: () {
// Увеличиваем num
data.num++;
controller.notifyListeners(); // Уведомляем о изменении
},
icon: const Icon(Icons.add),
),
IconButton(
onPressed: () {
controller.removeWidget(index); // Удаляем виджет
},
icon: const Icon(Icons.close),
),
],
),
),
);
}
}
// Основной виджет
class Test extends StatefulWidget {
const Test({Key? key}) : super(key: key);
@override
State<Test> createState() => _Test();
}
class _Test extends State<Test> {
final WidgetController controller = WidgetController();
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ValueListenableBuilder(
valueListenable: controller,
builder: (context, value, child) {
return ListView.builder(
padding: const EdgeInsets.symmetric(horizontal: 15),
shrinkWrap: true,
itemCount: controller.widgetsCount,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.only(top: 15),
child: DynamicWidget(controller: controller, index: index,),
);
},
);
},
),
const SizedBox(height: 50),
Center(
child: TextButton(
onPressed: () {
controller.addWidget(); // Добавляем новый виджет
},
child: const Text('Add More'),
),
),
],
),
),
),
);
}
}
Краткое объяснение внесенных изменений:
-
Обновление UI: Используется
ValueListenableBuilder
для автоматического обновления списка виджетов при изменении состояния в контроллере. -
Управление данными виджета: Создан отдельный класс
DynamicWidgetData
, который хранит данные для каждого виджета. Это позволяет каждому виджету иметь свое состояние. -
Использование
notifyListeners()
: Каждый раз, когда виджет добавляется или удаляется, вызываетсяnotifyListeners()
, что обеспечивает обновление виджета.
Данное решение гарантирует, что каждый виджет обновляется независимо, и вы сможете добавлять и удалять их без каких-либо проблем.