6

我正在做类似于此视频的操作:https ://youtu.be/fpqHUp4Sag0

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

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));
}
4

4 回答 4

4

Using center and shrinkWrap: true

    Center(
        child: new ListView.builder(
                          shrinkWrap: true,
                          itemCount: list.length,
                          itemBuilder: (BuildContext context, int index) {
                            return Text("Centered item");
                          },
                        ),
  );
于 2019-03-18T15:54:27.217 回答
1

我有一个类似的问题,但使用水平列表视图。您应该使用 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}"),
              ),
            );
          }),
    );
  }
}
于 2019-04-13T06:43:52.210 回答
1

你将不得不做一些数学运算!(不,不是mathssssss)

似乎您的 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,
    );
  }
}
于 2018-08-02T20:59:00.093 回答
1

IMO 您发布的链接有一些类似动画的轮子。Flutter 通过ListWheelScrollView提供了这种类型的动画,其余的可以通过动画淡入和 ScrollController 改变字体粗细来完成。

于 2020-03-26T21:20:05.370 回答