大家好,今天用的是FutureBuilder
,通常我們使用的資料都是從雲端獲取數據,是一個異步任務,我們可以用FutureBuilder
對這種異步更新UI
const FutureBuilder({
Key key,
this.future,
this.initialData,
@required this.builder,
}) : assert(builder != null),
super(key: key);
先來看這一段先前在做選擇使用者頁面程式,我們假設_users
代表某個從雲端獲取的資料
GridView.count(
shrinkWrap: true,
crossAxisCount: 2,
crossAxisSpacing: 16.0,
mainAxisSpacing: 16.0,
childAspectRatio: 10 / 12,
children: List.generate(_users.length,
(index) => _buildUser(_users[index])),
)
現在我們來做一個異步任務,假裝它從雲端取得的過程,它會花兩秒鐘取得數據
Future<List<User>> mockNetworkData() async {
print('future work');
return Future.delayed(Duration(seconds: 2), () => _users);
}
接著使用FutureBuilder,看一下snapshot會是什麼樣子吧
FutureBuilder<List<User>>(
future: mockNetworkData(),
builder: (context,AsyncSnapshot snapshot) {
print("snapshot: $snapshot");
return Container();
}),
AsyncSnapshot中有connectionState、data、error
我們可以看到ConnectionState.waiting變成ConnectionState.done,數據也取得了
flutter: future work
flutter: snapshot: AsyncSnapshot<List<User>>(ConnectionState.waiting, null, null)
flutter: snapshot: AsyncSnapshot<List<User>>(ConnectionState.done, [Instance of 'User', Instance of 'User', Instance of 'User', Instance of 'User', Instance of 'User'], null)
所以我們可以根據不同的ConnectionState去return不同的Widget
FutureBuilder<List>中的List是data的型別
FutureBuilder<List<User>>(
future: mockNetworkData(),
builder: (context, snapshot) {
print("snapshot: $snapshot");
if (snapshot.connectionState == ConnectionState.done) {
return GridView.count(
shrinkWrap: true,
crossAxisCount: 2,
crossAxisSpacing: 16.0,
mainAxisSpacing: 16.0,
childAspectRatio: 10 / 12,
children: List.generate(snapshot.data.length,
(index) => _buildUser(snapshot.data[index])),
); //使用snapshot.data為數據來源
}
return GridView.count(
shrinkWrap: true,
crossAxisCount: 2,
crossAxisSpacing: 16.0,
mainAxisSpacing: 16.0,
childAspectRatio: 10 / 12,
children: List.generate(4, (index) => _buildWaiting()),
);//其他狀態返回一個loading頁面
}),
Waiting的Widget只是把原本的做一些小刪減,修改
_buildWaiting() {
return Container(
// color: Colors.red,
child: Column(
children: [
Expanded(
child: Container(
color: Colors.white.withOpacity(0.1),
child: Stack(
alignment: Alignment.center,
children: [
// Image.asset(user.assetName),
],
),
)),
SizedBox(
height: 8.0,
),
Text(
"",
style: userNameStyle,
)
],
),
);
}
還有一個問題是每次setState的時候它又會重新創建一次,即又撈一次資料,因為我們傳的是mockNetworkData(),每次build都會執行一次
所以有一個解決辦法是用一個Future來取代mockNetworkData(),然後在initState時讓他執行一次
Future _future;
@override
void initState() {
_future =mockNetworkData();
super.initState();
}
FutureBuilder<List<User>>(
future: _future,
builder: (context,AsyncSnapshot snapshot) {
print("snapshot: $snapshot");
return Container();
}),
之後如果有再需要更新的時候,執行如下程式,它才會重新更新
_future = mockNetworkData();
setState(() {});
今天的效果如下
GitHub連結: flutter-netflix-clone