简体   繁体   English

如何在 Flutter 中自动滚动 Listview.seperated 中的所有 ListTiles?

[英]How to automatically scroll through all the ListTiles in the Listview.seperated in Flutter?

Scroll automatically (without any user interaction) through all the ListTiles in the Listview using a Timer in flutter.使用 flutter 中的计时器自动滚动(无需任何用户交互)通过 Listview 中的所有 ListTiles。 The below method makes only one ListTile to animate but I want to animate all the ListTiles from top to bottom one by one and again from bottom to top one by one.下面的方法只制作一个 ListTile 动画,但我想将所有 ListTiles 从上到下一个一个地动画,然后从下到上一个一个地动画。

The below is the Listview:下面是列表视图:

@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(),
              );
            }
          },
        ),
      ),
    );
  }
}

This is the automatic scrolling i have tried:这是我尝试过的自动滚动:

@override
  void initState() {
    super.initState();
    timer = Timer.periodic(Duration(seconds: 2), (Timer t) async {
        await controller.scrollToIndex(1,
            preferPosition: AutoScrollPosition.begin);
    });

Here is a solution assuming that all your items in the ListView have the same itemExtent .这是一个解决方案,假设ListView中的所有项目都具有相同的itemExtent

在此处输入图像描述

In this solution, I highlight the current Item as selected.在此解决方案中,我将当前项目突出显示为选中状态。 You could also want to stop autoscrolling as soon as you reach the bottom of the list.您还可能希望在到达列表底部后立即停止自动滚动。

Full source code完整的源代码

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(),
  ),
);

Packages used in the solution:解决方案中使用的软件包:


UPDATE: Scroll only once更新:只滚动一次

To stop the autoscrolling at the bottom of the listview, you just need to modify the initState method:要停止列表视图底部的自动滚动,您只需要修改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);
      }
    });
  });
}

We don't need the scroll direction defined as _autoScrollIncrement .我们不需要定义为_autoScrollIncrement的滚动方向。 However, I would introduce a new _selectedNews to easily unselect the last News item when we arrive at the bottom of the list.但是,当我们到达列表底部时,我会引入一个新的_selectedNews以轻松取消选择最后一个新闻项目。 The selected flag of our ListTile would then become:我们的ListTileselected标志将变为:

@override
Widget build(BuildContext context) {
  return ListView(
    [...]
    children: widget.newsList
      .map((news) => ListTile(
        [...]
        selected: _selectedNews?.id == news.id,
        [...]
      ))
      .toList(),
  );
}

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

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