没有互联网连接时,Flutter 中的下拉刷新不起作用

问题描述

我有一个类在列表视图中显示一些我通过 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();
  }
}