Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a listview that makes centering the desired element

I'm doing something similar to this video: https://youtu.be/fpqHUp4Sag0

With the following code I generate the listview but when using the controller in this way the element is located at the top of the listview and I need it to be centered

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));
}
like image 676
DAVID TEC Avatar asked Aug 02 '18 17:08

DAVID TEC


People also ask

How do I center a widget in ListView?

The solution is to place a Container as the only children in the ListView , and give it a minimum height equal to the available space for the height (Use LayoutBuilder to measure the maximum height for the widget). Then as the Container 's child, you can either place a Center or Column (with MainAxisAlignment.

How do I center my child in Flutter?

In Flutter, to vertically center a child widget inside a Container, you can wrap the child widget with Column and add mainAxisAlignment: MainAxisAlignment. center to it.

How do you center in Flutter?

Flutter – Center Align Text in Text Widget To center align the text in a Text widget, provide textAlign property with value TextAlign. center .

What is the difference between ListView and ListView builder?

builder takes properties similar to ListView but has two special parameters known as itemCount and itemBuilder .


Video Answer


3 Answers

Using center and shrinkWrap: true

    Center(
        child: new ListView.builder(
                          shrinkWrap: true,
                          itemCount: list.length,
                          itemBuilder: (BuildContext context, int index) {
                            return Text("Centered item");
                          },
                        ),
  );
like image 78
Pablo Cegarra Avatar answered Oct 31 '22 00:10

Pablo Cegarra


You're going to have to do some math! (Nooooo, not the mathssssss).

It seems as though your goToNext() function is called while the app is running, rather than during build time. This makes it a little easier - you can simply use context.size. Otherwise you'd have to use a LayoutBuilder and maxHeight.

You can then divide this in two to get the half, then add/subtract whatever you need to get your item positioned how you want (since you've specified it's height as 90 in the example, I assume you could use 45 to get what you want).

Here's an example you can paste into a file to run:

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,
    );
  }
}
like image 23
rmtmckenzie Avatar answered Oct 30 '22 22:10

rmtmckenzie


I had a similar problem, but with the horizontal listview. You should use ScrollController and NotificationListener. When you receive endScroll event you should calculate offset and use scroll controller animateTo method to center your items.

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}"),
              ),
            );
          }),
    );
  }
}
like image 39
WorieN Avatar answered Oct 30 '22 23:10

WorieN