Flutter有没有办法把一个很长的ListView拆分成多个"pages"?

Is there a way in Flutter to split a long ListView into multiple "pages"?

我是编码初学者,我正在尝试创建一个应用程序来练习和学习 dart 和 flutter。

我的应用程序有一个包含长 ListView(700 个项目)的页面,我想制作某种“页面导航器”以将 ListView 分成 7 个(每页 100 个项目)。 我知道分页 ListView,但我不知道 link 结果。 很抱歉我在解释我想做的事情时词汇量很差,这是一个例子

Example of "pages navigator"

最终,我希望这个“拆分的 ListView”中的所有项目都可以通过搜索栏进行过滤。

这是我目前的 list.dart 代码:

class SongList extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      child: _buildListView(context),
    );
  }

  ListView _buildListView(BuildContext context) {
    return ListView.separated(
      physics: NeverScrollableScrollPhysics(),
      shrinkWrap: true,
      itemCount: 700,
      itemBuilder: (_, index) {
        final count = index + 1;

        return new ListTile(
          leading: new CircleAvatar(
            child: new Text(
              "$count",
              style: TextStyle(color: kBackgroundColor),
            ),
            backgroundColor: kPrimaryColor,
          ),
          title: new Text("Song #$count"),
          trailing: Icon(Icons.navigate_next),
          onTap: () {
            Navigator.push(
              context,
              MaterialPageRoute(
                builder: (context) => SongDetail(index),
              ),
            );
          },
        );
      },
      separatorBuilder: (context, index) {
        return Divider();
      },
    );
  }
}

更新:我做到了!

我发现这个包太棒了! https://pub.dev/packages/number_paginator

所以我从下面更新了代码,并将 CupertinoSlidingSegmentedControl 替换为 NumberPaginator 小部件。

这里是代码:

              child: NumberPaginator(
                numberPages: 7,
                onPageChange: (int index) {
                  setState(() {
                    currentPage = index;
                  });
                },
              ),

这些包甚至提供了一些属性来自定义页面指示器的颜色和形状。


旧解

我希望这对某人有所帮助。请注意,我仍然是一名学习者,如果此代码中的某些内容已关闭,可能会更好地发表评论并添加您的贡献。

我在 Flutter 中没有找到任何原生的“分页小部件”,所以我最终使用 CupertinoSlidingSegmentedControl 作为“分页小部件”和 CupertinoUserInterfaceLevel 用于 segments/pages.

的“正文”

我的歌曲列表由数据库助手和一些查询提供,我致力于将我的 700 个项目列表拆分为 7 个查询并根据选定的 segment/page 指标调用它们。

这是我的代码,其中包含一些有用的注释。

import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart'; // Required in order to use Cupertino Widgets

import '/assets/data/queries.dart'; // My queries are in another dart file
import '/assets/data/db_tables.dart';

import 'songs_detail.dart';

class SongsBody extends StatefulWidget {
  @override
  _SongsBodyState createState() => _SongsBodyState();
}

class _SongsBodyState extends State<SongsBody> {
  // Change this in order to edit the segment/paging widget, you can put text and icons
  final Map<int, Widget> children = const <int, Widget>{
    0: Text('1'),
    1: Text('2'),
    2: Text('3'),
    3: Text('4'),
    4: Text('5'),
    5: Text('6'),
    6: Text('7'),
  };

  // Change this to set the initial segment/page
  int currentSegment = 0;

  // This is required to update the state when you tap on another segment/page
  void onValueChanged(newValue) {
    setState(() {
      currentSegment = newValue;
    });
  }

  // My queries from queries.dart are called here and used later in the code
  final List<Future<List?>> _query = [
    QueryCtr().getSongsFrom1To100(),
    QueryCtr().getSongsFrom101To200(),
    QueryCtr().getSongsFrom201To300(),
    QueryCtr().getSongsFrom301To400(),
    QueryCtr().getSongsFrom401To500(),
    QueryCtr().getSongsFrom501To600(),
    QueryCtr().getSongsFrom601To700(),
  ];

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        // This is the segment control widget (our "paging" widget)
        CupertinoSlidingSegmentedControl<int>(
          children: children,
          onValueChanged: onValueChanged,
          groupValue: currentSegment,
        ),
        const Divider(),
        // This is the "body" of our segment/page
        CupertinoUserInterfaceLevel(
          data: CupertinoUserInterfaceLevelData.base,
          // My listview is now called with a future builder because I'm getting the data from my database
          child: Builder(
            builder: (BuildContext context) {
              return FutureBuilder<List?>(
                // This is the interesting part: I update only the query according to the selected segment
                future: _query[currentSegment],
                initialData: const [],
                builder: (context, snapshot) {
                  return snapshot.hasData
                  // If there is some data show them in a listview wrapped in expanded
                      ? Expanded(
                          child: ListView.separated(
                            physics: const ScrollPhysics(),
                            shrinkWrap: true,
                            itemCount: snapshot.data!.length,
                            itemBuilder: (context, i) {
                              // Here I decided to code a separate widget to build the rows of my listview
                              return _buildRow(snapshot.data![i]);
                            },
                            separatorBuilder: (context, index) {
                              return const Divider();
                            },
                          ),
                        ) 
                  // If there is no data return a progress indicator
                      : const Center(
                        child: CircularProgressIndicator(),
                      );
                },
              );
            },
          ),
        ),
      ],
    );
  }

  Widget _buildRow(Raccolta get) {
    return ListTile(
      leading: CircleAvatar(
        child: Text(
          get.songId.toString(),
        ),
      ),
      title: Text(get.songTitle),
      trailing: const Icon(
        Icons.navigate_next,
        color: somecolor,
      ),
      onTap: () {
        FocusScope.of(context).unfocus();
        int songId = get.songId;
        String songTitle = get.songTitle;
        String songText = get.songText;
        Navigator.push(
          context,
          MaterialPageRoute(
            builder: (context) {
              return SongsDetail(songId, songTitle, songText);
            },
          ),
        );
      },
    );
  }
}