嵌套層級深的問題讓衆多剛接觸Flutter的同窗感到困擾,它不只是看起來讓人感到不適,還很是影響編碼體驗。git
大佬們會告訴你應該拆分本身的嵌套代碼(自定義widget或者抽取build方法)來減小嵌套層級。這確實是個行之有效的方法,除此以外,還有沒有別的方法呢,本文將向您介紹另外一種減小嵌套層級的方法。github
嵌套過深影響代碼的視覺觀感面試
這段代碼演示了什麼叫作:編程
嵌套地獄架構
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"), ], ), ), ], ), ), ), ); } } Thread thread = new Thread(new Runnable() { @Override public void run() { Looper.prepare(); Toast.makeText(MainActivity.this,"子線程彈出Toast",Toast.LENGTH_SHORT).show(); Looper.loop(); } }); thread.start();
提取build方法後,嵌套層級獲得了明顯的改善:app
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), ], ), ); } }
還能不能繼續優化呢?less
舉個例子:ide
想要給下面這段代碼中的第2個Textwidget加上marginTop:10屬性函數
@override Widget build(BuildContext context) { return Container( padding: EdgeInsets.all(10), child: Column( children: <Widget>[ Text('billy'), Text('say hello'), //add margin top?? ], ), ); }
此時,我心裏但願能夠這樣寫:oop
顯然,flutter不支持這麼寫,幸運的是:dart2.7發佈時正式宣佈支持擴展函數(Extension Methods)
https://medium.com/dartlang/d...
實際上從dart 2.6.0就開始支持擴展函數了 若是pubspec.yaml中設置的dart版本低於2.6.0則會出現警告提示 如: environment: sdk: ">=2.1.0 <3.0.0" 警告提示: Extension methods weren’t supported until version 2.6.0
先來定義一個擴展函數
extension WidgetExt on Widget { Container intoContainer({ //複製Container構造函數的全部參數(除了child字段) Key key, AlignmentGeometry alignment, EdgeInsetsGeometry padding, Color color, Decoration decoration, Decoration foregroundDecoration, double width, double height, BoxConstraints constraints, EdgeInsetsGeometry margin, Matrix4 transform, }) { //調用Container的構造函數,並將當前widget對象做爲child參數 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, ); } }
如今,全部widget對象都多了一個intoContainer(...)擴展函數,並且參數與Container構造方法一致,因而,咱們就能夠這樣寫了:
除了Container,其它容器也能夠經過一樣的方式來擴展。因而,編程體驗大大提高。
還能夠支持鏈式調用:
Text("billy") .intoExpanded(flex: 1) .intoContainer(color: Colors.white)
有些widget有多個子widget (children), 能夠添加以下的擴展函數:
extension WidgetExt on Widget { //添加一個相鄰的widget,返回List<Widget> List<Widget> addNeighbor(Widget widget) { return <Widget>[this, widget]; } //添加各類單child的widget容器 //如:Container、Padding等... } extension WidgetListExt<T extends Widget> on List<T> { //子List<Widget>列表中再添加一個相鄰的widget,並返回當前列表 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, ); } //添加其它多child的widget容器 //如:Column、ListView等... }
回到本文最初的嵌套地獄,如今咱們的代碼能夠寫成這樣
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),); } }
爲了讓咱們的代碼更加符合鏈式編程風格,再定義一個靜態方法吧
class WidgetChain { static Widget addNeighbor(Widget widget) { return widget; } }
另外,再定義一個從數據到widget的映射擴展方法
extension ListExt<T> on List<T> { List<Widget> buildAllAsWidget(Widget Function(T) builder) { return this.map<Widget>((item) { return builder(item); }).toList(); } } class WidgetChain { static Widget addNeighbor(Widget widget) { return widget; } }
如今,代碼是這樣的:
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() ); } }
值得指出的是,擴展函數(無嵌套)跟構造函數(有嵌套)是能夠混用的。
上面的代碼也能夠寫成這樣(Container和Offstage這2層改爲了構造函數):
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() ), ), ); } }
這樣的擴展函數你想不想試試呢?
我已經替你們封裝好了經常使用Widget對應的into擴展函數,能夠直接食用:
dependencies: widget_chain: ^0.1.0
導入:
import 'package:widget_chain/widget_chain.dart';
而後就能夠起飛了!
Github源碼地址:
https://github.com/luckybilly...
就算你如今用不上,也能夠先star收藏之,從此若是有人向你吐槽flutter嵌套深,除了叫他拆分封裝以外,還能夠把widget_chain甩給他 :D
本文介紹了Flutter中的嵌套地獄,並使用擴展函數的方式來解決flutter的嵌套地獄問題。
因爲大篇幅的擴展函數調用會影響代碼的閱讀體驗(由於intoXxx函數的調用順序與widget層級是相反的),須要保留關鍵嵌套層級結構以使得佈局的層級結構保持清晰,文中的擴展函數支持與構造函數混用,具體使用到什麼程度,就看你們本身的選擇了。
本文初次發佈時包含了使用擴展函數提高編碼體驗的相關內容。
由於我以前編寫flutter代碼時添加父容器的方式爲:
後來經大佬告知我:IDE中其實提供了添加父容器的快捷鍵(光標定位在Widget上,而後Alt + Enter,選擇須要添加的父容器便可),很是方便。
以前我確實不知道,特別感謝大佬!
因而,從新編輯了本文,重點介紹如何使用擴展函數以不同的編碼風格來解決flutter嵌套層級過深的問題。
但願你們喜歡!
在這我也分享一份本身收錄整理的 Android學習PDF+架構視頻+面試文檔+源碼筆記 ,還有高級架構技術進階腦圖、Android開發面試專題資料,高級進階架構資料這些都是我閒暇還會反覆翻閱的精品資料
總之也是在這裏幫助你們學習提高進階,也節省你們在網上搜索資料的時間來學習,也能夠分享給身邊好友一塊兒學習
若是你有須要的話,能夠點贊+評論,關注我, 加Vx:15388039515(備註思否,須要資料)