StatefulWidget表示爲:帶狀態會變化的組件
StatelessWidget表示爲:無狀態不變化的組件
Hello World
敲一個簡單的Hello World界面:
import 'package:flutter/material.dart'; void main()=>runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Welcome to Flutter', home: Scaffold( appBar: AppBar( title: Text('Hello World'), ), body: Center( child: Text('Hello World!'), ), ), ); } }
上述代碼中,
MyApp是我們自定義的一個類, 繼承StatelessWidget代表它是靜態組件, 這個靜態組件類内部已經有build(BuildContext context)這個方法, 需要重寫, 所以用到了@override這種dart寫法。
import引入到的material.dart是谷歌研發的材質樣式包。
MatrialApp裡的title屬性: App的名稱
Scaffold組件出現在MaterialApp組件中的home區域, 他代表一個腳手架。
appBar屬性: 標題欄, 用到了AppBar組件, 組件中包含一個title屬性。
body: 標題欄下方的身體區域, 後面的Center組件代表四方位居中組件,再在裡面寫代碼接著填充東西。
文本組件Text
在flutter當中, 一切都是組件, TextWidget就是基本組件效果之一。
爲了學習TextWidget, 我們在child: Text掛件裡面接著填充更多的内容, 以展示Text内部調用style等衆多方法的效果。
以下是案例:
import 'package:flutter/material.dart'; void main()=>runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'TextWidget展示效果', home: Scaffold( appBar: AppBar( title: Text('我是文本掛件的標題'), ), body: Center( child: Text( '我是文本掛件的内容,内容包含一大堆如此這般,内容包含一大堆如此這般,内容包含一大堆如此這般,内容包含一大堆如此這般,内容包含一大堆如此這般,内容包含一大堆如此這般', textAlign: TextAlign.left, maxLines: 2, overflow: TextOverflow.fade, style: TextStyle( fontSize: 25.0, color: Color.fromARGB(255, 255, 150, 150), decoration: TextDecoration.underline, decorationStyle: TextDecorationStyle.solid, ), ), ), ), ); } }
上述代碼涉及到了文本對齊, 最大行數, 溢出處理和細節樣式控制這幾個組件方法。
其中:
fontSize屬性表示文字大小,
maxtLines屬性代表最大行數,
overflow屬性代表文字溢出處理方式, 用到TextOverflow對象,
fontSize屬性對應的值是float小數點精確1位, 否則填寫其他將導致報錯。
color屬性當中, Color對象裡的fromARGB, A代表透明度。
docoration屬性是文字修飾類型, 一般都是下劃線, 用到了TextDecoration對象
decorationStyle屬性是修飾的樣式, 這裡用實線, 用到了TextDecorationStyle對象裡的solid
層組件Container
相當於html標簽p中的div標簽,
代碼案例:
import 'package:flutter/material.dart'; void main()=>runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'TextWidget展示效果', home: Scaffold( appBar: AppBar( title: Text('我是文本掛件的標題'), ), body: Center( child: Container( child: Text( '我是文本掛件的内容,内容包含一大堆如此這般,内容包含一大堆如此這般,内容包含一大堆如此這般,内容包含一大堆如此這般,内容包含一大堆如此這般,内容包含一大堆如此這般', textAlign: TextAlign.left, maxLines: 2, overflow: TextOverflow.fade, style: TextStyle( fontSize: 25.0, color: Color.fromARGB(255, 255, 150, 150), decoration: TextDecoration.underline, decorationStyle: TextDecorationStyle.solid, ), ), alignment: Alignment.center, width:400.0, height:300.0, color: Colors.lightBlue, padding: const EdgeInsets.fromLTRB(10.0,60.0,40.0,0.0), margin: const EdgeInsets.all(20.0), ), ), ), ); } }
上述代碼中, 出現了alignment, width, height, color這幾個屬性, 其中alignment用到了Alignment組件。
padding屬性和css一樣, 是child子模塊和文本組件之間存在的内邊距,
margin屬性是child子模塊和body組件之間存在的外邊距
decoration屬性(定義如何修飾)
這個decoration和css當中的text-decoration不一樣, 是修飾這裡的Container盒子模型的, 因爲書寫起來比css複雜, 所以單獨拉出來記錄一下。
用到decoration的語句就不要用color屬性來定義盒子顔色了, 把color屬性注釋掉。
代碼案例如下:
import 'package:flutter/material.dart'; void main()=>runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'TextWidget展示效果', home: Scaffold( appBar: AppBar( title: Text('我是文本掛件的標題'), ), body: Center( child: Container( child: Text( '我是文本掛件的内容,内容包含一大堆如此這般,内容包含一大堆如此這般,内容包含一大堆如此這般,内容包含一大堆如此這般,内容包含一大堆如此這般,内容包含一大堆如此這般', textAlign: TextAlign.left, maxLines: 2, overflow: TextOverflow.fade, style: TextStyle( fontSize: 25.0, color: Color.fromARGB(255, 255, 150, 150), decoration: TextDecoration.underline, decorationStyle: TextDecorationStyle.solid, ), ), alignment: Alignment.topLeft, width:400.0, height:300.0, // color: Colors.lightBlue, padding: const EdgeInsets.fromLTRB(20.0,60.0,40.0,0.0), margin: const EdgeInsets.all(20.0), decoration: BoxDecoration( gradient: const LinearGradient( colors: [ Colors.red, Colors.greenAccent, Colors.tealAccent ] ) ), ), ), ), ); } }
上述代碼當中:
gradient屬性, 定義漸變的意思
LinearGradient組件, 是線性漸變組件的意思。
colors屬性後面多了個s複數, 裡面傳入數組, 再用Colors組件挨個代入元素
圖片組件 Image
Image組件可以通過以下方式來加載:
Image.asset 本地加載, 會導致安裝包過大,
Image.network 常用的通過網路讀圖,
Image.file 本地圖庫加載, 加載圖庫地址xxx, 用戶拍完照, 把圖片讀進來,
Image.memory 透過内存加載, 不常用
以下是代碼案例:
import 'package:flutter/material.dart'; void main()=>runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'TextWidget展示效果', home: Scaffold( appBar: AppBar( title: Text('我是文本掛件的標題'), ), body: Center( child: Container( child: Image.network( 'http://www.wkwkk.com/filepool/cover/cover/1804/180415200453_6863.jpg', scale: 2.5, color: Colors.greenAccent, colorBlendMode: BlendMode.colorBurn, fit: BoxFit.cover, // repeat: ImageRepeat.repeatY, ), color: Colors.lightBlue, width: 400.0, height: 300.0, ), ), ), ); } }
上述代碼當中, 在圖片組件裡涉及到了以下屬性:
scale屬性, 伸縮比例, 這裡要注意, 相對於傳統的css寫法, scale屬性指定的值越大, 圖片反而會越小。
repeat屬性, 和css一樣, 圖片重複方式, 如果寫到Container組件裡, 是要報錯的, 它是圖片組件裡的屬性
fit屬性, 填充方式, 此時scale屬性指定的值失效, 使用BoxFit組件, 也就是盒子模型内填充組件的意思, 一般用到的是BoxFit.cover, 這樣通過不改變寬高比例的方式把圖片剛好覆蓋到圖片上層的Container容器裡, 如果是BoxFit.contain, 則剛剛接觸到最大寬或者最大高的邊界, 圖片就不會膨脹下去。
colorBlendMode屬性, 這個blend順便查了一下字典, 有交融混合調配的意思, 那設定這個屬性的大概目的也就明白了, 指定了color屬性之後, colorBlendMode屬性的值用到了BlendModel組件, 也就是混合模式, 目前1.0版本有那麽十多種可供選擇,這裡也說不清楚,自己上IDE上調整來看吧。
ListView和ListTile組件
這兩個組件通常成對出現, ListView組件相當於html5概念中的ul標簽, ListTile組件相當於html5概念中的li標簽。查字典時看到單詞tile翻譯成甎、瓦片,此時代碼工作者就是搬這個甎的。
代碼案例如下:
import 'package:flutter/material.dart'; void main()=>runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'flutter app test', home: Scaffold( appBar: AppBar( title: Text('ListView'), ), body: ListView( children: <Widget>[ ListTile( leading: Icon(Icons.person), title: Text('人物1'), ), ListTile( leading: Icon(Icons.person), title: Text('人物2'), ), ListTile( leading: Icon(Icons.person), title: Text('人物3'), ), ], ), ), ); } }
上述代碼中,涉及到了children: <Widget>[...]這種dart專用寫法,習慣就好, 之後就在數組裡依次寫進各組件。
ListView組件内, 嵌套Image組件列表
就和html5書寫一樣, 我們想在ul裡面寫上一排圖片, <li><img ... /></li>...<li><img ... /></li>如何做到的呢?
修改上邊一部分代碼片段, 改爲如下:
body: ListView( children: <Widget>[ Image.network('http://www.wkwkk.com/filepool/cover/img_17021914350735944.jpg'), Image.network('http://www.wkwkk.com/filepool/cover/img_17021914350735944.jpg'), Image.network('http://www.wkwkk.com/filepool/cover/img_17021914350735944.jpg'), Image.network('http://www.wkwkk.com/filepool/cover/img_17021914350735944.jpg'), ], ),
上述代碼會實現竪向的圖片列表, 有人會糾結於new不new這個Image組件, 無論如何寫法,效果都雷同。
橫向組件列表
ListView組件當中, 有scrollingDirection屬性, 是定義滾動方向的, 其中:
Axis組件就是網格控制組件。
Axis.horizontal 代表橫向滾動
Axis.vertical 代表縱向滾動
代碼案例如下:
import 'package:flutter/material.dart'; void main()=>runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'flutter app test', home: Scaffold( appBar: AppBar( title: Text('ListView橫向列表'), ), body: Center( child: Container( height: 200.0, child: ListView( scrollDirection: Axis.horizontal, children: <Widget>[ Container( width: 180.0, color: Colors.green ), Container( width: 180.0, color: Colors.lightBlue, ), Container( width: 180.0, color: Colors.yellowAccent ), Container( width: 180.0, color: Colors.blueGrey ), ], ), ), ), ), ); } }
上述代碼中, 使用幾個Container加載色塊, 加以橫向排列來生成了預期效果。
ListView.builder動態列表建立和傳參
以下是代碼案例:
import 'package:flutter/material.dart'; void main()=>runApp(MyApp( item: new List<String>.generate(200, (i)=>"Item $i") )); class MyApp extends StatelessWidget { final List<String> item; MyApp({Key key, @required this.item}):super(key:key); @override Widget build(BuildContext context) { return MaterialApp( title: 'flutter app test', home: Scaffold( appBar: AppBar( title: Text('ListView動態列表'), ), body: ListView.builder( itemCount: item.length, itemBuilder:(context, index){ return new ListTile( title: new Text("{$item[index]}"), ); } ), ), ); } }
上述代碼當中ListView.builer方法即是生成動態列表方法。
在該方法内,
itemCount屬性傳入參數的長度, 即參數名.length
itemBuilder屬性固定寫入兩個參數, context代表上下文, index代表List數組索引, 此時, 值作用域return所返回的是ListTile甎塊, 簡單寫了一個$item套索引的方式來列印出每個List索引指向的值。
GridView組件
以下是代碼案例:
import "package:flutter/material.dart"; void main()=>runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: '電影海報測試', home: Scaffold( appBar: AppBar( title: Text("我是標題"), ), body: GridView.count( padding:const EdgeInsets.all(10.0), crossAxisSpacing: 10.0, crossAxisCount: 3, children: <Widget>[ Text('It was mine'), Text('It was mine'), Text('It was mine'), Text('It was mine'), Text('It was mine'), Text('It was mine'), Text('It was mine'), Text('It was mine'), Text('It was mine'), ] ) ), ); } }
上述代碼當中GridView.count是其中一種寫法,也可以直接寫爲GridView(...)這種方式。
crossAxisSpacing屬性代表橫向格子之間的間距, 可以理解成html的td左右間距,
crossAxisCount屬性代表一行多少個元素,
另外還有一個mainAxisSpacing屬性, 代表竪向(列所在)各個格子之間的間距, 可以理解成html的td上下間距。
這些都可以通過padding屬性來定義格子的間距, 不過padding屬性不允許直接傳double類型值, 需要傳類似EdgeInsets組件中的all方法, 如例子中那樣。
接下來把用Text組件填充的部分做成圖片:
import "package:flutter/material.dart"; void main()=>runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: '電影海報測試', home: Scaffold( appBar: AppBar( title: Text("我是標題"), ), body: GridView.count( // padding:const EdgeInsets.all(10.0), crossAxisSpacing: 10.0, crossAxisCount: 3, mainAxisSpacing: 10.0, childAspectRatio: 1.2, children: <Widget>[ Image.network( 'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg', fit: BoxFit.cover, ), Image.network( 'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg', fit: BoxFit.cover, ), Image.network( 'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg', fit: BoxFit.cover, ), Image.network( 'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg', fit: BoxFit.cover, ), Image.network( 'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg', fit: BoxFit.cover, ), Image.network( 'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg', fit: BoxFit.cover, ), Image.network( 'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg', fit: BoxFit.cover, ), Image.network( 'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg', fit: BoxFit.cover, ), Image.network( 'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg', fit: BoxFit.cover, ), Image.network( 'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg', fit: BoxFit.cover, ), Image.network( 'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg', fit: BoxFit.cover, ), Image.network( 'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg', fit: BoxFit.cover, ), Image.network( 'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg', fit: BoxFit.cover, ), Image.network( 'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg', fit: BoxFit.cover, ), Image.network( 'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg', fit: BoxFit.cover, ), Image.network( 'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg', fit: BoxFit.cover, ), Image.network( 'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg', fit: BoxFit.cover, ), Image.network( 'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg', fit: BoxFit.cover, ), Image.network( 'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg', fit: BoxFit.cover, ), Image.network( 'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg', fit: BoxFit.cover, ), Image.network( 'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg', fit: BoxFit.cover, ), ] ) ), ); } }
上述代碼中
childAspectRatio屬性代表的是寬高比,1是正方形,值越大,越寬,反之圖片範圍越高。
GridView組件直接傳值的寫法(不使用count)
代碼案例如下:
import "package:flutter/material.dart"; void main()=>runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: '電影海報測試', home: Scaffold( appBar: AppBar( title: Text("我是標題"), ), body: GridView( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( // padding:const EdgeInsets.all(10.0), crossAxisSpacing: 10.0, crossAxisCount: 3, mainAxisSpacing: 10.0, childAspectRatio: 1.2, ), children: <Widget>[ Image.network( 'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg', fit: BoxFit.cover, ), Image.network( 'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg', fit: BoxFit.cover, ), Image.network( 'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg', fit: BoxFit.cover, ), Image.network( 'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg', fit: BoxFit.cover, ), Image.network( 'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg', fit: BoxFit.cover, ), Image.network( 'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg', fit: BoxFit.cover, ), Image.network( 'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg', fit: BoxFit.cover, ), Image.network( 'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg', fit: BoxFit.cover, ), Image.network( 'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg', fit: BoxFit.cover, ), Image.network( 'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg', fit: BoxFit.cover, ), Image.network( 'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg', fit: BoxFit.cover, ), Image.network( 'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg', fit: BoxFit.cover, ), Image.network( 'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg', fit: BoxFit.cover, ), Image.network( 'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg', fit: BoxFit.cover, ), Image.network( 'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg', fit: BoxFit.cover, ), Image.network( 'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg', fit: BoxFit.cover, ), Image.network( 'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg', fit: BoxFit.cover, ), Image.network( 'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg', fit: BoxFit.cover, ), Image.network( 'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg', fit: BoxFit.cover, ), Image.network( 'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg', fit: BoxFit.cover, ), Image.network( 'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg', fit: BoxFit.cover, ), ] ) ), ); } }