Ответ 1
Чтобы вызвать функцию родителя, вы можете использовать шаблон обратного вызова. В этом примере функция (onColorSelected
) передается onColorSelected
. Ребенок вызывает функцию при нажатии кнопки:
import 'package:flutter/material.dart';
class Parent extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return ParentState();
}
}
class ParentState extends State<Parent> {
Color selectedColor = Colors.grey;
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Container(
color: selectedColor,
height: 200.0,
),
ColorPicker(
onColorSelect: (Color color) {
setState(() {
selectedColor = color;
});
},
)
],
);
}
}
class ColorPicker extends StatelessWidget {
const ColorPicker({this.onColorSelect});
final ColorCallback onColorSelect;
@override
Widget build(BuildContext context) {
return Row(
children: <Widget>[
RaisedButton(
child: Text('red'),
color: Colors.red,
onPressed: () {
onColorSelect(Colors.red);
},
),
RaisedButton(
child: Text('green'),
color: Colors.green,
onPressed: () {
onColorSelect(Colors.green);
},
),
RaisedButton(
child: Text('blue'),
color: Colors.blue,
onPressed: () {
onColorSelect(Colors.blue);
},
)
],
);
}
}
typedef ColorCallback = void Function(Color color);
Виджеты внутреннего флаттера, такие как кнопки или поля формы, используют точно такой же шаблон. Если вы хотите только вызвать функцию без каких-либо аргументов, вы можете использовать тип VoidCallback
вместо этого определяя свой собственный тип обратного вызова.
Если вы хотите уведомить более высокого родителя, вы можете просто повторить этот шаблон на каждом уровне иерархии:
class ColorPickerWrapper extends StatelessWidget {
const ColorPickerWrapper({this.onColorSelect});
final ColorCallback onColorSelect;
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.all(20.0),
child: ColorPicker(onColorSelect: onColorSelect),
)
}
}
Вызов метода дочернего виджета из родительского виджета не рекомендуется в Flutter. Вместо этого Flutter рекомендует вам передать состояние дочернего элемента в качестве параметров конструктора. Вместо вызова метода для ребенка вы просто вызываете setState
в родительском setState
для обновления своих дочерних setState
.
Один альтернативный подход - это классы controller
в Flutter (ScrollController
, AnimationController
,...). Они также передаются детям как параметры конструктора, и они содержат методы для управления состоянием дочернего setState
без вызова setState
в родительском setState
. Пример:
scrollController.animateTo(200.0, duration: Duration(seconds: 1), curve: Curves.easeInOut);
Затем детям необходимо прослушать эти изменения, чтобы обновить их внутреннее состояние. Конечно, вы также можете реализовать свой собственный класс контроллера. Если вам нужно, я рекомендую вам посмотреть исходный код Flutter, чтобы понять, как это работает.
Фьючерсы и потоки являются еще одной альтернативой для передачи состояния, а также могут использоваться для вызова функции дочернего элемента.
Но я действительно не рекомендую. Если вам нужно вызвать метод дочернего виджета, это очень похоже на то, что ваша архитектура приложения ошибочна. Попробуйте переместить состояние до общего предка!