Is there a Flutter equivalent to Bootstrap Scrollspy?

Issue

I am looking for a flutter package that is equivalent to that of Bootstrap’s Scrollspy:

https://getbootstrap.com/docs/4.0/components/scrollspy/

The intended functionality is to have a vertical scrollable list of items with a sticky horizontal scrollable “header/navbar menu” on top of it. When the user scrolls through the vertical list and reaches a new “section” this is reflected in the horizontal navbar by highlighting the “section name” in the navbar and scrolling to it if necessary. When the user presses on a section name in the horizontal navbar, it should scroll to the start of that section in the vertical list.

Ex:

Section1 !!!Section2!!! Section3 Section4
——————————————————————
(Section1 is not visible)

!!!Section2!!!

Item3

Item4

Section3

Item1

Item2

Section4

Item5

Item6

Solution

I think you can achieve this with the scrollable_positioned_list package made by Google Fuchsia Authors.

enter image description here

The ScrollablePositionedList provides a ItemPositionsListener:

_itemPositionsListener.itemPositions.addListener(() {
  final positions = _itemPositionsListener.itemPositions.value;
  setState(() {
    _topItem = positions.isNotEmpty ? positions.first.index : null;
  });
});

Full source code

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

void main() {
  runApp(
    MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Flutter Demo',
      home: HomePage(),
    ),
  );
}

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  final _nbItems = 6;
  final _itemHeight = 200.0;
  final _itemPositionsListener = ItemPositionsListener.create();

  int _topItem = 0;

  @override
  void initState() {
    super.initState();
    _itemPositionsListener.itemPositions.addListener(() {
      final positions = _itemPositionsListener.itemPositions.value;
      setState(() {
        _topItem = positions.isNotEmpty ? positions.first.index : null;
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
          Row(
            mainAxisAlignment: MainAxisAlignment.end,
            children: List.generate(
              _nbItems,
              (index) => Padding(
                padding: const EdgeInsets.all(8.0),
                child: Container(
                  padding: EdgeInsets.all(4.0),
                  decoration: _topItem == index
                      ? BoxDecoration(
                          color: Colors.black26,
                          border: Border.all(color: Colors.black54),
                        )
                      : BoxDecoration(),
                  child: Text(
                    'S$index',
                    style: TextStyle(
                      fontWeight: _topItem == index
                          ? FontWeight.bold
                          : FontWeight.normal,
                    ),
                  ),
                ),
              ),
            ),
          ),
          Expanded(
            child: ScrollablePositionedList.builder(
              itemCount: _nbItems,
              itemBuilder: (context, index) => SizedBox(
                height: _itemHeight,
                child: Card(
                  child: Text('Item $index'),
                ),
              ),
              itemPositionsListener: _itemPositionsListener,
            ),
          ),
        ],
      ),
    );
  }
}

Answered By – Thierry

Answer Checked By – Marie Seifert (FlutterFixes Admin)

Leave a Reply

Your email address will not be published.