Menu
CoddyTech

在 initState 中获取一次数据

本课属于 Coddy Flutter 学习旅程的“数据与网络”单元。第 7 课,共 27 课。

每次屏幕重绘时,Flutter 都会再次调用 build:每次调用 setState 后、手机旋转时、父组件发生变化时都如此。一秒内可能调用很多次。build 只应该描述屏幕,绝不能启动任务。

所以,写在 build 里的 fetch 会在每次重绘时再次运行。每次运行都会向服务器发送一个新请求,而当没有任何操作要求获取新结果时,屏幕上的答案也可能突然改变:

// 每次重绘都会发起新请求
FutureBuilder<String>(
  future: fetch('/tip'),
  ...
)

在屏幕打开时,于 initState 中创建一次 future,并在每次构建时都将同一个 future 传给 FutureBuilder:

late Future<String> _tip;

@override
void initState() {
  super.initState();
  _tip = fetch('/tip');
}

initState 只会在第一次 build 之前运行一次。按照 Flutter 的要求,用 super.initState(); 启动它。

challenge icon

挑战

中等

点击顶部栏中的心形图标:提示内容会改变,因为 fetch 在 build 中。只加载一次提示内容:

  • 将 late Future<String> _tip; 添加到状态中
  • 在 initState 中使用 fetch('/tip') 设置它
  • 将 _tip 传给 FutureBuilder

亲手试试

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();
              },
            ),
        ),
      ),
    );
  }
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

数据与网络中的所有课

自己练习: Flutter 在线编译器