#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
это из typeFuture<List<CheckIn>>
, это не работает, потомуawait _helper.fetchCheckIns(_eventArg.getEvent().id.toString());
что из typeList<CheckIn>
…2. Ваш RefreshIndicator находится внутри FutureBuilder? Если это так, попробуйте вернуть _helper.fetchCheckIns() в вашем onRefresh()
3. можете ли вы попробовать поместить свой futurecheckinв setState()?
4. К сожалению, исключение все еще возникает.. Я постараюсь написать минимально воспроизводимый пример на следующий день.
5. ОК. Попробуйте поместить futureCheckIn = _helper …. внутри блока try catch.