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

Pull-to-refresh in Flutter don't work when no internet connection

我有一个 class,它在列表视图中显示一些 JSON 数据(事件),这些数据是我通过 API 请求获得的,并将它们保存到设备的存储中,所以不要每次都下载,除非用户进行下拉刷新操作以下载新闻事件。 如果在下载操作期间没有互联网连接,应用程序会显示“无法下载事件列表:检查您的互联网连接!”。 所以我认为,如果这是用户第一次打开应用程序,它应该下载事件或在互联网连接丢失上述消息的情况下显示(或者如果事件数组的长度已下载=没有事件) = 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!",
                  style: TextStyle(
                    fontWeight: FontWeight.w500,
                    fontSize: 20,
                  ));
            } 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,
          fontSize: 20,
        )),
  );
}

我是 Flutter 新手,我做错了什么?

FutureBuilder一旦评估未来就不会刷新。如果您希望下拉刷新起作用,您可以将列表数据存储为小部件的状态并根据状态呈现不同的 UI。

除此之外,如果子项不可滚动,RefreshIndicator 将不起作用。当没有数据时 returning 普通 Text 小部件,return 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();
  }
}