LaVOZs

The World’s Largest Online Community for Developers

'; Change the color of current top visible item in ListView - Flutter - LavOzs.Com

I want to have the top visible on screen item in a list change color as the user scrolls through a list.

The closest I've got is with the pluggin ScrollablePositionedList: https://pub.dev/packages/scrollable_positioned_list

The first item in the list now changes color OR whichever item is at the top screen when I hot reload. But they don't do it in real time as I scroll.

I would really appreciate any help. Please find the relevant code below. Thanks in advance!

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:scrollviewexperiment/models/place_data.dart';
import 'package:scrollviewexperiment/widgets/place_tile.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';

class PlacesList extends StatefulWidget {
  @override
  _PlacesListState createState() => _PlacesListState();
}

class _PlacesListState extends State<PlacesList> {
  final ItemPositionsListener itemPositionsListener =
      ItemPositionsListener.create();

  ItemScrollController _itemScrollController = ItemScrollController();
  int currentIndex = 0;



  @override
  void initState() {
    super.initState();
    currentVisibleIndex();
  }

  @override
  Widget build(BuildContext context) {
    return Consumer<PlaceData>(
      builder: (context, placeData, child) {
        return ScrollablePositionedList.builder(
          itemCount: placeData.placeCount,
          itemPositionsListener: itemPositionsListener,
          itemBuilder: (context, index) {
            final place = placeData.places[index];
            return Card(
              color: index == currentIndex ? Colors.blue : Colors.white,
              elevation: 2.0,
              shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.circular(10.0)),
              child: PlaceTile(
                placeName: place.name,
                distance: place.distance,
              ),
            );
          },
        );
      },
    );
  }

  currentVisibleIndex() {
    itemPositionsListener.itemPositions.addListener(() {
      int min;
      if (itemPositionsListener.itemPositions.value.isNotEmpty) {
        min = itemPositionsListener.itemPositions.value
            .where((ItemPosition position) => position.itemTrailingEdge > 0)
            .reduce((ItemPosition min, ItemPosition position) =>
                position.itemTrailingEdge < min.itemTrailingEdge
                    ? position
                    : min)
            .index;
        print('Min Index $min');
        currentIndex = min;
      }
    });
  }
}

I figured it out!

I returned a ValueListenableBuilder inside the ScrollablePositionedList.builder's itemBuilder

It's not the cleanest solution in the world so if anyone can improve on this I'd love to know.

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:scrollviewexperiment/models/place_data.dart';
import 'package:scrollviewexperiment/widgets/place_tile.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';

class PlacesList extends StatefulWidget {
  @override
  _PlacesListState createState() => _PlacesListState();
}

class _PlacesListState extends State<PlacesList> {
  final ItemPositionsListener itemPositionsListener =
      ItemPositionsListener.create();

  @override
  Widget build(BuildContext context) {
    return Consumer<PlaceData>(
      builder: (context, placeData, child) {
        return ScrollablePositionedList.builder(
          itemCount: placeData.placeCount,
          itemPositionsListener: itemPositionsListener,
          itemBuilder: (context, index) {
            final place = placeData.places[index];
            return ValueListenableBuilder<Iterable<ItemPosition>>(
              valueListenable: itemPositionsListener.itemPositions,
              builder: (context, positions, child) {
                int min;
                if (positions.isNotEmpty) {
                  min = positions
                      .where((ItemPosition position) =>
                          position.itemTrailingEdge > 0)
                      .reduce((ItemPosition min, ItemPosition position) =>
                          position.itemTrailingEdge < min.itemTrailingEdge
                              ? position
                              : min)
                      .index;
                }
                return Card(
                  color: index == min ? Colors.blue : Colors.white,
                  elevation: 2.0,
                  shape: RoundedRectangleBorder(
                      borderRadius: BorderRadius.circular(10.0)),
                  child: PlaceTile(
                    placeName: place.name,
                    distance: place.distance,
                  ),
                );
              },
            );
          },
        );
      },
    );
  }
}
Related
How to add a ListView to a Column in Flutter?
How do I use hexadecimal color strings in Flutter?
How to offset a scaffold widget in Flutter?
Flutter - Selected items move to top of ListView
Flutter Animated List: Conditionally add ListView item
Find out which items in a ListView are visible
How to make flutter card auto adjust its height depend on content
Flutter: Continue scrolling in top Listview when reaching bottom of nested Listview
Web view page is empty if clicks the back arrow in flutter?
How to fix black screen in flutter while Navigating?