flutterscrollable

Flutter scrollable_positioned_list does not handle the index correctly


I am trying to create the vertical list of items (Containers) of different height which could be scrolled to the fixed position with ScrollablePositionedList.builder. To manage my list of items, I want to use buttons located in horizontal (also scrollable) menu, one button (for instance, "About object") for one specific widget (for instance, _aboutObject). Now my code looks like that:

...
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
...

class _SaleOfficeState extends State<SaleOffice> {
  final itemController = ItemScrollController(); // for body
  final List<String> menuItems = ["Location", "About object"];

  Future scrollToItem(int index) async {
    itemController.scrollTo(
      index: index,
      alignment: 0,
      duration: Duration(seconds: 1),
    );
  }

  Widget _location() {Container(...)}

  Widget _aboutObject() {Container(...)}

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        // AppBar definition
      ),
      body: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Container(
            // Top menu container
            height: 50,
            child: ListView.builder(
              scrollDirection: Axis.horizontal,
              itemCount: menuItems.length,
              itemBuilder: (BuildContext context, int index) {
                return Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: TextButton(
                    onPressed: () {
                      print("top menu button is pressed");
                      scrollToItem(index); 
                    },
                    child: Text(
                      menuItems[index],
                    ),
                  ),
                );
              },
            ),
          ),
          Expanded(
            child: ScrollablePositionedList.builder(
              itemCount: menuItem.length, 
              itemScrollController: itemController,
              itemBuilder: (BuildContext context, int index) {
                  if (index == 0) {
                    return _location();
                  } else if (index == 1) {
                    return _aboutObject();
                  }
                  return Container();
                },
            ),
          ),
        ],
      ),
    );
  }
} 

It scrolls correctly when I press the relevant button with needed duration, but immediately goes back to the starting position. I want it to be pinned on the upper position, until another button is presses.

Another strange behavior is: if I use non-existent index, for instance, 2:

   await Scrollable.ensureVisible(context);
   itemController.scrollTo(
       index: 2,
       alignment: 0,
       duration: Duration(seconds: 1),
   );
 }

the code reacts and do the same as with index: index. It seems I do not understand how this scrollable_positioned_list works. Any advices are highly appreciated.


Solution

  • The task turned out to be more difficult than I thought. The fact is that my widgets, which should be scrolled in a vertical sheet, are of different heights. Therefore, I had to calculate their height dynamically:

        // Calculate the height of the container dynamically based on its content
        double _calculateContainerHeight(BuildContext context,
            Widget Function() widget) {
        final RenderBox? renderBox = context.findRenderObject() as RenderBox?;
        return renderBox?.paintBounds.size.height ?? 0.0;
        }
    

    and then take this into account when adjusting the scrolling:

        Expanded(
            child: ScrollablePositionedList.builder(
                itemCount: menuItems.length,
                itemScrollController: itemController,
                itemPositionsListener: itemListener,
                itemBuilder: (BuildContext context, int index) {
                    switch (index) {
                        case 0:
                          return _location();
                        case 1:
                          return !_isFirstBuild ? _aboutObject() : SizedBox();
                        default:
                          return Container();
                      }
                  },
                ),
              ),
    

    , with declaring a new variable bool _isFirstBuild = true;. If somebody knows more elegant solution, please let me know. Also, something tells me this isn't the end yet as I want to increase the number of scrollable widgets. Perhaps something else will come out along the way.