Search code examples
flutterflutter-layoutflutter-sliverflutter-gridview

Scroll/JumpTo Correct Position in Flutter GridView


I am creating a calendar interface in GridView. I want to scroll to the first day of the current month. However, whenever I try to scroll to the position I calculate, it is wrong. I am currently working with a starting point of January 1, 2016 for the calendar. I am currently using a fixed height for each calendar row, but it still doesn't scroll to the right location.

Note: The images below were generated on October 11, 2019. I am attempting to automatically scroll to October 1, 2019.

In portrait it scrolls too far:

enter image description here

When I rotate to landscape, it doesn't scroll enough:

enter image description here

Creating the GridView:

  double calendarHeight = 100.0;
  ...
  @override
  Widget build(BuildContext context) {
    // Get the size of the screen
    var size = MediaQuery.of(context).size;
    var ratio = size.width / calendarHeight / 7; // Divide ratio by 7 because there are 7 columns

    Scaffold scaffold = Scaffold(
      ...
      body: new Stack(
        children: <Widget>[
          new PageView(
            children: <Widget>[
              // Calendar Page
              new Container(
                child: new Center(
                  child: GridView.builder(
                    gridDelegate: _calendarGridDelegate(ratio),
                    controller: scrollController,
                    itemCount: _calendarDates.length,
                    itemBuilder: (BuildContext context, int index) {
                      if (index < _calendarDates.length) {
                        return _calendarItemBuilder(index);
                      } else
                        return null;
                    },
                  ),
                ),
                color: Colors.blue,
              ),
              ...

    WidgetsBinding.instance.addPostFrameCallback((_) {
      double scrollTo = (dayInList ~/ 7) * calendarHeight;
      ...
      scrollController.jumpTo(scrollTo);
    });

    return scaffold;

Creating the Grid delegate:

/// Defines the [SliverGridDelegateWithFixedCrossAxisCount] grid delegate used
/// by the calendar page
SliverGridDelegateWithFixedCrossAxisCount _calendarGridDelegate(double ratio) {
  return new SliverGridDelegateWithFixedCrossAxisCount(
          childAspectRatio: ratio,
          crossAxisCount: 7,
          mainAxisSpacing: _MAIN_AXIS_SPACING, // _MAIN_AXIS_SPACING = 1.0
          crossAxisSpacing: _CROSS_AXIS_SPACING); // _CROSS_AXIS_SPACING = 1.0
}

Create content for one day in the calendar:

/// Builds a [Widget] for one calendar day in the calendar page
Widget _calendarItemBuilder(int index) {
  String output;
  if (_calendarDates[index].day == 1) {
    output = Constants().monthNames[_calendarDates[index].month - 1] +
        " " +
        _calendarDates[index].day.toString();
  } else {
    output = _calendarDates[index].day.toString();
  }

  return new Container(
    padding: EdgeInsets.all(5.0),
    child: new Center(
      child: Text(output),
    ),
  );
}

Solution

  • I found an answer, but it is far from satisfying, because it doesn't make any sense:

    double scrollTo = (dayInList ~/ 7) * (calendarHeight - (calendarHeight - size.width/6)/(size.width/6));
    ...
    scrollController.jumpTo(scrollTo);
    

    It took a lot of trial and error, but it works on the two devices I was using for testing.