Flutter aims for linear performance for initial layout, and sublinear layout performance in the common case of subsequently updating an existing layout. Typically, the amount of time spent in layout should scale more slowly than the number of render objects. Flutter performs one layout per frame, and the layout algorithm works in a single pass.
Constraints are passed down the tree by parent objects calling the layout method on each of their children. The children recursively perform their own layout and then return geometry up the tree by returning from their layout method. Importantly, once a render object has returned from its layout method, that render object will not be visited again 1 until the layout for the next frame. This approach combines what might otherwise be separate measure and layout passes into a single pass and, as a result, each render object is visited at most twice 2 during layout: once on the way down the tree, and once on the way up the tree.
Flutter has several specializations of this general protocol. The most common specialization is RenderBox , which operates in two-dimensional, cartesian coordinates. In box layout, the constraints are a min and max width and a min and max height. During layout, the child determines its geometry by choosing a size within these bounds. As a result, the parent is free to reposition the child without needing to recompute its layout.
More generally, during layout, the only information that flows from parent to child are the constraints and the only information that flows from child to parent is the geometry. These invariants can reduce the amount of work required during layout:. If the child has not marked its own layout as dirty, the child can return immediately from layout, cutting off the walk, as long as the parent gives the child the same constraints as the child received during the previous layout.
If, as often happens, the parent does not use the size information, then the parent need not recompute its layout if the child selects a new size because the parent is guaranteed that the new size will conform to the existing constraints. Tight constraints are those that can be satisfied by exactly one valid geometry. For example, if the min and max widths are equal to each other and the min and max heights are equal to each other, the only size that satisfies those constraints is one with that width and height.
A render object can declare that it uses the constraints provided by the parent only to determine its geometry. As a result of these optimizations, when the render object tree contains dirty nodes, only those nodes and a limited part of the subtree around them are visited during layout. After being built, the widgets are held by the element tree , which retains the logical structure of the user interface. The element tree is necessary because the widgets themselves are immutable , which means among other things , they cannot remember their parent or child relationships with other widgets.
- Inside Georgia's $200 million quest to take down Alabama.
- Inside Flutter - Flutter?
- St. Lucys Home for Girls Raised by Wolves.
The element tree also holds the state objects associated with stateful widgets. In response to user input or other stimuli , an element can become dirty, for example if the developer calls setState on the associated state object. The framework keeps a list of dirty elements and jumps directly to them during the build phase, skipping over clean elements.
During the build phase, information flows unidirectionally down the element tree, which means each element is visited at most once during the build phase. Once cleaned, an element cannot become dirty again because, by induction, all its ancestor elements are also clean 4. Because widgets are immutable , if an element has not marked itself as dirty, the element can return immediately from build, cutting off the walk, if the parent rebuilds the element with an identical widget.
Moreover, the element need only compare the object identity of the two widget references in order to establish that the new widget is the same as the old widget. Developers exploit this optimization to implement the reprojection pattern, in which a widget includes a prebuilt child widget stored as a member variable in its build. During build, Flutter also avoids walking the parent chain using InheritedWidgets. To avoid these parent walks, the framework pushes information down the element tree by maintaining a hash table of InheritedWidget s at each element.
Typically, many elements will reference the same hash table, which changes only at elements that introduce a new InheritedWidget. Contrary to popular belief, Flutter does not employ a tree-diffing algorithm. Instead, the framework decides whether to reuse elements by examining the child list for each element independently using an O N algorithm. The child list reconciliation algorithm optimizes for the following cases:.
The general approach is to match up the beginning and end of both child lists by comparing the runtime type and key of each widget, potentially finding a non-empty range in the middle of each list that contains all the unmatched children. The framework then places the children in the range in the old child list into a hash table based on their keys. Next, the framework walks the range in the new child list and queries the hash table by key for matches.
Unmatched children are discarded and rebuilt from scratch whereas matched children are rebuilt with their new widgets. Reusing elements is important for performance because elements own two critical pieces of data: the state for stateful widgets and the underlying render objects.
What's Inside the Box? - Play it now at dacanegi.tk
When the framework is able to reuse an element, the state for that logical part of the user interface is preserved and the layout information computed previously can be reused, often avoiding entire subtree walks. In fact, reusing elements is so valuable that Flutter supports non-local tree mutations that preserve state and layout information.
Developers can perform a non-local tree mutation by associating a GlobalKey with one of their widgets. Each global key is unique throughout the entire application and is registered with a thread-specific hash table. During the build phase, the developer can move a widget with a global key to an arbitrary location in the element tree. Rather than building a fresh element at that location, the framework will check the hash table and reparent the existing element from its previous location to its new location, preserving the entire subtree.
The render objects in the reparented subtree are able to preserve their layout information because the layout constraints are the only information that flows from parent to child in the render tree. The new parent is marked dirty for layout because its child list has changed, but if the new parent passes the child the same layout constraints the child received from its old parent, the child can return immediately from layout, cutting off the walk.
Global keys and non-local tree mutations are used extensively by developers to achieve effects such as hero transitions and navigation.
In addition to these algorithmic optimizations, achieving aggressive composability also relies on several important constant-factor optimizations. These optimizations are most important at the leaves of the major algorithms discussed above.
Child-model agnostic. For example, the RenderBox class has an abstract visitChildren method rather than a concrete firstChild and nextSibling interface. Many subclasses support only a single child, held directly as a member variable, rather than a list of children. For example, RenderPadding supports only a single child and, as a result, has a simpler layout method that takes less time to execute.
Visual render tree, logical widget tree.
In Flutter, the render tree operates in a device-independent, visual coordinate system, which means smaller values in the x coordinate are always towards the left, even if the current reading direction is right-to-left. The widget tree typically operates in logical coordinates, meaning with start and end values whose visual interpretation depends on the reading direction. The transformation from logical to visual coordinates is done in the handoff between the widget tree and the render tree. This approach is more efficient because layout and painting calculations in the render tree happen more often than the widget-to-render tree handoff and can avoid repeated coordinate conversions.
Text handled by a specialized render object. The vast majority of render objects are ignorant of the complexities of text. Instead, text is handled by a specialized render object, RenderParagraph , which is a leaf in the render tree. Rather than subclassing a text-aware render object, developers incorporate text into their user interface using composition. This pattern means RenderParagraph can avoid recomputing its text layout as long as its parent supplies the same layout constraints, which is common, even during tree surgery.
Observable objects. Flutter uses both the model-observation and the reactive paradigms.
Obviously, the reactive paradigm is dominant, but Flutter uses observable model objects for some leaf data structures. For example, Animations notify an observer list when their value changes. Flutter hands off these observable objects from the widget tree to the render tree, which observes them directly and invalidates only the appropriate stage of the pipeline when they change.
Taken together and summed over the large trees created by aggressive composition, these optimizations have a substantial effect on performance. An obvious simplification would be to combine these trees into one tree. However, in practice there are a number of benefits to having these trees be separate:. When the layout changes, only the relevant parts of the layout tree need to be walked. Due to composition, the element tree frequently has many additional nodes that would have to be skipped.
The clearer separation of concerns allows the widget protocol and the render object protocol to each be specialized to their specific needs, simplifying the API surface and thus lowering the risk of bugs and the testing burden. Type safety. The render object tree can be more type safe since it can guarantee at runtime that children will be of the appropriate type each coordinate system, e. Composition widgets can be agnostic about the coordinate system used during layout for example, the same widget exposing a part of the app model could be used in both a box layout and a sliver layout , and thus in the element tree, verifying the type of render objects would require a tree walk.
Infinite scrolling lists are notoriously difficult for toolkits. Flutter supports infinite scrolling lists with a simple interface based on the builder pattern, in which a ListView uses a callback to build widgets on demand as they become visible to the user during scrolling. Supporting this feature requires viewport-aware layout and building widgets on demand. Like most things in Flutter, scrollable widgets are built using composition.
However, rather than having RenderBox children, a viewport has RenderSliver children, known as slivers , which have a viewport-aware layout protocol. The sliver layout protocol matches the structure of the box layout protocol in that parents pass constraints down to their children and receive geometry in return. However, the constraint and geometry data differs between the two protocols. In the sliver protocol, children are given information about the viewport, including the amount of visible space remaining.
The geometry data they return enables a variety of scroll-linked effects, including collapsible headers and parallax. Different slivers fill the space available in the viewport in different ways. For example, a sliver that produces a linear list of children lays out each child in order until the sliver either runs out of children or runs out of space. Similarly, a sliver that produces a two-dimensional grid of children fills only the portion of its grid that is visible.
Because they are aware of how much space is visible, slivers can produce a finite number of children even if they have the potential to produce an unbounded number of children. Slivers can be composed to create bespoke scrollable layouts and effects. For example, a single viewport can have a collapsible header followed by a linear list and then a grid.
All three slivers will cooperate through the sliver layout protocol to produce only those children that are actually visible through the viewport, regardless of whether those children belong to the header, the list, or the grid. If Flutter had a strict build-then-layout-then-paint pipeline, the foregoing would be insufficient to implement an infinite scrolling list because the information about how much space is visible through the viewport is available only during the layout phase. Without additional machinery, the layout phase is too late to build the widgets necessary to fill the space.
Flutter solves this problem by interleaving the build and layout phases of the pipeline. At any point in the layout phase, the framework can start building new widgets on demand as long as those widgets are descendants of the render object currently performing layout. Interleaving build and layout is possible only because of the strict controls on information propagation in the build and layout algorithms.
Specifically, during the build phase, information can propagate only down the tree. When a render object is performing layout, the layout walk has not visited the subtree below that render object, which means writes generated by building in that subtree cannot invalidate any information that has entered the layout calculation thus far. Additionally, linear reconciliation and tree surgery are essential for efficiently updating elements during scrolling and for modifying the render tree when elements are scrolled into and out of view at the edge of the viewport.
Use the Dropbox badge to see the version history of any file saved to your Dropbox account. It can help you recover an o. Learn how to add a shared folder to your account, and get help accessing shared folders that you were invited to. Can I share a folder that's inside another shared folder?
However, you do have some other options: Remove the subfolder from the shared folder, and then share it Move your folder to a regular not shared folder, or unshare the folder that contains it You can place multiple shared folders inside of one regular not shared folder. New to Dropbox? How helpful was this article?
Let us know how we can improve: Thanks for your feedback! Let us know how this article helped: Submit Thanks for your feedback! Related articles Community answers. Related articles. Related articles Troubleshooting shared links Access version history with the Dropbox badge Add shared folders.
Copyright 2019 - All Right Reserved