How to know when a list is long enough to be scrollable?

Viewed 359

The screenshot below consists of two product listings, one which is too short to be scrollable, the other which is long enough to be scrollable.

enter image description here

Below is the screenshot with more visible colours for better clarity for the sake of this question:

enter image description here

To indicate to the user that the list is scrollable, I have stacked the list with a gradient which is at the end of the list.

I would like this gradient to only appear when the list is long enough to be scrollable, but I am not able to find a way to differentiate between the scrollable and non-scrollable lists.

Below is the code for the stack which has the underlying list and the gradient:

Stack(
  children: <Widget>[
    ListView(
      scrollDirection: Axis.horizontal,
      children: <Widget>[
        //children go here
      ],
    ),
    Align(
      alignment: Alignment.centerRight,
      child: Container(
        width: 24,
        height: 24,
        decoration: BoxDecoration(
          gradient: LinearGradient(
            begin: FractionalOffset.centerRight,
            end: FractionalOffset.centerLeft,
            colors: [
              Colors.green,
              Colors.yellow
            ],
            stops: [
              0.0,
              1.0
            ]))),
    ),
  ],
),
3 Answers

I made a simple case for your request. At first build list and calculate all item's widht sum,
and if all item's width is over Listview container's width,
redraw show indicator that it can be scrollable.

modified

I changed to get a width dynamic ListView width.
You can just test with chaning 'listItemLength' value.

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

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: "App",
      theme: new ThemeData(primarySwatch: Colors.amber),
      home: Test(),
    );
  }
}

class Test extends StatefulWidget {
  @override
  _TestState createState() => _TestState();
}

class _TestState extends State<Test> {
  List<GlobalKey> _globalKeys = [];
  GlobalKey _listviewGLobalKey = GlobalKey();
  double listTotalWidth = 0.0;
  double listviewWidth = 0.0;
  int listItemLength = 14;

  @override
  void initState() {
    super.initState();
    for (var i = 0; i < listItemLength; i++) {
      _globalKeys.add(GlobalKey());
    }

    WidgetsBinding.instance.addPostFrameCallback((_) => _getSizes());
  }

  _getSizes() {
    // Get a total Items width
    listTotalWidth = 0.0;
    _globalKeys.forEach((key) {
      final RenderBox renderBoxRed = key.currentContext.findRenderObject();
      final containerSize = renderBoxRed.size;
      listTotalWidth += containerSize.width;
    });
    print('total items width sum: $listTotalWidth');

    // Get a ListView's width
    final RenderBox listviewRenderBoxRed =
        _listviewGLobalKey.currentContext.findRenderObject();
    final listviewContainerSize = listviewRenderBoxRed.size;
    listviewWidth = listviewContainerSize.width;
    print('ListView width: $listviewWidth');

    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    // print('**** ${_scrollController.position.maxScrollExtent}');
    //  WidgetsBinding.instance
    //           .addPostFrameCallback((_) => _getSizes());
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: "Title",
      theme: new ThemeData(primarySwatch: Colors.amber),
      home: Scaffold(
        body: SafeArea(
          child: Container(
            child: Row(
              children: <Widget>[
                Expanded(
                  child: ListView.builder(
                    key: _listviewGLobalKey,
                    scrollDirection: Axis.horizontal,
                    itemCount: listItemLength,
                    itemBuilder: (context, index) {
                      return Container(
                          key: _globalKeys[index],
                          padding: EdgeInsets.symmetric(horizontal: 5),
                          child: Center(
                              child: Text(
                            'aaa$index',
                          )));
                    },
                  ),
                ),
                if (listTotalWidth > listviewWidth) Icon(Icons.add),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

attach a ScrollController to the ListView and then use it (in a hack way) to determine scrollability of the list

    ScrollController _scrollController = ScrollController();


    return Container(
      height: 24,
      child: Stack(
        children: <Widget>[
          Container(
            padding: EdgeInsets.only(right: 24),
            child: ListView(
              scrollDirection: Axis.horizontal,
              controller: _scrollController,
              children: <Widget>[
                Text('aaaindex'),
                Text('aaaindex'),
                Text('aaaindex'),
                Text('aaaindex'),
                Text('aaaindex'),
                Text('aaaindex'),
                Text('aaaindex'),
                Text('aaaindex'),
                Text('aaaindex'),
                Text('aaaindex'),
              ],
            ),
          ),
          FutureBuilder(
            future: Future.delayed(Duration(milliseconds: 500))
            builder: (context,async){
              if(async.connectionState == ConnectionState.waiting){
                return Container();
              }
              return _scrollController.position.extentAfter > 0
                  ? Align(
                alignment: Alignment.centerRight,
                child: Container(
                  width: 24,
                  height: 24,
                  decoration: BoxDecoration(
                    gradient: LinearGradient(
                      begin: FractionalOffset.centerRight,
                      end: FractionalOffset.centerLeft,
                      colors: [Colors.green, Colors.yellow],
                      stops: [0.0, 1.0],
                    ),
                  ),
                ),
              )
                  : Container();
            },
          ),
        ],
      ),
    );

This idea is from 'Muhammad Adam'.
When judge whether listview is scrollable, I used 'extentAfter' rather than 'renderBox size'.

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  ScrollController _scrollController = ScrollController();
  bool _scrollable = false;

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) => _getIsScrollable());
  }

  _getIsScrollable() {
    print(_scrollController.position.extentAfter > 0);
    _scrollable = _scrollController.position.extentAfter > 0;
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: _buildBody(),
      floatingActionButton: FloatingActionButton(
        onPressed: () {},
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }

  Widget _buildBody() {
    return Container(
      height: 24,
      child: Stack(
        children: <Widget>[
          Container(
            padding: EdgeInsets.only(right: 24),
            child: ListView(
              scrollDirection: Axis.horizontal,
              controller: _scrollController,
              children: <Widget>[
                Text('aaaindex'),
                Text('aaaindex'),
                Text('aaaindex'),
                Text('aaaindex'),
                Text('aaaindex'),
                Text('aaaindex'),
                Text('aaaindex'),
                Text('aaaindex'),
                Text('aaaindex'),
                Text('aaaindex'),
                Text('aaaindex'),
                Text('aaaindex'),
                Text('aaaindex'),
                Text('aaaindex'),
                Text('aaaindex'),
                Text('aaaindex'),
              ],
            ),
          ),
          if (_scrollable)
            Align(
              alignment: Alignment.centerRight,
              child: Container(
                width: 24,
                height: 24,
                decoration: BoxDecoration(
                  gradient: LinearGradient(
                    begin: FractionalOffset.centerRight,
                    end: FractionalOffset.centerLeft,
                    colors: [Colors.green, Colors.yellow],
                    stops: [0.0, 1.0],
                  ),
                ),
              ),
            ),
        ],
      ),
    );
  }
}

Related