简体   繁体   English

Flutter resizeToAvoidBottomInset true 不适用于展开的 ListView

[英]Flutter resizeToAvoidBottomInset true not working with Expanded ListView

The keyboard hides my ListView (GroupedListView).键盘隐藏了我的ListView (GroupedListView)。 I think it's because of the Expanded Widget.我认为这是因为Expanded的小部件。

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])
        ],
      );

在此处输入图像描述

Why the resizeToAvoidBottomInset isn't working?为什么resizeToAvoidBottomInset不起作用?

I have opened an issue to the Flutter team我已向Flutter 团队提出问题

It looks like you want the GroupedListView to be visible from the last line.看起来您希望 GroupedListView 从最后一行可见。 The WriteMessageBox is pushed up by the keyboard and obscures the last messages. WriteMessageBox 由键盘向上推动并隐藏最后的消息。 The most direct solution is to scroll the list to the bottom when the keyboard is visible.最直接的解决方案是在键盘可见时将列表滚动到底部。 That is, when the WriteMessageBox gains focus.也就是说,当 WriteMessageBox 获得焦点时。

Add a FocusScope to the WriteMessageBox in the build() method.在 build() 方法中将 FocusScope 添加到 WriteMessageBox。 It becomes它成为了

FocusScope(
  child: Focus(
   child: WriteMessageBox(),
   onFocusChange: (focused) {
    if (focused) {
      _scrollController.jumpTo(_scrollController.position.maxScrollExtent);
    }
  )
)

It appears that you are using text fields so it hides data or sometimes it may overflow borders by black and yellow stripes您似乎正在使用文本字段,因此它隐藏了数据,或者有时它可能会溢出黑色和黄色条纹的边框

better to use SingleChildScrollView and for scrolling direction use scrollDirection with parameters Axis.vertical or Axis.horizontal最好使用SingleChildScrollView和滚动方向使用带有参数scrollDirection或 Axis.horizo Axis.verticalAxis.horizontal

return SingleChildScrollView(
      scrollDirection: Axis.vertical,
      child :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])
        ],
      );


);

Please try this solution.请尝试此解决方案。 Hope it will work for you.希望它对你有用。 Thanks.谢谢。

 Expanded(
          child: Padding(
            padding: const EdgeInsets.all(8.0),
            child: GroupedListView<dynamic, String>(
              scrollDirection: Axis.vertical,
              shrinkWrap: true,
              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])
     

Screenshot:截屏:

在此处输入图像描述


Code:代码:

You can use MediaQueryData to get the height of keyboard, and then scroll the ListView up by that number.您可以使用MediaQueryData获取键盘的高度,然后将ListView向上滚动该数字。

final ScrollController _controller = ScrollController();
  
@override
Widget build(BuildContext context) {
  var bottom = MediaQuery.of(context).viewInsets.bottom;
  if (bottom > 10) {
    _controller.jumpTo(_controller.position.maxScrollExtent);
  }
  
  return Scaffold(
    appBar: AppBar(),
    body: Column(
      children: [
        Expanded(
          child: ListView.builder(
            physics: ClampingScrollPhysics(),
            controller: _controller,
            itemCount: 20,
            itemBuilder: (_, i) => ListTile(title: Text('Messages #$i')),
          ),
        ),
        TextField(
          decoration: InputDecoration(
            hintText: 'Write a message',
          ),
        ),
      ],
    ),
  );
}

In short: use reversed: true .简而言之:使用reversed: true

What you see is the expected behavior for the following reason:您看到的是预期的行为,原因如下:

ListView preserves its scroll offset when something on your screen resizes.当屏幕上的某些内容调整大小时, ListView会保留其滚动偏移量。 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.如果使用reversed: true ,则滚动 position 从底部开始计数,因此最底部的 position 为0 ,并且列表从底部到顶部增长。 It has many benefits:它有很多好处:

  1. The bottommost position of 0 is preserved when the keyboard opens.键盘打开时会保留最底部的 position 0 So does any other position.任何其他 position 也是如此。 At any position it just appears that the list shifts to the top, and the last visible element remains the last visible element.在任何 position 中,似乎列表移动到顶部,最后一个可见元素仍然是最后一个可见元素。

  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.您只需按日期时间降序和 append 排序到列表中,无需在将 object 列表提供给 ListView 之前反转它。

  3. It just works with no listeners and the controller manipulations.它只适用于没有听众和 controller 操作。 Declarative solutions are more reliable in general.声明式解决方案通常更可靠。

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(),
          ],
        ),
      ),
    );
  }
}

In short: use reversed: true, jump the scrolling position to 0.简而言之:使用 reversed: true,将滚动的 position 跳转到 0。

  @override
  Widget build(BuildContext context) {
    WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
      scrollController.jumpTo(scrollController.position.maxScrollExtent);
    });
}
  final scrollController = ScrollController();

  Widget _buildScrollView(){
    return SingleChildScrollView(
      reverse: true,
      controller: scrollController,
      child: [....],
    );
  }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM