Menu
CoddyTech

Загрузка данных один раз в initState

Часть раздела Данные и сеть путешествия по Flutter на Coddy. Урок 7 из 27.

Flutter снова вызывает build каждый раз, когда экран перерисовывается: после каждого вызова setState, при повороте телефона, при изменении родительского элемента. Это может происходить много раз в секунду. Метод build должен только описывать экран, а не запускать какие-либо действия.

Поэтому вызов fetch, написанный внутри build, выполняется при каждой перерисовке. Каждый вызов создаёт новый запрос к серверу, и экран может переключиться на другой ответ, хотя никто его не запрашивал:

// новый запрос при каждой перерисовке
FutureBuilder<String>(
  future: fetch('/tip'),
  ...
)

Создай future один раз при открытии экрана, в initState, и каждый раз передавай FutureBuilder тот же future:

late Future<String> _tip;

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

initState выполняется один раз, перед первым вызовом build. Начни его с super.initState();, как того требует Flutter.

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