background
The deep level of nesting is a problem that disturbs many people who are new to Flutter. Not only is it uncomfortable to look at, but it also affects the coding experience.
The big guns will tell you that you should break down your nested code (custom widgets or extract build methods) to reduce the level of nesting. This is indeed an effective method, and if there is any other way, this article will show you another way to reduce the nesting hierarchy.
Too much nesting affects the look and feel of the code
This code demonstrates what is called: nested hell
class Test extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Demo'),),
body: Container(
child: Offstage(
offstage: false,
child: ListView(
children: <Widget>[
Container(
color: Colors.white,
padding: EdgeInsets.all(20),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Icon(Icons.phone),
Text("amy"),
],
),
),
Container(
color: Colors.white,
padding: EdgeInsets.all(20),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Icon(Icons.phone),
Text("billy""" "" "" "" "" "" "" }}Copy the code
With the build method extracted, the nesting hierarchy is significantly improved
class Test extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Demo'),),
body: Container(
child: Offstage(
offstage: false,
child: ListView(
children: <Widget>[
buildItem("amy"),
buildItem("billy""" "" "" "" "" " } Container buildItem(String name) {
return Container(
color: Colors.white,
padding: EdgeInsets.all(20), child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: <Widget>[ Icon(Icons.phone), Text(name), ], ), ); }}Copy the code
Can we continue to optimize?
Custom extension functions
For example: I want to add marginTop:10 to the second Textwidget in the code below
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(10),
child: Column(
children: <Widget>[
Text('billy'),
Text('say hello'), //add margin top??],),); }Copy the code
At this point, I wish I could write this:
Obviously, the Flutter does not support this writing, but fortunately: Dart2.7 officially announced support for Extension Methods when it was released.
In fact, the dart extension function has been supported since dart 2.6.0. If the dart version set in pubspec.yaml is later than 2.6.0, an alert will appear, such as: Environment: SDK:"> = 2.1.0 < 3.0.0"Warning: Extension methods weren't supported until version 2.6.0Copy the code
Let’s define an extension function
extension WidgetExt on Widget {
Container intoContainer({
// Copy all arguments to the Container constructor (except the child field)
Key key,
AlignmentGeometry alignment,
EdgeInsetsGeometry padding,
Color color,
Decoration decoration,
Decoration foregroundDecoration,
double width,
double height,
BoxConstraints constraints,
EdgeInsetsGeometry margin,
Matrix4 transform,
}) {
// Call the Container constructor with the current Widget object as the child parameter
return Container(
key: key,
alignment: alignment,
padding: padding,
color: color,
decoration: decoration,
foregroundDecoration: foregroundDecoration,
width: width,
height: height,
constraints: constraints,
margin: margin,
transform: transform,
child: this,); }}Copy the code
All widget objects now have an intoContainer(…) The Container constructor takes the same parameters as the Container constructor, so we can write:
Containers other than Containers can be extended in the same way. As a result, the programming experience is greatly improved.
Chained calls can also be supported:
Text("billy")
.intoExpanded(flex: 1)
.intoContainer(color: Colors.white)
Copy the code
Some widgets have more than one child. You can add the following extension functions:
extension WidgetExt on Widget {
// Add an adjacent widget, return List< widget >
List<Widget> addNeighbor(Widget widget) {
return <Widget>[this, widget];
}
// Add a widget container with a single child
// Container, Padding, etc...
}
extension WidgetListExt<T extends Widget> on List<T> {
// Add another adjacent Widget to the sublist
and return the current List
List<Widget> addNeighbor(Widget widget) {
return this. add(widget); } Row intoRow({ Key key, MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start, MainAxisSize mainAxisSize = MainAxisSize.max, CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center, TextDirection textDirection, VerticalDirection verticalDirection = VerticalDirection.down, TextBaseline textBaseline, {})return Row(
key: key,
mainAxisAlignment: mainAxisAlignment,
mainAxisSize: mainAxisSize,
crossAxisAlignment: crossAxisAlignment,
textDirection: textDirection,
verticalDirection: verticalDirection,
textBaseline: textBaseline,
children: this,); }// Add another widget container with multiple children
// Column, ListView, etc...
}
Copy the code
Use extension functions to solve the problem of too deep nesting
Going back to the original nesting hell of this article, our code can now be written like this
class Test extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Demo'),),
body: buildItem("amy")
.addNeighbor(buildItem("billy"),)
.intoListView()
.intoOffstage(offstage: false)
.intoContainer()
);
}
Container buildItem(String name) {
return Icon(Icons.phone)
.addNeighbor(Text(name))
.intoRow(crossAxisAlignment: CrossAxisAlignment.center,)
.intoContainer(color: Colors.white, padding: EdgeInsets.all(20)); }}Copy the code
To make our code more chaining, let’s define another static method
class WidgetChain {
static Widget addNeighbor(Widget widget) {
returnwidget; }}Copy the code
In addition, define an extension of the mapping from the data to the widget
extension ListExt<T> on List<T> {
List<Widget> buildAllAsWidget(Widget Function(T) builder) {
return this.map<Widget>((item) {
returnbuilder(item); }).toList(); }}Copy the code
Now, the code looks like this:
class Test extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Demo'),),
body: ["amy"."billy"]
.buildAllAsWidget((name) =>
WidgetChain
.addNeighbor(Icon(Icons.phone))
.addNeighbor(Text(name))
.intoRow(crossAxisAlignment: CrossAxisAlignment.center,)
.intoContainer(color: Colors.white, padding: EdgeInsets.all(20),)
)
.intoListView()
.intoOffstage(offstage: false) .intoContainer() ); }}Copy the code
It is worth noting that extension functions (which are not nested) and constructors (which are nested) can be mixed. The code above can also be written like this (Container and Offstage are replaced with constructors) :
class Test extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Demo'),),
body: Container(
child: Offstage(
offstage: false,
child: ["amy"."billy"]
.buildAllAsWidget((name) =>
WidgetChain
.addNeighbor(Icon(Icons.phone))
.addNeighbor(Text(name))
.intoRow(crossAxisAlignment: CrossAxisAlignment.center,)
.intoContainer(color: Colors.white, padding: EdgeInsets.all(20),) ) .intoListView() ), ), ); }}Copy the code
Do you want to try this extension function? I’ve packaged the into extension function for common widgets for you to eat:
dependencies:
widget_chain: ^ 0.1.0 from
Copy the code
Import:
import 'package:widget_chain/widget_chain.dart';
Copy the code
Then we can take off!
If someone gives you a laugh flutter is too deep, you can throw widget_chain to them 😀
conclusion
This article introduces the nested hell in Flutter and uses the extension function to solve the nested hell problem in Flutter.
Due to the expansion of large space function call can affect the reading experience of code (because of intoXxx function call order and widget hierarchy are opposite), needs to retain key nested hierarchical structure to make the layout of the hierarchy to keep clear, in this paper, the extension function of support and the mixture of constructor, the specific use of to what extent, depends on how much you own choice
Additional instructions after editing of 2019-12-30
This article was originally published with information on how to use extension functions to improve the coding experience. Because the way I added the parent container when I was writing my Flutter code was:
- Cut the widget code to add the parent container
- Write the parent container and its properties at the cut
- Add the child under the parent container node and paste the content you just cut as the value of the child
Later, in the Wechat group (Gityuan Flutter technology communication group), the big guy @Shudaffi informed me that the IDE actually provides shortcut keys for adding parent containers (place the cursor on the Widget, then Alt + Enter, select the parent container to add), which is very convenient.
Before I really don’t know, special thanks big guy: @shu Dafei
Therefore, this article was reedited to focus on how to use extension functions in a different coding style to solve the problem of the deep level of nesting of flutter. I hope you enjoy it!