This article mainly introduces the Flutter layout related content, the relevant knowledge points are sorted out, and triggered from the actual example, to further explain how to go to the layout.
1. Introduction
Before introducing Flutter layouts, we have to understand some layout-related features in Flutter.
1.1 Boundary constraints (box constraints)
box constraints some people also translated as box constraints, box constraints, I personally still think that boundary constraints may be more intuitive.
Boundary constraints in Flutter means that widgets can decide how to occupy the layout space according to the specified qualifications.Flutter borrows a lot of React-related things, including some layout ideas, but it doesn’t abstract away from the layout styles, but uses different widgets to realize different layouts, embedding the styles into widgets so that the user can write layouts as if they were building blocks. can be like building blocks to write the layout, writing method is very similar to React, but no style settings.
The benefit of this, I think, might be for uniform rendering. Adding styles would complicate the layout quite a bit and reduce performance a lot at the rendering level. Therefore, Flutter adds different types of layout widgets in the big direction. in the small direction, it gives very little customization and limits the layout to a limited scope, which allows the whole rendering to be unified while completing the layout, speeding up updates and rendering.
However, the disadvantages are just as obvious, a lot less flexibility, different layout methods are abstracted out of widgets, people need to know very much about widgets, increasing the cost of learning.
1.2 Types of constraints
In Flutter, widgets are rendered by their underlying RenderBox, and the constraints on the render boundary are given by the parent, and the widget resizes itself under these constraints. The constraints include a minimum and maximum width and height, and the dimensions are the specific width and height.
In Android, there are three types of width and height constraints for layouts, match_parent, wrap_content, and specific dimensions. In Flutter, there are also these three constraints.
Constraints that are as large as possible, such as Center, ListView, and so on;
Follows content size constraints such as Transform, Opacity, etc;- Specifies constraints on size, e.g. Image, Text, etc;
However, Flutter doesn’t treat widgets as absolute; these constraints are contained within the widget, unlike Android where they can be specified outside. As a result, some widgets, such as Container, will have different constraints depending on the parameters; Container is by default as large as possible, but given a size, the specific value will be prioritized. Different widgets may have different constraints depending on their setup conditions, and their sub-widgets may have different constraints; Flutter limits each widget to a different set of constraints, which will need to be taken into account when laying out the actual widgets.
2. Classification
Categorized according to constraints, many widgets are not very distinguishable, and are officially categorized according to the number of their child elements.
Layout of a single child element (child), including Container, Padding, etc.18
(currently as of May 25, 2018, and I’m sure it will be added in subsequent years, similar below);
Layout of multiple children, including Row, Column, etc.11
layout helper, such as ListView.Builder, is more efficient when there are many elements, similar to Android’s RecyclerView, which has an automatic recycling mechanism. This kind of strict sense can not be considered a kind, I think this kind of helper will be more and more.
2.1 Advantages and disadvantages
One of the daily use of more, such as Container, Padding, Center, Align, Row, Column, Stack, ListView, etc. There are also dozens of kinds.
Flutter provides close to 30 different layout widgets, or from its positioning of the widgets (not elaborated here, if you want to understand, you can look up the author’s previous article’s introduction). Comparing to other mobile development platforms, you can see that Flutter’s layout widgets are huge, which is also a reason why Flutter has a long learning curve now.
Let’s start with the pros, unified rendering and more efficient updates. However, for the average developer, it’s not going to matter too much. High performance is the most basic ability that the platform should provide, isn’t it what you should provide?
Then let’s talk about the disadvantages, it is still very laborious to master, and the layout is quite painful. Conventional layouts are fine, but when it comes to complex layouts, I feel that this can be achieved, that can also be achieved, and finally I do not know which one can be achieved.
2.2 Personal views
Flutter for performance optimization, the platform side of some of the costs transferred to the developer, but then, it is also the early days of Flutter, you can see that the overall design thinking is still very good, and only Google this kind of wheel big factory dare to do so. However, it is clear that there is less human care, different widgets between the constraints interspersed, it can also be said that the increase in the types of Flutter layout controls, is caused by its constant patching, followed by a variety of helper, but also in order to fill the pit, this piece of Flutter obviously did not deal with very well.
However, everything has a process, can not say Flutter these places do not do a good job, just seems to be more chaotic at present, the ideal architectural design, down to earth, may not be so simple, the developer’s needs are very different, with the ecology, what all good, of course, this process, is expected to be very slow.
3. widget details
In Flutter, the widgets we usually customize are usually inherited from StatefulWidget or StatelessWidget (not only these two), these two widgets are also the most commonly used two. If a control will not change its own state, it will be displayed directly after it is created, and will not change its color value, size or other attributes, this kind of widget is usually inherited from StatelessWidget, and the common ones are Container, ScrollView and so on. If a control needs to dynamically change or correspond to some states, such as the click state, color value, content area, etc., then it is generally inherited from StatefulWidget, common CheckBox, AppBar, TabBar, etc.. In fact, simply from the name can also see the difference between these two widgets, these two widgets are inherited from the Widget class.
3.1 Widget Class
The Widget class is very important in Flutter, and the ones that inherit from the Widget class are PreferredSizeWidget, ProxyWidget, RenderObjectWidget, StatefulWidget, and StatelessWidget. widgets we use every day are inherited from the Widget class.
Looking at the source code of the Widget class, the internal implementation is very simple, the constructor is as follows
const Widget({ this.key });
final Key key;
The role of the key, as stated clearly in the note, is to control the replacement of widgets in the widget tree. The Key class is a unique identifier for Widgets, Element and SemanticsNode, and is inherited from Key as well as LocalKey and GlobalKey.
3.2 State
Before we get to StatefulWidget, let’s talk about State.State does two things:
- It can be read synchronously while the widget is being built;
- It may be changed during the life cycle of the widget.
3.2.1 State life cycle
There are four states in the life cycle of State:
created: the State.initState method is called when the State object is created;
initialized: when the State object is created, but not ready to build, State.didChangeDependencies is called at this time;
ready: when the State object is ready to build and State.dispose is not called;
defunct: the State object cannot be constructed after State.dispose is called.
The full life cycle is as follows:
StatefulWidget.createState is called when creating a State object;
Associated with a BuildContext, it can be considered loaded (mounted);- Call initState;
Calls didChangeDependencies;
After the above steps, the State object is fully initialized and build is called;
Calls didUpdateWidget if needed;
If you are in development mode, hot loading will call reassemble;
If its subtree contains State objects that need to be removed, deactivate is called;
Calling dispose,State objects are not constructed later;
When dispose is called, the State object is unmounted, and there is no way to remount a State object that has been disposed.
3.2.2 setState
One of the more important methods in State is setState
, which updates the widget when the state is modified. For example, if you click on a CheckBox, it will switch between checked and unchecked state, which is achieved by modifying the state.
Check out the setState source code, which will throw exceptions in some unusual cases:
- Passed in as null;
- In defunct phase;
The created stage has not been loaded (mounted);- parameter returns a Future object.
After checking a series of exceptions, the final call code is as follows:
_element.markNeedsBuild();
Inside markNeedsBuild, it rebuilds (rebuilds) on the next frame by marking the element as diry. You can see that setState doesn’t take effect immediately, it just marks the widget, and the actual rebuild operation waits until the next frame.
3.3 StatefulWidget and StatelessWidget
StatefulWidget and StatelessWidget are as follows
A StatelessWidget can be built with several different BuildContexts, and a StatefulWidget creates a State object for each BuildContext.
3.3.1 StatelessWidget
For StatelessWidgets, the build method is called in the following three cases, the
- The widget is inserted into the tree for the first time;
The widget’s parent node has changed its configuration;
The widget dependency InheritedWidget changed.
class GreenFrog extends StatelessWidget {
const GreenFrog({ Key key }) : super(key: key);
@override
Widget build(BuildContext context) {
return new Container(color: const Color(0xFF2DBD3A));
}
}
3.3.2 StatefulWidget
There are two main categories of StatefulWidgets:
Creates resources in initState and destroys them in dispose, but does not rely on InheritedWidget or call the setState method, which is basically used as the root of an application or page;
Use setState or rely on InheritedWidget, which will be rebuilt many times during the business lifecycle.
class YellowBird extends StatefulWidget {
const YellowBird({ Key key }) : super(key: key);
@override
_YellowBirdState createState() => new _YellowBirdState();
}
class _YellowBirdState extends State<YellowBird> {
@override
Widget build(BuildContext context) {
return new Container(color: const Color(0xFFFFE306));
}
}
4. How it is laid out
Each page design is different, the same page can choose a different layout, if simply say how to layout, I think it is unrealistic, we can refer to the official Flutter layout tutorial. Next, I, through a simple page, to step by step to disassemble the layout process. The whole process, basically in accordance with the disassembly, component packaging, specific layout of the three steps.
4.1 Disassembly
4.1.1 Overall disassembly
According to the design diagram, you can see that the whole when the branch display, so the outermost layer is a Column element
The first row is the title, which involves an asymmetrical layout, and can be done with a Stack or a Row; with a Row, you need to fill in a blank widget placeholder on the right. It may also be possible to use an AppBar, and the same effect can be achieved by removing the bottom shadow;
The second row can be seen as a Row, laid out in two pieces. The right part, which involves stacking, will be considered a Stack;
The third row is more complex, the overall look, but also a line for the display, so the outermost layer of a Column. the middle of the text part of the need to be based on the number of automatic line breaks, so consider the use of Wrap. pre-prepared for this place involves stacking, consider the implementation of the Stack;- The fourth row can be thought of as a Row, laid out in three pieces;
- The fifth row can be seen as a Row, laid out in two pieces.
For the spacing between each row, consider using Padding or Container.
Through the above such a step by step analysis, basically have an understanding of the general layout, the outermost control roughly choose the right (as long as it can be realized, it is the complexity as well as the efficiency of the problem), and then step by step to dismantle the elements of each line, if there is a duplication of or feel that can be encapsulated out of the part, then the next step.
4.1.2 Partial disassembly
The disassembly of each line is roughly along the same lines, so the author will not explain it here.
4.2 Component Packaging
For example, the above, the author would like to encapsulate this display of the fourth row, think the future layout may be used, so in this step, you can first pull this piece out of a control. The use of Row’s mainAxisAlignment and Expanded to achieve this effect, the specific implementation of the author will not describe in detail.
After this step, the overall planning and design drawings are available, the individual components are available, and the next step is assembly.
4.3 Specific layout
Specific layout design to some of the details of the place, such as spacing (Padding or Container), left-right center (Align), click events (GestureDetector) and rounded corners (ClipRRect) and some other special cases, basically nested, layer by layer to achieve.
In the actual layout, I actually use Scaffold, the top of the AppBar will be directly removed from the shadow to achieve the effect, the body part of the realization of the contents of the 2-5 lines. The outermost set of a Column can also be realized, in essence, there is no difference, running the effect is shown below.