Flutter,在延迟加载后保持位置的方法

huangapple go评论74阅读模式
英文:

Flutter, How to stay in position after lazy loading

问题

我正在尝试从HTTP服务器构建一个列表,并希望实现大数据集的分页加载。我尝试使用lazy_load_scrollview包并按照示例操作,可以在页面末尾加载额外的记录,但是当新记录加载时,列表会跳到第一条记录。我该如何避免这种情况?

int totalRecords = 0;
int recordsPerPage = 5;
int currentPage = 1;
int totalPages = 1;

List<dataRecord> dataList = [];
List<dataRecord> fullList = [];
bool isLoading = false;

// 类定义...

class searchClient extends StatefulWidget {
  @override
  _searchClientState createState() => _searchClientState();
}

class _searchClientState extends State<searchClient> {
  final _searchItemController = TextEditingController();

  @override
  void initState() {
    super.initState();
    dataList = [];
    currentPage = 1;
  }

  List<dataRecord> parseJson(String responseBody) {
    final parsed =
        convert.jsonDecode(responseBody).cast<Map<String, dynamic>>();
    return parsed.map<dataRecord>((json) => dataRecord.fromJson(json)).toList();
  }

  @override
  void dispose() {
    _searchItemController.dispose();
    super.dispose();
  }

  void loadData() async {
    setState(() {
      isLoading = true;
    });

    if (totalRecords == 0) {
      final response2 = await http.get(
        Uri.parse('http://192.168.0.8:88/searchcustomer_gettotal' +
            '?userID=' +
            globals.userID.toString() +
            '&token=' +
            globals.token +
            '&name=' +
            _searchItemController.text),
        headers: {
          "Content-type": "application/json",
          "Accept": "application/json",
          "Connection": "Keep-Alive",
        },
      );
      Map<String, dynamic> totalMap = convert.jsonDecode(response2.body);
      var total = totals.fromJson(totalMap);
      totalRecords = total.total;
      var t = totalRecords / recordsPerPage;
      if (totalRecords < recordsPerPage) {
        totalPages = 1;
      } else if (totalRecords % recordsPerPage == 0) {
        totalPages = t.round();
      } else {
        var t = totalRecords / recordsPerPage;
        totalPages = t.round() + 1;
      }
      print(totalRecords);
    }

    final response = await http.get(
      Uri.parse('http://192.168.0.8:88/searchcustomer' +
          '?userID=' +
          globals.userID.toString() +
          '&token=' +
          globals.token +
          '&name=' +
          _searchItemController.text +
          '&pageno=' +
          currentPage.toString() +
          '&perpage=' +
          recordsPerPage.toString()),
      headers: {
        "Content-type": "application/json",
        "Accept": "application/json",
        "Connection": "Keep-Alive",
      },
    );
    setState(() {
      isLoading = false;
    });
    dataList = parseJson(response.body);
    fullList = List.from(fullList)..addAll(dataList);
  }

  void loadMore() {
    if (currentPage < totalPages) {
      currentPage += 1;
      loadData();
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Search Client')),
      body: Column(
        children: [
          Padding(
            padding: const EdgeInsets.fromLTRB(12, 10, 12, 20),
            child: TextField(
              decoration: InputDecoration(
                  border: OutlineInputBorder(), labelText: 'Search Item'),
              controller: _searchItemController,
            ),
          ),
          Container(
            height: 45,
            width: 250,
            decoration: BoxDecoration(
                color: Colors.teal, borderRadius: BorderRadius.circular(16)),
            child: TextButton(
              onPressed: () {
                totalRecords = 0;
                fullList = [];
                loadData();
              },
              child: Text(
                'Search',
                style: TextStyle(color: Colors.white, fontSize: 20),
              ),
            ),
          ),
          SizedBox(height: 20),
          isLoading
              ? Center(
                  child: Padding(
                    padding: const EdgeInsets.all(50.0),
                    child: CircularProgressIndicator(),
                  ),
                )
              : Expanded(
                  child: Padding(
                    padding: const EdgeInsets.fromLTRB(12, 0, 12, 0),
                    child: LazyLoadScrollView(
                      isLoading: isLoading,
                      onEndOfPage: () {
                        loadMore();
                      },
                      child: ListView.builder(
                          itemCount: fullList.length,
                          itemBuilder: (BuildContext context, int index) {
                            return dataCard(context, fullList, index);
                          }),
                    ),
                  ),
                ),
        ],
      ),
    );
  }
}

这是您的代码的翻译部分。如果您需要进一步的帮助或有任何问题,请随时告诉我。

英文:

I am trying to build a list from http server, and would like to implement page load for large data set. I tried with lazy_load_scrollview package and follow the example, I can load additional records from the server on End of page, but when new records are loaded, the list jumps up to the first record. How could I avoid this?

int totalRecords = 0;
int recordsPerPage = 5;
int currentPage = 1;
int totalPages = 1;
List&lt;dataRecord&gt; dataList = [];
List&lt;dataRecord&gt; fullList = [];
bool isLoading = false;
//class definitions...
class searchClient extends StatefulWidget {
@override
_searchClientState createState() =&gt; _searchClientState();
}
class _searchClientState extends State&lt;searchClient&gt; {
final _searchItemController = TextEditingController();
@override
void initState() {
super.initState();
dataList = [];
currentPage = 1;
}
List&lt;dataRecord&gt; parseJson(String responseBody) {
final parsed =
convert.jsonDecode(responseBody).cast&lt;Map&lt;String, dynamic&gt;&gt;();
return parsed.map&lt;dataRecord&gt;((json) =&gt; dataRecord.fromJson(json)).toList();
}
@override
void dispose() {
_searchItemController.dispose();
super.dispose();
}
void loadData() async {
setState(() {
isLoading = true;
});
if (totalRecords == 0) {
final response2 = await http.get(
Uri.parse(&#39;http://192.168.0.8:88/searchcustomer_gettotal&#39; +
&#39;?userID=&#39; +
globals.userID.toString() +
&#39;&amp;token=&#39; +
globals.token +
&#39;&amp;name=&#39; +
_searchItemController.text),
headers: {
&quot;Content-type&quot;: &quot;application/json&quot;,
&quot;Accept&quot;: &quot;application/json&quot;,
&quot;Connection&quot;: &quot;Keep-Alive&quot;,
},
);
Map&lt;String, dynamic&gt; totalMap = convert.jsonDecode(response2.body);
var total = totals.fromJson(totalMap);
totalRecords = total.total;
var t = totalRecords / recordsPerPage;
if (totalRecords &lt; recordsPerPage) {
totalPages = 1;
}else if (totalRecords%recordsPerPage == 0) {
totalPages = t.round();
}else{
var t = totalRecords / recordsPerPage;
totalPages = t.round()+ 1;
}
print(totalRecords);
}
;
final response = await http.get(
Uri.parse(&#39;http://192.168.0.8:88/searchcustomer&#39; +
&#39;?userID=&#39; +
globals.userID.toString() +
&#39;&amp;token=&#39; +
globals.token +
&#39;&amp;name=&#39; +
_searchItemController.text +
&#39;&amp;pageno=&#39; +
currentPage.toString() +
&#39;&amp;perpage=&#39; +
recordsPerPage.toString()),
headers: {
&quot;Content-type&quot;: &quot;application/json&quot;,
&quot;Accept&quot;: &quot;application/json&quot;,
&quot;Connection&quot;: &quot;Keep-Alive&quot;,
},
);
setState(() {
isLoading = false;
});
dataList = parseJson(response.body);
fullList = List.from(fullList)..addAll(dataList);
}
void loadMore() {
if (currentPage &lt; totalPages) {
currentPage += 1;
loadData();
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(&#39;Search Client&#39;)),
body: Column(
children: [
Padding(
padding: const EdgeInsets.fromLTRB(12, 10, 12, 20),
child: TextField(
decoration: InputDecoration(
border: OutlineInputBorder(), labelText: &#39;Search Item&#39;),
controller: _searchItemController,
),
),
Container(
height: 45,
width: 250,
decoration: BoxDecoration(
color: Colors.teal, borderRadius: BorderRadius.circular(16)),
child: TextButton(
onPressed: () {
totalRecords = 0;
fullList = [];
loadData();
},
child: Text(
&#39;Search&#39;,
style: TextStyle(color: Colors.white, fontSize: 20),
),
),
),
SizedBox(height: 20),
isLoading
? Center(
child: Padding(
padding: const EdgeInsets.all(50.0),
child: CircularProgressIndicator(),
),
)
: Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(12, 0, 12, 0),
child: LazyLoadScrollView(
isLoading: isLoading,
onEndOfPage: () {
loadMore();
},
child: ListView.builder(
itemCount: fullList.length,
itemBuilder: (BuildContext context, int index) {
return dataCard(context, fullList, index);
}),
),
),
),
],
),
);
}
}

答案1

得分: 0

请尝试将树中的此小部件移除,以仅保留您的ScrollView。

我猜测由于您在加载时调用了setState方法,整个小部件树都会被重建,当获取新数据时,整个ListView都会被重建,导致丢失先前的位置。

如果这不能解决您的问题,我会在我的答案中添加一些代码。

英文:

Could you try removing this widget of your tree

Center(
child: Padding(
padding: const EdgeInsets.all(50.0),
child: CircularProgressIndicator(),
),
)

in order to only have your Scrollview ?

My guess is that since you're calling the setState method at load time, your whole widget tree is rebuilt, and when your new datas are fetched the whole listview is rebuilt, losing the previous position.

I'll edit my answer with some code if this doesn't fix your problem

huangapple
  • 本文由 发表于 2023年1月6日 14:28:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/75027667.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定