我正在开发一个从外部数据库查询数据的应用程序,该数据库返回按日期排序的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]}'),
);
},
),
),
)
],
),
),
);
} }
答案 0 :(得分:1)
如果您能找到更好的解决方案,请告诉我。
如果列表是无限的或很长,则可以将第一个项目设置为第5000个项目,如果达到了第一个项目(我是说当尝试查看以前的项目时),则应该获取(显示)更多数据,例如。 ..,4998th 4999th。
滚动的想法来自This blog
我更改您的代码来完成这项工作,但考虑到我也在寻找更好的解决方案。
如果适合,可以使用其他小部件,例如ListWheelScrollView
和PageView
代替列表视图。
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或等等。
答案 1 :(得分:1)
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会以太快的速度将新项目插入列表中
再次感谢大家的帮助