Как вызвать метод в дочернем виджете из родительского виджета или вызвать родительский метод из дочернего виджета.
Когда вы читаете заголовок, вы можете подумать: «Чувак, с этим может справиться управление состоянием». Что ж, я не собираюсь этого отрицать. Но в этой статье мы рассмотрим, как выполнять методы родительского и дочернего классов.
Мы рассмотрим 2 части:
- Как вызвать метод в классе PARENT из дочернего класса
- Как вызвать метод класса CHILD из класса parent.
Первая тема очень похожа, и я думаю, что большинство разработчиков флаттера часто используют ее. А вот второй — что-то вроде антипатерна.
Зачем нужны отдельные родительский и дочерний виджеты?
Есть много преимуществ, если ваш код хорошо разделен. Для меня разделение виджетов более читабельно и упрощает поддержку кода. Некоторые другие источники также говорят, что мы можем избежать ненужных перестроек виджетов, разделив виджеты на классы. Это верно, но в данном случае это неприменимо.
Это потому, что мы передаем метод в качестве аргумента дочернему классу. Это сделает его непостоянным объектом. Таким образом, когда родительский виджет перестраивается, дочерний виджет также перестраивается.
Как вызвать метод класса PARENT из дочернего класса
Родительский метод легко выполнить из дочернего класса. Что нам нужно сделать, так это использовать .call()
в методе, который мы собираемся выполнить.
// example call void method methodName?.call(); // if its nullable variable, use ? sign // example if method with argument methodName?.call(args);
eg:
Мы хотим вызвать значение обновления String
в родительском классе с помощью метода methodFromParent
в дочернем виджете.
child.dart
class Child extends StatelessWidget { const Child({super.key, this.methodFromParent}); final Function(String val)? methodFromParent; @override Widget build(BuildContext context) { return ElevatedButton( onPressed: () { methodFromParent?.call("Updated from child"); }, child: const Text("Update parent"), ); } }
parent.dart
class ParentWidget extends StatefulWidget { const ParentWidget({super.key}); @override State<ParentWidget> createState() => _ParentWidgetState(); } class _ParentWidgetState extends State<ParentWidget> { String parentTxt = "Initial Text"; int count = 0; void updateParentTxt(String param) { count++; parentTxt = '$param $count times'; setState(() {}); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text(parentTxt)), body: Column( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ /// another child widget Child(methodFromParent: updateParentTxt), ], ), // pass the method as argument ); } }
Полный код и демонстрация: DartPad
В этом примере мы видим, что updateParentTxt
находятся в родительском классе.
void updateParentTxt(String param) { count++; parentTxt = '$param $count times'; setState(() {}); }
Мы выполняем его, нажимая кнопку ElevatedButton в дочернем классе. В начальном представлении текст на панели приложений имеет вид initial Text
, затем после нажатия дочерней кнопки он меняется на время нажатия кнопки.
Если у вас в голове много вопросов, пожалуйста, сохраните ее и прочитайте вторую тему. Мы будем использовать сложные примеры в конце этой статьи.
😄😄😄
Как вызвать метод класса CHILD из класса parent.
У нас есть 2 варианта вызова метода из родительского класса. Первый — с помощью GlobalKey
, а второй — с помощью custombuilder.
Мне нравится использовать builder
, и мы рассмотрим его в этой статье. Но если вас интересует GlobalKey
, вы можете найти его в этом вопросе [ссылка]
шаги:
- определить
typedef
для пользовательского компоновщика
typedef MyBuilder = void Function(BuildContext context, void Function() methodFromChild);
- используйте конструктор как
arguments
в конструктореChild
. - Так как нам нужно
context
для билдера, мы можем вызвать билдер внутри методаWidget build()
child.dart
class Child extends StatefulWidget { final MyBuilder builder; const Child ({Key? key, required this.builder}) : super(key: key); @override _ChildState createState() => _ChildState(); } class _HomePageState extends State<HomePage> { @override Widget build(BuildContext context) { widget.builder.call(context, childMethod); // <<<<< call it here return Text('Child widget'); } void childMethod() { print('test'); } }
следующий…
- Инициализируйте метод в родительском классе: `
late void Function() myMethod;
и используйте конструктор для виджетаChild
. myMethod
выполнит метод в дочернем виджете.
parent.dart
class ParentWidget extends StatelessWidget { late void Function() myMethod; @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( body:Column( children: [ IconButton( icon: Icon(Icons.help), onPressed: () { myMethod.call(); // << this will execute the methodFromChild }, ), Child( builder: (BuildContext context, void Function() methodFromChild) { myMethod = methodFromChild; }, ) ], ), ), ); } }
Полный код и демонстрация: DartPad
из приведенного выше примера, когда мы нажимаем кнопку IconButton, она выполняет функцию myMethod
. Это означает, что мы уже назначаем строитель myMethod = methodFromChild;
В дочернем классе мы используем childMethod
для аргумента построителя.
void childMethod() { print('test'); }
теперь в родительском классе каждый раз, когда мы вызываем, нажмите IconButton и выполните myMethod
, он выполнит childMethod
Честно говоря, мне это кажется потрясающим. 😃
Мы можем вызвать метод с обеих сторон. Родительский класс и дочерний класс. Хорошо, теперь давайте посмотрим, как это реализовано.
Вот один пример обработки представления на рабочем столе. Размер экрана шире мобильного, помещается по горизонтали.
Это родительский код выглядит так:
class ParentWidget extends StatefulWidget { .... @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text("Responsive View")), body: Row( children: [ Expanded( child: // some widgets in the parents, Expanded( child: Child1()), Expanded( child: Child2()), .....
Полный код и демонстрация: DartPad
Демонстрационный результат:
— Parent
, Child1
и Child2
являются отдельными классами. Невозможно обновить значение из разных классов.
— Case on Child2:
в этом случае действие пользователя по нажатию кнопки. После показа диалогового окна и заполнения формы оно АВТОМАТИЧЕСКИ обновит локальное значение child2Txt
, а также обновит переменную в родительском классе.
После закрытия диалога мы видим
- в дочернем виджете 2:
Text from dialog : LOREM ipsum
- в родительском виджете:
Text from Child 2: LOREM ipsum
в этом случае мы реализуем: Вызовите метод в родительском классе из дочернего класса, используя: widget.methodFromParent?.call(dialgTxt);
— Случай с Child1:
в этом виджете у нас есть TextField. Этот виджет имеет свой собственный контроллер состояния. Как видите, я могу набирать все виджеты TextField.
Как мы можем обновить значение родительского класса?
Здесь 2 варианта:
- Вызов метода в родительском классе внутри
onChaged
свойства.
TextField( onChanged: (val) { widget.methodFromParent?.call(val); }, )
Конечно, это обновит значение в родительском классе. Но, как мы знаем, onChanged
прослушивает каждое изменение в TextField. Можете себе представить, когда я наберу Lorem
, метод выполнится 5 раз.
L
, Lo
, Lor
, Lore
и последний Lorem
Я думаю, что это совсем плохо, так как в родительском методе мы также вызываем setState().
только тип Lorem
, нам нужно 5 раз пересобрать виджет.
В Child2 мы вызываем родительский метод только тогда, когда диалоговое окно закрыто.
- Вызов метода в дочернем классе из родительского класса
Сначала создайте метод для сбора всех значений в классе Child1:
void _localMethod() { print("invoke method in Child 1"); final collectedString = [_ctlr.text, _ctlr2.text, _ctlr3.text]; widget.methodFromParent?.call(collectedString); }
этим методом мы собираем все значения контроллера, а затем снова вызываем метод у родителя.
Мы можем собрать все данные из дочернего класса и сохранить их в базу данных. Нет необходимости прослушивать значение onChanged
из виджета TextField.
Спасибо за конец. Хлопайте 👏 и делитесь, если вам понравилась эта статья. Не стесняйтесь оставлять любые комментарии. Я хотел бы обсудить больше.
Пометьте код на Github, чтобы сохранить его на потом: call_method_parent_child.dart (github.com)