我在渲染树上遇到类似于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'),
],
),
],
),
],
),
);
}
}
以便显示出来):
PageView
这是我可以解决的方法,但它并不完美,我将在后面显示:
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,),
],
),
);
}
}
和Spacer
或Flexible
始终尝试保持其与Expanded
权重的比例,这意味着对于较小的显示器,{ {1}}不会消失,它将保留在那里,而第一张和所需的图像不会有这样的空白空间。正如我们在下图中看到的,垫片始终在其中。此外,我还必须针对此设计中的每次更改计算flex
因子,而在我的第一个代码示例中,小部件将自动将其大小调整到屏幕中心。
我如何指示Spacer
尽可能的小,而不是尽可能地扩展,因为这是我在网上可以找到的唯一解决方案?
答案 0 :(得分:3)
您正在请求PageView内的项目来设置PageView本身的大小。这不适用于标准类。如果查看其源代码,则会发现它在Scrollable中使用了视口。视口会获取最大大小,并忽略子项(条形)的大小。您将需要使用另一种方法,或者创建一个自定义的PageView,也许是使用ShrinkWrappingViewport的一种。
https://api.flutter.dev/flutter/widgets/ShrinkWrappingViewport-class.html
答案 1 :(得分:2)
PageView
使用SliverFillViewport
来包装给定的children
。
此SliverFillViewport
并不关心children
有多小。它只关心它可以从其父空间中占据多少空间。因此Flex
和Align
小部件是无用的。
当前使用PageView
,除非我们进行一些计算并使用SizedBox
或ConstrainedBox
,否则极有可能无法实现。
我也找到了这个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
,并且高度固定。
为了解决此问题,我们暂时将顶部PageView
替换为SizedBox
,并向其中添加Container
属性,以查看有多少可用空间和高度。
因此,我们可以使用color
来为我们计算高度,而不是将sizebox的高度硬编码为32。
如果打印顶部MediaQuery
的高度,它将显示为597
SizedBox
我进行演示时,将SizedBox(height: MediaQuery.of(context).size.height,
除以5,得到包含height
的{{1}}小部件的119.4高度,该小部件解决了renderflow异常并维持了页面视图在中心的位置屏幕。
如果我们再次使用SizedBox
来查看我们有多少可用空间和高度,它将看起来像:
因此,通过这种方法,您将不需要像您提到的那样使用PageView
或Container
小部件,并且不需要更改很多代码。
工作代码如下:
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包裹小部件