如何让 child 小部件在 parent 的状态发生变化时进行更新?
How do I get a child widget to update when a parent's state changes?
为张贴伪代码提前致歉。真正的代码会太长。
我有一个屏幕,顶部有一个下拉菜单,用户可以在其中 select 选择一个选项。页面的其余部分根据该选项更新。像这样:
// state variable
String idFromDropdown;
Column(
children: [
DropDownWidget(),
ChildWidget1(myId: idFromDropDown),
ChildWidget2(myId: idFromDropDown),
ChildWidget3(myId: idFromDropDown),
]
)
在 child 小部件中,我使用 widget.myId 传递到后端服务并读取新数据。
预期是当下拉列表发生变化时我调用
setState((val)=>{idFromDropdown = val});
然后该值将级联到三个 child 小部件中。以某种方式触发小部件根据 widget.myId.
的新值重新连接到后端服务
如何触发 child 小部件的状态更新?
我最终使用了 ValueNotifier。而不是直接使用字符串并将其传递给 child 小部件。我最终做了类似的事情:
ValueNotifier<String> idFromDropdown;
...
setState((val)=>{idFromDropdown.value = val});
然后在每个小部件中,我在传入的 ValueNotifier 上添加一个侦听器并重新触发对后端服务的读取。
虽然这有效,但我觉得我遗漏了一些明显的东西。我的 child 小部件现在采用 ValueNotifier 而不是值。恐怕这会使我的 ChildWidgets 在其他情况下更难使用。
这是正确的做法吗?
使用提供程序包,您的问题将轻松解决
这里是 Riverpod
的例子。
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
final fetureDataForChild =
FutureProvider.family<List<String>, String>((ref, id) {
return Future.delayed(Duration(seconds: 1), () {
return <String>["active", "${id}", "got it "];
});
});
class MainWidgetR extends StatefulWidget {
@override
_MainWidgetState createState() => _MainWidgetState();
}
class _MainWidgetState extends State<MainWidgetR> {
String id = "id 0";
final items = List.generate(
4,
(index) => DropdownMenuItem(
child: Text("Company $index"),
value: "id $index",
),
);
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
DropdownButton(
items: items,
value: id,
onChanged: (value) {
setState(() {
id = value as String;
});
},
),
RiverPodRespone(
id: id,
),
],
),
),
);
}
}
class RiverPodRespone extends ConsumerWidget {
final String id;
RiverPodRespone({
required this.id,
});
@override
Widget build(BuildContext context, watch) {
final futureData = watch(fetureDataForChild("$id"));
return futureData.map(
data: (value) {
final items = value.value;
return Column(
children: [
...items.map((e) => Text("$e")).toList(),
],
);
},
loading: (value) => CircularProgressIndicator(),
error: (value) => Text(value.toString()),
);
}
}
为张贴伪代码提前致歉。真正的代码会太长。
我有一个屏幕,顶部有一个下拉菜单,用户可以在其中 select 选择一个选项。页面的其余部分根据该选项更新。像这样:
// state variable
String idFromDropdown;
Column(
children: [
DropDownWidget(),
ChildWidget1(myId: idFromDropDown),
ChildWidget2(myId: idFromDropDown),
ChildWidget3(myId: idFromDropDown),
]
)
在 child 小部件中,我使用 widget.myId 传递到后端服务并读取新数据。
预期是当下拉列表发生变化时我调用
setState((val)=>{idFromDropdown = val});
然后该值将级联到三个 child 小部件中。以某种方式触发小部件根据 widget.myId.
的新值重新连接到后端服务如何触发 child 小部件的状态更新?
我最终使用了 ValueNotifier。而不是直接使用字符串并将其传递给 child 小部件。我最终做了类似的事情:
ValueNotifier<String> idFromDropdown;
...
setState((val)=>{idFromDropdown.value = val});
然后在每个小部件中,我在传入的 ValueNotifier 上添加一个侦听器并重新触发对后端服务的读取。
虽然这有效,但我觉得我遗漏了一些明显的东西。我的 child 小部件现在采用 ValueNotifier 而不是值。恐怕这会使我的 ChildWidgets 在其他情况下更难使用。
这是正确的做法吗?
使用提供程序包,您的问题将轻松解决
这里是 Riverpod
的例子。
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
final fetureDataForChild =
FutureProvider.family<List<String>, String>((ref, id) {
return Future.delayed(Duration(seconds: 1), () {
return <String>["active", "${id}", "got it "];
});
});
class MainWidgetR extends StatefulWidget {
@override
_MainWidgetState createState() => _MainWidgetState();
}
class _MainWidgetState extends State<MainWidgetR> {
String id = "id 0";
final items = List.generate(
4,
(index) => DropdownMenuItem(
child: Text("Company $index"),
value: "id $index",
),
);
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
DropdownButton(
items: items,
value: id,
onChanged: (value) {
setState(() {
id = value as String;
});
},
),
RiverPodRespone(
id: id,
),
],
),
),
);
}
}
class RiverPodRespone extends ConsumerWidget {
final String id;
RiverPodRespone({
required this.id,
});
@override
Widget build(BuildContext context, watch) {
final futureData = watch(fetureDataForChild("$id"));
return futureData.map(
data: (value) {
final items = value.value;
return Column(
children: [
...items.map((e) => Text("$e")).toList(),
],
);
},
loading: (value) => CircularProgressIndicator(),
error: (value) => Text(value.toString()),
);
}
}