问题描述
在使用FutureBuilder来显示加载指示时,如何仅获取一次数据?
问题在于,即使我在initState()中设置了将来,每次用户打开屏幕时,它都会重新获取数据。
我只想在用户第一次打开屏幕时获取数据,然后我将使用保存的获取数据。
我应该只使用带有加载变量的有状态小部件并将其设置在setState()中吗?
我正在使用提供程序包
Future<void> fetchData() async {
try {
final response =
await http.get(url,headers: {'Authorization': 'Bearer $_token'});......
和我的屏幕小部件:
class _MyScreenState extends State<MyScreen> {
Future<void> fetchData;
@override
void initState() {
fetchData =
Provider.of<Data>(context,listen: false).fetchData();
super.initState();
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: fetchData,builder: (ctx,snapshot) =>
snapshot.connectionState == ConnectionState.done
? Consumer<Data>(
builder: (context,data,child) => Text(data.fetchedData)): Center(
child: CircularProgressIndicator(),),);
}
}
解决方法
一种简单的方法是引入StatefulWidget,将Future存放在变量中。现在,每次重建都将引用相同的Future实例:
class MyWidget extends StatefulWidget {
@override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
Future<String> _future;
@override
void initState() {
_future = callAsyncFetch();
super.initState();
}
@override
Widget build(context) {
return FutureBuilder<String>(
future: _future,builder: (context,snapshot) {
// ...
}
);
}
}
或者您可以简单地使用FutureProvider代替上面的StatefulWidget:
class MyWidget extends StatelessWidget {
// Future<String> callAsyncFetch() => Future.delayed(Duration(seconds: 2),() => "hi");
@override
Widget build(BuildContext context) {
// print('building widget');
return FutureProvider<String>(
create: (_) {
// print('calling future');
return callAsyncFetch();
},child: Consumer<String>(
builder: (_,value,__) => Text(value ?? 'Loading...'),),);
}
}
,
如果即使小部件重建,也只希望获取一次数据,则必须为此创建一个模型。这是您的制作方法:
class MyModel{
String value;
Future<String> fetchData() async {
if(value==null){
try {
final response =
await http.get(url,headers: {'Authorization': 'Bearer $_token'});......
value=(YourReturnedString)
}
}
return value;
}
}
不要忘记将MyModel设置为提供程序。在您的FutureBuilder中:
@override
Widget build(context) {
final myModel=Provider.of<MyModel>(context)
return FutureBuilder<String>(
future: myModel.fetchData(),snapshot) {
// ...
}
);
}
,
您可以实现 provider 并在其子级之间传递数据。 请参阅此example for fetching the data once and using it throughout its child.