如何在 Flutter 中自动滚动 Listview.separated 中的所有 List Tiles?
Posted
技术标签:
【中文标题】如何在 Flutter 中自动滚动 Listview.separated 中的所有 List Tiles?【英文标题】:How to automatically scroll through all the ListTiles in the Listview.seperated in Flutter? 【发布时间】:2021-06-01 23:15:30 【问题描述】:使用 Flutter 中的 Timer 自动滚动(无需任何用户交互)在 Listview 中的所有 ListTiles。下面的方法只制作一个 ListTile 动画,但我想将所有 ListTiles 从上到下一个一个地动画化,然后从下到上一个一个地动画化。
下面是列表视图:
@override
Widget build(BuildContext context)
return Scaffold(
body: Container(
child: FutureBuilder(
future: fetchNews(),
builder: (context, snap)
if (snap.hasData)
news = snap.data;
return ListView.separated(
//controller: _controller,
scrollDirection: scrollDirection,
controller: controller,
itemBuilder: (context, i)
final NewsModel _item = news[i];
return AutoScrollTag(
key: ValueKey(i),
controller: controller,
index: i,
child: ListTile(
title: Text('$_item.title'),
subtitle: Text(
'$_item.description',
// maxLines: 1,
//overflow: TextOverflow.ellipsis,
),
),
);
,
separatorBuilder: (context, i) => Divider(),
itemCount: news.length,
);
else if (snap.hasError)
return Center(
child: Text(snap.error.toString()),
);
else
return Center(
child: CircularProgressIndicator(),
);
,
),
),
);
这是我尝试过的自动滚动:
@override
void initState()
super.initState();
timer = Timer.periodic(Duration(seconds: 2), (Timer t) async
await controller.scrollToIndex(1,
preferPosition: AutoScrollPosition.begin);
);
【问题讨论】:
【参考方案1】:这是一个假设您在ListView
中的所有项目都具有相同itemExtent
的解决方案。
在此解决方案中,我将当前项目突出显示为选中状态。您还可能希望在到达列表底部后立即停止自动滚动。
完整源代码
import 'dart:async';
import 'package:faker/faker.dart';
import 'package:flutter/material.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part '66455867.auto_scroll.freezed.dart';
void main()
runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
home: HomePage(),
),
);
class HomePage extends StatelessWidget
Future<List<News>> _fetchNews() async => dummyData;
@override
Widget build(BuildContext context)
return Scaffold(
appBar: AppBar(title: Text('News')),
body: FutureBuilder(
future: _fetchNews(),
builder: (context, snapshot)
if (snapshot.hasData)
return NewsList(newsList: snapshot.data);
else if (snapshot.hasError)
return Center(child: Text(snapshot.error.toString()));
else
return Center(child: CircularProgressIndicator());
,
),
);
class NewsList extends StatefulWidget
final List<News> newsList;
const NewsList(
Key key,
this.newsList,
) : super(key: key);
@override
_NewsListState createState() => _NewsListState();
class _NewsListState extends State<NewsList>
ScrollController _scrollController = ScrollController();
Timer _timer;
double _itemExtent = 100.0;
Duration _scrollDuration = Duration(milliseconds: 300);
Curve _scrollCurve = Curves.easeInOut;
int _autoScrollIncrement = 1;
int _currentScrollIndex = 0;
@override
void initState()
super.initState();
_timer = Timer.periodic(Duration(seconds: 2), (_) async
_autoScrollIncrement = _currentScrollIndex == 0
? 1
: _currentScrollIndex == widget.newsList.length - 1
? -1
: _autoScrollIncrement;
_currentScrollIndex += _autoScrollIncrement;
_animateToIndex(_currentScrollIndex);
setState(() );
);
void _animateToIndex(int index)
_scrollController.animateTo(
index * _itemExtent,
duration: _scrollDuration,
curve: _scrollCurve,
);
@override
void dispose()
_timer?.cancel();
super.dispose();
@override
Widget build(BuildContext context)
return ListView(
controller: _scrollController,
itemExtent: _itemExtent,
children: widget.newsList
.map((news) => ListTile(
title: Text(news.title),
subtitle: Text(
news.description,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
selected: widget.newsList[_currentScrollIndex].id == news.id,
selectedTileColor: Colors.amber.shade100,
))
.toList(),
);
@freezed
abstract class News with _$News
const factory News(int id, String title, String description) = _News;
final faker = Faker();
final dummyData = List.generate(
10,
(index) => News(
id: faker.randomGenerator.integer(99999999),
title: faker.sport.name(),
description: faker.lorem.sentence(),
),
);
解决方案中使用的包:
freeze 用于News
域类
build_runner生成冻结代码
faker生成随机新闻列表
更新:只滚动一次
要停止listview底部的自动滚动,只需要修改initState
方法即可:
int _currentScrollIndex;
News _selectedNews;
@override
void initState()
super.initState();
_currentScrollIndex = -1;
_timer = Timer.periodic(Duration(seconds: 2), (_) async
setState(()
if (_currentScrollIndex == widget.newsList.length - 1)
_timer.cancel();
_selectedNews = null;
else
_selectedNews = widget.newsList[++_currentScrollIndex];
_animateToIndex(_currentScrollIndex);
);
);
我们不需要定义为_autoScrollIncrement
的滚动方向。但是,我会引入一个新的_selectedNews
,以便在我们到达列表底部时轻松取消选择最后一个新闻项目。 ListTile
的 selected
标志将变为:
@override
Widget build(BuildContext context)
return ListView(
[...]
children: widget.newsList
.map((news) => ListTile(
[...]
selected: _selectedNews?.id == news.id,
[...]
))
.toList(),
);
【讨论】:
一切都变成蓝色文本,而且它不滚动@Thierry 很奇怪。您是否安装了依赖项并运行 build_runner 以使用flutter pub run build_runner watch --delete-conflicting-outputs
生成 News
类?
你知道如何停止列表视图底部的自动滚动吗? @蒂埃里
好的,请查看此答案的最新更新。以上是关于如何在 Flutter 中自动滚动 Listview.separated 中的所有 List Tiles?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Flutter 中自动滚动 Listview.separated 中的所有 List Tiles?
如何在 Flutter 中自动滚动到 SingleChildScrollView 内的行的位置