[英]Flutter: Understanding how does a Stateless/Statefull widget layout its child?
How can I know the layout rules of a widget (what size will it request from its parent and what constraints will it pass to its children) if there is no documentation about it?如果没有关于小部件的文档,我如何知道小部件的布局规则(它会从其父级请求什么大小以及它将向其子级传递什么约束)?
I have this very basic app我有这个非常基本的应用程序
void main() {
runApp(
Container(
color: Colors.red,
width: 20,
height: 20,
),
);
}
I was expecting the Container
to have width and height of 20 but I got a Container
that filled up the whole screen.我期望Container
的宽度和高度为 20,但我得到了一个填满整个屏幕的Container
。
Reading this article on flutter.dev about understanding constraints, in its last part called "Learning the layout rules for specific widgets " , they mention how to do this by finding the createRenderObject
method and then finding the performLayout
method.在flutter.dev上阅读这篇关于理解约束的文章,在其最后一部分“学习特定小部件的布局规则”中,他们提到如何通过找到createRenderObject
方法然后找到performLayout
方法来做到这一点。
However this createRenderObject
method is only available for subclasses of RenderObjectWidget
.但是,此createRenderObject
方法仅适用于RenderObjectWidget
的子类。 For example, navigating through code of Transform
widget, I find createRenderObject
that returns a RenderTransform
, that extends RenderProxyBox
, which finally implements performLayout
as:例如,浏览Transform
小部件的代码,我发现createRenderObject
返回一个RenderTransform
,它扩展了RenderProxyBox
,最终performLayout
实现为:
@override
void performLayout() {
if (child != null) {
child!.layout(constraints, parentUsesSize: true);
size = child!.size;
} else {
size = computeSizeForNoChild(constraints);
}
}
I can conclude that Transform
widget will finally take the size of its child due to this line size = child.;size;
我可以得出结论,由于这条线size = child.;size;
, Transform
小部件最终将采用其子部件的大小。 . .
But in case of Container
above, is directly extends StatelessWidget
.但在上面的Container
的情况下,是直接扩展StatelessWidget
。 I couldn't find by navigating through its code the methods performLayout
and createRenderObject
, I could only find createElement
, but I am looking for the RenderObject
in the render tree associated with the Container and not the element.通过浏览它的代码,我找不到方法performLayout
和createRenderObject
,我只能找到createElement
,但我正在寻找渲染树中与 Container 而不是元素关联的RenderObject
。
So the question is how to find this render object associated with a stateless widget/stateful widget in order to know the layout rules that this widget will give to its children and will follow them itself in this case?所以问题是如何找到与无状态小部件/有状态小部件相关联的渲染 object,以便了解该小部件将为其子项提供的布局规则,并在这种情况下自行遵循这些规则?
You have a point.你有一定道理。 I'd say my article is imprecise in that regard.我会说我的文章在这方面不够精确。
A widget doesn't need to create a RenderObject
.小部件不需要创建RenderObject
。 It can, instead, use a composition of other widgets that create RenderObjects
themselves.相反,它可以使用自己创建RenderObjects
的其他小部件的组合。
If a widget is a composition of other widgets, then instead of looking at the performLayout
you can simply look at that widget's build
method to see what it's doing.如果小部件是其他小部件的组合,则无需查看performLayout
您只需查看该小部件的build
方法即可了解它在做什么。 In the case of a Container
, this is its build
method:对于Container
,这是它的build
方法:
Widget build(BuildContext context) {
Widget? current = child;
if (child == null && (constraints == null || !constraints!.isTight)) {
current = LimitedBox(
maxWidth: 0.0,
maxHeight: 0.0,
child: ConstrainedBox(constraints: const BoxConstraints.expand()),
);
}
if (alignment != null)
current = Align(alignment: alignment!, child: current);
final EdgeInsetsGeometry? effectivePadding = _paddingIncludingDecoration;
if (effectivePadding != null)
current = Padding(padding: effectivePadding, child: current);
if (color != null)
current = ColoredBox(color: color!, child: current);
if (clipBehavior != Clip.none) {
assert(decoration != null);
current = ClipPath(
clipper: _DecorationClipper(
textDirection: Directionality.maybeOf(context),
decoration: decoration!,
),
clipBehavior: clipBehavior,
child: current,
);
}
if (decoration != null)
current = DecoratedBox(decoration: decoration!, child: current);
if (foregroundDecoration != null) {
current = DecoratedBox(
decoration: foregroundDecoration!,
position: DecorationPosition.foreground,
child: current,
);
}
if (constraints != null)
current = ConstrainedBox(constraints: constraints!, child: current);
if (margin != null)
current = Padding(padding: margin!, child: current);
if (transform != null)
current = Transform(transform: transform!, alignment: transformAlignment, child: current);
return current!;
}
As you can see, it is defined in terms of other widgets.如您所见,它是根据其他小部件定义的。 And these widgets may also be defined in terms of other widgets and so on.并且这些widget也可以根据其他widget等来定义。 But at some point you will reach the widgets that create the RenderObject
s.但是在某些时候,您将到达创建RenderObject
的小部件。
Regarding the reason why the Container
is not 20x20, it's because, as the article explains, sizes are set by parents.关于Container
不是 20x20 的原因,这是因为,正如文章所解释的,尺寸是由父母设置的。 So the Container
s size is set by the Container
's parent, which in this case is the screen .因此Container
的大小由Container
的父级设置,在这种情况下是screen 。 And the screen always forces its child to occupy all the available space, in this case ignoring the Container
's desire to be 20x20.并且屏幕总是强制它的孩子占据所有可用空间,在这种情况下忽略了Container
想要成为 20x20 的愿望。 The fix here is giving the Container
another parent.这里的修复是给Container
另一个父级。 One which allows the Container
to choose its own size.一种允许Container
选择自己的大小。 For example, both Center
and Align
will let that happen, and that's why you can fix the problem by doing:例如, Center
和Align
都会让这种情况发生,这就是您可以通过执行以下操作来解决问题的原因:
void main() {
runApp(
Center(
child: Container(
color: Colors.red,
width: 20,
height: 20,
),),);
}
As to why the screen forces its child to occupy all the available space: That's just the way Flutter creators decided it should be.至于为什么屏幕会强迫它的孩子占据所有可用空间:这正是 Flutter 创作者决定的方式。 If you dig into Flutter's code you will find it there.如果你深入研究 Flutter 的代码,你会在那里找到它。 But it's probably best that you just remember this fact.但最好记住这个事实。
Hope it helps!希望能帮助到你!
Meh, don't get too complex yet.嗯,不要太复杂了。 here is simplest thing i can explain to you, lets separate an application into 4 different layer.这是我可以向您解释的最简单的事情,让我们将应用程序分成 4 个不同的层。
MaterialApp
to create an material application应用程序 -> 您可以使用MaterialApp
创建一个材质应用程序Stateless
or StatefulWidget
, it depends on your need. Widget Pages -> 您可以选择使用Stateless
或StatefulWidget
,这取决于您的需要。 If you need dynamic, with aa lot of changing states you can use StatefulWidget
or you can create a static page with StatelessWidget
.如果你需要动态的,有很多变化的状态,你可以使用StatefulWidget
或者你可以使用StatelessWidget
创建一个静态页面。Scaffold
, you can add appBar, fab, body, bottomNavigationBar & etc. Scaffold -> Widget 页面必须返回一个脚手架才能形成一个材质页面,这里可以使用Scaffold
,可以添加 appBar、fab、body、bottomNavigationBar 等。So your code must be look like this, kind of所以你的代码必须是这样的
/// This block is the first point of your application, this will run your application
void main() {
runApp(myApp());
}
/// Then you need an material app, this part should return your app
class MyApp extends StatelessWidget{
/// This is very important, both StatelessWidget / StatefullWidget
/// always having a build method. It's should returning a Widget. But in this case we will return an material application
@override
Widget build(BuildContext context){
return MaterialApp(
home: MyHome(),
),
}
}
class MyHome extends StatelessWidget{
/// This is home page, it's have Scaffold so the page will using, material guidelines.
@override
Widget build(BuildContext context){
return Scaffold(
body:Container(
color: Colors.red,
width: 20,
height: 20,
),
);
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.