如何使小部件尽可能小(Android中的wrap_content)?

时间:2019-12-11 21:33:19

标签: flutter flutter-layout

我在渲染树上遇到类似于Column> PageView> Column的渲染问题,其中最后一个Column位于PageView的页面内

PageView的呈现方式不正确,因此我得到一个异常(Horizontal viewport was given unbounded height.),因为框架无法计算其大小,可以通过将其包装到{{1 }}或Flexible,但我不希望Expanded占据整个屏幕,我希望它尽可能小并位于屏幕中央。

这是我的问题的代表:

PageView

这是我想要实现的(我删除了// This code throws an exception: class Widget_1_Has_Problem extends StatelessWidget { final PageController pageController = PageController(); @override Widget build(BuildContext context) { return Scaffold( body: Column( crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Icon(Icons.done,size: 112), SizedBox(height: 32), PageView( physics: NeverScrollableScrollPhysics(), controller: pageController, children: <Widget>[ // Many widgets go here, I am just simplifying with a Column. Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: <Widget>[ Text('TEXT TEXT TEXT TEXT TEXT TEXT'), SizedBox(height: 16), Text('TEXT2 TEXT2 TEXT2 TEXT2 TEXT2 TEXT2'), ], ), ], ), ], ), ); } } 以便显示出来):

Desired UI

PageView

这是我可以解决的方法,但它并不完美,我将在后面显示:

Solution with Flexible/Expanded

class Widget_2_No_PageView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Icon(Icons.done,size: 112),
          SizedBox(height: 32),
          Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: <Widget>[
              Text('TEXT TEXT TEXT TEXT TEXT TEXT'),
              SizedBox(height: 16),
              Text('TEXT2 TEXT2 TEXT2 TEXT2 TEXT2 TEXT2'),
            ],
          ),
        ],
      ),
    );
  }
}

此解决方案并不完美,因为class Widget_3_With_Flex_Not_Perfect_Solution extends StatelessWidget { final PageController pageController = PageController(); @override Widget build(BuildContext context) { return Scaffold( body: Column( crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Spacer(flex: 2,), Icon(Icons.done,size: 112), SizedBox(height: 32), Flexible( flex: 1, child: PageView( physics: NeverScrollableScrollPhysics(), controller: pageController, children: <Widget>[ Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: <Widget>[ Text('TEXT TEXT TEXT TEXT TEXT TEXT'), SizedBox(height: 16), Text('TEXT2 TEXT2 TEXT2 TEXT2 TEXT2 TEXT2'), ], ), ], ), ), Spacer(flex: 2,), ], ), ); } } SpacerFlexible始终尝试保持其与Expanded权重的比例,这意味着对于较小的显示器,{ {1}}不会消失,它将保留在那里,而第一张和所需的图像不会有这样的空白空间。正如我们在下图中看到的,垫片始终在其中。此外,我还必须针对此设计中的每次更改计算flex因子,而在我的第一个代码示例中,小部件将自动将其大小调整到屏幕中心。

Spacer into the UI

我如何指示Spacer尽可能的小,而不是尽可能地扩展,因为这是我在网上可以找到的唯一解决方案?

5 个答案:

答案 0 :(得分:3)

您正在请求PageView内的项目来设置PageView本身的大小。这不适用于标准类。如果查看其源代码,则会发现它在Scrollable中使用了视口。视口会获取最大大小,并忽略子项(条形)的大小。您将需要使用另一种方法,或者创建一个自定义的PageView,也许是使用ShrinkWrappingViewport的一种。

https://api.flutter.dev/flutter/widgets/ShrinkWrappingViewport-class.html

答案 1 :(得分:2)

PageView使用SliverFillViewport来包装给定的children

SliverFillViewport并不关心children有多小。它只关心它可以从其父空间中占据多少空间。因此FlexAlign小部件是无用的。

当前使用PageView,除非我们进行一些计算并使用SizedBoxConstrainedBox,否则极有可能无法实现。

我也找到了这个discussion

答案 2 :(得分:0)

通过将PageView包装在SizedBox内,我们可以摆脱您遇到的异常。 (Horizontal viewport was given unbounded height.

SizedBox(height: 32, child:
          PageView(
            physics: NeverScrollableScrollPhysics(),
            controller: pageController,
            children: <Widget>[
            // Many widgets go here, I am just simplifying with a Column.
              Column(
              ...

但是,这将为您提供16个像素的bottom overflow exception,因为您在SizedBox的{​​{1}}内还有另一个Column,并且高度固定。

![enter image description here

为了解决此问题,我们暂时将顶部PageView替换为SizedBox,并向其中添加Container属性,以查看有多少可用空间和高度。

enter image description here

因此,我们可以使用color来为我们计算高度,而不是将sizebox的高度硬编码为32。

如果打印顶部MediaQuery的高度,它将显示为597

SizedBox

我进行演示时,将SizedBox(height: MediaQuery.of(context).size.height,除以5,得到包含height的{​​{1}}小部件的119.4高度,该小部件解决了renderflow异常并维持了页面视图在中心的位置屏幕。

enter image description here

如果我们再次使用SizedBox来查看我们有多少可用空间和高度,它将看起来像:

enter image description here

因此,通过这种方法,您将不需要像您提到的那样使用PageViewContainer小部件,并且不需要更改很多代码。

工作代码如下:

Expanded

您可能需要根据需要调整高度,但是我希望这可以解决您的问题并回答您的问题。

答案 3 :(得分:0)

这可以完成,但不能使用PageView。我必须将SingleChildScrollView与行一起使用。这样做的原因是:如果您想适当地调整PageView的大小,则需要构建和布局其拥有的每个子视图。 PageView不会这样做,但Row可以,您甚至可以决定应如何定位较小的元素。如果您正确设置行中子级的宽度,那么PageController也可以使用。

请注意,这仅在页面数量较少时才能很好地工作。

class SizingPageView extends StatelessWidget {
  final ScrollPhysics physics;
  final PageController controller;
  final List<Widget> children;

  const SizingPageView({Key key, this.physics, this.controller, this.children})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    Widget transform(Widget input) {
      return SizedBox(
        width: MediaQuery.of(context).size.width,
        child: input,
      );
    }

    return SingleChildScrollView(
      physics: physics ?? PageScrollPhysics(),
      controller: controller,
      scrollDirection: Axis.horizontal,
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: children.map(transform).toList(),
      ),
    );
  }
}

class Solution extends StatelessWidget {
  final PageController pageController = PageController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Spacer(
            flex: 2,
          ),
          Icon(Icons.done, size: 112),
          SizedBox(height: 32),
          Text('BEFORE SCROLL'),
          SizingPageView(
            physics: NeverScrollableScrollPhysics(),
            controller: pageController,
            children: <Widget>[
              Column(
                crossAxisAlignment: CrossAxisAlignment.stretch,
                children: <Widget>[
                  Text('TEXT TEXT TEXT TEXT TEXT TEXT'),
                  SizedBox(height: 16),
                  Text('TEXT2 TEXT2 TEXT2 TEXT2 TEXT2 TEXT2'),
                ],
              ),
              Column(
                crossAxisAlignment: CrossAxisAlignment.stretch,
                children: <Widget>[
                  Text('TEXT TEXT TEXT TEXT TEXT TEXT'),
                ],
              ),
              Text('Third'),
            ],
          ),
          Text('AFTER SCROLL'),
          Spacer(
            flex: 2,
          ),
          FlatButton(
            child: Text('Next'),
            onPressed: () {
              var next = pageController.page.round() + 1;
              if (next >= 3) {
                next = 0;
              }
              pageController.animateToPage(
                next,
                duration: Duration(seconds: 1),
                curve: Curves.easeInOut,
              );
            },
          )
        ],
      ),
    );
  }
}

答案 4 :(得分:-1)

您只需用Flexible小部件https://api.flutter.dev/flutter/widgets/Flexible-class.html包裹小部件