Flutter開發(fā):AS編寫第一個Flutter App

原文引用:Flutter中文網(wǎng)

本文將構(gòu)建一個簡單的如下功能的APP:為一個創(chuàng)業(yè)公司生成建議的名稱。用戶可以選擇和取消選擇的名稱、保存(收藏)喜歡的名稱艳丛。該代碼一次生成十個名稱诈嘿,當用戶滾動時,會生成一新批名稱。用戶可以點擊導航欄右邊的列表圖標,以打開到僅列出收藏名稱的新頁面。

注:本篇是基于Flutter中文網(wǎng)的案例溪食,為了更加詳細易懂,我添加了更多的圖片示例說明臀防,所以篇幅較長眠菇,但其實實際步驟并沒有太多內(nèi)容,不要怕袱衷。

示意圖

包含知識點:

1.Flutter應用程序的基本結(jié)構(gòu).

2.查找和使用packages來擴展功能.

3.使用熱重載加快開發(fā)周期.

4.如何實現(xiàn)有狀態(tài)的widget.

5.如何創(chuàng)建一個無限的捎废、延遲加載的列表.

6.如何創(chuàng)建并導航到第二個頁面.

7.如何使用主題更改應用程序的外觀.


第一步:創(chuàng)建Flutter App


根據(jù)上篇文章的方式,新建一個項目致燥,命名為startip_namer

Flutter開發(fā):環(huán)境配置與新建項目

1.替換lib/main.dart

刪除lib / main.dart中的所有代碼登疗,然后替換為下面的代碼,它將在屏幕的中心顯示“Hello World”.

import 'package:flutter/material.dart';?

void main()=>runApp(new MyApp());

class MyApp extends StatelessWidget{? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?@override

Widget build (BuildContext context){

return new MaterialApp(

title:'Welcome to Flutter',

home:new Scaffold(

? ? ? ? ? ?appBar:new AppBar(

? ? ? ? ? ? ? ? ? ? ? ? ? title:new Text('Welcome to Flutter'),),

? ? ? ? ? ?body:new Center(

? ? ? ? ? ? ? ? ? ? ? ? ? child:new Text('Hello World'),),

),

);

}}

提示:?將代碼粘貼到應用中時,縮進可能會變形辐益。您可以使用Flutter工具自動修復此問題:

Android Studio / IntelliJ IDEA: 右鍵單擊Dart代碼断傲,然后選擇?Reformat Code with dartfmt.

VS Code: 右鍵單擊并選擇?Format Document.

Terminal: 運行?flutter format <filename>.

示例圖

2.運行應用程序,你應該看到如下界面.

示例圖

分析

? 本示例創(chuàng)建一個Material APP智政。Material是一種標準的移動端和web端的視覺設計語言认罩。 Flutter提供了一套豐富的Material widgets。

? main函數(shù)使用了(=>)符號, 這是Dart中單行函數(shù)或方法的簡寫续捂。

? 該應用程序繼承了 StatelessWidget垦垂,這將會使應用本身也成為一個widget。 在Flutter中牙瓢,大多數(shù)東西都是widget劫拗,包括對齊(alignment)、填充(padding)和布局(layout)

? Scaffold 是 Material library 中提供的一個widget, 它提供了默認的導航欄矾克、標題和包含主屏幕widget樹的body屬性页慷。widget樹可以很復雜。

? widget的主要工作是提供一個build()方法來描述如何根據(jù)其他較低級別的widget來顯示自己胁附。

? 本示例中的body的widget樹中包含了一個Center widget, Center widget又包含一個 Text 子widget酒繁。 Center widget可以將其子widget樹對其到屏幕中心。


第2步: 使用外部包(package)


在這一步中控妻,您將開始使用一個名為english_words的開源軟件包 欲逃,其中包含數(shù)千個最常用的英文單詞以及一些實用功能.

可以在pub.dartlang.org上面找到其他許多開源軟件包

1.在如下圖的位置(dependencies 中)添加依賴

pubspec文件管理Flutter應用程序的assets(資源,如圖片饼暑、package等)。?在pubspec.yaml中洗做,將依賴包添加到項目

english_words:^3.1.0

示例圖

2.加入后點擊Packages get,將依賴包安裝到項目中.

Packages get

3.在?lib/main.dart?中, 引入?english_words

import'package:english_words/english_words.dart';

圖中灰色是因為沒有使用過弓叛,此時在我們輸入時便會有該庫的相關(guān)提示。

示例圖

4.使用 English words 包生成文本來替換字符串“Hello World”.

為方便復制诚纸,我將需要的代碼粘貼過來:

final wordPair = new WordPair.random();

child: new Text(wordPair.asPascalCase),

示例圖

5.使用熱更新

如果你之前的應用程序正在運行撰筷,界面是之前的hello world,即可點擊工具欄中的閃電圖標進行熱更新畦徘,這樣可以更快速地將代碼更新到手機或虛擬機

示例圖


第3步: 添加一個?有狀態(tài)的部件(Stateful widget)


Stateless?widgets 是不可變的, 這意味著它們的屬性不能改變 - 所有的值都是最終的.

Stateful?widgets 持有的狀態(tài)可能在widget生命周期中發(fā)生變化. 實現(xiàn)一個 stateful widget 至少需要兩個類: 一個 StatefulWidget類毕籽。一個 State類。 StatefulWidget類本身是不變的井辆,但是 State類在widget生命周期中始終存在.

在這一步中关筒,將添加一個有狀態(tài)的widget-RandomWords,它創(chuàng)建其State類RandomWordsState杯缺。State類將最終為widget維護生成的隨機單詞對和選中的單詞對蒸播。

1.添加有狀態(tài)的 RandomWords widget 到 main.dart

它也可以在MyApp之外的文件的任何位置使用,但是本示例將它放到了文件的底部。RandomWords widget除了創(chuàng)建State類之外幾乎沒有其他任何東西

class RandomWords extends StatefulWidget {

? ? ? ? ? @override

? ? ? ? ? createState() => new RandomWordsState();

}

2.添加 RandomWordsState 類

該應用程序的大部分代碼都在該類中袍榆, 該類持有RandomWords widget的狀態(tài)胀屿。這個類將保存隨著用戶滾動而無限增長的生成的單詞對, 以及喜歡的單詞對包雀,用戶通過重復點擊心形 ?? 圖標來將它們從列表中添加或刪除宿崭。

class RandomWordsState extends State<RandomWords> {

@override

Widget build(BuildContext context) {

? ? ? ? ? ? ? final wordPair = new WordPair.random();

? ? ? ? ? ? ? return new Text(wordPair.asPascalCase);

? ? ? ? ? ? ? ?}

}

3.生成單詞對代的碼從MyApp移動到RandomWordsState中

class MyApp extends StatelessWidget {

@override

Widget build(BuildContext context) {

final wordPair = new WordPair.random(); // 刪除此行

return new MaterialApp(

title: 'Welcome to Flutter',

home: new Scaffold(

? ? ? ? ? appBar: new AppBar( title: new Text('Welcome to Flutter'), ),

? ? ? ? ? body: new Center(

? ? ? ? ? ? ? ? ? ?//child: new Text(wordPair.asPascalCase),

? ? ? ? ? ? ? ? ? ?child: new RandomWords(),

), ), );

}

}

最終main.dart文件如下圖所示:

示例圖


第4步: 創(chuàng)建一個無限滾動ListView


在這一步中,我們將擴展(繼承)RandomWordsState類才写,以生成并顯示單詞對列表葡兑。 當用戶滾動時,ListView中顯示的列表將無限增長琅摩。 ListView的builder工廠構(gòu)造函數(shù)允許您按需建立一個懶加載的列表視圖铁孵。

1.向RandomWordsState類中添加一個_suggestions列表以保存建議的單詞對。 該變量以下劃線(_)開頭房资,在Dart語言中使用下劃線前綴標識符蜕劝,會強制其變成私有的。另外轰异,添加一個biggerFont變量來增大字體大小

class RandomWordsState extends State<RandomWords> {

? ? ? ? final _suggestions = <WordPair>[];

? ? ? ? final _biggerFont = const TextStyle(fontSize: 18.0);

? ? ? ? ...

}

2.向RandomWordsState類添加一個?_buildSuggestions()?函數(shù). 此方法構(gòu)建顯示建議單詞對的ListView岖沛。

ListView類提供了一個builder屬性,itemBuilder?值是一個匿名回調(diào)函數(shù)搭独, 接受兩個參數(shù)- BuildContext和行迭代器i婴削。迭代器從0開始, 每調(diào)用一次該函數(shù)牙肝,i就會自增1唉俗,對于每個建議的單詞對都會執(zhí)行一次。該模型允許建議的單詞對列表在用戶滾動時無限增長配椭。

class RandomWordsState extends State<RandomWords> {

...

? ? Widget _buildSuggestions() {

? ? ? ? ? ?return new ListView.builder(

? ? ? ? ? ? ? ? ? ? padding: const EdgeInsets.all(16.0),

// 對于每個建議的單詞對都會調(diào)用一次itemBuilder虫溜,然后將單詞對添加到ListTile行中

// 在偶數(shù)行,該函數(shù)會為單詞對添加一個ListTile row.

// 在奇數(shù)行股缸,該函數(shù)會添加一個分割線widget衡楞,來分隔相鄰的詞對。

// 注意敦姻,在小屏幕上瘾境,分割線看起來可能比較吃力。

? ? ? ? ? ? ? ? ? itemBuilder: (context, i) {

// 在每一列之前镰惦,添加一個1像素高的分隔線widget

? ? ? ? ? ? ? ? ? ? ? ? ? if (i.isOdd) return new Divider();

// 語法 "i ~/ 2" 表示i除以2迷守,但返回值是整形(向下取整),比如i為:1, 2, 3, 4, 5

// 時陨献,結(jié)果為0, 1, 1, 2, 2盒犹, 這可以計算出ListView中減去分隔線后的實際單詞對數(shù)量? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? final index = i ~/ 2;

// 如果是建議列表中最后一個單詞對

? ? ? ? ? ? ? ? ? ? ? ? ? if (index >= _suggestions.length) {

// ...接著再生成10個單詞對,然后添加到建議列表? ? ?

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? _suggestions.addAll(generateWordPairs().take(10));

}

? ? ? ? ? ? ? ? ? ? ? ? ?return _buildRow(_suggestions[index]);

} ); }}

3.在RandomWordsState中添加一個_buildRow函數(shù) :

對于每一個單詞對,_buildSuggestions函數(shù)都會調(diào)用一次_buildRow急膀。 這個函數(shù)在ListTile中顯示每個新詞對沮协,這使您在下一步中可以生成更漂亮的顯示行

class RandomWordsState extends State<RandomWords> {

...

? ? ? ? ?Widget _buildRow(WordPair pair) {

? ? ? ? ? ? ? ? ? ? ? ?return new ListTile(

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? title: new Text(

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?pair.asPascalCase,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?style: _biggerFont,

), ); }}

4.更新RandomWordsState的build方法以使_buildSuggestions(),而不是直接調(diào)用單詞生成庫卓嫂。

class RandomWordsState extends State<RandomWords> {

...

@override

Widget build(BuildContext context) {

//final wordPair = new WordPair.random(); // 刪除這兩行

//return new Text(wordPair.asPascalCase);

return new Scaffold (

appBar: new AppBar(

title: new Text('Startup Name Generator'), ),

body: _buildSuggestions(),

); }

...

}

5.更新MyApp的build方法慷暂。從MyApp中刪除Scaffold和AppBar實例。

用下面部分替換最初的build方法:

class MyApp extends StatelessWidget {

@override

Widget build(BuildContext context) {

return new MaterialApp(

title: 'Startup Name Generator',

home: new RandomWords(),

); }}

第四步最終代碼:第四步代碼


第5步: 添加交互

在這一步中晨雳,您將為每一行添加一個可點擊的心形 ?? 圖標行瑞。當用戶點擊列表中的條目,切換其“收藏”狀態(tài)時餐禁,將該詞對添加到或移除出“收藏夾”血久。

1.添加一個?_saved?Set(集合) 到RandomWordsState。

這個集合存儲用戶喜歡(收藏)的單詞對帮非。 在這里氧吐,Set比List更合適,因為Set中不允許重復的值末盔。

class RandomWordsState extends State<RandomWords> {

final _suggestions = <WordPair>[];

final _saved = new Set<WordPair>();

final _biggerFont = const TextStyle(fontSize: 18.0);

...

}

2.在?_buildRow?方法中添加?alreadySaved來檢查確保單詞對還沒有添加到收藏夾中筑舅。

final alreadySaved = _saved.contains(pair);

3.同時在?_buildRow()中, 添加一個心形 ?? 圖標到 ListTiles以啟用收藏功能陨舱。

Widget _buildRow(WordPair pair) {

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,),

); }

4.在?_buildRow中讓心形??圖標變得可以點擊翠拣。

如果單詞條目已經(jīng)添加到收藏夾中, 再次點擊它將其從收藏夾中刪除游盲。當心形??圖標被點擊時误墓,函數(shù)調(diào)用setState()通知框架狀態(tài)已經(jīng)改變。

Widget _buildRow(WordPair pair) {

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); } }); },?

); }

此時可以看到界面:

示例圖

對比源碼:第五步代碼


第6步: 導航到新頁面

在這一步中益缎,您將添加一個顯示收藏夾內(nèi)容的新頁面(在Flutter中稱為路由(route))优烧。您將學習如何在主路由和新路由之間導航(切換頁面)。

在Flutter中链峭,導航器管理應用程序的路由棧。將路由推入(push)到導航器的棧中又沾,將會顯示更新為該路由頁面弊仪。 從導航器的棧中彈出(pop)路由,將顯示返回到前一個路由杖刷。

1.在RandomWordsState的build方法中為AppBar添加一個列表圖標励饵。當用戶點擊列表圖標時,包含收藏夾的新路由頁面入棧顯示滑燃。

appBar: new AppBar(

title: new Text('Startup Name Generator'),

actions: <Widget>[

new IconButton(icon: new Icon(Icons.list), onPressed: _pushSaved), ], ),

2.向RandomWordsState類添加一個?_pushSaved()?方法.

當用戶點擊導航欄中的列表圖標時役听,建立一個路由并將其推入到導航管理器棧中。此操作會切換頁面以顯示新路由。

新頁面的內(nèi)容在在MaterialPageRoute的builder屬性中構(gòu)建典予,builder是一個匿名函數(shù)甜滨。

添加Navigator.push調(diào)用,這會使路由入棧(以后路由入棧均指推入到導航管理器的棧)

添加MaterialPageRoute及其builder瘤袖。 現(xiàn)在衣摩,添加生成ListTile行的代碼。ListTile的divideTiles()方法在每個ListTile之間添加1像素的分割線捂敌。 該?divided?變量持有最終的列表項艾扮。

builder返回一個Scaffold,其中包含名為“Saved Suggestions”的新路由的應用欄占婉。 新路由的body由包含ListTiles行的ListView組成; 每行之間通過一個分隔線分隔泡嘴。

class RandomWordsState extends State<RandomWords> {

...

void _pushSaved() {

Navigator.of(context).push(

new MaterialPageRoute( builder: (context) {

final tiles = _saved.map( (pair) {

return new ListTile(

title: new Text( pair.asPascalCase, style: _biggerFont, ), ); }, );

final divided = ListTile .divideTiles( context: context, tiles: tiles, ) .toList();

return new Scaffold( appBar: new AppBar( title: new Text('Saved Suggestions'), ),

body: new ListView(children: divided), ); }, ), );?}

}

此時界面如圖:


選中效果


選中條目跳轉(zhuǎn)


第7步:使用主題更改UI

在這最后一步中,您將會使用主題逆济。主題控制您應用程序的外觀和風格酌予。您可以使用默認主題,該主題取決于物理設備或模擬器纹腌,也可以自定義主題以適應您的品牌霎终。

您可以通過配置ThemeData類輕松更改應用程序的主題。 您的應用程序目前使用默認主題升薯,下面將更改primary color顏色為白色莱褒。

class MyApp extends StatelessWidget {

@override

Widget build(BuildContext context) {

return new MaterialApp(

title: 'Startup Name Generator',

theme: new ThemeData( primaryColor: Colors.white, ),

home: new RandomWords(),

); } }

實現(xiàn)效果如圖,可見標題欄變?yōu)榘咨珮邮?/p>

示例圖

這里放我本人的最終實現(xiàn)代碼截圖涎劈,每張截圖最下方是下一張截圖的上方广凸,也可以根據(jù)代碼行數(shù)對比。方便查找蛛枚。同學們可以對比實現(xiàn)

第一部分
第二部分
第三部分
第四部分
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末谅海,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子蹦浦,更是在濱河造成了極大的恐慌扭吁,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件盲镶,死亡現(xiàn)場離奇詭異侥袜,居然都是意外死亡,警方通過查閱死者的電腦和手機溉贿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門枫吧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人宇色,你說我怎么就攤上這事九杂“浜” “怎么了?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵例隆,是天一觀的道長甥捺。 經(jīng)常有香客問我,道長裳擎,這世上最難降的妖魔是什么涎永? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮鹿响,結(jié)果婚禮上羡微,老公的妹妹穿的比我還像新娘。我一直安慰自己惶我,他們只是感情好妈倔,可當我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著绸贡,像睡著了一般盯蝴。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上听怕,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天捧挺,我揣著相機與錄音,去河邊找鬼尿瞭。 笑死闽烙,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的声搁。 我是一名探鬼主播黑竞,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼疏旨!你這毒婦竟也來了很魂?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤檐涝,失蹤者是張志新(化名)和其女友劉穎遏匆,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體谁榜,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡拉岁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了惰爬。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡惫企,死狀恐怖撕瞧,靈堂內(nèi)的尸體忽然破棺而出陵叽,到底是詐尸還是另有隱情,我是刑警寧澤丛版,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布巩掺,位于F島的核電站,受9級特大地震影響页畦,放射性物質(zhì)發(fā)生泄漏胖替。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一豫缨、第九天 我趴在偏房一處隱蔽的房頂上張望独令。 院中可真熱鬧,春花似錦好芭、人聲如沸燃箭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽招狸。三九已至,卻和暖如春邻薯,著一層夾襖步出監(jiān)牢的瞬間裙戏,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工厕诡, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留累榜,地道東北人。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓木人,卻偏偏與公主長得像信柿,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子醒第,可洞房花燭夜當晚...
    茶點故事閱讀 42,925評論 2 344

推薦閱讀更多精彩內(nèi)容