接着上一篇,咱們作一個這樣的APP:html
開始以前,我發現了一個好玩的東西,每次咱們在終端中輸入命令:vue
flutter run
終端裏會有這個東西:react
按照上圖所示,咱們的進入這個網頁看看是個啥:編程
好高大上的感受,具體是幹嗎的,我也不知道,有興趣的同窗能夠點進去把玩把玩,之後搞明白了再更吧。數組
先建立一個列表。微信
回到main.dart
中,把原來的代碼所有清空,複製如下代碼:app
import 'package:flutter/material.dart'; import 'package:english_words/english_words.dart'; void main() => runApp(new MyApp()); class MyApp extends StatelessWidget { @override //構建一個容器 Widget build(BuildContext context) { return new MaterialApp( title: 'Startup Name Generator', home: new RandomWords(),//定義子組件爲有狀態控件RandomWords類的實例 ); } } //定義有狀態控件RandomWords類 class RandomWords extends StatefulWidget { @override createState() => new RandomWordsState();//建立有狀態控件RandomWords的狀態類實例:RandomWordsState } //定義狀態類RandomWordsState class RandomWordsState extends State<RandomWords> { @override final _suggestions = <WordPair>[]; //用於保存隨機字符串詞組,注意這是一個數組變量 final _biggerFont = const TextStyle(fontSize: 18.0); //用於標識字符串的樣式 //構建一個腳手架,裏面塞入前面定義好的_buildSuggestions類 Widget build(BuildContext context) { return new Scaffold ( appBar: new AppBar( title: new Text('Startup Name Generator'), ), body: _buildSuggestions(), ); } //定義一個子控件,這個控件就是放置隨機字符串詞組的列表 Widget _buildSuggestions() { return new ListView.builder( //ListView(列表視圖)是material.dart中的基礎控件 padding: const EdgeInsets.all(16.0), //padding(內邊距)是ListView的屬性,配置其屬性值 //經過ListView自帶的函數itemBuilder,向ListView中塞入行,變量 i 是從0開始計數的行號 //此函數會自動循環並計數,咋結束的我也不知道,走着瞧咯 itemBuilder: (context, i) { if (i.isOdd) return new Divider();//奇數行塞入分割線對象 final index = i ~/ 2; //當前行號除以2取整,獲得的值就是_suggestions數組項索引號 // 若是計算獲得的數組項索引號超出了_suggestions數組的長度,那_suggestions就再生10個隨機組合的字符串詞組 if (index >= _suggestions.length) { _suggestions.addAll(generateWordPairs().take(10)); } return _buildRow(_suggestions[index]);//把這個數據項塞入ListView中 } ); } //定義的_suggestions數組項屬性 Widget _buildRow(WordPair pair) { //ListTile和Text都是material.dart中的基礎控件 return new ListTile( title: new Text( pair.asPascalCase, //使用駝峯樣式 style: _biggerFont, ), ); } }
看到這裏,是否是有點暈,各類聲明、各類引用,還有回調,把上面的代碼,用下面的圖解析下結構,看看到底怎麼個狀況:less
能夠發如今StatelessWidget和State類中都有一個Widget類型的函數build()
,感受有點像類的初始化方法construct()
,而RandomWords對象爲何只使用了createState()
卻沒有build()
,我也不知道,走着瞧吧。當對象實例化的時候,首先執行Widget build(BuildContext context){}函數,函數中BuildContext類型的參數context,到目前爲止還不知道幹嗎用的,暫且忽略其意義吧。dom
material類型的子控件都經過回調函數的方式建立,我讀起來有些不習慣,但經過回調,免去了先聲明再使用的麻煩,而且能夠直接對對象的屬性進行配置,經過build()
一層層回調,代碼簡潔很多,而代碼中使用到的material內置控件,我就不一一介紹了,有興趣的同窗請參考material官方API,注意material控件索引在頁面右邊的列表,別找到左邊去了。ide
注意,遇到這種聲明類屬性的格式:_[變量名]
。按官方的意思是,若是變量名的前綴有_
下劃線,表示強制轉換爲私有變量,至關於聲明變量爲private,但使用這個變量的時候,仍是要將下劃線進行完整的書寫。
保存代碼後運行一下,能夠看到APP變成了這個樣子:
向下滾動試試,發現能夠一直滾下去~
向列表里加個_收藏_標籤按鈕,使每行能夠標記收藏或取消收藏。這個_收藏_標籤就是狀態,既然要修改狀態,確定要到state中進行啦。
到對象RandomWordsState中定義一個對象,用於存儲標記。爲何要單獨存儲標記呢?由於這樣就不須要往行對象(ListTile)中添加標記,下降了對象的複雜度。以下:
class RandomWordsState extends State<RandomWords> { final _suggestions = <WordPair>[]; final _saved = new Set<WordPair>(); //新加這一句 final _biggerFont = const TextStyle(fontSize: 18.0); ... }
爲何存儲標記的是對象而不是一個數組呢?大概是想順便教咱們使用一下Set對象吧,聽說Set對象不容許有重複的項目,方便後面模擬堆棧的效果,很是適合這種場景。而後咱們到每一個行添加一個標記收藏的控件:
Widget _buildRow(WordPair pair) { //定義一個布爾變量,用於判斷行控件ListTile是否被標記爲收藏 final alreadySaved = _saved.contains(pair); return new ListTile( title: new Text( pair.asPascalCase, style: _biggerFont, ), //安放圖標控件 trailing: new Icon( alreadySaved ? Icons.favorite : Icons.favorite_border, color: alreadySaved ? Colors.red : null, ), //定義點擊事件,控制圖標的樣式的切換 onTap: () { setState(() { if (alreadySaved) { _saved.remove(pair); } else { _saved.add(pair); } }); }, ); }
注意,在onTap
事件中,使用到了setState()
方法(用過vue或react的玩家是否是很熟悉呀),在這個方法裏修改變量值,便可觸發state對象執行build()
方法重繪對象。這裏每次變動對象_saved後,都會重繪ListTile對象,而靜態變量alreadySaved
也被從新定義,所以不用擔憂alreadySaved
值不被更新的問題,若是去除final
關鍵字,Dart語法會報錯,還請路過大神點撥一下緣由。
保存代碼後,能夠看到APP刷新了,每一行都添加了一個心型圖標,反覆戳這個行,還自動配有動畫效果:
加入一個導航欄樣式的堆棧。
先往主頁面控件(AppBar)中添加一個能夠進入收藏列表的入口:
class RandomWordsState extends State<RandomWords> { ... @override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar( title: new Text('Startup Name Generator'), //爲AppBar對象的actions屬性添加一個IconButton對象,actions屬性值能夠是Widget類型的數組 actions: <Widget>[ new IconButton(icon: new Icon(Icons.list), onPressed: _pushSaved), //能夠試試添加這兩行後APP上有什麼效果 new IconButton(icon: new Icon(Icons.add), onPressed: _pushSaved), new IconButton(icon: new Icon(Icons.create), onPressed: _pushSaved), ], ), body: _buildSuggestions(), ); } ... }
因爲沒有定義_pushSaved
函數,直接複製上面註釋說明的代碼會報錯,所以,定義一個void類型的函數_pushSaved
:
class RandomWordsState extends State<RandomWords> { ... void _pushSaved() { } }
這時候能夠看到右上角多了3個圖標,如圖:
而後咱們往_pushSaved()
函數中添加代碼,讓收藏夾玩起來:
void _pushSaved() { //建立導航欄控件Navigator,而後往裏面塞入MaterialPageRoute控件 Navigator.of(context).push( new MaterialPageRoute( builder: (context) { //經過遍歷_saved對象,獲取已收藏的行對象 final tiles = _saved.map( (pair) { return new ListTile( title: new Text( pair.asPascalCase, style: _biggerFont, ), ); }, ); //函數的的鏈式調用,獲取到添加好分割線的ListTile控件 final divided = ListTile .divideTiles( //divideTiles()函數,向每一個tile間隔插入一個1像素寬的邊框 context: context, tiles: tiles, ) .toList(); //不要漏掉這個函數,不然進入收藏夾直接崩潰 return new Scaffold( appBar: new AppBar( title: new Text('收藏的列表項目'), ), body: new ListView(children: divided), //直接將準備好的ListTile塞入其中,完成內容填充 ); }, ), ); }
保存代碼後,刷新APP,如圖:
如上圖所示,紅色的箭頭表示點擊按鈕後頁面的切換,綠色箭頭表示收藏夾的值的對照,有沒有發現咱們並無寫返回主頁按鈕的代碼,這個返回按鈕從哪來的呢?是由Navigator對象自動生成的,而且自動指向到主頁面的路由,不過遺憾的是,沒有加入返回手勢的支持,若有須要,還得本身寫,具體怎麼寫,跟着個人筆記走吧,我如今也不知道。
你們注意看主頁列表和收藏夾列表的區別,二者都是列表,都是使用的ListView和ListTile對象,但實現的方式徹底不一樣,插入行對象和分割線的差別頗有意思,有興趣的同窗能夠自行修改下代碼,看看能不能將兩種列表的構建方法對調一下,參考官方資料ListView和ListTile
變動UI主題風格。
這一步超級簡單,往MaterialApp對象裏添加theme
屬性值便可:
class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return new MaterialApp( title: 'Startup Name Generator', //添加theme屬性值,塞入ThemeData對象 theme: new ThemeData( primaryColor: Colors.white, ), home: new RandomWords(), ); } }
再保存,刷新APP試試,主題變成了白色風格:
ThemeData()
方法自己提供了不少配色方案,玩家能夠參考ThemeData官方說明,掌握其強大功能。沒想到變動主題如此輕鬆,在當前APP界愈來愈追求視覺體驗升級的趨勢下,使用flutter開發APP的玩家應該欣慰很多吧~
好勒,此次官方萌新課程的搬運就到這裏,我是真的超喜歡這個萌新課程,連課程總結都幫我寫好了:
自我總結一下,flutter是一個控件高度集成化的開發平臺,控件的完整度極高,控件之間的交互實現也傾向於傻瓜化,好比自動生成返回按鈕、主題風格全局可控。只要把握好狀態值和控件之間的嵌套關係,開發者幾乎不須要單獨敲代碼實現跳轉邏輯,代碼簡直不要太簡潔,不知不覺間就寫好了一個APP。而VScode中的Dart Code插件也實在太好用,代碼提示、函數用法和參數都有詳盡的說明,看得出谷歌拿出了十足的誠意要在跨平臺開發上面大搞特搞一番。
固然了,滿屏幕的回調函數讓我這種編程思惟還停留在C語言時代的菜鳥來講,扶牆~ 頭有點暈,還須要點時間慢慢適應一下下,也因爲我沒有那麼深厚的技術功底,對這個教程的理解還比較有限,可能有寫的不對或很差的地方,也歡迎你們指正,尤爲我花了一天一晚上寫了這篇稿也是蠻不容易了,有路過的高手不說兩句也是哪啥了是吧。固然,我有空的時候抓緊讀一讀Dart 語法基礎和官方原版,有了新的發現也會寫稿分享出來。
好啦就寫到這裏,廣告時間,對flutter感興趣的小夥伴能夠關注我,歡迎你們到Flutter圈子中投稿,也能夠聯繫管理員加入咱們的flutter微信羣嗨聊,謝謝捧場~!
flutter 中文社區(官方QQ羣:338252156)