Search code examples
flutterdartflutter-layout

Flutter resizeToAvoidBottomInset true not working with Expanded ListView


The keyboard hides my ListView (GroupedListView). I think it's because of the Expanded Widget.

My body:

Column(
        children: [
          Expanded(
            child: Padding(
              padding: const EdgeInsets.all(8.0),
              child: GroupedListView<dynamic, String>(
              controller: _scrollController,
              keyboardDismissBehavior:
                    ScrollViewKeyboardDismissBehavior.onDrag,
              physics: const BouncingScrollPhysics(
                    parent: AlwaysScrollableScrollPhysics()),
              itemBuilder: (context, message) {
                  return ListTile(
                      title: ChatBubble(message),
                  );
                },
              elements: messages,
              groupBy: (message) => DateFormat('MMMM dd,yyyy')
                    .format(message.timestamp.toDate()),
              groupSeparatorBuilder: (String groupByValue) =>
                    getMiddleChatBubble(context, groupByValue),
              itemComparator: (item1, item2) =>
                    item1.timestamp.compareTo(item2.timestamp),
              useStickyGroupSeparators: false,
              floatingHeader: false,
              order: GroupedListOrder.ASC,
              ),
            ),
          ),
          WriteMessageBox(
              group: group,
              groupId: docs[0].id,
              tokens: [widget.friendToken])
        ],
      );

enter image description here

Why the resizeToAvoidBottomInset isn't working?

I have opened an issue to the Flutter team


Solution

  • In short: use reversed: true.

    What you see is the expected behavior for the following reason:

    ListView preserves its scroll offset when something on your screen resizes. This offset is how many pixels the list is scrolled to from the beginning. By default the beginning counts from the top and the list grows to bottom.

    If you use reversed: true, the scroll position counts from the bottom, so the bottommost position is 0, and the list grows from bottom to the top. It has many benefits:

    1. The bottommost position of 0 is preserved when the keyboard opens. So does any other position. At any position it just appears that the list shifts to the top, and the last visible element remains the last visible element.

    2. Its easier to sort and paginate messages when you get them from the DB. You just sort by datetime descending and append to the list, no need to reverse the object list before feeding it to the ListView.

    3. It just works with no listeners and the controller manipulations. Declarative solutions are more reliable in general.

    The rule of thumb is to reverse the lists that paginate with more items loading at the top.

    Here is the example:

    import 'package:flutter/material.dart';
    
    void main() async {
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      const MyApp({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: MyHomePage(),
        );
      }
    }
    
    class MyHomePage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Expanded(
                  child: ListView.builder(
                    itemCount: 30,
                    reverse: true,
                    itemBuilder: (context, i) => ListTile(title: Text('Item $i')),
                  ),
                ),
                const TextField(),
              ],
            ),
          ),
        );
      }
    }
    

    As for resizeToAvoidBottomInset, it does its job. The Scaffold is indeed shortened with the keyboard on. So is ListView. So it shows you less items. For non-reversed list, gone are the bottommost.