Flutter resizeToAvoidBottomInset true 不适用于 Expanded ListView

问题描述 投票:0回答:7

键盘隐藏了我的

ListView
(GroupedListView)。我认为这是因为
Expanded
小部件。

我的身体:

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

为什么

resizeToAvoidBottomInset
不起作用?

我已经向 Flutter 团队提出了 问题

flutter dart flutter-layout
7个回答
9
投票

简而言之:使用

reversed: true

您所看到的是预期行为,原因如下:

当屏幕上的某些内容调整大小时,

ListView
会保留其滚动偏移量。此偏移量是列表从开头滚动到的像素数。默认情况下,从顶部开始计数,列表增长到底部。

如果使用

reversed: true
,则滚动位置从底部开始计算,因此最底部的位置是
0
,列表从下到上增长。它有很多好处:

    当键盘打开时,
  1. 0
    的最底部位置将被保留。任何其他位置也是如此。在任何位置,列表都会移到顶部,最后一个可见元素仍然是最后一个可见元素。

  2. 从数据库获取消息后,对消息进行排序和分页会更容易。您只需按日期时间降序排序并附加到列表中,无需在将对象列表提供给 ListView 之前反转对象列表。

  3. 它只适用于没有监听器和控制器操作的情况。一般来说,声明式解决方案更可靠。

经验法则是反转分页列表,在顶部加载更多项目

这是例子:

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

至于

resizeToAvoidBottomInset
,它完成了它的工作。键盘打开时,
Scaffold
确实缩短了。
ListView
也是如此。因此它显示的项目较少。对于非反转列表,最底部的已经消失了。


2
投票

看起来您希望 GroupedListView 从最后一行开始可见。 WriteMessageBox 被键盘推起并遮盖了最后的消息。最直接的解决方案是当键盘可见时将列表滚动到底部。也就是说,当 WriteMessageBox 获得焦点时。

在 build() 方法中向 WriteMessageBox 添加 FocusScope。就变成了

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

1
投票

截图:

代码:

您可以使用

MediaQueryData
获取键盘的高度,然后将
ListView
向上滚动该数字。

创建这个类:

class HandleScrollWidget extends StatefulWidget {
  final BuildContext context;
  final Widget child;
  final ScrollController controller;
  
  HandleScrollWidget(this.context, {required this.controller, required this.child});

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

class _HandleScrollWidgetState extends State<HandleScrollWidget> {
  double? _offset;

  @override
  Widget build(BuildContext context) {
    final bottom = MediaQuery.of(widget.context).viewInsets.bottom;
    if (bottom == 0) {
      _offset = null;
    } else if (bottom != 0 && _offset == null) {
      _offset = widget.controller.offset;
    }
    if (bottom > 0) widget.controller.jumpTo(_offset! + bottom);
    return widget.child;
  }
}

用途:

final ScrollController _controller = ScrollController();

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: Text('ListView')),
    body: HandleScrollWidget(
      context,
      controller: _controller,
      child: Column(
        children: [
          Expanded(
            child: ListView.builder(
              controller: _controller,
              itemCount: 100,
              itemBuilder: (_, i) => ListTile(title: Text('Messages #$i')),
            ),
          ),
          TextField(decoration: InputDecoration(hintText: 'Write a message')),
        ],
      ),
    ),
  );
}

0
投票

您似乎正在使用文本字段,因此它会隐藏数据,或者有时可能会通过黑色和黄色条纹溢出边框

更好地使用

SingleChildScrollView
,对于滚动方向,使用
scrollDirection
和参数
Axis.vertical
Axis.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])
        ],
      );


);

0
投票

请尝试此解决方案。希望它对你有用。谢谢。

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

0
投票

简而言之:使用reverse: true,将滚动位置跳至0。

  final scrollController = ScrollController();

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

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

0
投票

await Future.delayed(const Duration(毫秒: 100)); 滚动之前

© www.soinside.com 2019 - 2024. All rights reserved.