第1步: 創建 Flutter app
創建一個簡單的、基于模闆的Flutter應用(yòng)程序,按照創建您的第一個Flutter應用(yòng)中的指南的步驟, 然後将項目命名爲startup_namer(而不是myapp),接下(xià)來(lái)你(nǐ)将會(huì)修改這(zhè)個應用(yòng)來(lái)完成最終的APP。
在這(zhè)個示例中,你(nǐ)将主要編輯Dart代碼所在的 lib/main.dart 文(wén)件,
提示: 将代碼粘貼到(dào)應用(yòng)中時(shí),縮進可能(néng)會(huì)變形。您可以使用(yòng)Flutter工(gōng)具自(zì)動修複此問題:
Android Studio / IntelliJ IDEA: 右鍵單擊Dart代碼,然後選擇 Reformat Code with dartfmt.
VS Code: 右鍵單擊并選擇 Format Document.
Terminal: 運行 flutter format
替換 lib/main.dart.删除lib / main.dart中的所有代碼,然後替換爲下(xià)面的代碼,它将在屏幕的中心顯示“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'),
),
),
);
}
}
運行應用(yòng)程序,你(nǐ)應該看(kàn)到(dào)如下(xià)界面.
screenshot of hello world app
分析
本示例創建一個Material APP。Material是一種标準的移動端和(hé)web端的視(shì)覺設計(jì)語言。 Flutter提供了(le)一套豐富的Material widgets。
main函數使用(yòng)了(le)(=>)符号, 這(zhè)是Dart中單行函數或方法的簡寫。
該應用(yòng)程序繼承了(le) StatelessWidget,這(zhè)将會(huì)使應用(yòng)本身也(yě)成爲一個widget。 在Flutter中,大(dà)多數東西都是widget,包括對(duì)齊(alignment)、填充(padding)和(hé)布局(layout)
Scaffold 是 Material library 中提供的一個widget, 它提供了(le)默認的導航欄、标題和(hé)包含主屏幕widget樹的body屬性。widget樹可以很(hěn)複雜(zá)。
widget的主要工(gōng)作(zuò)是提供一個build()方法來(lái)描述如何根據其他(tā)較低(dī)級别的widget來(lái)顯示自(zì)己。
本示例中的body的widget樹中包含了(le)一個Center widget, Center widget又包含一個 Text 子widget。 Center widget可以将其子widget樹對(duì)其到(dào)屏幕中心。
第2步: 使用(yòng)外(wài)部包(package)
在這(zhè)一步中,您将開(kāi)始使用(yòng)一個名爲english_words的開(kāi)源軟件包 ,其中包含數千個最常用(yòng)的英文(wén)單詞以及一些(xiē)實用(yòng)功能(néng).
您可以 在pub.dartlang.org上(shàng)找到(dào)english_words軟件包以及其他(tā)許多開(kāi)源軟件包
pubspec文(wén)件管理(lǐ)Flutter應用(yòng)程序的assets(資源,如圖片、package等)。 在pubspec.yaml中,将english_words(3.1.0或更高(gāo)版本)添加到(dào)依賴項列表,如下(xià)面高(gāo)亮(liàng)顯示的行:
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^0.1.0
english_words: ^3.1.0
在Android Studio的編輯器視(shì)圖中查看(kàn)pubspec時(shí),單擊右上(shàng)角的 Packages get,這(zhè)會(huì)将依賴包安裝到(dào)您的項目。您可以在控制台中看(kàn)到(dào)以下(xià)内容:
flutter packages get
Running "flutter packages get" in startup_namer...
Process finished with exit code 0
在 lib/main.dart 中, 引入 english_words, 如高(gāo)亮(liàng)顯示的行所示:
import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';
在您輸入時(shí),Android Studio會(huì)爲您提供有關庫導入的建議(yì)。然後它将呈現(xiàn)灰色的導入字符串,讓您知(zhī)道(dào)導入的庫尚未使用(yòng)(到(dào)目前爲止)
使用(yòng) English words 包生成文(wén)本來(lái)替換字符串“Hello World”.
Tip: “駝峰命名法” (稱爲 “upper camel case” 或 “Pascal case” ), 表示字符串中的每個單詞(包括第一個單詞)都以大(dà)寫字母開(kāi)頭。所以,“uppercamelcase” 變成 “UpperCamelCase”進行以下(xià)更改, 如:
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) {
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('Hello World'),
child: new Text(wordPair.asPascalCase),
),
),
);
}
}
如果應用(yòng)程序正在運行,請(qǐng)使用(yòng)熱重載按鈕 (lightning bolt icon) 更新正在運行的應用(yòng)程序。每次單擊熱重載或保存項目時(shí),都會(huì)在正在運行的應用(yòng)程序中随機選擇不同的單詞對(duì)。 這(zhè)是因爲單詞對(duì)是在 build 方法内部生成的。每次MaterialApp需要渲染時(shí)或者在Flutter Inspector中切換平台時(shí) build 都會(huì)運行.
screenshot at completion of second step
第3步: 添加一個 有狀态的部件(Stateful widget)
Stateless widgets 是不可變的, 這(zhè)意味着它們的屬性不能(néng)改變 - 所有的值都是最終的.
Stateful widgets 持有的狀态可能(néng)在widget生命周期中發生變化. 實現(xiàn)一個 stateful widget 至少需要兩個類:
一個 StatefulWidget類。
一個 State類。 StatefulWidget類本身是不變的,但(dàn)是 State類在widget生命周期中始終存在.
在這(zhè)一步中,您将添加一個有狀态的widget-RandomWords,它創建其State類RandomWordsState。State類将最終爲widget維護建議(yì)的和(hé)喜歡的單詞對(duì)。
添加有狀态的 RandomWords widget 到(dào) main.dart。 它也(yě)可以在MyApp之外(wài)的文(wén)件的任何位置使用(yòng),但(dàn)是本示例将它放(fàng)到(dào)了(le)文(wén)件的底部。RandomWords widget除了(le)創建State類之外(wài)幾乎沒有其他(tā)任何東西
class RandomWords extends StatefulWidget {
@override
createState() => new RandomWordsState();
}
添加 RandomWordsState 類.該應用(yòng)程序的大(dà)部分代碼都在該類中, 該類持有RandomWords widget的狀态。這(zhè)個類将保存随着用(yòng)戶滾動而無限增長的生成的單詞對(duì), 以及喜歡的單詞對(duì),用(yòng)戶通過重複點擊心形 ❤️ 圖标來(lái)将它們從(cóng)列表中添加或删除。你(nǐ)會(huì)一步一步地建立這(zhè)個類。首先,通過添加高(gāo)亮(liàng)顯示的代碼創建一個最小(xiǎo)類
class RandomWordsState extends State
}
在添加狀态類後,IDE會(huì)提示該類缺少build方法。接下(xià)來(lái),您将添加一個基本的build方法,該方法通過将生成單詞對(duì)的代碼從(cóng)MyApp移動到(dào)RandomWordsState來(lái)生成單詞對(duì)。将build方法添加到(dào)RandomWordState中,如下(xià)面高(gāo)亮(liàng)代碼所示
class RandomWordsState extends State
@override
Widget build(BuildContext context) {
final wordPair = new WordPair.random();
return new Text(wordPair.asPascalCase);
}
}
通過下(xià)面高(gāo)亮(liàng)顯示的代碼,将生成單詞對(duì)代的碼從(cóng)MyApp移動到(dào)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(),
),
),
);
}
}
重新啓動應用(yòng)程序。如果您嘗試熱重載,則可能(néng)會(huì)看(kàn)到(dào)一條警告:
Reloading...
Not all changed program elements ran during view reassembly; consider
restarting.
這(zhè)可能(néng)是誤報(bào),但(dàn)考慮到(dào)重新啓動可以确保您的更改在應用(yòng)界面中生效。
應用(yòng)程序應該像之前一樣運行,每次熱重載或保存應用(yòng)程序時(shí)都會(huì)顯示一個單詞對(duì)。
screenshot at completion of third step
遇到(dào)問題?
如果您的應用(yòng)程序運行不正常,可以使用(yòng)下(xià)面鏈接中的代碼來(lái)對(duì)比更正。
lib/main.dart
第4步: 創建一個無限滾動ListView
在這(zhè)一步中,您将擴展(繼承)RandomWordsState類,以生成并顯示單詞對(duì)列表。 當用(yòng)戶滾動時(shí),ListView中顯示的列表将無限增長。 ListView的builder工(gōng)廠(chǎng)構造函數允許您按需建立一個懶加載的列表視(shì)圖。
向RandomWordsState類中添加一個_suggestions列表以保存建議(yì)的單詞對(duì)。 該變量以下(xià)劃線(_)開(kāi)頭,在Dart語言中使用(yòng)下(xià)劃線前綴标識符,會(huì)強制其變成私有的。另外(wài),添加一個biggerFont變量來(lái)增大(dà)字體大(dà)小(xiǎo)
class RandomWordsState extends State
final _suggestions =
final _biggerFont = const TextStyle(fontSize: 18.0);
...
}
向RandomWordsState類添加一個 _buildSuggestions() 函數. 此方法構建顯示建議(yì)單詞對(duì)的ListView。ListView類提供了(le)一個builder屬性,itemBuilder 值是一個匿名回調函數, 接受兩個參數- BuildContext和(hé)行叠代器i。叠代器從(cóng)0開(kāi)始, 每調用(yòng)一次該函數,i就會(huì)自(zì)增1,對(duì)于每個建議(yì)的單詞對(duì)都會(huì)執行一次。該模型允許建議(yì)的單詞對(duì)列表在用(yòng)戶滾動時(shí)無限增長。添加如下(xià)高(gāo)亮(liàng)的行:
class RandomWordsState extends State
...
Widget _buildSuggestions() {
return new ListView.builder(
padding: const EdgeInsets.all(16.0),
// 對(duì)于每個建議(yì)的單詞對(duì)都會(huì)調用(yòng)一次itemBuilder,然後将單詞對(duì)添加到(dào)ListTile行中
// 在偶數行,該函數會(huì)爲單詞對(duì)添加一個ListTile row.
// 在奇數行,該函數會(huì)添加一個分割線widget,來(lái)分隔相鄰的詞對(duì)。
// 注意,在小(xiǎo)屏幕上(shàng),分割線看(kàn)起來(lái)可能(néng)比較吃力。
itemBuilder: (context, i) {
// 在每一列之前,添加一個1像素高(gāo)的分隔線widget
if (i.isOdd) return new Divider();
// 語法 "i ~/ 2" 表示i除以2,但(dàn)返回值是整形(向下(xià)取整),比如i爲:1, 2, 3, 4, 5
// 時(shí),結果爲0, 1, 1, 2, 2, 這(zhè)可以計(jì)算(suàn)出ListView中減去分隔線後的實際單詞對(duì)數量
final index = i ~/ 2;
// 如果是建議(yì)列表中最後一個單詞對(duì)
if (index >= _suggestions.length) {
// ...接着再生成10個單詞對(duì),然後添加到(dào)建議(yì)列表
_suggestions.addAll(generateWordPairs().take(10));
}
return _buildRow(_suggestions[index]);
}
);
}
}
對(duì)于每一個單詞對(duì),_buildSuggestions函數都會(huì)調用(yòng)一次_buildRow。 這(zhè)個函數在ListTile中顯示每個新詞對(duì),這(zhè)使您在下(xià)一步中可以生成更漂亮(liàng)的顯示行在RandomWordsState中添加一個_buildRow函數 :
class RandomWordsState extends State
...
Widget _buildRow(WordPair pair) {
return new ListTile(
title: new Text(
pair.asPascalCase,
style: _biggerFont,
),
);
}
}
更新RandomWordsState的build方法以使用(yòng)_buildSuggestions(),而不是直接調用(yòng)單詞生成庫。 更改後如下(xià)面高(gāo)亮(liàng)部分:
class RandomWordsState extends State
...
@override
Widget build(BuildContext context) {
final wordPair = new WordPair.random(); // 删除這(zhè)兩行
return new Text(wordPair.asPascalCase);
return new Scaffold (
appBar: new AppBar(
title: new Text('Startup Name Generator'),
),
body: _buildSuggestions(),
);
}
...
}
更新MyApp的build方法。從(cóng)MyApp中删除Scaffold和(hé)AppBar實例。 這(zhè)些(xiē)将由RandomWordsState管理(lǐ),這(zhè)使得用(yòng)戶在下(xià)一步中從(cóng)一個屏幕導航到(dào)另一個屏幕時(shí), 可以更輕松地更改導航欄中的的路由名稱。用(yòng)下(xià)面高(gāo)亮(liàng)部分替換最初的build方法:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Startup Name Generator',
home: new RandomWords(),
);
}
}
重新啓動應用(yòng)程序。你(nǐ)應該看(kàn)到(dào)一個單詞對(duì)列表。盡可能(néng)地向下(xià)滾動,您将繼續看(kàn)到(dào)新的單詞對(duì)。
screenshot at completion of fourth step
遇到(dào)問題?
如果你(nǐ)的應用(yòng)沒有正常運行,你(nǐ)可以使用(yòng)一下(xià)鏈接中的代碼對(duì)比更正。
lib/main.dart
第5步: 添加交互
在這(zhè)一步中,您将爲每一行添加一個可點擊的心形 ❤️ 圖标。當用(yòng)戶點擊列表中的條目,切換其“收藏”狀态時(shí),将該詞對(duì)添加到(dào)或移除出“收藏夾”。
添加一個 _saved Set(集合) 到(dào)RandomWordsState。這(zhè)個集合存儲用(yòng)戶喜歡(收藏)的單詞對(duì)。 在這(zhè)裏,Set比List更合适,因爲Set中不允許重複的值。
class RandomWordsState extends State
final _suggestions =
final _saved = new Set
final _biggerFont = const TextStyle(fontSize: 18.0);
...
}
在 _buildRow 方法中添加 alreadySaved來(lái)檢查确保單詞對(duì)還沒有添加到(dào)收藏夾中。
Widget _buildRow(WordPair pair) {
final alreadySaved = _saved.contains(pair);
...
}
同時(shí)在 _buildRow()中, 添加一個心形 ❤️ 圖标到(dào) ListTiles以啓用(yòng)收藏功能(néng)。接下(xià)來(lái),你(nǐ)就可以給心形 ❤️ 圖标添加交互能(néng)力了(le)。添加下(xià)面高(gāo)亮(lià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,
),
);
}
重新啓動應用(yòng)。你(nǐ)現(xiàn)在可以在每一行看(kàn)到(dào)心形❤️圖标️,但(dàn)它們還沒有交互。
在 _buildRow中讓心形❤️圖标變得可以點擊。如果單詞條目已經添加到(dào)收藏夾中, 再次點擊它将其從(cóng)收藏夾中删除。當心形❤️圖标被點擊時(shí),函數調用(yòng)setState()通知(zhī)框架狀态已經改變。添加如下(xià)高(gāo)亮(lià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);
}
});
},
);
}
提示: 在Flutter的響應式風(fēng)格的框架中,調用(yòng)setState() 會(huì)爲State對(duì)象觸發build()方法,從(cóng)而導緻對(duì)UI的更新
熱重載你(nǐ)的應用(yòng)。你(nǐ)就可以點擊任何一行收藏或移除。請(qǐng)注意,點擊一行時(shí)會(huì)生成從(cóng)心形 ❤️ 圖标發出的水(shuǐ)波動畫(huà)
screenshot at completion of 5th step
遇到(dào)了(le)問題?
如果您的應用(yòng)沒有正常運行,請(qǐng)查看(kàn)下(xià)面鏈接處的代碼,對(duì)比更正。
lib/main.dart
第6步: 導航到(dào)新頁面
在這(zhè)一步中,您将添加一個顯示收藏夾内容的新頁面(在Flutter中稱爲路由(route))。您将學習如何在主路由和(hé)新路由之間導航(切換頁面)。
在Flutter中,導航器管理(lǐ)應用(yòng)程序的路由棧。将路由推入(push)到(dào)導航器的棧中,将會(huì)顯示更新爲該路由頁面。 從(cóng)導航器的棧中彈出(pop)路由,将顯示返回到(dào)前一個路由。
在RandomWordsState的build方法中爲AppBar添加一個列表圖标。當用(yòng)戶點擊列表圖标時(shí),包含收藏夾的新路由頁面入棧顯示。提示: 某些(xiē)widget屬性需要單個widget(child),而其它一些(xiē)屬性,如action,需要一組widgets(children),用(yòng)方括号[]表示。将該圖标及其相應的操作(zuò)添加到(dào)build方法中:
class RandomWordsState extends State
...
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('Startup Name Generator'),
actions:
new IconButton(icon: new Icon(Icons.list), onPressed: _pushSaved),
],
),
body: _buildSuggestions(),
);
}
...
}
向RandomWordsState類添加一個 _pushSaved() 方法.
class RandomWordsState extends State
...
void _pushSaved() {
}
}
熱重載應用(yòng),列表圖标将會(huì)出現(xiàn)在導航欄中。現(xiàn)在點擊它不會(huì)有任何反應,因爲 _pushSaved 函數還是空(kōng)的。
當用(yòng)戶點擊導航欄中的列表圖标時(shí),建立一個路由并将其推入到(dào)導航管理(lǐ)器棧中。此操作(zuò)會(huì)切換頁面以顯示新路由。新頁面的内容在在MaterialPageRoute的builder屬性中構建,builder是一個匿名函數。添加Navigator.push調用(yòng),這(zhè)會(huì)使路由入棧(以後路由入棧均指推入到(dào)導航管理(lǐ)器的棧)
void _pushSaved() {
Navigator.of(context).push(
);
}
添加MaterialPageRoute及其builder。 現(xiàn)在,添加生成ListTile行的代碼。ListTile的divideTiles()方法在每個ListTile之間添加1像素的分割線。 該 divided 變量持有最終的列表項。
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();
},
),
);
}
builder返回一個Scaffold,其中包含名爲“Saved Suggestions”的新路由的應用(yòng)欄。 新路由的body由包含ListTiles行的ListView組成; 每行之間通過一個分隔線分隔。添加如下(xià)高(gāo)亮(liàng)的代碼:
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),
);
},
),
);
}
熱重載應用(yòng)程序。收藏一些(xiē)選項,并點擊應用(yòng)欄中的列表圖标,在新路由頁面中顯示收藏的内容。 請(qǐng)注意,導航器會(huì)在應用(yòng)欄中添加一個“返回”按鈕。你(nǐ)不必顯式實現(xiàn)Navigator.pop。點擊後退按鈕返回到(dào)主頁路由。
screenshot at completion of 6th stepsecond route
遇到(dào)了(le)問題?
如果您的應用(yòng)不能(néng)正常工(gōng)作(zuò),請(qǐng)參考下(xià)面鏈接處的代碼,對(duì)比并更正。
lib/main.dart
第7步:使用(yòng)主題更改UI
在這(zhè)最後一步中,您将會(huì)使用(yòng)主題。主題控制您應用(yòng)程序的外(wài)觀和(hé)風(fēng)格。您可以使用(yòng)默認主題,該主題取決于物理(lǐ)設備或模拟器,也(yě)可以自(zì)定義主題以适應您的品牌。
您可以通過配置ThemeData類輕松更改應用(yòng)程序的主題。 您的應用(yòng)程序目前使用(yòng)默認主題,下(xià)面将更改primary color顔色爲白(bái)色。通過如下(xià)高(gāo)亮(liàng)部分代碼,将應用(yòng)程序的主題更改爲白(bái)色:
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(),
);
}
}
熱重載應用(yòng)。 請(qǐng)注意,整個背景将會(huì)變爲白(bái)色,包括應用(yòng)欄。
作(zuò)爲讀者的一個練習,使用(yòng) ThemeData 來(lái)改變UI的其他(tā)方面。 Material library中的 Colors類提供了(le)許多可以使用(yòng)的顔色常量, 你(nǐ)可以使用(yòng)熱重載來(lái)快(kuài)速簡單地嘗試、實驗。
網站(zhàn)建設開(kāi)發|APP設計(jì)開(kāi)發|小(xiǎo)程序建設開(kāi)發