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

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

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()?

0
harunB10 1 Май 2019 в 19:17

4 ответа

Лучший ответ

Если вы не хотите использовать 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;
  }
2
Doc 1 Май 2019 в 18:09

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

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

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

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

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

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

1
ishaan 1 Май 2019 в 16:33

Вы можете сделать загрузочную переменную, например, bool _loading, тогда где бы вы ни начинали loading свою функцию async, вы можете setState загрузить в true, а затем setState из {{X5} } в false, когда функция 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
    });



}

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

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

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

0
JideGuru 2 Май 2019 в 10:09

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

0
Leoog 1 Май 2019 в 17:47