#flutter #listview #dart #widget
#flutter #просмотр списка #dart #виджет
Вопрос:
Автоматически прокручивать (без какого-либо взаимодействия с пользователем) все элементы списка в Listview, используя таймер в flutter. Приведенный ниже метод позволяет анимировать только один ListTile, но я хочу анимировать все ListTile сверху вниз один за другим и снова снизу вверх один за другим.
Ниже приведен список:
@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(),
),
);
Пакеты, используемые в растворе:
- замораживание для
News
класса домена - build_runner для генерации замороженного кода
- фейкер для генерации списка случайных новостей
ОБНОВЛЕНИЕ : Прокрутите только один раз
Чтобы остановить автоматическую прокрутку в нижней части списка, вам просто нужно изменить 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
, чтобы легко отменить выбор последней новости, когда мы окажемся в нижней части списка. selected
Флаг наш ListTile
тогда стал бы:
@override
Widget build(BuildContext context) {
return ListView(
[...]
children: widget.newsList
.map((news) => ListTile(
[...]
selected: _selectedNews?.id == news.id,
[...]
))
.toList(),
);
}
Комментарии:
1. Все превращается в синий текст и не прокручивается @Thierry
2. Странно. Вы установили зависимости и запустили build_runner для создания
News
классаflutter pub run build_runner watch --delete-conflicting-outputs
?3. Есть ли у вас какие-либо идеи, как остановить автоматическую прокрутку в нижней части listview? @Thierry
4. Конечно, проверьте последнее обновление этого ответа.