Как выполнить разбивку на страницы в Flutter

#flutter #dart #pagination #flutter-layout

#flutter #dart #разбивка на страницы #flutter-layout

Вопрос:

Я просматривал различные статьи о разбивке на страницы в flutter, но ни одна из них, похоже, не подходит для меня. Конечная точка API, с которой я работаю, выглядит следующим образом http://camx.heropp.com/api/connect?offset=0 (это пример ссылки, и поэтому она не будет работать), в то время как ответ, который я получаю, когда я делаю запрос, выглядит следующим образом

 {
    "result": {
        "connectUsers": [
            {
                "_id": "5f6a412d2ea9350017bec99f",
                "userProfile": {
                    "rep_points": 0.75,
                    "visits": 0,
                    "bio": "Nothing for you ",
                    "gender": "Male",
                    "university": "University Of Technology",
                    "avatar": "https://camx.heroapp.com/5f6a412d2ea9350017bec99f"
                },
                "name": "Joseph Henshaw ",
                "userTag": "bigjo",
                "id": "5f6a412d2ea9350017bec99f",
                "sameCampus": true
            },
            {
                "_id": "5f6bbf1cbd5faa00170d92b1",
                "userProfile": {
                    "rep_points": 0,
                    "visits": 0
                },
                "name": "John Doe",
                "userTag": "@doee",
                "id": "5f6bbf1cbd5faa00170d92b1",
                "sameCampus": false
            }
        ]
    }
}
  

чего я пытаюсь добиться, так это разбиения на страницы данных, поступающих из api .. смещение начинается с 0 и увеличивается на 10, т.е. Для получения большего количества данных 0ffset= 20 ..30 .. и так далее

Это запрос, который я делаю, чтобы получить ответ JSON, показанный выше

 Future<void> fetchConnect() async {
    var uri = Uri.parse('http://campusx.herokuapp.com/api/v1/users/connect');
    uri = uri.replace(query: 'offset=$offsetNumber');
    print(uri);
    try {
      final response = await http.get(
        uri,
        headers: {
          HttpHeaders.authorizationHeader: "Bearer $userToken",
        },

      );
      // List<Photo> fetchedPhotos = Photo.parseList(json.decode(response.body));
      if (response.statusCode == 200 || response.statusCode == 201) {
         print("IT works")
      } else {
        print(response.statusCode);
      }
      List<ConnectUsers> fetchedConnects =
          ConnectUsers.parseList(json.decode(response.body));
      setState(() {
        connectMore = fetchedConnects.length == defaultConnectsPerPageCount;
        loading = false;
        offsetNumber = offsetNumber   10;
        connects.addAll(fetchedConnects);
      });
    } catch (e) {
      setState(() {
        loading = false;
        error = true;
      });
    }
  }
  

и вот как мой пользовательский интерфейс для отображения выбранных данных (виджет getConnect помещается в тело моего эшафота

  Widget getConnects() {
    if (connects.isEmpty) {
      if (loading) {
        return Center(
            child: Padding(
          padding: const EdgeInsets.all(8),
          child: CircularProgressIndicator(),
        ));
      } else if (error) {
        return Center(
            child: InkWell(
          onTap: () {
            setState(() {
              loading = true;
              error = false;
              fetchConnects();
            });
          },
          child: Padding(
            padding: const EdgeInsets.all(16),
            child: Text("Error while loading connects, tap to try again"),
          ),
        ));
      }
    } else {
      return ListView.builder(
          itemCount: connects.length   (connectMore ? 10 : 0),
          itemBuilder: (context, index) {
            if (index == connects.length - nextPageThreshold) {
              fetchConnects();
            }
            if (index == connects.length) {
              if (error) {
                return Center(
                    child: InkWell(
                  onTap: () {
                    setState(() {
                      loading = true;
                      error = false;
                      fetchConnects();
                    });
                  },
                  child: Padding(
                    padding: const EdgeInsets.all(16),
                    child:
                        Text("Error while loading connects, tap to try agin"),
                  ),
                ));
              } else {
                return Center(
                    child: Padding(
                  padding: const EdgeInsets.all(8),
                  child: CircularProgressIndicator(),
                ));
              }
            }
            // final Photo photo = photos[index];
            final ConnectUsers connect = connects[index];
            return Card(
              child: Column(
                children: <Widget>[
                  // Image.network(
                  //   connect.name.connectUsers[index].userProfile.avatar,
                  //   fit: BoxFit.fitWidth,
                  //   width: double.infinity,
                  //   height: 160,
                  // ),
                  Padding(
                    padding: const EdgeInsets.all(16),
                    child: Text(connect.name,
                        // connect
                        // .result.connectUsers[index].userProfile.university,
                        style: TextStyle(
                            fontWeight: FontWeight.bold, fontSize: 16)),
                  ),
                ],
              ),
            );
          });
    }
    return Container();
  }
  

это ошибка, которую я получаю при использовании этого кода

 The getter 'isEmpty' was called on null.
Receiver: null
Tried calling: isEmpty
  

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

1. Итак, с чем именно у вас возникли проблемы? Вы получаете ошибки?

2. @nvoigt да, я получаю ошибки, один метод, который я пробовал, постоянно сообщал мне, что connectUsers вызывался с нулевым значением, другой просто показывал круговой индикатор выполнения, увеличивая значение смещения в моей консоли отладки и выполняя запрос снова и снова

3. Что ж, вам придется опубликовать этот код, чтобы мы могли найти ошибки. Мы не можем посоветовать вам код, который мы не видели.

4. Вы должны опубликовать свой код и точные ошибки, чтобы кто-нибудь мог вам помочь.

5. @nvoigt Я добавил свой код и обнаружил ошибку

Ответ №1:

Похоже, вы вызываете connects.isEmpty в своем виджете, но вы не инициализировали connects -> connects равно нулю, поэтому вы получаете ошибку выше. Вы могли бы выполнить нулевую проверку или инициализировать соединения

 ...
// initialise so connects is not null
connects = []
...
 Widget getConnects() {
    if (connects.isEmpty) {
      if (loading) {
        return Center(
(...)
  
  Widget getConnects() {
// do a null check before calling member
    if (connects == null || connects.isEmpty) {
      if (loading) {
        return Center(
(...)
  

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

1. Я думаю, что более безопасным и чистым способом было бы, если (подключается? .Пусто?? ложь) … Я никогда не доверяю проверке null и проверке значения объекта в одном и том же if..

2. Я вас понимаю, но в dart логические выражения завершаются, как только результат был определен — я не был уверен в этом, поэтому я начал новый проект и протестировал все соответствующие комбинации. Это дало мне гораздо больше уверенности в моем понимании языка. — Кстати, вам нужно будет вернуть true, а не false, если оно равно null.

Ответ №2:

Я решил проблему, вот как я использовал

это моя модель.класс dart

 
import 'package:flutter/foundation.dart';

ConnectModel payloadFromJson(String str) =>
    ConnectModel.fromJson(json.decode(str));

String payloadToJson(ConnectModel data) => json.encode(data.toJson());

class ConnectModel {
  ConnectModel({
    this.result,
  });

  Result resu<

  factory ConnectModel.fromJson(Map<String, dynamic> json) => ConnectModel(
        result: Result.fromJson(json["result"]),
      );

  Map<String, dynamic> toJson() => {
        "result": result.toJson(),
      };
}

class Result {
  Result({
    this.connect,
  });

  List<Connect> connect;

  factory Result.fromJson(Map<String, dynamic> json) => Result(
        connect: List<Connect>.from(
            json["connectUsers"].map((x) => Connect.fromJson(x))),
      );

  Map<String, dynamic> toJson() => {
        "connectUsers": List<dynamic>.from(connect.map((x) => x.toJson())),
      };
}

class Connect with ChangeNotifier {
  Connect({
    this.id,
    this.name,
    this.userTag,
    this.university,
    this.checkIsFollowing,
    this.avatar,
  });
  String name;
  String userTag;
  String id;
  String university;
  String avatar;
  bool checkIsFollowing;

  factory Connect.fromJson(Map<String, dynamic> json) => Connect(
        id: json["_id"],
        name: json["name"],
        userTag: json["userTag"],
        university: json["userProfile"]["university"],
        checkIsFollowing: json["checkIsFollowing"],
        avatar: json["userProfile"]["avatar"],
      );

  Map<String, dynamic> toJson() => {
        "_id": id,
        "name": name,
        "userTag": userTag,
        "userProfile"
            "university": university,
        "userProfile"
            "avatar": avatar,
        "checkIsFollowing": checkIsFollowing
      };
}
  

это мой метод, где offsetNumber по умолчанию инициализируется равным 0

   List<Connect> mainList = [];
  List<String> nameList = [];
  Future<List<Connect>> getConnectionsList(var offsetNumber) async {
    var uri = "http://campusx.herokuapp.com/api/v1/users/connect?$offsetNumber";
    var token = "pass_your_token_here";
    try {
      final response =
          await http.get(uri, headers: {"Authorization": "Bearer $token"});
      if (response.statusCode == 200) {
        var data = json.decode(response.body);
        var d = data['result']['connectUsers'];
        for (var u in d) {
          Connect c = Connect.fromJson(u);
          mainList.add(c);
        }
        setState(() {
          nameList = mainList.map((e) => e.name).toList();
          //add other parameters gotten from the endpoint here, i used name only for test purposes
        });
      } else {
        print(response.body);
      }
    } catch (e) {
      print(e);
    }
    return mainList;
  }
  

это конструктор ListView, в котором я отображал имена

      ListView.builder(
          itemCount: mainList.length,
          itemBuilder: (_, index) {
            return Container(
              height: 120,
              child: Text(
                nameList[index],
                style: TextStyle(fontSize: 39),
               ),
            );
          },
       )

  

затем либо onClick, либо кнопка, либо когда список попадает в нижнюю часть экрана вашего телефона, вы должны установить state (() {}); и увеличить размер смещенного числа. Что-то вроде этого:

 setState(() {
  offsetNumber = offsetNumber   10;
  future = getConnectionsList(offsetNumber);
});