Как убедиться, что асинхронная функция загружена до сборки виджета?

У меня есть экран с некоторой информацией о пользователе.

В моем виджете сборки у меня есть это:

Column
 (
    mainAxisAlignment: MainAxisAlignment.center,
    crossAxisAlignment: CrossAxisAlignment.start,
    children: <Widget>
    [
    Text('User Points', style: TextStyle(color: Colors.blueAccent)),
    Text('${this.user.points}', style: TextStyle(color: Colors.black, fontWeight: FontWeight.w700, fontSize: 34.0))
    ],
 ),

И у меня есть функция обновления панели инструментов каждый раз, когда пользователь обращается к ней.

void updateDashboard() async{
    SharedPreferences prefs = await SharedPreferences.getInstance();
    var api_key = prefs.getString('api_key');
    var userID = prefs.getInt('userID');

    var res = await http.post(('http://myApp.com/getDashboardPreferences/'+userID.toString() + '/' + api_key),
        headers: {
          "Accept": "application/json",
          "content-type": "application/json"
        });

    this.user = User.fromJson(json.decode(res.body));

    setState(() {
      if (res.statusCode == 200) { 
        this.user.setPoints(this.user.points);
      } else {
        print("Something is wrong");
      }
    });

  }

И в initstate я вызываю эту функцию:

@override
  void initState() {
    updateDashboard();
    super.initState();
  }

Это работает, но, тем не менее, я понимаю, что ${this.user.points} есть null в течение нескольких секунд (я получаю этот экран с предупреждением), а затем он загружается, и все в порядке. Я думаю, это потому, что моя функция async... Но как я могу гарантировать, что эта updateDashboard() вызывается перед рендерингом Widget build()?

Посмотрите на FutureBuilder. Это удобный виджет, который позволяет вам создавать фиктивный экран (например, круговой индикатор прогресса по центру) до тех пор, пока будущее не завершится, когда вместо этого вы строите весь экран.

Richard Heap 01.05.2019 19:42
0
1
1 605
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Что ж, асинхронность означает, что для загрузки данных может потребоваться время, и они передаются для загрузки другому потоку. Это можно сделать несколькими неблокирующими способами:

  1. Укажите значение по умолчанию для объекта user, чтобы к моменту завершения асинхронного метода вы показывали прокси-значения/значения по умолчанию, а setState() обеспечивал отображение нового значения при загрузке.

  2. Этот метод требует, чтобы вы загрузили виджеты условно, то есть загрузили виджет Text, когда есть данные, иначе загрузите Container().

Пример для второго подхода:

this.user!=null?Text('${this.user.points}'):Container()

Таким образом, вам не нужно показывать виджет, пока значение не загрузится. Любой из подходов работает, и в зависимости от вашего варианта использования вы можете выбрать, какой из них вы можете применить.

Попробовал ваше решение, но безуспешно :( ... Есть ли способ поставить экран «Загрузка ...», пока все не загрузится в сборке?

harunB10 01.05.2019 19:02

Это просто сокращение для написания if/else. Я думаю, вам следует пойти с принятым ответом, он нормальный.

ishaan 02.05.2019 09:10

Вы можете поместить функцию прямо перед виджетом возврата.

это не имеет значения. Виджет по-прежнему будет загружаться до завершения функции. Ответ Дока работает очень хорошо.

Axes Grinds 14.02.2020 12:48
Ответ принят как подходящий

Если вы не хотите использовать FutureBuilder, вы можете сделать что-то вроде

if (done)
    Column(  /* whatever */ )
else
    Center(child: CircularProgressIndicator())
void updateDashboard() async{
    /* whatever */

     if (res.statusCode == 200) { 
        setState(() {
           this.user.setPoints(this.user.points);

           done = true;

      });          
 }

@override
  void initState() {
    /* whatever */
    done = false;
  }

Вы можете сделать переменную загрузки, например bool _loading, тогда в любом месте, где вы начинаете loading свою async функцию, вы можете setState загрузки в true, затем setState из loading в ложь, когда async функция перестает загружаться, как это.

void updateDashboard() async{

    setState(() {
      _loading = true;
    });

    SharedPreferences prefs = await SharedPreferences.getInstance();
    var api_key = prefs.getString('api_key');
    var userID = prefs.getInt('userID');

    var res = await http.post(('http://myApp.com/getDashboardPreferences/'+userID.toString() + '/' + api_key),
        headers: {
          "Accept": "application/json",
          "content-type": "application/json"
        });

    this.user = User.fromJson(json.decode(res.body));

    setState(() {
      if (res.statusCode == 200) { 
        this.user.setPoints(this.user.points);
      } else {
        print("Something is wrong");
      }

      _loading = false; //Set Loading to false
    });



}

Затем в сборке вашего виджета проверьте, когда загрузка верна, и покажите индикатор выполнения, подобный этому.

body: _loading
? Center(
   child: CircularProgressIndicator( //Show a Circular Progress indicator
     valueColor: new AlwaysStoppedAnimation<Color>(Colors.cyan),
    ),
  )
:Column(//Show your Dashboard)

Я надеюсь, что это помогает

Другие вопросы по теме