Flutter 40: 日常問題小結 (一)
小菜作為一個小學生在實際操作中遇到很多問題,相對比較常見,小菜來整理記錄一下。
問題一:巢狀權重異常
小菜做 Android 時為了螢幕適配,用到 權重/比例 比較多, Flutter 也提供了實現權重的功能,小菜嘗試過權重基本用法,當時沒有涉及到巢狀權重的問題,小菜想要實現的是左側一張大圖,右側垂直兩張小圖,水平方向 1:1 均分,小菜用 Expanded 配合 flex 在巢狀權重時遇到如下問題,於是重新研究一下權重的使用;

嘗試一:
在根 Widget 中嘗試如下, Expanded 與 Flexible 均正常,預設填滿布局,與小菜預計的相同;
小菜理解 Container 未設定寬高,但 Row/Column 預設 mainAxisSize=MainAxisSize.max ,因此正常填滿;
// Expanded return new SafeArea( top: false, child: Scaffold( appBar: new AppBar( title: Text('Expanded Demo'), ), body: Container( child: Row(children: <Widget>[ Expanded(flex: 1, child: Container(color: Colors.red)), Expanded(flex: 1, child: Column(children: <Widget>[ Expanded(flex: 1, child: Container(color: Colors.blue)), Expanded(flex: 1, child: Container(color: Colors.green)) ])) ])))); // Flexible return new SafeArea( top: false, child: Scaffold( appBar: new AppBar( title: Text('Flexible Demo'), ), body: Container( child: Row(children: <Widget>[ Flexible(flex: 1, child: Container(color: Colors.red)), Flexible( flex: 1, child: Column(children: <Widget>[ Flexible(flex: 1, child: Container(color: Colors.blue)), Flexible(flex: 1, child: Container(color: Colors.green)) ])) ]))));

嘗試二:
大多數情況頁面元素較多,最外層可能會巢狀 ListView/Column 或其他 Widget ,此時用 Expanded 便會出現如上問題;日誌中建議使用 Flexible 和 Column mainAxisSize: MainAxisSize.min ,雖然不報錯,但是頁面空白無效果;若只用 Flexible 結果依然是上述問題;
小菜理解兩層 Column 加上 Expanded/Flexible 無法計算 Container 高度,子 Widget 無法自適應父 Widget 高度;
return new SafeArea( top: false, child: Scaffold( body: ListView(children: <Widget>[ Container( child: Row(children: <Widget>[ Flexible(flex: 1, child: Container(color: Colors.red)), Flexible( flex: 1, child: Column( mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.center, children: <Widget>[ Flexible(flex: 1, child: Container(color: Colors.blue)), Flexible(flex: 1, child: Container(color: Colors.green)) ])) ])) ])));
嘗試三:
根據上一步嘗試,小菜理解 Container 高度為 0 ,需要手動設定高度,但是設定在 Row 層還是 Column 層或子 Container 層是不同的;
小菜理解不顯示的原因是高度未匹配,故嘗試不同位置設定預設高度,但基本都在最外層設定並以最外層為基準;
return new SafeArea( top: false, child: Scaffold( body: ListView(children: <Widget>[ Container( height: 120, child: Row(children: <Widget>[ Flexible(flex: 1, child: Container(color: Colors.red)), Flexible( flex: 1, child: Container( child: Column( mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.center, children: <Widget>[ Flexible( flex: 1, child: Container(color: Colors.blue)), Flexible( flex: 1, child: Container(color: Colors.green)) ]))) ])) ])));

嘗試四:
如果最外層不設定高度時,則考慮根據需求使用帶有寬高的 Widget ,小菜需要展示圖片,故直接用圖片佔位;
小菜主要想實現圖片高度自適應,而寬度隨螢幕比例佔有,右側兩圖正常為左圖佔位一半,但為了防止異常,通常在最外層設定高度;
return new SafeArea( top: false, child: Scaffold( body: ListView(children: <Widget>[ Container( child: Row(children: <Widget>[ Expanded( flex: 1, child: Image.network( 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1544938569112&di=feeab11968f3870520482630563c4f54&imgtype=0&src=http%3A%2F%2Fi1.hdslb.com%2Fbfs%2Farchive%2Fac5c906111130579c6909aae47f6656e20d0906b.jpg')), Expanded( flex: 1, child: Container( child: Column( mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.center, children: <Widget>[ Flexible( child: Image.network( 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1544938569112&di=feeab11968f3870520482630563c4f54&imgtype=0&src=http%3A%2F%2Fi1.hdslb.com%2Fbfs%2Farchive%2Fac5c906111130579c6909aae47f6656e20d0906b.jpg'), flex: 1), Flexible( child: Image.network( 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1544938569112&di=feeab11968f3870520482630563c4f54&imgtype=0&src=http%3A%2F%2Fi1.hdslb.com%2Fbfs%2Farchive%2Fac5c906111130579c6909aae47f6656e20d0906b.jpg'), flex: 1) ]))) ])) ])));

擴充套件:
Flexible的 FlexFit 分兩種,在巢狀權重時只可以用預設的 loose ,在其他情況下,小菜測試差別不大;但小菜理解的官方說明: tight 模式是強制填補剩餘空間,而 loose 模式可以填滿剩餘空間,也允許稍小不填滿,非強制;相對 loose 使用範圍更廣。
問題二:SnackBar 找不到 Context
小菜嘗試 SnackBar 時總是找不到上下文環境而打不開;

嘗試一:
小菜將整體放在一個 Widget 中,傳入 BuildContext 引數,無果,依舊是找不到上下文環境;
嘗試二:
按官網推薦,可新建一個 Builder 動態新增 BuildContext ,但是小菜又出現新的問題,不可在 Builder 中返回無大小的 Container ;仔細找了很久發現 Builder 需要 return 一個 Widget ,太粗心;

Widget _childExpandedWid() { return SafeArea( top: false, child: Scaffold( appBar: new AppBar(title: new Text("SnackBar Demo")), body: Builder(builder: (BuildContext context) { return Row(children: <Widget>[ Expanded( flex: 1, child: GestureDetector( onTap: () { Scaffold.of(context).showSnackBar( SnackBar(content: Text('測試一下 SnackBar 使用!'))); }, child: Container(color: Colors.red))), Expanded( flex: 1, child: Column(children: <Widget>[ Expanded(flex: 1, child: Container(color: Colors.blue)), Expanded(flex: 1, child: Container(color: Colors.green)) ])) ]); }))); }
嘗試三:
按官網推薦,可以將這些子 Widget 拆分為單獨的 StatelessWidget 元件,測試正常;
class ChildExpandWidget extends StatelessWidget { @override Widget build(BuildContext context) { return SafeArea( top: false, child: Scaffold( appBar: new AppBar(title: new Text("SnackBar Demo")), body: Row(children: <Widget>[ Expanded( flex: 1, child: GestureDetector( onTap: () { Scaffold.of(context).showSnackBar( SnackBar(content: Text('測試一下 SnackBar 使用!'))); }, child: Container(color: Colors.red))), Expanded( flex: 1, child: Column(children: <Widget>[ Expanded(flex: 1, child: Container(color: Colors.blue)), Expanded(flex: 1, child: Container(color: Colors.green)) ])) ]))); } }

小菜作為初學者,遇到很多基礎的問題,僅做記錄;如有錯誤請多多指導!如下是小菜公眾號,歡迎閒來吐槽~

公眾號