0

When nested CustomScrollView's shrink wrap set true, i can't collapse child slivers.

How to scroll both customScrollViews and make child customScrollView collapsable into minimum height.

Here is live demo

My final goal is to make stack card list view with scrolling animation

Any other solutions ? or got a way to solve this issue ?

Give me some advise masters :)

class AnimationTest extends StatefulWidget {
  @override
  _AnimationTestState createState() => _AnimationTestState();
}

class _AnimationTestState extends State<AnimationTest>
    with WidgetsBindingObserver {
  @override
  void initState() {
    WidgetsBinding.instance.addObserver(this);
    super.initState();
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    print(state.toString());
  }

  @override
  Widget build(BuildContext context) {
    const double goldenRatio = 1.618;
    final Size displaySize = MediaQuery.of(context).size;
    final double minCardHeight = 24;
    final double maxCardHeight = 80;
    final List<Color> colors = Colors.primaries;
    List<Color> _colors = List();
    _colors.addAll(colors);
    _colors.addAll(colors);
    print(_colors.length);

    return Scaffold(
      body: Container(
        color: Colors.white,
        padding: EdgeInsets.only(top: 40),
        alignment: AlignmentDirectional.center,
        child: CustomScrollView(
          slivers: <Widget>[
            SliverList(
              delegate: SliverChildListDelegate([
                Column(
                  crossAxisAlignment: CrossAxisAlignment.center,
                  children: <Widget>[
                    CreditCard(),
                    Container(
                      width: displaySize.width * 0.85,
                      height: minCardHeight * _colors.length,
                      alignment: AlignmentDirectional.bottomCenter,
                      margin: EdgeInsets.all(0),
                      padding: EdgeInsets.all(0),
                      decoration: BoxDecoration(
                        borderRadius: BorderRadius.circular(8),
                        color: Colors.white,
                      ),
                      child: StackedList(
                        minCardHeight: minCardHeight,
                        maxCardHeight: maxCardHeight,
                        colors: _colors,
                      ),
                    )
                  ],
                ),
              ]),
            )
          ],
        ),
      ),
    );
  }
}

class StackedList extends StatefulWidget {
  final double minCardHeight;
  final double maxCardHeight;
  final List<Color> colors;
  StackedList({
    Key key,
    this.colors,
    this.maxCardHeight,
    this.minCardHeight,
  }) : super(key: key);
  @override
  _StackedListState createState() => _StackedListState();
}

class _StackedListState extends State<StackedList> {
  @override
  Widget build(BuildContext context) {
    return CustomScrollView(
      physics: AlwaysScrollableScrollPhysics(),
      shrinkWrap: true,
      slivers: <Widget>[
        ...widget.colors
            .map(
              (color) => StackedListChild(
                minHeight: widget.minCardHeight,
                maxHeight: widget.maxCardHeight,
                floating: false,
                pinned: true,
                child: getCard(
                  cardId: widget.colors.indexOf(color).toString(),
                  color: color,
                ),
              ),
            )
            .toList(),
        SliverToBoxAdapter(
            child: Container(
          height: MediaQuery.of(context).size.height,
          alignment: Alignment.center,
        ))
      ],
    );
  }

  Widget getCard({String cardId, Color color}) {
    const double cardWidth = 300;
    const double cardHeight = 370;
    return Hero(
      tag: cardId,
      child: Material(
        elevation: 3,
        color: Colors.white,
        child: InkWell(
          onTap: () => navigateToCardDetail(cardId: cardId, color: color),
          child: Container(
            width: cardWidth,
            height: cardHeight,
            color: color,
            // decoration: BoxDecoration(
            //   borderRadius: BorderRadius.only(
            //     topLeft: Radius.circular(8),
            //     topRight: Radius.circular(8),
            //   ),
            //   color: color,
            // ),
          ),
        ),
      ),
    );
  }

  navigateToCardDetail({String cardId, Color color}) {
    var route = PageRouteBuilder(
      pageBuilder: (context, animation, secondAnimation) =>
          SecondPage(cardId: cardId, color: color),
      barrierDismissible: true,
      transitionDuration: const Duration(milliseconds: 300),
      transitionsBuilder: (context, animation, secondaryAnimation, child) {
        // var begin = Offset(0.0, 1.0);
        // var end = Offset.zero;
        // var curve = Curves.fastOutSlowIn;

        // var tween =
        //     Tween(begin: begin, end: end).chain(CurveTween(curve: curve));

        return FadeTransition(
          //opacity: animation,
          //position: animation.drive(tween),
          opacity: animation,
          child: child,
        );
      },
    );
    Navigator.of(context).push(route);
  }
}

class StackedListChild extends StatelessWidget {
  final double minHeight;
  final double maxHeight;
  final bool pinned;
  final bool floating;
  final Widget child;

  SliverPersistentHeaderDelegate get _delegate => _StackedListDelegate(
      minHeight: minHeight, maxHeight: maxHeight, child: child);

  const StackedListChild({
    Key key,
    @required this.minHeight,
    @required this.maxHeight,
    @required this.child,
    this.pinned = false,
    this.floating = false,
  })  : assert(child != null),
        assert(minHeight != null),
        assert(maxHeight != null),
        assert(pinned != null),
        assert(floating != null),
        super(key: key);

  @override
  Widget build(BuildContext context) => SliverPersistentHeader(
      key: key, pinned: pinned, floating: floating, delegate: _delegate);
}

class _StackedListDelegate extends SliverPersistentHeaderDelegate {
  final double minHeight;
  final double maxHeight;
  final Widget child;

  _StackedListDelegate({
    @required this.minHeight,
    @required this.maxHeight,
    @required this.child,
  });

  @override
  double get minExtent => minHeight;

  @override
  double get maxExtent => math.max(maxHeight, minHeight);

  @override
  Widget build(
      BuildContext context, double shrinkOffset, bool overlapsContent) {
    return new SizedBox.expand(child: child);
  }

  @override
  bool shouldRebuild(_StackedListDelegate oldDelegate) {
    return maxHeight != oldDelegate.maxHeight ||
        minHeight != oldDelegate.minHeight ||
        child != oldDelegate.child;
  }
}

1 Answers1

0

Putting CustomScrollView inside another CustomScrollView is an anti pattern.

You can use the NestedScrollView to wrap your CustomScrollView to scroll smoothly..

Darish
  • 11,032
  • 5
  • 50
  • 70