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