老孟導讀:Flutter中有這麼一類組件,用於定位、裝飾、控制子組件,好比 Container (定位、裝飾)、Expanded (擴展)、SizedBox (固定尺寸)、AspectRatio (寬高比)、FractionallySizedBox (佔父組件比例)。這些組件的使用頻率很是高,下面一一介紹,最後給出項目中實際案例熟悉其用法。
【Flutter實戰】系列文章地址:http://laomengit.com/guide/introduction/mobile_system.htmlhtml
Container 是最經常使用的組件之一,它是單容器類組件,即僅能包含一個子組件,用於裝飾和定位子組件,例如設置背景顏色、形狀等。git
最簡單的用法以下:程序員
Container( child: Text('老孟'), )
子組件不會發生任何外觀上的變化:
github
設置背景顏色:api
Container( color: Colors.blue, child: Text('老孟'), )
設置內邊距( padding ) 和 外邊距( margin )微信
Container( color: Colors.blue, child: Container( margin: EdgeInsets.all(10), padding: EdgeInsets.all(20), color: Colors.red, child: Text('老孟'), ), )
效果以下:
less
decoration 屬性設置子組件的背景顏色、形狀等。設置背景爲圓形,顏色爲藍色:ide
Container( child: Text('老孟,專一分享Flutter技術及應用'), decoration: BoxDecoration(shape: BoxShape.circle, color: Colors.blue), )
默認狀況下,圓形的直徑等於 Container 窄邊長度,至關於在矩形內繪製內切圓。佈局
上面的狀況明顯不是咱們但願看到了,但願背景是圓角矩形:flex
Container( child: Text('老孟,專一分享Flutter技術及應用'), padding: EdgeInsets.symmetric(horizontal: 10), decoration: BoxDecoration( shape: BoxShape.rectangle, borderRadius: BorderRadius.all(Radius.circular(20)), color: Colors.blue), )
除了背景咱們能夠設置邊框效果,代碼以下:
Container( child: Text('老孟,專一分享Flutter技術及應用'), padding: EdgeInsets.symmetric(horizontal: 10), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( color: Colors.blue, width: 2, ), ), )
建立圓角圖片和圓形圖片:
Container( height: 200, width: 200, decoration: BoxDecoration( image: DecorationImage( image: NetworkImage( 'https://flutter.github.io/assets-for-api-docs/assets/widgets/owl-2.jpg'), fit: BoxFit.cover, ), border: Border.all( color: Colors.blue, width: 2, ), borderRadius: BorderRadius.circular(12), ), )
修改其形狀爲圓形,代碼以下:
Container( height: 200, width: 200, decoration: BoxDecoration( image: DecorationImage( image: NetworkImage( 'https://flutter.github.io/assets-for-api-docs/assets/widgets/owl-2.jpg'), fit: BoxFit.cover, ), border: Border.all( color: Colors.blue, width: 2, ), shape: BoxShape.circle, ), )
設置對齊方式爲居中,背景色爲藍色,代碼以下:
Container( color: Colors.blue, child: Text('老孟,一個有態度的程序員'), alignment: Alignment.center, )
注意:設置對齊方式後,Container將會充滿其父控件,至關於Android中 match_parent 。
Alignment 已經封裝了經常使用的位置,
經過名字就知道其位置,這裏要介紹一下其餘的位置,好比在距離左上角1/4處:
Container( alignment: Alignment(-.5,-.5), child: Text('老孟,專一分享Flutter技術及應用'), )
因此這裏有一個很是重要的座標系,Alignment 座標系以下:
組件的中心爲座標原點。
設置固定的寬高屬性:
Container( color: Colors.blue, child: Text('老孟,專一分享Flutter技術及應用'), alignment: Alignment.center, height: 60, width: 250, )
經過 constraints 屬性設置最大/小寬、高來肯定大小,若是不設置,默認最小寬高是0,最大寬高是無限大(double.infinity),約束width代碼以下:
Container( color: Colors.blue, child: Text('老孟,專一分享Flutter技術及應用'), alignment: Alignment.center, constraints: BoxConstraints( maxHeight: 100, maxWidth: 300, minHeight: 100, minWidth: 100, ), )
經過transform能夠旋轉、平移、縮放Container,旋轉代碼以下:
Container( color: Colors.blue, child: Text('老孟,專一分享Flutter技術及應用'), alignment: Alignment.center, height: 60, width: 250, transform: Matrix4.rotationZ(0.5), )
注意:Matrix4.rotationZ()參數的單位是弧度而不是角度
SizedBox 是具備固定寬高的組件,直接指定具體的寬高,用法以下:
SizedBox( height: 60, width: 200, child: Container( color: Colors.blue, alignment: Alignment.center, child: Text('老孟,專一分享Flutter技術及應用'), ), )
設置尺寸無限大,以下:
SizedBox( height: double.infinity, width: double.infinity, ... )
雖然設置了無限大,子控件是否會無限長呢?不,不會,子控件依然會受到父組件的約束,會擴展到父組件的尺寸,還有一個便捷的方式設置此方式:
SizedBox.expand( child: Text('老孟,專一分享Flutter技術及應用'), )
SizedBox 能夠沒有子組件,但仍然會佔用空間,因此 SizedBox 很是適合控制2個組件之間的空隙,用法以下:
Column( children: <Widget>[ Container(height: 30,color: Colors.blue,), SizedBox(height: 30,), Container(height: 30,color: Colors.red,), ], )
AspectRatio 是固定寬高比的組件,用法以下:
Container( height: 300, width: 300, color: Colors.blue, alignment: Alignment.center, child: AspectRatio( aspectRatio: 2 / 1, child: Container(color: Colors.red,), ), )
aspectRatio 是寬高比,能夠直接寫成分數的形式,也能夠寫成小數的形式,但建議寫成分數的形式,可讀性更高。效果以下:
FractionallySizedBox 是一個相對父組件尺寸的組件,好比佔父組件的70%:
Container( height: 200, width: 200, color: Colors.blue, child: FractionallySizedBox( widthFactor: .8, heightFactor: .3, child: Container( color: Colors.red, ), ), )
經過 alignment 參數控制子組件顯示的位置,默認爲居中,用法以下:
FractionallySizedBox( alignment: Alignment.center, ... )
Expanded、Flexible 和 Spacer 都是具備權重屬性的組件,能夠控制 Row、Column、Flex 的子控件如何佈局的組件。
Flexible 組件能夠控制 Row、Column、Flex 的子控件佔滿父組件,好比,Row 中有3個子組件,兩邊的寬是100,中間的佔滿剩餘的空間,代碼以下:
Row( children: <Widget>[ Container( color: Colors.blue, height: 50, width: 100, ), Flexible( child: Container( color: Colors.red, height: 50, ) ), Container( color: Colors.blue, height: 50, width: 100, ), ], )
仍是有3個子組件,第一個佔1/6,第二個佔2/6,第三個佔3/6,代碼以下:
Column( children: <Widget>[ Flexible( flex: 1, child: Container( color: Colors.blue, alignment: Alignment.center, child: Text('1 Flex/ 6 Total',style: TextStyle(color: Colors.white),), ), ), Flexible( flex: 2, child: Container( color: Colors.red, alignment: Alignment.center, child: Text('2 Flex/ 6 Total',style: TextStyle(color: Colors.white),), ), ), Flexible( flex: 3, child: Container( color: Colors.green, alignment: Alignment.center, child: Text('3 Flex/ 6 Total',style: TextStyle(color: Colors.white),), ), ), ], )
子組件佔比 = 當前子控件 flex / 全部子組件 flex 之和。
Flexible中 fit 參數表示填滿剩餘空間的方式,說明以下:
這2個看上去不是很好理解啊,什麼叫儘量大的填滿剩餘空間?何時填滿?看下面的例子:
Row( children: <Widget>[ Container( color: Colors.blue, height: 50, width: 100, ), Flexible( child: Container( color: Colors.red, height: 50, child: Text('Container',style: TextStyle(color: Colors.white),), ) ), Container( color: Colors.blue, height: 50, width: 100, ), ], )
這段代碼是在最上面代碼的基礎上給中間的紅色Container添加了Text子控件,此時紅色Container就不在充滿空間,再給Container添加對齊方式,代碼以下:
Row( children: <Widget>[ Container( color: Colors.blue, height: 50, width: 100, ), Flexible( child: Container( color: Colors.red, height: 50, alignment: Alignment.center, child: Text('Container',style: TextStyle(color: Colors.white),), ) ), Container( color: Colors.blue, height: 50, width: 100, ), ], )
此時又填滿剩餘空間。
你們是否還記得 Container 組件的大小是如何調整的嗎?Container 默認是適配子控件大小的,但當設置對齊方式時 Container 將會填滿父組件,所以是否填滿剩餘空間取決於子組件是否須要填滿父組件。
若是把 Flexible 中子組件由 Container 改成 OutlineButton,代碼以下:
Row( children: <Widget>[ Container( color: Colors.blue, height: 50, width: 100, ), Flexible( child: OutlineButton( child: Text('OutlineButton'), ), ), Container( color: Colors.blue, height: 50, width: 100, ), ], )
OutlineButton 正常狀況下是不充滿父組件的,所以最終的效果應該是不填滿剩餘空間:
下面再來介紹另外一個權重組件 Expanded ,源代碼以下:
class Expanded extends Flexible { /// Creates a widget that expands a child of a [Row], [Column], or [Flex] /// so that the child fills the available space along the flex widget's /// main axis. const Expanded({ Key key, int flex = 1, @required Widget child, }) : super(key: key, flex: flex, fit: FlexFit.tight, child: child); }
Expanded 繼承字 Flexible,fit 參數固定爲 FlexFit.tight,也就是說 Expanded 必須(強制)填滿剩餘空間。上面的 OutlineButton 想要充滿剩餘空間能夠直接使用 Expanded :
Row( children: <Widget>[ Container( color: Colors.blue, height: 50, width: 100, ), Expanded( child: OutlineButton( child: Text('OutlineButton'), ), ), Container( color: Colors.blue, height: 50, width: 100, ), ], )
Spacer 也是一個權重組件,源代碼以下:
@override Widget build(BuildContext context) { return Expanded( flex: flex, child: const SizedBox.shrink(), ); }
Spacer 的本質也是 Expanded 的實現的,和Expanded的區別是:Expanded 能夠設置子控件,而 Spacer 的子控件尺寸是0,所以Spacer適用於撐開 Row、Column、Flex 的子控件的空隙,用法以下:
Row( children: <Widget>[ Container(width: 100,height: 50,color: Colors.green,), Spacer(flex: 2,), Container(width: 100,height: 50,color: Colors.blue,), Spacer(), Container(width: 100,height: 50,color: Colors.red,), ], )
三個權重組建總結以下:
先看下效果:
拿到效果圖先不要慌 (取出手機拍照發個朋友圈😊),整個列表每一行的佈局基本同樣,因此先寫出一行的效果:
class _SettingItem extends StatelessWidget { const _SettingItem( {Key key, this.iconData, this.iconColor, this.title, this.suffix}) : super(key: key); final IconData iconData; final Color iconColor; final String title; final Widget suffix; @override Widget build(BuildContext context) { return Container( height: 45, child: Row( children: <Widget>[ SizedBox( width: 30, ), Icon(iconData,color: iconColor,), SizedBox( width: 30, ), Expanded( child: Text('$title'), ), suffix, SizedBox( width: 15, ), ], ), ); } }
消息中心和其餘行最後的樣式不同,單獨封裝,帶紅色背景的組件:
class _NotificationsText extends StatelessWidget { final String text; const _NotificationsText({Key key, this.text}) : super(key: key); @override Widget build(BuildContext context) { return Container( padding: EdgeInsets.symmetric(horizontal: 10), decoration: BoxDecoration( shape: BoxShape.rectangle, borderRadius: BorderRadius.all(Radius.circular(50)), color: Colors.red), child: Text( '$text', style: TextStyle(color: Colors.white), ), ); } }
灰色後綴組件:
class _Suffix extends StatelessWidget { final String text; const _Suffix({Key key, this.text}) : super(key: key); @override Widget build(BuildContext context) { return Text( '$text', style: TextStyle(color: Colors.grey.withOpacity(.5)), ); } }
將這些封裝好的組件組合起來:
class SettingDemo extends StatelessWidget { @override Widget build(BuildContext context) { return Column( children: <Widget>[ _SettingItem( iconData: Icons.notifications, iconColor: Colors.blue, title: '消息中心', suffix: _NotificationsText( text: '2', ), ), Divider(), _SettingItem( iconData: Icons.thumb_up, iconColor: Colors.green, title: '我贊過的', suffix: _Suffix( text: '121篇', ), ), Divider(), _SettingItem( iconData: Icons.grade, iconColor: Colors.yellow, title: '收藏集', suffix: _Suffix( text: '2個', ), ), Divider(), _SettingItem( iconData: Icons.shopping_basket, iconColor: Colors.yellow, title: '已購小冊', suffix: _Suffix( text: '100個', ), ), Divider(), _SettingItem( iconData: Icons.account_balance_wallet, iconColor: Colors.blue, title: '個人錢包', suffix: _Suffix( text: '10萬', ), ), Divider(), _SettingItem( iconData: Icons.location_on, iconColor: Colors.grey, title: '閱讀過的文章', suffix: _Suffix( text: '1034篇', ), ), Divider(), _SettingItem( iconData: Icons.local_offer, iconColor: Colors.grey, title: '標籤管理', suffix: _Suffix( text: '27個', ), ), ], ); } }
至此就結束了。
先來看下效果:
關於動畫部分的內容會在後面的章節具體介紹。這個效果分爲3大部分:
座標軸的實現以下:
class _Axis extends StatelessWidget { final Widget child; const _Axis({Key key, this.child}) : super(key: key); @override Widget build(BuildContext context) { return Container( decoration: BoxDecoration( border: Border( left: BorderSide(color: Colors.black, width: 2), bottom: BorderSide(color: Colors.black, width: 2), ), ), child: child, ); } }
單個柱狀圖實現:
class _Cylinder extends StatelessWidget { final double height; final double width; final Color color; const _Cylinder({Key key, this.height, this.width, this.color}) : super(key: key); @override Widget build(BuildContext context) { return AnimatedContainer( duration: Duration(seconds: 1), height: height, width: width, color: color, ); } }
生成多個柱狀圖:
final double _width = 20.0; List<double> _heightList = [60.0, 80.0, 100.0, 120.0, 140.0]; Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.end, children: List.generate(_heightList.length, (index) { return _Cylinder( height: _heightList[index], width: _width, color: Colors.primaries[index % Colors.primaries.length], ); }))
將此合併,而後更改每個柱狀圖的高度:
class CylinderChart extends StatefulWidget { @override _CylinderChartState createState() => _CylinderChartState(); } class _CylinderChartState extends State<CylinderChart> { final double _width = 20.0; List<double> _heightList = [60.0, 80.0, 100.0, 120.0, 140.0]; @override Widget build(BuildContext context) { return Center( child: Container( height: 200, width: 250, child: Stack( children: <Widget>[ _Axis(), Positioned.fill( left: 5, right: 5, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.end, children: List.generate(_heightList.length, (index) { return _Cylinder( height: _heightList[index], width: _width, color: Colors.primaries[index % Colors.primaries.length], ); })), ), Positioned( top: 0, left: 30, child: OutlineButton( child: Text('反轉'), onPressed: () { setState(() { _heightList = _heightList.reversed.toList(); }); }, ), ) ], ), ), ); } }
搞定。
老孟Flutter博客地址(330個控件用法):http://laomengit.com
歡迎加入Flutter交流羣(微信:laomengit)、關注公衆號【老孟Flutter】:
![]() |
![]() |