问题描述
我有一个类在列表视图中显示一些我通过 API 请求获得的 JSON 数据(事件)并将它们保存到设备的存储中,这样就不会每次都下载,除非用户发出 Pull-刷新操作以便下载新闻事件。 如果在下载操作期间没有互联网连接,应用程序会显示“无法下载事件列表:检查您的互联网连接!”。 所以我认为如果这是用户第一次打开应用程序,它应该下载事件或在互联网连接丢失上述消息的情况下显示(或者在下载的事件数组长度的情况下没有事件= = 0)。如果不是第一次显示之前下载和保存的事件列表。
我的问题是,例如,如果我关闭了互联网并在我打开后,拉动刷新不起作用,相反,当我下载列表时,我可以进行拉动刷新操作。
>这是我的代码:
class EventDetails {
String data;
int id;
String name;
String description;
String applicationStatus;
String applicationStarts;
String applicationEnd;
String starts;
String ends;
int fee;
EventDetails({
this.data,this.id,this.name,this.description,this.applicationStatus,this.applicationStarts,this.applicationEnd,this.starts,this.ends,this.fee,});
EventDetails.fromJson(Map<String,dynamic> json) {
data = json['data'];
id = json['id'];
name = json['name'];
description = json['description'];
applicationStatus = json['application_status'];
applicationStarts = json['application_starts'];
applicationEnd = json['application_ends'];
starts = json['starts'];
ends = json['ends'];
fee = json['fee'];
}
}
class EventsListView extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _EventListState();
}
}
class _EventListState extends State<EventsListView> {
List<EventDetails> list;
Storage storage = Storage();
@override
Widget build(BuildContext context) {
return Refreshindicator(
onRefresh: _getData,child: FutureBuilder<List<EventDetails>>(
future: loadEvents(),builder: (context,snapshot) {
if (snapshot.hasData) {
List<EventDetails> data = snapshot.data;
if (data.length == 0) {
return Text("No events found",style: TextStyle(
fontWeight: FontWeight.w500,fontSize: 20,));
} else {
return _eventsListView(data);
}
} else if (snapshot.hasError) {
if (snapshot.error.runtimeType.toString() == "SocketException") {
return Text(
"Impossible to download the events list: check your internet connection!",));
} else {
return Text("${snapshot.error}");
}
}
return CircularProgressIndicator();
},),);
}
Future<List<EventDetails>> loadEvents() async {
String content = await storage.readList();
if (content != 'no file available') {
list = getListFromData(content,list);
}
if ((list != null) && (list.length != 0)) {
print('not empty');
return list;
} else {
return await downloadEvents(list,storage);
}
}
Future<List<EventDetails>> downloadEvents(
List<EventDetails> list,Storage storage) async {
String url = "https://myurl";
final response = await http.get(url);
if (response.statusCode == 200) {
String responseResult = response.body;
list = getListFromData(responseResult,list);
storage.writeList(response.body);
return list;
} else {
throw Exception('Failed to load events from API');
}
}
List<EventDetails> getListFromData(String response,List<EventDetails> list) {
Map<String,dynamic> map = json.decode(response);
List<dynamic> jsonResponse = map["data"];
list = jsonResponse.map((job) => new EventDetails.fromJson(job)).toList();
return list;
}
ListView _eventsListView(data) {
return ListView.separated(
itemCount: data.length,separatorBuilder: (context,index) => Divider(
color: const Color(0xFFCCCCCC),itemBuilder: (BuildContext context,int index) {
return GestureDetector(
child: _tile(data[index].name),onTap: () {
Navigator.pushNamed(
context,SingleEvent.routeName,arguments: ScreenArguments(
data[index].name,data[index].description,data[index].starts,data[index].ends,);
});
});
}
Future<void> _getData() async {
setState(() {
downloadEvents(list,storage);
});
}
@override
void initState() {
super.initState();
loadEvents();
}
ListTile _tile(String title) => ListTile(
title: Text(title,style: TextStyle(
fontWeight: FontWeight.w500,)),);
}
我真的是 Flutter 新手,我做错了什么?
解决方法
FutureBuilder
在评估未来后不会刷新。如果您希望拉动刷新工作,您可以将列表数据存储为小部件的状态,并根据该状态呈现不同的 UI。
除此之外,如果子项不可滚动,RefreshIndicator
将不起作用。当没有数据时返回普通的 Text
小部件,而是返回带有文本的 SingleChildScrollView
,这样您的 RefreshIndicator
内就有一个可滚动的。
这是一个例子:
class EventsListView extends StatefulWidget {
@override
_EventsListViewState createState() => _EventsListViewState();
}
class _EventsListViewState extends State<EventsListView> {
List list;
Storage storage = Storage();
String errorMessage;
@override
Widget build(BuildContext context) {
return RefreshIndicator(
onRefresh: downloadEvents,child: listWidget(),);
}
Widget listWidget() {
if (list != null) {
return ListView(); // here you would return the list view with contents in it
} else {
return SingleChildScrollView(child: Text('noData')); // You need to return a scrollable widget for the refresh to work.
}
}
Future<void> loadEvents() async {
String content = await storage.readList();
if (content != 'no file available') {
list = getListFromData(content,list);
}
if ((list != null) && (list.length != 0)) {
print('not empty');
errorMessage = null;
setState(() {});
} else {
await downloadEvents();
}
}
Future<void> downloadEvents() async {
String url = "https://myurl";
final response = await http.get(url);
if (response.statusCode == 200) {
String responseResult = response.body;
list = getListFromData(responseResult,list);
storage.writeList(response.body);
errorMessage = null;
setState(() {});
} else {
setState(() {
errorMessage =
'Error occured'; // here,you would actually add more if,else statements to show better error message
});
throw Exception('Failed to load events from API');
}
}
List<EventDetails> getListFromData(String response,List<EventDetails> list) {
Map<String,dynamic> map = json.decode(response);
List<dynamic> jsonResponse = map["data"];
list = jsonResponse.map((job) => new EventDetails.fromJson(job)).toList();
return list;
}
@override
void initState() {
super.initState();
loadEvents();
}
}