This article, the seventh in a series of articles, builds on the previous one and takes a closer look at some common sense issues surrounding widgets and layouts.

Article summary address:

A complete series of articles on Flutter

A series of articles on the world outside Flutter

In chapter 6, we learned about the relationship among widgets, Elements, and RenderObjects. The Widget we are most familiar with exists as a “configuration file”, and its function in Flutter is relatively single, belonging to “existence with relatively fine granularity”. Writing code is like putting together legos. How do you put together legos? Let’s dig a little deeper and find something interesting. ( ̄▽ ̄)

Layout of monad elements

Container is definitely the most widely used layout Widget of Flutter, because it is not as “functional” as the Padding Widget. Why?

As you can see from the source code below, Container simply rewraps other “single” widgets and configures them to be “multifunctional.”

Then we see first ConstrainedBox source code, can be seen from the chart source, it is inherited SingleChildRenderObjectWidget, the key is to override the createRenderObject method, RenderConstrainedBox is returned.

This shows the relationship between widgets and RenderObjects in Article 6

RenderConstrainedBox: RenderBox, RenderBox, RenderBox, RenderBox, RenderBox

Widget RenderObject
ConstrainedBox RenderConstrainedBox

Then we continue to look at the other each Widget, you can see they are also inherit SingleChildRenderObjectWidget, and “simple” they different place is RenderObject implemented:

Widget RenderBox (RenderObject)
Align RenderPositionedBox
Padding RenderPadding
Transform RenderTransform
Offstage RenderOffstage

So we can conclude that the real layout and sizing is done in RenderBox. Different widgets achieve “differentiated” layout effects through their renderBoxes. So look for the implementation of each Widget, look for its RenderBox implementation. (And, of course, RenderSliver, which I won’t discuss here.)

The Offstage Widget controls whether or not a child is displayed using the Offstage flag. It also has a RenderOffstage Widget, as shown below. With RenderOffstage, we can see the offstage flag bit in action:

So most of the time, our widgets are laid out by implementing RenderBox. Can we throw our widgets and use RenderBox directly? The answer is obviously ok, if you idle 🥚 pain!

Flutter to treat our official “🥚 pain”, provides a class called CustomSingleChildLayout, it abstracts the object, a man named SingleChildLayoutDelegate RenderBox allows you to easily manipulate RenderBox to achieve custom effects.

Three source code, as shown in the diagram below SingleChildLayoutDelegate object provides the following interfaces, and interface is according to the sequence is called the first three, by implementing this interface, you can easily control the layout of the RenderBox location, size, etc.

2. Multi-child layout

In fact, a polychild layout is similar to a monad layout, and we can see how it relates to each other. For example:

  • Row,ColumAll inheritedFlexAnd Flex inheritsMultiChildRenderObjectWidgetAnd through theRenderFlexTo create theRenderBox;
  • StackThe same inheritanceMultiChildRenderObjectWidgetAnd through theRenderStackTo create theRenderBox;
Widget RenderBox (RenderObject)
Row/Colum/Flex RenderFlex
Stack RenderStack
Flow RenderFlow
Wrap RenderWrap

The multichild Layout also provides CustomMultiChildLayout and MultiChildLayoutDelegate for your 🥚 pain.

3. Multi-child sliding layout

Slide layout is another branch of “multi-child layout”, such as ListView, GridView and Pageview. They are much more complex in implementation. From the flow in the following figure, we can roughly know their relationship:

As we can see from the figure above, the process eventually generates two RenderObjects:

  • RenderSliverBase class for the render objects that implement scroll effects in viewports.

  • RenderViewportA render object that is bigger on the inside.

/// [RenderViewport] cannot contain [RenderBox] children directly. Instead, use
/// a [RenderSliverList], [RenderSliverFixedExtentList], [RenderSliverGrid], or
/// a [RenderSliverToBoxAdapter], for example.
Copy the code

RenderViewport can not be directly placed inside the RenderBox. RenderSliver family is used to complete the layout. Conclusion from the source: RenderViewport corresponding Widget Viewport is a MultiChildRenderObjectWidget. (you see, back to MultiChildRenderObjectWidget.)

A little more on the flow of the figure above:

  • ListView, Pageview, GridView, etc. are all Scrollable, ViewPort, and Sliver family effects. The simple non-canonical description here is: a “slideable” control with a nested “visual window” that displays children internally through “shards”.

  • The difference is that PageView does not inherit from SrollView, but is implemented directly by nesting NotificationListener and ScrollNotification.

Notice that inside TabBarView is NotificationListener + PageView

Do you feel like something’s missing? A custom sliding CustomScrollView that inherits the ScrollView and uses the slivers parameter to implement the layout. These slivers are eventually added to the ViewPort via Scrollable’s buildViewport, as shown in the following code:

CustomScrollView(
  slivers: <Widget>[
    const SliverAppBar(
      pinned: true,
      expandedHeight: 250.0,
      flexibleSpace: FlexibleSpaceBar(
        title: Text('Demo'),
      ),
    ),
    SliverGrid(
      gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
        maxCrossAxisExtent: 200.0,
        mainAxisSpacing: 10.0,
        crossAxisSpacing: 10.0,
        childAspectRatio: 4.0,
      ),
      delegate: SliverChildBuilderDelegate(
        (BuildContext context, int index) {
          return Container(
            alignment: Alignment.center,
            color: Colors.teal[100 * (index % 9)],
            child: Text('grid item $index')); }, childCount:20,
      ),
    ),
    SliverFixedExtentList(
      itemExtent: 50.0,
      delegate: SliverChildBuilderDelegate(
        (BuildContext context, int index) {
          return Container(
            alignment: Alignment.center,
            color: Colors.lightBlue[100 * (index % 9)],
            child: Text('list item $index')); },),),],)Copy the code

Have you gained a deeper understanding of the layout of Flutter after reading this article?Let’s pile up wood happily!

From there, chapter 7 is finally over! (/ / / del / / /)

Resources to recommend

  • Making: github.com/CarGuo/
  • Open Source Flutter complete project:Github.com/CarGuo/GSYG…
  • Open Source Flutter Multi-case learning:Github.com/CarGuo/GSYF…
  • Open Source Fluttre Combat Ebook Project:Github.com/CarGuo/GSYF…

Full open source project recommendation:

  • GSYGithubApp Flutter
  • GSYGithubApp React Native
  • GSYGithubAppWeex