颤动:水平列表视图上的最小高度

sin*_*enm 39 listview dart flutter

我正在尝试在Flutter中创建一个水平滚动项目列表,我希望该列表仅根据其子项占用必要的高度.通过设计" ListView尝试扩展以适应其横向可用的空间"(来自Flutter文档),我也注意到它占据了视口的整个高度,但有没有办法让它不做这个?理想情况下类似于此(显然不起作用):

new ListView(
  scrollDirection: Axis.horizontal,
  crossAxisSize: CrossAxisSize.min,
  children: <Widget>[
    new ListItem(),
    new ListItem(),
    // ...
  ],
);
Run Code Online (Sandbox Code Playgroud)

我知道这样做的一种方式是通过包装ListViewContainer一个固定的高度.但是,我不一定知道物品的高度:

new Container(
  height: 97.0,
  child: new ListView(
    scrollDirection: Axis.horizontal,
    children: <Widget>[
      new ListItem(),
      new ListItem(),
      // ...
    ],
  ),
);
Run Code Online (Sandbox Code Playgroud)

我能够通过嵌套一个砍一起"解决方案" RowSingleChildScrollViewColumn一个mainAxisSize: MainAxisSize.min.但是,对我来说,这不是一个解决方案:

new Column(
  mainAxisSize: MainAxisSize.min,
  children: <Widget>[
    new SingleChildScrollView(
      scrollDirection: Axis.horizontal,
      child: new Row(
        children: <Widget>[
          new ListItem(),
          new ListItem(),
          // ...
        ],
      ),
    ),
  ],
);
Run Code Online (Sandbox Code Playgroud)

fro*_*ega 38

据我了解,您不能在垂直 ListView 中放置水平 ListView 并动态设置其高度。如果您的要求允许(没有无限滚动、少量元素等),您可以使用 SingleChildScrollView 代替。

SingleChildScrollView(
  scrollDirection: Axis.horizontal,
  child: Row(
    children: [...],
  ),
);
Run Code Online (Sandbox Code Playgroud)

  • 最佳答案 (3认同)

use*_*613 12

我找到了一种为水平滚动动态调整ListView大小的方法。列表可以有无限个项目,但每个项目应具有相同的高度。

(如果您只有几件物品,或者您的物品必须具有不同的高度,请参阅此答案。)

关键点:

这个想法是测量一个项目,我们称之为“原型”。并基于所有项目高度相同的假设,然后我们设置 的高度ListView以匹配原型项目。

这样,我们就可以避免对任何值进行硬编码,并且当用户在系统中设置更大的字体并导致卡片展开时,列表可以自动调整大小。

演示:

无限项目滚动的演示 gif

代码:

我制作了一个自定义小部件,名为PrototypeHeight(名称灵感来自IntrinsicHeight)。要使用它,只需传入 aWidget作为“原型”,然后传入水平滚动的ListView,例如:

PrototypeHeight(
  prototype: MyItem(),
  listView: ListView.builder(
    scrollDirection: Axis.horizontal,
    itemBuilder: (_, index) => MyItem(),
  ),
)
Run Code Online (Sandbox Code Playgroud)

其实现PrototypeHeight如下(您可以将其粘贴到某处并开始使用它,或者根据需要进行修改):

class PrototypeHeight extends StatelessWidget {
  final Widget prototype;
  final ListView listView;

  const PrototypeHeight({
    Key? key,
    required this.prototype,
    required this.listView,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        IgnorePointer(
          child: Opacity(
            opacity: 0.0,
            child: prototype,
          ),
        ),
        SizedBox(width: double.infinity),
        Positioned.fill(child: listView),
      ],
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

说明:

让我简要解释一下这是如何工作的。

简而言之,Stack小部件的大小取决于其所有“非定位”子部件。基本上,如果 aStack有 3 个子级,但其中 2 个是Positioned,则Stack会简单地匹配剩余的“未定位”子级。

在这种情况下,我使用“原型项目”作为非定位子项之一,以帮助确定Stack其高度(与原型高度相同)。然后我使用非常宽的宽度SizedBox来帮助确定Stack其宽度(这将与父级的宽度相同,通常是设备的屏幕宽度,因为请记住:约束减小,尺寸增大。)。我还添加了一个Opacity小部件来隐藏原型(这也通过跳过部分渲染管道来提高性能),以及一个IgnorePointer小部件来防止用户交互,以防原型项上有按钮。

接下来,我使用 aPositioned.fill使其子项 ( ListView) 匹配 的大小Stack,而 反过来,将匹配原型项的高度,并匹配父项的宽度。这将是传递给 的约束ListView,因此ListView将创建一个该大小的“视口”,这正是我们想要实现的。

这实际上涵盖了 Flutter 中相当多的概念。我计划稍后制作一个视频教程。


Tah*_*Ali 11

只需将shrink属性设置ListViewtrue,它将适合空间而不是扩展。

例:

ListView(
        shrinkWrap: true, //just set this property
        padding: const EdgeInsets.all(8.0),
        children: listItems.toList(),
      ),
Run Code Online (Sandbox Code Playgroud)

  • 这不适用于最小高度。 (14认同)
  • `shrinkWrap`将沿主轴收缩`ListView`。不是沿着横轴问的。 (7认同)
  • 这太令人讨厌了,因为你和每个回答的人都忽略了实际的问题,而且不知怎的,这有这么多的赞成票。 (6认同)

use*_*613 8

(此答案涵盖了项目具有不同尺寸时的解决方法。如果您的项目具有相同的尺寸/高度,请参阅此答案以了解ListView方法。)

如果项目具有不同的尺寸,则不应使用 , 来完成ListView,这是有充分理由的:

AListView可能有很多项目(甚至无限),但找出“最大的一个”需要遍历每个项目,这违背了使用 a 的全部意义ListView(使用该方法动态加载每个项目builder)。

另一方面,Row小部件总是同时布置所有子部件,因此很容易找到最大的部件。所以如果你没有太多的物品(也许少于100件左右),你可以用它Row来代替。如果需要滚动,请RowSingleChildScrollView.

演示动图

示例中使用的代码:

Column(
  children: [
    Container(
      color: Colors.green.shade100,
      child: SingleChildScrollView(
        scrollDirection: Axis.horizontal,
        child: Row(
          children: [
            FlutterLogo(),
            FlutterLogo(size: 100),
            for (int i = 0; i < 50; i++) FlutterLogo(),
          ],
        ),
      ),
    ),
    const Text('The row has ended.'),
  ],
)
Run Code Online (Sandbox Code Playgroud)

  • 这是最好的答案。我什至可以进一步缩短它:“ListView”的设计方式禁止OP希望它使用的方式。因此“ListView”应始终具有定义的高度。而“shrinkwrap”也无济于事,因为 Flutter 团队本身就反对它,因为它会导致性能问题。所以最简单、最好的解决方案就是设置高度。 (2认同)

小智 6

使用ConstrainedBox到集minHeightmaxHeight

ConstrainedBox(
  constraints: new BoxConstraints(
    minHeight: 35.0,
    maxHeight: 160.0,
  ),

  child: new ListView(
    shrinkWrap: true,
    children: <Widget>[
      new ListItem(),
      new ListItem(),
    ],

  ),
)
Run Code Online (Sandbox Code Playgroud)

  • 在不知道列表项的最小/最大高度的情况下该如何做? (10认同)
  • 就我而言,ListView 会增长以适应 maxHeight,因此不合适。 (5认同)

Spe*_*eed 6

类似于 Gene 的回答,但我没有固定数量的小部件要添加。

也不需要知道小部件的高度

  Widget _horizontalWrappedRow(List data) {

    var list = <Widget>[Container(width: 16)]; // container is left padding

    //create a new row widget for each data element
    data.forEach((element) {
       list.add(MyRowItemWidget(element));
    });

    // add the list of widgets to the Row as children
    return SingleChildScrollView(
      scrollDirection: Axis.horizontal,
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: list,
      ),
    );

  }  
Run Code Online (Sandbox Code Playgroud)

  • 好吧,我无法建议编辑,因为队列已满,所以我将在这里考虑。这种方法的缺点是不能延迟构建元素。这意味着,虽然这种方法对于小列表应该很有效,但对于较大的列表,它可能会导致开销,因为所有项目都将被处理,无论是否在屏幕上可见。 (2认同)

Gen*_*ene 5

这与此处提出的问题非常相似:Flutter ListView.builder() widget's cross Axis is take up the entire Screen height

我相信 ListViews 要求每个项目都具有相同的横轴大小。这意味着在这种情况下,我们无法为每个对象设置唯一的高度,交叉轴的大小是固定的。

如果您想让子项本身唯一控制可滚动的高度,那么您可以使用与Row配对的SingleChildScrollView (注意:确保设置 scrollDirection: Axis.horizo​​ntal) 输出

import 'package:flutter/material.dart';

final Color darkBlue = Color.fromARGB(255, 18, 32, 47);

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: SafeArea(
          child: Container(
              // height: 100, // no need to specify here
              color: Colors.white,
              child: SingleChildScrollView(
                scrollDirection: Axis.horizontal,
                child: Row(
                  children: <Widget>[
                    Container(
                      height: 300,
                      width: 300,
                      color: Colors.amber[600],
                      child: const Center(child: Text('Entry A')),
                    ),
                    Container(
                      height: 100,
                      color: Colors.amber[500],
                      child: const Center(child: Text('Entry B')),
                    ),
                    Container(
                      height: 50,
                      width: 100,
                      color: Colors.amber[100],
                      child: const Center(child: Text('Entry C')),
                    ),
                    Container(
                      height: 300,
                      width: 300,
                      color: Colors.amber[600],
                      child: const Center(child: Text('Entry A')),
                    ),
                    Container(
                      height: 100,
                      color: Colors.amber[500],
                      child: const Center(child: Text('Entry B')),
                    ),
                    Container(
                      height: 50,
                      width: 100,
                      color: Colors.amber[100],
                      child: const Center(child: Text('Entry C')),
                    ),
                    Container(
                      height: 300,
                      width: 300,
                      color: Colors.amber[600],
                      child: const Center(child: Text('Entry A')),
                    ),
                    Container(
                      height: 100,
                      color: Colors.amber[500],
                      child: const Center(child: Text('Entry B')),
                    ),
                    Container(
                      height: 50,
                      width: 100,
                      color: Colors.amber[100],
                      child: const Center(child: Text('Entry C')),
                    ),
                    Container(
                      height: 300,
                      width: 300,
                      color: Colors.amber[600],
                      child: const Center(child: Text('Entry A')),
                    ),
                    Container(
                      height: 100,
                      color: Colors.amber[500],
                      child: const Center(child: Text('Entry B')),
                    ),
                    Container(
                      height: 50,
                      width: 100,
                      color: Colors.amber[100],
                      child: const Center(child: Text('Entry C')),
                    ),
                    Container(
                      height: 300,
                      width: 300,
                      color: Colors.amber[600],
                      child: const Center(child: Text('Entry A')),
                    ),
                    Container(
                      height: 100,
                      color: Colors.amber[500],
                      child: const Center(child: Text('Entry B')),
                    ),
                    Container(
                      height: 50,
                      width: 100,
                      color: Colors.amber[100],
                      child: const Center(child: Text('Entry C')),
                    ),
                  ],
                ),
              )),
        ),
      ),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)