如何创建一个列表视图,使所需元素居中

问题描述 投票:4回答:3

我正在做类似这个视频的事情:https://youtu.be/fpqHUp4Sag0

使用以下代码我生成listview,但是当以这种方式使用控制器时,元素位于listview的顶部,我需要它居中

Widget _buildLyric() {

  return ListView.builder(
    itemBuilder: (BuildContext context, int index) => _buildPhrase(lyric[index]),
    itemCount: lyric.length,
    itemExtent: 90.0,
    controller: _scrollController,
  );
}


void goToNext() {
  i += 1;
  if (i == lyric.length - 1) {
    setState(() {
      finishedSync = true;
    });
  }
  syncLyric.addPhrase(
      lyric[i], playerController.value.position.inMilliseconds);

  _scrollController.animateTo(i*90.0,
      curve: Curves.ease, duration: new Duration(milliseconds: 300));
}
flutter flutter-layout flutter-animation
3个回答
0
投票

你将不得不做一些数学! (Nooooo,不是数学家)。

看起来你的goToNext()函数在应用程序运行时被调用,而不是在构建时。这使它更容易 - 你可以简单地使用context.size。否则你必须使用LayoutBuilder和maxHeight。

然后你可以把它分成两半来得到一半,然后加/减你需要的任何东西来让你的项目定位你想要的(因为你在示例中指定它的高度为90,我假设你可以使用45得到什么你要)。

这是一个可以粘贴到要运行的文件中的示例:

import 'dart:async';

import 'package:flutter/material.dart';

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

class Wid extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text("Scrolling by time"),
        ),
        body: new Column(
          children: <Widget>[
            Expanded(child: Container()),
            Container(
              height: 300.0,
              color: Colors.orange,
              child: ScrollsByTime(
                itemExtent: 90.0,
              ),
            ),
            Expanded(child: Container()),
          ],
        ),
      ),
    );
  }
}

class ScrollsByTime extends StatefulWidget {
  final double itemExtent;

  const ScrollsByTime({Key key, @required this.itemExtent}) : super(key: key);

  @override
  ScrollsByTimeState createState() {
    return new ScrollsByTimeState();
  }
}

class ScrollsByTimeState extends State<ScrollsByTime> {
  final ScrollController _scrollController = new ScrollController();

  @override
  void initState() {
    super.initState();
    Timer.periodic(Duration(seconds: 1), (timer) {
      _scrollController.animateTo(
        (widget.itemExtent * timer.tick) - context.size.height / 2.0 + widget.itemExtent / 2.0,
        duration: Duration(milliseconds: 300),
        curve: Curves.ease,
      );
    });
  }

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemBuilder: (context, index) {
        return Center(child: Text("Item $index"));
      },
      itemExtent: widget.itemExtent,
      controller: _scrollController,
    );
  }
}

0
投票

使用centershrinkWrap: true

    Center(
        child: new ListView.builder(
                          shrinkWrap: true,
                          itemCount: list.length,
                          itemBuilder: (BuildContext context, int index) {
                            return Text("Centered item");
                          },
                        ),
  );

0
投票

我有类似的问题,但使用水平列表视图。您应该使用ScrollController和NotificationListener。当您收到endScroll事件时,您应该计算偏移量并使用滚动控制器animateTo方法来居中您的项目。

class SwipeCalendarState extends State<SwipeCalendar> {
  List<DateTime> dates = List();
  ScrollController _controller;
  final itemWidth = 100.0;

  @override
  void initState() {
    _controller = ScrollController();
    _controller.addListener(_scrollListener);
    for (var i = 1; i < 365; i++) {
      var date = DateTime.now().add(Duration(days: i));
      dates.add(date);
    }

    // TODO: implement initState
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Container(
      height: 200,
      child: Stack(
        children: <Widget>[buildListView()],
      ),
    );
  }

  void _onStartScroll(ScrollMetrics metrics) {

  }

  void _onUpdateScroll(ScrollMetrics metrics){

  }

  void _onEndScroll(ScrollMetrics metrics){
    print("scroll before = ${metrics.extentBefore}");
    print("scroll after = ${metrics.extentAfter}");
    print("scroll inside = ${metrics.extentInside}");
    var halfOfTheWidth = itemWidth/2;
    var offsetOfItem = metrics.extentBefore%itemWidth;
    if (offsetOfItem < halfOfTheWidth) {
      final offset = metrics.extentBefore - offsetOfItem;
      print("offsetOfItem = ${offsetOfItem} offset = ${offset}");
      Future.delayed(Duration(milliseconds: 50), (){
        _controller.animateTo(offset, duration: Duration(milliseconds: 100), curve: Curves.linear);
      });
    } else if (offsetOfItem > halfOfTheWidth){
      final offset = metrics.extentBefore + offsetOfItem;
      print("offsetOfItem = ${offsetOfItem} offset = ${offset}");
      Future.delayed(Duration(milliseconds: 50), (){
        _controller.animateTo(offset, duration: Duration(milliseconds: 100), curve: Curves.linear);
      });
    }
  }

  Widget buildListView() {
    return NotificationListener<ScrollNotification>(
      onNotification: (scrollNotification) {
        if (scrollNotification is ScrollStartNotification) {
          _onStartScroll(scrollNotification.metrics);
        } else if (scrollNotification is ScrollUpdateNotification) {
          _onUpdateScroll(scrollNotification.metrics);
        } else if (scrollNotification is ScrollEndNotification) {
          _onEndScroll(scrollNotification.metrics);
        }
      },
      child: ListView.builder(
          itemCount: dates.length,
          controller: _controller,
          scrollDirection: Axis.horizontal,
          itemBuilder: (context, i) {
            var item = dates[i];
            return Container(
              height: 100,
              width: itemWidth,
              child: Center(
                child: Text("${item.day}.${item.month}.${item.year}"),
              ),
            );
          }),
    );
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.