繁体   English   中英

Flutter:了解无状态/有状态小部件如何布局其子部件?

[英]Flutter: Understanding how does a Stateless/Statefull widget layout its child?

长话短说

如果没有关于小部件的文档,我如何知道小部件的布局规则(它会从其父级请求什么大小以及它将向其子级传递什么约束)?

问题详情

我有这个非常基本的应用程序

void main() {
  runApp(
    Container(
        color: Colors.red,
        width: 20,
        height: 20,
      ),
  );
}

我期望Container的宽度和高度为 20,但我得到了一个填满整个屏幕的Container

flutter.dev上阅读这篇关于理解约束的文章,在其最后一部分“学习特定小部件的布局规则”中,他们提到如何通过找到createRenderObject方法然后找到performLayout方法来做到这一点。

但是,此createRenderObject方法仅适用于RenderObjectWidget的子类。 例如,浏览Transform小部件的代码,我发现createRenderObject返回一个RenderTransform ,它扩展了RenderProxyBox ,最终performLayout实现为:

  @override
  void performLayout() {
    if (child != null) {
      child!.layout(constraints, parentUsesSize: true);
      size = child!.size;
    } else {
      size = computeSizeForNoChild(constraints);
    }
  }

我可以得出结论,由于这条线size = child.;size;Transform小部件最终将采用其子部件的大小。 .

但在上面的Container的情况下,是直接扩展StatelessWidget 通过浏览它的代码,我找不到方法performLayoutcreateRenderObject ,我只能找到createElement ,但我正在寻找渲染树中与 Container 而不是元素关联的RenderObject

问题

所以问题是如何找到与无状态小部件/有状态小部件相关联的渲染 object,以便了解该小部件将为其子项提供的布局规则,并在这种情况下自行遵循这些规则?

你有一定道理。 我会说我的文章在这方面不够精确。

小部件不需要创建RenderObject 相反,它可以使用自己创建RenderObjects的其他小部件的组合。

如果小部件是其他小部件的组合,则无需查看performLayout您只需查看该小部件的build方法即可了解它在做什么。 对于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!;
  }

如您所见,它是根据其他小部件定义的。 并且这些widget也可以根据其他widget等来定义。 但是在某些时候,您将到达创建RenderObject的小部件。


关于Container不是 20x20 的原因,这是因为,正如文章所解释的,尺寸是由父母设置的。 因此Container的大小由Container的父级设置,在这种情况下是screen 并且屏幕总是强制它的孩子占据所有可用空间,在这种情况下忽略了Container想要成为 20x20 的愿望。 这里的修复是给Container另一个父级。 一种允许Container选择自己的大小。 例如, CenterAlign都会让这种情况发生,这就是您可以通过执行以下操作来解决问题的原因:

void main() {
  runApp(
    Center( 
      child: Container(
        color: Colors.red,
        width: 20,
        height: 20,
      ),),);
}

至于为什么屏幕会强迫它的孩子占据所有可用空间:这正是 Flutter 创作者决定的方式。 如果你深入研究 Flutter 的代码,你会在那里找到它。 但最好记住这个事实。

希望能帮助到你!

您在这里缺少架构,我们需要使用Scaffold 喜欢

void main() {
  runApp(
    MaterialApp(
        home: Scaffold(
      body: Container(
        color: Colors.red,
        width: 20,
        height: 20,
      ),
    )),
  );
}

嗯,不要太复杂了。 这是我可以向您解释的最简单的事情,让我们将应用程序分成 4 个不同的层。

  1. 应用程序 -> 您可以使用MaterialApp创建一个材质应用程序
  2. Widget Pages -> 您可以选择使用StatelessStatefulWidget ,这取决于您的需要。 如果你需要动态的,有很多变化的状态,你可以使用StatefulWidget或者你可以使用StatelessWidget创建一个静态页面。
  3. Scaffold -> Widget 页面必须返回一个脚手架才能形成一个材质页面,这里可以使用Scaffold ,可以添加 appBar、fab、body、bottomNavigationBar 等。
  4. 小部件 -> 小部件可以是任何东西,可以是脚手架 appBar 的 appBar,也可以是 ListView、GridView 或自定义小部件。

所以你的代码必须是这样的

/// 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM