繁体   English   中英

如何制作从中心开始的listview.builder构建器?

[英]How to make listview.builder builder that starts from the center?

我正在创建一个从外部数据库查询数据的应用程序,该数据库返回按日期排序的100个事件的列表。 (50个未来日期,过去50天)

然后,主页将呈现一个填充了ExpansionTile的Listview.builder,然后将其显示给用户。

问题是,我们将屏幕上的第一个项目设置为项目编号51.这意味着从那时起,它将显示项目52,53,54等。

但是,当用户向上滚动时,我们希望显示项目50,49,48等,而不是使用Listview.Builder典型的触发物理刷新。

关于如何实现它的任何想法? 这个想法基本上总是最初显示今天的事件,但用户可以无缝浏览未来的事件和过去的事件

这没有达到预期的效果,因为它最终会出现'休息'

class MyApp extends StatelessWidget {
  final List<int> nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
  final List<int> nums2 = [11, 12, 13, 14, 15, 16, 17, 18, 19, 20];

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        body: Column(
          children: <Widget>[
            Expanded(
              child: Container(
                child: ListView.builder(
                  itemCount: nums.length,
                  itemBuilder: (BuildContext context, int index) {
                    return ExpansionTile(
                      title: Text('${nums[index]}'),
                    );
                  },
                ),
              ),
            ),
            Expanded(
              child: Container(
                child: ListView.builder(
                  itemCount: nums2.length,
                  itemBuilder: (BuildContext context, int index) {
                    return ExpansionTile(
                      title: Text('${nums2[index]}'),
                    );
                  },
                ),
              ),
            )
          ],
        ),
      ),
);

}}

如果您能找到更好的解决方案,请告诉我。

如果您的列表无限长或很长,您可以将您的第一项设置为第5000项,如果您到达第一项(我的意思是在尝试查看以前的项目时),您应该获取(显示)更多数据,例如..., 4998th 4999th。

滚动的想法来自本博客

我改变你的代码来完成这项工作,但考虑到我也在寻找更好的解决方案。

如果它们适合,可以使用其他小部件: ListWheelScrollViewPageView代替listview。

import 'package:flutter/material.dart';

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  List<int> nums;
  ScrollController ctrl;
  double start;
  bool isScroll = true;
  double end;

  @override
  void initState() {
    nums = List.generate(100, (i) => 4990 + i);
    ctrl = ScrollController(initialScrollOffset: 10 * 80.0)
      ..addListener(() {
        // fetch if ite reaches the end or start
        if (ctrl.offset == ctrl.position.minScrollExtent) {
          nums.insert(0, nums.first - 1);
          // ctrl.jumpTo(80.0 * 10);

          setState(() {});
        } else if (ctrl.offset == ctrl.position.maxScrollExtent) {
          nums.add(nums.last + 1);
          setState(() {});
        }
      });
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
          body: NotificationListener<ScrollNotification>(
        onNotification: (Notification notification) {
          if (notification is ScrollStartNotification) {
            start = notification.metrics.pixels;
            isScroll = false;
          } else if (!isScroll && notification is ScrollEndNotification) {
            end = notification.metrics.pixels;
            isScroll = true;

            double d = (end - start);
            // 80 is the items height
            double offset = d.abs() ~/ 80 * 80.0;
            if (d % 80.0 > 40) offset += 80.0;
            if (d.isNegative)
              ctrl.jumpTo(start - offset);
            else
              ctrl.jumpTo(start + offset);
          }
          return true;
        },
        child: ListView.builder(
          itemExtent: 80.0,
          controller: ctrl,
          itemCount: nums.length,
          itemBuilder: (BuildContext context, int index) {
            return ExpansionTile(
              title: Text('${nums[index]}'),
            );
          },
        ),
      )),
    );
  }
}

对于获取行为,这一切都取决于您的偏好,如果到达第一个项目或第10个项目然后获取新项目,或者每次取出(显示)一个项目10个左右,则可以执行此操作。

import 'package:async/async.dart';
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  List<int> nums;
  ScrollController ctrl;
  double start;
  bool isScroll = true;
  double end;
  bool allowScroll = true;
  RestartableTimer _timer = RestartableTimer(Duration(milliseconds: 50), () {});

  @override
  void initState() {
    nums = List.generate(30, (i) => 4990 + i);
    ctrl = ScrollController(initialScrollOffset: 10 * 80.0)
      ..addListener(() {
        // fetch if ite reaches the end or start
        if (ctrl.offset == ctrl.position.minScrollExtent) {
          nums.insert(0, nums.first - 1);
          // ctrl.jumpTo(80.0 * 10);

          setState(() {});
        } else if (ctrl.offset == ctrl.position.maxScrollExtent) {
          nums.add(nums.last + 1);
          setState(() {});
        }
      });
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        body: NotificationListener<ScrollNotification>(
          onNotification: (Notification notification) {
            if (notification is OverscrollNotification) {
              if (!_timer.isActive) {
                nums.insert(0, nums.first - 1);
                _timer.reset();
                setState(() {});
              }
            } else if (!isScroll && notification is ScrollEndNotification) {
              end = notification.metrics.pixels;
              isScroll = true;

              double d = (end - start);
              // 80 is the items height
              double offset = d.abs() ~/ 80 * 80.0;
              if (d % 80.0 > 40) offset += 80.0;
              if (d.isNegative)
                ctrl.jumpTo(start - offset);
              else
                ctrl.jumpTo(start + offset);
            }
            return true;
          },
          child: ListView.builder(
            itemExtent: 80.0,
            controller: ctrl,
            itemCount: nums.length,
            itemBuilder: (BuildContext context, int index) {
              print('build tile index $index');
              return ExpansionTile(
                title: Text('${nums[index]}'),
              );
            },
          ),
        ),
      ),
    );
  }
}

嗨,大家好

我已经重新设计了NoobN3rd给出的解决方案,它允许几乎没有经常重载的无负载加载

这绝不是一个完美的解决方案,你可以在控制台上看到,似乎有一个不间断的重建。

但我认为这暂时是一个很好的解决方案。

RestartableTimer是此解决方案的关键。 没有它,OverScrollNotification过快地将新项目列入列表

再次感谢大家的帮助

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM