颤振列表 + 拉取加载更多数据不会等待数据加载完成,从而导致列表/滚动变得不稳定



如果你创建一个新的 Flutter 项目并包含依赖项,然后替换你的 main.dart 文件,你应该在这个问题上处于我的位置。


我离开了原始负载:使用Future.delay,但这似乎并不重要。 我部分知道我的问题是什么,但无法提出更好的解决方案。

1)我似乎没有使用我的snapshot.data,相反,我只是用str做了一个空的列表,然后我只是将All添加到其中并使用它。 所以我不想这样做,我最初使用的是 snapshot.data,但当我尝试"拉取以加载更多数据"时遇到了问题,这发生在您滚动到列表底部后。

我当前执行此操作的方法的问题在于,如果您拉取以加载更多用户,然后在用户加载之前尝试再次拉取,则应用程序会中断并且不会等待数据正确加载。 我相信我需要在这个图书馆easy_refreshload:做这一切......但我不确定如何重写我的代码来完成这一点。

如何使用 snapshot.data 加载数据,然后在拉取刷新时,将 100 多个用户追加到该列表中,但 UI 在完成加载之前等待列表更新。 我最好只放置一个阻塞 UI 元素并在 str 列表更新后?加载新用户时,我取消阻止 UI? 这有点感觉很黑客,不是解决这个问题的正确方法。 插件本身应该能够进行加载,当它准备好时,它会停止列表下的微调器并说"完成"。

Pubspec.yaml

dependencies:
flutter:
sdk: flutter
flutter_easyrefresh: ^1.2.7
http: ^0.12.0+2

主飞镖

import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:convert';
import 'package:flutter_easyrefresh/easy_refresh.dart';
import 'package:http/http.dart' as http;
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
backgroundColor: Colors.white
),
home: DuelLeaderBoards(),
);
}
}

class DuelLeaderBoards extends StatefulWidget {
@override
_DuelLeaderBoardsState createState() => _DuelLeaderBoardsState();
}
class _DuelLeaderBoardsState extends State<DuelLeaderBoards> {
List<Entry> str = [];
GlobalKey<EasyRefreshState> _easyRefreshKey = new GlobalKey<EasyRefreshState>();
GlobalKey<RefreshHeaderState> _headerKey = new GlobalKey<RefreshHeaderState>();
GlobalKey<RefreshHeaderState> _connectorHeaderKey = new GlobalKey<RefreshHeaderState>();
GlobalKey<RefreshFooterState> _footerKey = new GlobalKey<RefreshFooterState>();
GlobalKey<RefreshFooterState> _connectorFooterKey = new GlobalKey<RefreshFooterState>();
Future<LeaderBoards> getLeaderBoards(start) async {
String apiURL = 'https://stats.quake.com/api/v2/Leaderboard?from=$start&board=duel&season=current';
final response = await http.get(apiURL);
if (response.statusCode == 200) {
final responseBody = leaderBoardsFromJson(response.body);
return responseBody;
} else {
throw Exception('Failed to load Data');
}
}
void updateLeaderBoardList(e) async {
setState(() {
str.addAll(e.entries);
});
}
@override
void initState() {
getLeaderBoards(0).then((onValue) => str = onValue.entries );
super.initState();
}
@override
Widget build(BuildContext context) {
Widget header = ClassicsHeader(
key: _headerKey,
refreshText: "pullToRefresh",
refreshReadyText: "releaseToRefresh",
refreshingText: "refreshing...",
refreshedText: "refreshed",
moreInfo: "updateAt",
bgColor: Colors.transparent,
textColor: Colors.white,
);
Widget footer = ClassicsFooter(
key: _footerKey,
loadHeight: 50.0,
loadText: "pushToLoad",
loadReadyText: "releaseToLoad",
loadingText: "loading",
loadedText: "loaded",
noMoreText: "Finished",
moreInfo: "updateAt",
bgColor: Colors.transparent,
textColor: Colors.white,
);
return FutureBuilder(
future: getLeaderBoards(0),
builder:
(BuildContext context, AsyncSnapshot<LeaderBoards> snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
} else {
return Builder(builder: (BuildContext context) {
return Center(
child: new EasyRefresh(
key: _easyRefreshKey,
behavior: ScrollOverBehavior(),
refreshHeader: ConnectorHeader(
key: _connectorHeaderKey,
header: header,
),
refreshFooter: ConnectorFooter(
key: _connectorFooterKey,
footer: footer,
),
child: CustomScrollView(
semanticChildCount: str.length,
slivers: <Widget>[
SliverList(
delegate: SliverChildListDelegate(<Widget>[header]),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
return new Container(
height: 70.0,
child: Card(
child: new Text(
'${index+1}: ${str[index].userName}',
style: new TextStyle(fontSize: 18.0),
),
));
},
childCount: str.length,
)),
SliverList(
delegate: SliverChildListDelegate(<Widget>[footer]),
)
],
),
onRefresh: () async {
await new Future.delayed(const Duration(seconds: 0), () {
setState(() {});
});
},
loadMore: () async {
getLeaderBoards(str.length).then((onValue) => {
updateLeaderBoardList(onValue)
});
},
//                    loadMore: () async {
//                      await new Future.delayed(const Duration(seconds: 0), () {
//                        getLeaderBoards(str.length).then((onValue) => {
//                              updateLeaderBoardList(onValue)
//                        });
//                      });
//                    },
)
);
});
}
});
}
}

LeaderBoards leaderBoardsFromJson(String str) {
final jsonData = json.decode(str);
return LeaderBoards.fromJson(jsonData);
}
String leaderBoardsToJson(LeaderBoards data) {
final dyn = data.toJson();
return json.encode(dyn);
}
class LeaderBoards {
String boardType;
List<Entry> entries;
int totalEntries;
LeaderBoards({
this.boardType,
this.entries,
this.totalEntries,
});
factory LeaderBoards.fromJson(Map<String, dynamic> json) => new LeaderBoards(
boardType: json["boardType"] == null ? null : json["boardType"],
entries: json["entries"] == null ? null : new List<Entry>.from(json["entries"].map((x) => Entry.fromJson(x))),
totalEntries: json["totalEntries"] == null ? null : json["totalEntries"],
);
Map<String, dynamic> toJson() => {
"boardType": boardType == null ? null : boardType,
"entries": entries == null ? null : new List<dynamic>.from(entries.map((x) => x.toJson())),
"totalEntries": totalEntries == null ? null : totalEntries,
};
}
class Entry {
String userName;
int eloRating;
String profileIconId;
String namePlateId;
Entry({
this.userName,
this.eloRating,
this.profileIconId,
this.namePlateId,
});
factory Entry.fromJson(Map<String, dynamic> json) => new Entry(
userName: json["userName"] == null ? null : json["userName"],
eloRating: json["eloRating"] == null ? null : json["eloRating"],
profileIconId: json["profileIconId"] == null ? null : json["profileIconId"],
namePlateId: json["namePlateId"] == null ? null : json["namePlateId"],
);
Map<String, dynamic> toJson() => {
"userName": userName == null ? null : userName,
"eloRating": eloRating == null ? null : eloRating,
"profileIconId": profileIconId == null ? null : profileIconId,
"namePlateId": namePlateId == null ? null : namePlateId,
};
}

我查看了loadMore的文档。既然它说分配给loadMore的函数的主体应该是async的,你就不需要用then

loadMore: () async {
final result = await getLeaderBoards(str.length);
updateLeaderboardList(result);
},
loadMore: () async {
await getLeaderBoards(str.length).then((onValue) => {
updateLeaderboardList(onValue)
});
},

但是输入"等待"我的加载器会等待函数完成,然后再完成动画。

最新更新