Трепетание — вытягивание для обновления с помощью RefreshIndicator в FutureBuilder

#flutter #dart #networking #futuretask

#трепетание #дротик #сеть #futuretask

Вопрос:

Редактировать: минимальный воспроизводимый пример. Пожалуйста, взгляните на метод обновления в main.dart .

Я использую a FutureBuilder для отображения результата сетевого запроса. Я вызываю свой метод выборки в initstate (как предложено здесь):

 Future<List<CheckIn>> futureCheckIn;

@override
void initState() {
  super.initState();
  _eventArg = Provider.of<EventArg>(context, listen: false);

  _helper = ApiHelper(context);
  futureCheckIn = _helper.fetchCheckIns(_eventArg.getEvent().id.toString());
}
  

Теперь я хочу использовать a RefreshIndicator , чтобы позволить пользователю обновлять экран.
Это отлично работает, пока мой сетевой запрос не завершится исключением (например, 404). Обычно исключение перехватывается FutureBuilder , но после выполнения запрос в RefreshIndicator исключении не обрабатывается…

Вот мой код:

 RefreshIndicator buildResult(List<CheckIn> checkIns, BuildContext context) {
return RefreshIndicator(
  key: _refreshKey,
  onRefresh: () async {
    setState(() {});
    futureCheckIn =
        _helper.fetchCheckIns(_eventArg.getEvent().id.toString()); // this could end with an exception
  },
  child: Container(
    height: MediaQuery.of(context).size.height,
    child: SingleChildScrollView(
      physics: AlwaysScrollableScrollPhysics(),
      scrollDirection: Axis.vertical,
      child: Column(
        children: [
          Container(
            margin: EdgeInsets.all(10),
            child: Text(
              constants.CHECK_IN_SCREEN_DESCRIPTION,
              style: TextStyle(fontSize: 16),
              textAlign: TextAlign.justify,
            ),
          ),
          ListView(
            physics: NeverScrollableScrollPhysics(),
            padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
            scrollDirection: Axis.vertical,
            shrinkWrap: true,
            children: checkIns.map((el) {
              return Card(
                elevation: 4,
                child: ListTile(
                  title: Text(el.name),
                  onTap: () async {
                   ...
                  },
                  trailing: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      Icon(
                        Icons.arrow_forward_ios,
                        color: Colors.black,
                      )
                    ],
                  ),
                ),
              );
            }).toList(),
          )
        ],
      ),
    ),
  ),
);
  

}

Как я могу RefreshIndicator правильно использовать с FutureBuilder ?

Редактировать @Шри Хари: это исключение выдается

 class AppException implements Exception {
 final _message;
 final _prefix;

 AppException([this._message, this._prefix]);

 String toString() {
   return "$_prefix$_message";
 }
}

class NotFoundException extends AppException {
  NotFoundException([String message]) : super(message, "Data not found: ");
}
  

введите описание изображения здесь

Редактировать @Mhark Batoctoy: Да, это часть FutureBuilder :

 @override
Widget build(BuildContext context) {
return FutureBuilder(
  future: futureCheckIn,
  builder: (context, snapshot) {
    if (snapshot.hasData) {
      List<CheckIn> checkIns = snapshot.data;

      return buildResult(checkIns, context); // RefreshIndicator is nested in here
    } else if (snapshot.hasError) {
      // Error handling
      ... 
    }

    // Spinner während der Datenabfrage
    return Center(
      child: CircularProgressIndicator(),
    );
  },
);
  

К сожалению, изменение метода onRefresh -из

  onRefresh: () async {
  setState(() {});
  futureCheckIn =
    _helper.fetchCheckIns(_eventArg.getEvent().id.toString()); // this could end with an exception
  },
  

Для

 onRefresh: () async {
  setState(() {});
  return futureCheckIn =
    _helper.fetchCheckIns(_eventArg.getEvent().id.toString()); // this could end with an exception
  },
  

или

 onRefresh: () async {
  setState(() {});
  return _helper.fetchCheckIns(_eventArg.getEvent().id.toString()); // this could end with an exception
  },
  

ошибка не изменяется…

Комментарии:

1. Можете ли вы опубликовать исключение?

Ответ №1:

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

RefreshIndicator не нравится исключение. То есть даже после успешного перемещения их в try / catch или onError и успешного использования исключения по- RefreshIndicator прежнему требуется необработанное исключение (немного странно, но мне придется изучить его подробнее).

Что я сделал, так это убедиться, что мой вызов API не вызывает никаких исключений. Это означает, что сам вызов API (в данном случае fetchCheckIns() вызов функции) улавливает все типы известных / неизвестных исключений [ catch (e) ] и устанавливает флаг в вашем объекте ответа, который вы возвращаете из API (в данном случае объект возвращаемого типа fetchCheckIns() ).

Теперь, в вашем FutureBuilder , когда вы вызываете buildResult(checkIns, context) , проверьте snapshot.data и найдите любой флаг сбоя. Если вы получаете сбой, отобразите экран по-другому. Это:

     if (snapshot.hasData) {
      if (snapshot.data.error == true) {
         return ...; // Render error message
      } else {
         return buildResult(snapshot.data, context);
      }
    }
  

Это также означает, что вы не сможете вернуть список объектов и, возможно, придется обернуть его в один объект, который будет содержать список объектов.

Означает, что ваш CheckIn объект может быть обернут чем-то вроде

 class AllCheckIn {
   List<CheckIn> checkIns;
   bool error;
   String errorMsg;
}
  

При желании вы также можете добавить дополнительное описание сообщения об ошибке на основе вашего исключения, которое вы получили и зафиксировали в своем вызове API, и использовать его в своем FutureBuilder .

Надеюсь, это полезно.

Комментарии:

1. Да, я тоже так подумал и не смог найти ни одного примера, который смог бы обработать исключения. Ваш подход звучит интересно. Я буду использовать его в следующий раз, когда мне понадобится RefreshIndicator. Тогда я дам вам обратную связь. На данный момент 1 и спасибо за ваш ответ @Virendra.

Ответ №2:

  _helper.fetchCheckIns(_eventArg.getEvent().id.toString()); 
  

Он возвращает будущее. Либо data то, что вам нужно, либо error . Вы можете использовать async await внутри try catch finally блока или вы можете использовать Future.then() / Future.catchError / Future.whenComplete() .

Пример:

 onRefresh: () async {
try {
   //You can use the await keyword here.
   return futureCheckIn =
    await _helper.fetchCheckIns(_eventArg.getEvent().id.toString());
}catch (err){
  //You can display the error here using a snackbar.
  }finally {
    //This executes whether there is an error or not. 
 }
  

},

Комментарии:

1. Спасибо за ваш ответ. Я уже пробовал использовать await , но, поскольку futureCheckIn это из type Future<List<CheckIn>> , это не работает, потому await _helper.fetchCheckIns(_eventArg.getEvent().id.toString()); что из type List<CheckIn>

2. Ваш RefreshIndicator находится внутри FutureBuilder? Если это так, попробуйте вернуть _helper.fetchCheckIns() в вашем onRefresh()

3. можете ли вы попробовать поместить свой futurecheckinв setState()?

4. К сожалению, исключение все еще возникает.. Я постараюсь написать минимально воспроизводимый пример на следующий день.

5. ОК. Попробуйте поместить futureCheckIn = _helper …. внутри блока try catch.