#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);
});