今晚月色真美

问题

最近在使用Flutter工具时,在使用FlutterBuilder出现了个比较奇葩的问题,大家都知道,FlutterBuilder是一款完美解决异步请求,动态渲染问题的控件,一般首页绘制UI的时候很常用。

我遇到的问题是:我用FlutterBuilder请求接口A数据后,再调用setState方法,接口A又会被调用一次,之前定位了比较久,真没想到是setState后会导致数据重载。

原因

那为什么会出现这个问题?去研究了下,这个就需要说到FutureBuilder的工作原理了。

先看看FutureBuilder应用时的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('测试问题')),
body: FutureBuilder(
future: request(xxx, data), // 接口请求
builder: (context, snapshot) {
if (snapshot.hasData) {
return: xxxx;
} else {
return Center(
child: Text(
'正在加载中...',
),
);
}
},
));
}
}

FutureBuilder是一个动态组件StatefulWidget。我们知道,StatefulWidget会维护一个长期存在的State对象。这种状态有一些管理其生命周期的方法,有方法initStatebuilddidUpdateWidgetinitState都比较熟悉,在创建对象时只会调用一次,build方法是在需要构建要显示的窗口小部件时调用它,那什么时候调用didUpdateWidget呢?

其实只要附加到此State对象的窗口小部件发生更改,就会调用didUpdateWidget方法,简单来说只要调用setState后导致内部控件或数据发生改变,就会触发到didUpdateWidget

再来看调用didUpdateWidget会怎样,代码如下,代码意思是:如果在重建时,新窗口小部件具有与旧窗口小部件不同的future实例,则重复所有内容:取消订阅,并再次订阅。

1
2
3
4
5
6
7
8
9
10
11
@override
void didUpdateWidget(FutureBuilder<T> oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.future != widget.future) {
if (_activeCallbackIdentity != null) {
_unsubscribe();
_snapshot = _snapshot.inState(ConnectionState.none);
}
_subscribe();
}
}

原来就是不同future实例就会触发页面重载,那我们用同一个future就可以解决问题了。

解决

将request请求设置为一个成员变量,定义到使用类里:

1
2
// 防止页面重绘时多次调用,增加内容消耗
var _futureBuilderFuture = request(xxx, data);

这里使用_futureBuilderFuture来保存request(xxx, data)的结果,这样我们传递给FutureBuilder的是一个成员变量,而不是一个方法就不会多次调用了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('测试问题')),
body: FutureBuilder(
future: request(xxx, data), // 接口请求
builder: (context, snapshot) {
if (snapshot.hasData) {
return: xxxx;
} else {
return Center(
child: Text(
'正在加载中...',
),
);
}
},
));
}
}

这样就解决了数据重复被调用的问题。

 评论

本站总字数统计:25.7k

感谢您的浏览, 本站总访问量为 次 。
载入天数...载入时分秒...