The ListView of fluent can automatically slide to the bottom

Usage scenario

In common social apps, after sending messages or receiving messages from others, the message list will automatically slide to the bottom. We don't need to slide manually. This is a good user experience.

thinking

ListView uses ScrollController to control sliding, including jumpTo and animateTo2 methods to slide to the specified position.

/// packages/flutter/lib/src/widgets/scroll_controller.dart

void jumpTo(double value){
    assert(_positions.isNotEmpty, 'ScrollController not attached to any scroll views.');
    for (final ScrollPosition position in List<ScrollPosition>.from(_positions))
      position.jumpTo(value);
}

Future<void> animateTo(
    double offset, {
      required Duration duration,
      required Curve curve,
    }) async {
    assert(_positions.isNotEmpty, 'ScrollController not attached to any scroll views.');
    await Future.wait<void>(<Future<void>>[
      for (int i = 0; i < _positions.length; i += 1) _positions[i].animateTo(offset, duration: duration, curve: curve),
    ]);
}

You can see from the naming that jumpTo does not have animation effects, and animateTo can set sliding animation.

The first parameters value and offset represent the specified position in the ListView.

ScrollPostion

So which value should be passed in to represent the bottom of the ListView?

Through the source code of the two methods of scrollcontroller, it is found that the final call is_ jumpTo and animateTo methods of position.

_ Position is a scrollposition. In scrollposition, there are two variables, minScrollExtent and maxScrollExtent, which represent the position of sliding to the top and bottom respectively.

Finally, you can use the following code to slide to the bottom

scrollController.jumpTo(scrollController.position.maxScrollExtent);

realization

layout

First, let's define a simple chat interface. A ListView, an input box, and a button to send a message.

///Message list
List<String> _messages = ["What's going on?","have you had dinner","I miss you!","go to bed early"];
TextEditingController _textEditingController = TextEditingController();
ScrollController _scrollController = ScrollController();

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text('goddess'),
    ),
    body: Column(
      children: [
        Expanded(
          child: ListView.builder(
            controller: _scrollController,
            itemBuilder: (context, index) {
              return SizedBox(
                child: Center(child: Text(_messages[index])),
                height: 50,
              );
            },
            itemCount: _messages.length,
          ),
        ),
        Row(
          children: [
            Expanded(
              child: Padding(
                padding: const EdgeInsets.symmetric(horizontal: 10),
                child: TextField(
                  controller: _textEditingController,
                  decoration: InputDecoration(
                    hintText: "Please enter a message",
                  ),
                  textInputAction: TextInputAction.send,
                  onSubmitted: sendMessage,
                ),
              ),
            ),
            IconButton(
              onPressed: () {
                ///Send message
                sendMessage(_textEditingController.text.trim());
                ///Clear the contents of the input box
                _textEditingController.text = "";
              },
              icon: Icon(Icons.send),
            ),
          ],
        ),
        SizedBox(height: 5),
      ],
    ),
  );
}

Slide to bottom

When you click the send button, get the message in the input box, add it to the message list, update the ui, and then slide to the bottom.

void sendMessage(String message) {
  if (message.isEmpty) return;
  setState(() {
    _messages.add(message);
  });
  _scrollController.jumpTo(_scrollController.position.maxScrollExtent);
}

problem

It seems that there is no problem, but the operation found that each time after sending the message, the list did not slide to the bottom, but to the penultimate data.

The reason is that when sliding to the bottom, the list data has not been updated, and maxScrollExtent still updates the previous value.

[external chain picture transfer failed. The source station may have anti-theft chain mechanism. It is recommended to save the picture and upload it directly (IMG pucoh0q3-1631608253352)( https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/308f11e872644472b83f78760105e2f3 ~tplv-k3u1fbpfcp-watermark.image)]

Solution

Then we need to wait for setState to refresh the list before performing the sliding operation.
So we can use the deferred execution scheme. Generally, the delay time can be set from 300 to 500 milliseconds.

void sendMessage(String message) {
  if (message.isEmpty) return;
  setState(() {
    _messages.add(message);
  });
  ///Delay 500 ms before sliding
  Future.delayed(Duration(milliseconds: 500), () {
    _scrollController.jumpTo(_scrollController.position.maxScrollExtent);
  });
}

ok! It's done. Here's the demonstration effect.

Keywords: Flutter dart listview

Added by gottes_tod on Mon, 20 Sep 2021 18:23:33 +0300