Tải dữ liệu một lần trong initState
Thuộc phần Dữ liệu và mạng trong hành trình Flutter của Coddy. Bài học 7 trên 27.
Flutter gọi lại build mỗi khi màn hình vẽ lại: sau mỗi lần gọi setState, khi điện thoại xoay, khi thành phần cha thay đổi. Việc này có thể xảy ra nhiều lần mỗi giây. build chỉ nên mô tả màn hình, không bao giờ bắt đầu công việc.
Vì vậy, một lệnh fetch viết bên trong build sẽ chạy lại sau mỗi lần màn hình vẽ lại. Mỗi lần chạy là một yêu cầu mới gửi đến máy chủ, và màn hình có thể chuyển sang một câu trả lời khác dù không có gì yêu cầu điều đó:
// một yêu cầu mới mỗi khi vẽ lại
FutureBuilder<String>(
future: fetch('/tip'),
...
)Hãy tạo future một lần khi màn hình mở, trong initState, rồi mỗi lần đều truyền cùng future đó cho FutureBuilder:
late Future<String> _tip;
@override
void initState() {
super.initState();
_tip = fetch('/tip');
}initState chạy một lần trước build đầu tiên. Hãy bắt đầu bằng super.initState(); theo yêu cầu của Flutter.
Thử thách
Trung bìnhChạm vào biểu tượng trái tim trên thanh trên cùng: mẹo thay đổi vì fetch nằm trong build. Chỉ tải mẹo một lần:
- thêm
late Future<String> _tip;vào trạng thái - gán giá trị cho nó trong
initStatebằngfetch('/tip') - truyền
_tipchoFutureBuilder
Tự làm thử
import 'package:flutter/material.dart';
import 'api.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: TipScreen(),
);
}
}
class TipScreen extends StatefulWidget {
const TipScreen({super.key});
@override
State<TipScreen> createState() => _TipScreenState();
}
class _TipScreenState extends State<TipScreen> {
bool liked = false;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Tip of the Day'),
actions: [
IconButton(
icon: Icon(liked ? Icons.favorite : Icons.favorite_border),
onPressed: () {
setState(() {
liked = !liked;
});
},
),
],
),
body: Center(
child: Padding(
padding: const EdgeInsets.all(24),
child: FutureBuilder<String>(
future: fetch('/tip'),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(
snapshot.data!,
textAlign: TextAlign.center,
style: const TextStyle(fontSize: 24),
);
}
return const CircularProgressIndicator();
},
),
),
),
);
}
}
Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.
Tất cả bài học trong Dữ liệu và mạng
2Màn hình tải
Hiển thị vòng quay khi tảiLàm quen với FutureBuilderTải dữ liệu một lần trong initStateTải lại dữ liệu bằng nútKéo để làm mớiTự luyện tập: Trình biên dịch Flutter online