Search code examples
flutterdartflutter-listview

How can I detect if ListView item added off-screen?


How can I detect if a ListView item was added off-screen? I would like to scroll to the newly added item if it was added off-screen.

Below is a minimal example. If you keep pressing the + button, eventually an item will be added off-screen. I would like to detect that case and then scroll to the newly added item.

class _MyHomePageState extends State<MyHomePage> {
  List<String> _items = [];

  void _addItem() {
    final count = _items.length + 1;

    setState(() {
      _items.add('Item $count');
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: ListView.builder(
          itemCount: _items.length,
          itemBuilder: (_, index) {
            return Container(
              height: 50,
              child: Card(
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceAround,
                  children: <Widget>[
                    Text(_items[index]),
                  ],
                ),
              ),
            );
          }),
      floatingActionButton: FloatingActionButton(
        onPressed: _addItem,
        tooltip: 'Add Item',
        child: Icon(Icons.add),
      ),
    );
  }
}

Solution

  • https://dartpad.dev/81dcfd6e4255bde548751180c54a9ade

    Check this. You need to use a scroll controller

    Update

    Here is the code as the above Dartpad link is not working:

    import 'package:flutter/material.dart';
    
    void main() {
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          debugShowCheckedModeBanner: false,
          home: Scaffold(
            body: MyHomePage()
          ),
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      @override
      createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      List<String> _items = [];
      
      // Add a scroll controller
      ScrollController _scrollController = ScrollController();
    
      void _addItem() {
        final count = _items.length + 1;
        
        setState(() {
          _items.add('Item $count');
        });
        
        // Wait a frame for the new item to be built in your list view
        WidgetsBinding.instance.addPostFrameCallback((duration) {
           if(_scrollController.hasClients) {
             // Move to the maxScrollExtent once the item has been added
            _scrollController.animateTo(
            _scrollController.position.maxScrollExtent,
            curve: Curves.easeIn, // Use any curve you like
            duration: const Duration(milliseconds: 200) // Set the anim duration
           );
          }
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('Demo'),
          ),
          body: ListView.builder(
              controller: _scrollController, // Assign the controller
              itemCount: _items.length,
              itemBuilder: (_, index) {
                return Container(
                  height: 50,
                  child: Card(
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.spaceAround,
                      children: <Widget>[
                        Text(_items[index]),
                      ],
                    ),
                  ),
                );
              }),
          floatingActionButton: FloatingActionButton(
            onPressed: _addItem,
            tooltip: 'Add Item',
            child: Icon(Icons.add),
          ),
        );
      }
    }