skip to Main Content

In my app I want to display a comment section below an expandable widget. The comment section should be expandable to the top and to the bottom, without changing the position of the scrollview.

So far, this is the code I have come up with (simplified example):

import 'package:flutter/material.dart';

class TestPage extends StatefulWidget {
  const TestPage({super.key});

  @override
  State<TestPage> createState() => _TestPageState();
}

class _TestPageState extends State<TestPage> {
  List<Widget> newList = List.generate(
    20,
    (index) => Text('Upper ${index.toString()}'),
  );
  List<Widget> myList = List.generate(
    20,
    (index) => Text('Lower ${index.toString()}'),
  );

  bool showMore = false;

  final Key centerKey = const ValueKey('second-sliver-list');

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: Column(
        mainAxisAlignment: MainAxisAlignment.end,
        children: [
          FloatingActionButton.extended(
            onPressed: () {
              setState(() {
                newList.add(Text('Upper ${newList.length}'));
              });
            },
            label: const Text('Add to Upper'),
          ),
          const SizedBox(height: 10),
          FloatingActionButton.extended(
            onPressed: () {
              setState(() {
                myList.add(Text('Lower ${newList.length}'));
              });
            },
            label: const Text('Add to Lower'),
          ),
        ],
      ),
      appBar: AppBar(),
      body: CustomScrollView(
        center: centerKey,
        slivers: [
          SliverToBoxAdapter(
            child: Column(
              children: [
                SizedBox(
                  height: showMore ? null : 200,
                  child: const Text(
                    'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis ac risus quis nisl blandit tristique at nec velit. In placerat ultrices ante a finibus. Nullam feugiat sapien eget neque vulputate vulputate. Quisque et lobortis odio, non condimentum sapien. Nam tristique nisi faucibus metus semper dictum. Nunc tristique, lorem vulputate interdum tempus, eros magna fringilla diam, vitae euismod augue ipsum laoreet tellus. Maecenas faucibus ante sagittis arcu imperdiet ornare. Vestibulum a malesuada dui. Integer interdum, leo ut tincidunt accumsan, justo dolor venenatis erat, quis pellentesque felis odio at orci. Sed eget neque mi. Etiam cursus nisl eget dolor porttitor, non aliquet sapien sollicitudin. Suspendisse sodales tellus purus, quis ultricies eros congue in. Nam eget odio at orci rutrum porta quis nec turpis. Cras non mattis metus. Duis eu auctor metus. Curabitur ac vestibulum urna. Nunc turpis augue, condimentum id fringilla at, tincidunt et libero. Curabitur facilisis, justo eu lobortis lobortis, magna diam fermentum quam, et fermentum ante leo eu tellus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed in gravida ex. Aenean non lacinia lacus, at viverra nulla. Nullam sit amet velit varius, efficitur quam vitae, ornare justo. Pellentesque imperdiet nisi quis fringilla varius. Vivamus ut neque vitae arcu commodo faucibus. Donec tincidunt quam vitae eleifend tempor. Phasellus convallis, sapien vel dapibus suscipit, libero risus hendrerit lacus, ut tincidunt nunc justo at quam. Duis sagittis viverra augue vitae eleifend. Suspendisse pellentesque metus lectus, quis iaculis felis sollicitudin quis. Ut tincidunt congue euismod. Mauris fermentum finibus rutrum. Morbi eget mauris in nulla volutpat volutpat in dictum odio. Nulla pretium nisl augue, eget efficitur odio ultrices id. Donec varius sed metus a efficitur. Etiam lacinia magna non felis auctor, eu lobortis sem sagittis. Nunc molestie consectetur consequat. Cras nec sem a turpis pulvinar iaculis eget at nisl. Praesent tincidunt interdum metus ac feugiat. Vestibulum dui neque, rutrum eu aliquam ultricies, dignissim ac purus. Phasellus eu posuere ligula, in tempus arcu. Etiam eu lorem non ipsum tempor tempor a quis ligula. Quisque porta finibus neque. Cras placerat a lacus sed laoreet. Quisque eget rhoncus lacus, sed bibendum ex. Praesent egestas lacus vitae felis tempor, eget placerat enim lobortis. Mauris tempus bibendum mauris. Fusce tempor, enim sit amet blandit convallis, lectus dui maximus nibh, id sollicitudin lectus urna non metus. Donec sed posuere augue. Morbi sodales pulvinar dolor a aliquam. Etiam gravida magna dui, ac porta tellus tincidunt vitae. Nulla facilisi. Donec eget ex vel tortor fermentum congue. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer eu ligula et felis luctus rutrum id quis tellus. Pellentesque ut diam non orci eleifend consectetur. Duis lacinia mollis lorem at maximus. Nulla facilisi. Nam sollicitudin ut sapien non molestie. Morbi sit amet sagittis mauris. Proin vel tincidunt elit. Morbi ut est et purus vehicula posuere. Nulla volutpat tincidunt turpis at iaculis. Sed bibendum magna lectus, in semper sapien congue auctor. Sed auctor mi eu dui ornare placerat. Mauris ullamcorper egestas leo, vel dignissim elit iaculis et. Pellentesque ultrices eget felis vel tristique. Cras aliquam lacinia tortor quis ullamcorper. Fusce dapibus tincidunt dui quis lobortis. Ut eget nunc elit. Proin elementum sagittis congue. Suspendisse euismod risus diam, eu dictum erat egestas sed. Aenean sodales nec leo ut pellentesque. Donec rhoncus ante id turpis pretium vulputate. Sed et blandit nisl, ut condimentum purus. Nunc placerat, diam vitae elementum posuere, nibh quam aliquam nibh, vel commodo enim tellus vel felis. Pellentesque ut lorem sit amet neque imperdiet fringilla. Quisque malesuada sem ut odio eleifend hendrerit. Quisque convallis semper justo a tincidunt.',
                  ),
                ),
                TextButton(
                  onPressed: () {
                    setState(() {
                      showMore = !showMore;
                    });
                  },
                  child: showMore
                      ? const Text('Show less')
                      : const Text('Show more'),
                ),
              ],
            ),
          ),
          SliverList(
            delegate: SliverChildBuilderDelegate(
              (context, index) {
                return ListTile(
                  title: newList[index],
                );
              },
              childCount: newList.length,
            ),
          ),
          SliverList(
            key: centerKey,
            delegate: SliverChildBuilderDelegate(
              (context, index) {
                return ListTile(title: myList[index]);
              },
              childCount: myList.length,
            ),
          ),
        ],
      ),
    );
  }
}

The comment section works like I want it to work, but when expanding the upper text widget, the widget is expanded to the top, rather than to the bottom.

How could I adjust my code to change this behaviour?

2

Answers


  1. I am not totally sure about your requirement but here I have made 1 tweak to your code. Is it what you are looking for?

    import 'package:flutter/material.dart';
    
    class TestPage extends StatefulWidget {
      const TestPage({super.key});
    
      @override
      State<TestPage> createState() => _TestPageState();
    }
    
    class _TestPageState extends State<TestPage> {
      List<Widget> newList = List.generate(
        20,
        (index) => Text('Upper ${index.toString()}'),
      );
      List<Widget> myList = List.generate(
        20,
        (index) => Text('Lower ${index.toString()}'),
      );
    
      bool showMore = false;
      ScrollController _scrollController = ScrollController();
      final Key centerKey = const ValueKey('second-sliver-list');
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          floatingActionButton: Column(
            mainAxisAlignment: MainAxisAlignment.end,
            children: [
              FloatingActionButton.extended(
                onPressed: () {
                  setState(() {
                    newList.add(Text('Upper ${newList.length}'));
                  });
                },
                label: const Text('Add to Upper'),
              ),
              const SizedBox(height: 10),
              FloatingActionButton.extended(
                onPressed: () {
                  setState(() {
                    myList.add(Text('Lower ${newList.length}'));
                  });
                },
                label: const Text('Add to Lower'),
              ),
            ],
          ),
          appBar: AppBar(),
          body: CustomScrollView(
            controller: _scrollController,
            slivers: [
              SliverToBoxAdapter(
                child: Column(
                  children: [
                    SizedBox(
                      height: showMore ? null : 200,
                      child: const Text(
                        'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis ac risus quis nisl blandit tristique at nec velit. In placerat ultrices ante a finibus. Nullam feugiat sapien eget neque vulputate vulputate. Quisque et lobortis odio, non condimentum sapien. Nam tristique nisi faucibus metus semper dictum. Nunc tristique, lorem vulputate interdum tempus, eros magna fringilla diam, vitae euismod augue ipsum laoreet tellus. Maecenas faucibus ante sagittis arcu imperdiet ornare. Vestibulum a malesuada dui. Integer interdum, leo ut tincidunt accumsan, justo dolor venenatis erat, quis pellentesque felis odio at orci. Sed eget neque mi. Etiam cursus nisl eget dolor porttitor, non aliquet sapien sollicitudin. Suspendisse sodales tellus purus, quis ultricies eros congue in. Nam eget odio at orci rutrum porta quis nec turpis. Cras non mattis metus. Duis eu auctor metus. Curabitur ac vestibulum urna. Nunc turpis augue, condimentum id fringilla at, tincidunt et libero. Curabitur facilisis, justo eu lobortis lobortis, magna diam fermentum quam, et fermentum ante leo eu tellus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed in gravida ex. Aenean non lacinia lacus, at viverra nulla. Nullam sit amet velit varius, efficitur quam vitae, ornare justo. Pellentesque imperdiet nisi quis fringilla varius. Vivamus ut neque vitae arcu commodo faucibus. Donec tincidunt quam vitae eleifend tempor. Phasellus convallis, sapien vel dapibus suscipit, libero risus hendrerit lacus, ut tincidunt nunc justo at quam. Duis sagittis viverra augue vitae eleifend. Suspendisse pellentesque metus lectus, quis iaculis felis sollicitudin quis. Ut tincidunt congue euismod. Mauris fermentum finibus rutrum. Morbi eget mauris in nulla volutpat volutpat in dictum odio. Nulla pretium nisl augue, eget efficitur odio ultrices id. Donec varius sed metus a efficitur. Etiam lacinia magna non felis auctor, eu lobortis sem sagittis. Nunc molestie consectetur consequat. Cras nec sem a turpis pulvinar iaculis eget at nisl. Praesent tincidunt interdum metus ac feugiat. Vestibulum dui neque, rutrum eu aliquam ultricies, dignissim ac purus. Phasellus eu posuere ligula, in tempus arcu. Etiam eu lorem non ipsum tempor tempor a quis ligula. Quisque porta finibus neque. Cras placerat a lacus sed laoreet. Quisque eget rhoncus lacus, sed bibendum ex. Praesent egestas lacus vitae felis tempor, eget placerat enim lobortis. Mauris tempus bibendum mauris. Fusce tempor, enim sit amet blandit convallis, lectus dui maximus nibh, id sollicitudin lectus urna non metus. Donec sed posuere augue. Morbi sodales pulvinar dolor a aliquam. Etiam gravida magna dui, ac porta tellus tincidunt vitae. Nulla facilisi. Donec eget ex vel tortor fermentum congue. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer eu ligula et felis luctus rutrum id quis tellus. Pellentesque ut diam non orci eleifend consectetur. Duis lacinia mollis lorem at maximus. Nulla facilisi. Nam sollicitudin ut sapien non molestie. Morbi sit amet sagittis mauris. Proin vel tincidunt elit. Morbi ut est et purus vehicula posuere. Nulla volutpat tincidunt turpis at iaculis. Sed bibendum magna lectus, in semper sapien congue auctor. Sed auctor mi eu dui ornare placerat. Mauris ullamcorper egestas leo, vel dignissim elit iaculis et. Pellentesque ultrices eget felis vel tristique. Cras aliquam lacinia tortor quis ullamcorper. Fusce dapibus tincidunt dui quis lobortis. Ut eget nunc elit. Proin elementum sagittis congue. Suspendisse euismod risus diam, eu dictum erat egestas sed. Aenean sodales nec leo ut pellentesque. Donec rhoncus ante id turpis pretium vulputate. Sed et blandit nisl, ut condimentum purus. Nunc placerat, diam vitae elementum posuere, nibh quam aliquam nibh, vel commodo enim tellus vel felis. Pellentesque ut lorem sit amet neque imperdiet fringilla. Quisque malesuada sem ut odio eleifend hendrerit. Quisque convallis semper justo a tincidunt.',
                      ),
                    ),
                    TextButton(
                      onPressed: () {
                        setState(() {
                          showMore = !showMore;
                          _scrollController.animateTo(
                            0.0,
                            duration:const Duration(milliseconds: 600),
                            curve: Curves.easeInOut,
                          );
                        });
                      },
                      child: showMore
                          ? const Text('Show less')
                          : const Text('Show more'),
                    ),
                  ],
                ),
              ),
              SliverList(
                delegate: SliverChildBuilderDelegate(
                  (context, index) {
                    return ListTile(
                      title: newList[index],
                    );
                  },
                  childCount: newList.length,
                ),
              ),
              SliverList(
                key: centerKey,
                delegate: SliverChildBuilderDelegate(
                  (context, index) {
                    return ListTile(title: myList[index]);
                  },
                  childCount: myList.length,
                ),
              ),
            ],
          ),
        );
      }
    }
    
    Login or Signup to reply.
  2. I think you want the scroll position to be initially in the second SliverList. That’s why you’ve used the ‘center’ attribute inside the CustomScrollView. However, you’ve experienced side effects of using the ‘center’ attribute for this scenario.

    Docs says about CustomScrollView’ center attribute; "Children after center will be placed in the AxisDirection determined by scrollDirection and reverse relative to the center. Children before the center will be placed in the opposite of the axis direction relative to the center. This makes the center the inflection point of the growth direction."

    I believe you can achieve your requests as follows;

    1. Let’s remove the ‘center’ attribute from CustomScrollView.
    2. Use a method that will provide us with the initial scroll position. Here it is: WidgetsBinding.instance.addPostFrameCallback((_) => Scrollable.ensureVisible(dataKey.currentContext!));
    3. Lastly, we should reverse your first sliverlist as your desired scenario.

    Here, full codes that you need;

    import 'package:flutter/material.dart';
    
    class TestPage extends StatefulWidget {
      const TestPage({super.key});
    
      @override
      State<TestPage> createState() => _TestPageState();
    }
    
    class _TestPageState extends State<TestPage> {
      ScrollController scrollController = ScrollController();
      List<Widget> newList = List.generate(
        20,
        (index) => Text('Upper ${index.toString()}'),
      );
      List<Widget> myList = List.generate(
        20,
        (index) => Text('Lower ${index.toString()}'),
      );
    
      bool showMore = false;
    
      final Key centerKey = const ValueKey('second-sliver-list');
    
      final dataKey = new GlobalKey(); // we created GlobalKey rather than Key
    
      @override
      void initState() {
        WidgetsBinding.instance.addPostFrameCallback(
          (_) => Scrollable.ensureVisible(dataKey.currentContext!),
        ); // this will scroll to desired position after build completes
        super.initState();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          floatingActionButton: Column(
            mainAxisAlignment: MainAxisAlignment.end,
            children: [
              FloatingActionButton.extended(
                onPressed: () {
                  setState(() {
                    newList.add(Text('Upper ${newList.length}'));
                  });
                },
                label: const Text('Add to Upper'),
              ),
              const SizedBox(height: 10),
              FloatingActionButton.extended(
                onPressed: () {
                  setState(() {
                    myList.add(Text('Lower ${newList.length}'));
                  });
                },
                label: const Text('Add to Lower'),
              ),
            ],
          ),
          appBar: AppBar(),
          body: CustomScrollView(
            // removed center
            slivers: [
              SliverToBoxAdapter(
                child: Column(
                  children: [
                    SizedBox(
                      height: showMore ? null : 200,
                      child: const Text(
                        'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis ac risus quis nisl blandit tristique at nec velit. In placerat ultrices ante a finibus. Nullam feugiat sapien eget neque vulputate vulputate. Quisque et lobortis odio, non condimentum sapien. Nam tristique nisi faucibus metus semper dictum. Nunc tristique, lorem vulputate interdum tempus, eros magna fringilla diam, vitae euismod augue ipsum laoreet tellus. Maecenas faucibus ante sagittis arcu imperdiet ornare. Vestibulum a malesuada dui. Integer interdum, leo ut tincidunt accumsan, justo dolor venenatis erat, quis pellentesque felis odio at orci. Sed eget neque mi. Etiam cursus nisl eget dolor porttitor, non aliquet sapien sollicitudin. Suspendisse sodales tellus purus, quis ultricies eros congue in. Nam eget odio at orci rutrum porta quis nec turpis. Cras non mattis metus. Duis eu auctor metus. Curabitur ac vestibulum urna. Nunc turpis augue, condimentum id fringilla at, tincidunt et libero. Curabitur facilisis, justo eu lobortis lobortis, magna diam fermentum quam, et fermentum ante leo eu tellus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed in gravida ex. Aenean non lacinia lacus, at viverra nulla. Nullam sit amet velit varius, efficitur quam vitae, ornare justo. Pellentesque imperdiet nisi quis fringilla varius. Vivamus ut neque vitae arcu commodo faucibus. Donec tincidunt quam vitae eleifend tempor. Phasellus convallis, sapien vel dapibus suscipit, libero risus hendrerit lacus, ut tincidunt nunc justo at quam. Duis sagittis viverra augue vitae eleifend. Suspendisse pellentesque metus lectus, quis iaculis felis sollicitudin quis. Ut tincidunt congue euismod. Mauris fermentum finibus rutrum. Morbi eget mauris in nulla volutpat volutpat in dictum odio. Nulla pretium nisl augue, eget efficitur odio ultrices id. Donec varius sed metus a efficitur. Etiam lacinia magna non felis auctor, eu lobortis sem sagittis. Nunc molestie consectetur consequat. Cras nec sem a turpis pulvinar iaculis eget at nisl. Praesent tincidunt interdum metus ac feugiat. Vestibulum dui neque, rutrum eu aliquam ultricies, dignissim ac purus. Phasellus eu posuere ligula, in tempus arcu. Etiam eu lorem non ipsum tempor tempor a quis ligula. Quisque porta finibus neque. Cras placerat a lacus sed laoreet. Quisque eget rhoncus lacus, sed bibendum ex. Praesent egestas lacus vitae felis tempor, eget placerat enim lobortis. Mauris tempus bibendum mauris. Fusce tempor, enim sit amet blandit convallis, lectus dui maximus nibh, id sollicitudin lectus urna non metus. Donec sed posuere augue. Morbi sodales pulvinar dolor a aliquam. Etiam gravida magna dui, ac porta tellus tincidunt vitae. Nulla facilisi. Donec eget ex vel tortor fermentum congue. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer eu ligula et felis luctus rutrum id quis tellus. Pellentesque ut diam non orci eleifend consectetur. Duis lacinia mollis lorem at maximus. Nulla facilisi. Nam sollicitudin ut sapien non molestie. Morbi sit amet sagittis mauris. Proin vel tincidunt elit. Morbi ut est et purus vehicula posuere. Nulla volutpat tincidunt turpis at iaculis. Sed bibendum magna lectus, in semper sapien congue auctor. Sed auctor mi eu dui ornare placerat. Mauris ullamcorper egestas leo, vel dignissim elit iaculis et. Pellentesque ultrices eget felis vel tristique. Cras aliquam lacinia tortor quis ullamcorper. Fusce dapibus tincidunt dui quis lobortis. Ut eget nunc elit. Proin elementum sagittis congue. Suspendisse euismod risus diam, eu dictum erat egestas sed. Aenean sodales nec leo ut pellentesque. Donec rhoncus ante id turpis pretium vulputate. Sed et blandit nisl, ut condimentum purus. Nunc placerat, diam vitae elementum posuere, nibh quam aliquam nibh, vel commodo enim tellus vel felis. Pellentesque ut lorem sit amet neque imperdiet fringilla. Quisque malesuada sem ut odio eleifend hendrerit. Quisque convallis semper justo a tincidunt.',
                      ),
                    ),
                    TextButton(
                      onPressed: () {
                        setState(() {
                          showMore = !showMore;
                        });
                      },
                      child: showMore
                          ? const Text('Show less')
                          : const Text('Show more'),
                    ),
                  ],
                ),
              ),
              SliverList(
                delegate: SliverChildBuilderDelegate(
                  (context, index) {
                    // reversed list can be achieved alike
                    final reversedIndex = newList.length - index - 1;
                    return ListTile(
                      title: newList[reversedIndex],
                    );
                  },
                  childCount: newList.length,
                ),
              ),
              SliverList(
                key: dataKey, // Global key
                delegate: SliverChildBuilderDelegate(
                  (context, index) {
                    return ListTile(title: myList[index]);
                  },
                  childCount: myList.length,
                ),
              ),
            ],
          ),
        );
      }
    }
    

    After all, your expandable algorithm will expand to downward. Good luck 👋

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search