Skip to content

Unexpected jump happened when crossAxisCount >= 2 in SliverMasonryGridΒ #356

@cc673459362

Description

@cc673459362

https://github.com/user-attachments/assets/789583f0-155b-4e93-8829-31e6c1556a0b
Hi, I have found some unexpected jump. You can reproduce by follow code.
Main reason:
The footer sliver is too high and if user scroll the view to bottom all the time some unexpected jump will happen!
Btw, it's only happened when crossAxisCount >= 2.

import 'package:flutter/material.dart';
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class LifecycleMonitor extends StatefulWidget {
  final Widget child;
  final String tag;
  final int index;

  const LifecycleMonitor({
    super.key,
    required this.child,
    this.tag = 'Container',
    this.index = 0,
  });

  @override
  State<LifecycleMonitor> createState() => _LifecycleMonitorState();
}

class _LifecycleMonitorState extends State<LifecycleMonitor> with WidgetsBindingObserver {
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
    print('πŸš€ ${widget.tag} - initState index:${widget.index}');
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    print('πŸ”„ ${widget.tag} - didChangeDependencies index:${widget.index}');
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    print('πŸ’€ ${widget.tag} - dispose index:${widget.index}');
    super.dispose();
  }

  @override
  void didUpdateWidget(LifecycleMonitor oldWidget) {
    super.didUpdateWidget(oldWidget);
    print('πŸ“¦ ${widget.tag} - didUpdateWidget index:${widget.index}');
  }

  @override
  Widget build(BuildContext context) {
    print('πŸ—οΈ ${widget.tag} - build index:${widget.index}');
    return widget.child;
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    print('🌐 ${widget.tag} - App Lifecycle: $state');
  }
}

class MyHomePage extends StatelessWidget {
  final GlobalKey scrollViewKey = GlobalKey();
  final ScrollController finalScrollController = ScrollController();
  final Axis scrollDirection = Axis.vertical;
  final double? cacheExtent = 1000.0;
  final int? crossAxisCount = 2;
  final double? mainAxisSpacing = 10.0;
  final double? crossAxisSpacing = 10.0;
  final int _finalItemCount = 20;

  ScrollPhysics getScrollPhysics() {
    return BouncingScrollPhysics();
  }

  Widget _buildChildWidget(BuildContext context, int index) {
    print('πŸ”¨ _buildChildWidget $index');
    return LifecycleMonitor(
      tag: 'MySpecialContainer',
      index: index,
      child: Container(
        color: Colors.blueAccent,
        height: 100,
        margin: EdgeInsets.all(5),
        child: Center(
          child: Text('Item $index', style: TextStyle(color: Colors.white)),
        ),
      ),
    );
  }

  void _finishLayoutCallBack(int index) {
    print('🏁 _finishLayoutCallBack $index');
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Custom Scroll View Example'),
      ),
      body: CustomScrollView(
        key: scrollViewKey,
        physics: getScrollPhysics(),
        controller: finalScrollController,
        scrollDirection: scrollDirection,
        cacheExtent: cacheExtent,
        slivers: [
          SliverList(
            delegate: CustomSliverChildBuilderDelegate(
              (context, index) => Center(
                child: Text(
                  'Header',
                  style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
                ),
              ),
              _finishLayoutCallBack,
              childCount: 1,
            ),
          ),
          SliverMasonryGrid(
            gridDelegate: SliverSimpleGridDelegateWithFixedCrossAxisCount(
              crossAxisCount: crossAxisCount ?? 1, // Number of columns
            ),
            delegate: CustomSliverChildBuilderDelegate(
              _buildChildWidget,
              _finishLayoutCallBack,
              childCount: _finalItemCount,
            ),
            mainAxisSpacing: mainAxisSpacing ?? 0,
            crossAxisSpacing: crossAxisSpacing ?? 0,
          ),
          SliverList(
            delegate: CustomSliverChildBuilderDelegate(
              (context, index) => _buildFooterItem(index),
              _finishLayoutCallBack,
              childCount: 3,
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildFooterItem(int index) {
    return Container(
      color: Colors.red,
      height: 500,
      margin: EdgeInsets.all(10),
      child: Center(
        child: Text(
          'Footer Item $index',
          style: TextStyle(color: Colors.white, fontSize: 24),
        ),
      ),
    );
  }
}

class CustomSliverChildBuilderDelegate extends SliverChildBuilderDelegate {
  CustomSliverChildBuilderDelegate(
    Widget Function(BuildContext, int) builder,
    this.finishLayoutCallBack, {
    int? childCount,
  }) : super(builder, childCount: childCount);

  final void Function(int) finishLayoutCallBack;

  @override
  void didFinishLayout(int firstIndex, int lastIndex) {
    super.didFinishLayout(firstIndex, lastIndex);
    finishLayoutCallBack(lastIndex);
  }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions