這是建立您的第一個Flutter應用程序的指南。 若是您熟悉面向對象的代碼和基本編程概念(如變量,循環和條件),則能夠完成本教程。 您不須要之前使用Dart或移動編程的經驗。html
您將實施一個簡單的移動應用程序,爲一家創業公司生成建議名稱。 用戶能夠選擇和取消選擇名稱,保存最好的名稱。 該代碼一次生成十個名稱。 當用戶滾動時,會生成新批次的名稱。 用戶能夠點擊應用欄右上方的列表圖標,以移動到僅列出收藏名稱的新路由。java
動畫GIF顯示完成的應用程序的工做方式。git
你會學到什麼:github
Flutter應用程序的基本結構。
查找和使用包來擴展功能。
使用熱重載加快開發週期。
如何實現有狀態的小部件。
如何建立一個無限的,延遲加載的列表。
如何建立並導航到第二個屏幕。
如何使用主題更改應用程序的外觀。編程
你會到用什麼:數組
您須要安裝如下內容:網絡
- Flutter SDK
- Flutter SDK包括Flutter的引擎,框架,小部件,工具和Dart SDK。 這個codelab須要v0.1.4或更高版本。
- Android Studio IDE
- 該codelab具備Android Studio IDE,但您可使用其餘IDE,或者從命令行運行。
- 您的IDE插件
- Flutter和Dart插件必須爲您的IDE單獨安裝。 除了Android Studio,Flutter和Dart插件也可用於VS Code和IntelliJ IDE。
有關如何設置環境的信息,請參閱Flutter安裝和設置。app
使用第一個Flutter應用程序入門中的說明建立一個簡單的模板化Flutter應用程序。 將項目命名爲startup_namer(而不是myapp)。 你將會修改這個初學者應用程序來建立完成的應用程序。框架
在這個codelab中,你將主要編輯Dart代碼所在的lib / main.dart。less
提示:將代碼粘貼到應用程序中時,縮進可能會變形。 您可使用Flutter工具自動修復此問題:
- Android Studio / IntelliJ IDEA:右鍵單擊飛鏢代碼,而後選擇Reformat Code with dartfmt格式化代碼。
- VS代碼:右鍵單擊並選擇Format Document。
- 終端:運行flutter格式<filename>。
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'), ), ), ); } }
2.運行應用程序。 你應該看到下面的屏幕。
意見
在這一步中,您將開始使用名爲english_words的開源軟件包,其中包含數千個最經常使用的英文單詞以及一些實用功能。
您能夠在pub.dartlang.org上找到english_words軟件包以及其餘許多開源軟件包。
1.pubspec文件管理Flutter應用程序的資產。 在pubspec.yaml中,將english_words(3.1.0或更高版本)添加到依賴項列表。 新行高亮以下:
dependencies:
flutter:
sdk: fluttercupertino_icons: ^0.1.0
english_words: ^3.1.0
2.在Android Studio編輯器視圖中查看pubspec時,單擊右上角的Packages get。 這將該包加入您的項目。 您應該在控制檯中看到如下內容:
flutter packages get Running "flutter packages get" in startup_namer... Process finished with exit code 0
3.在lib/main.dart中,添加english_words導入語句,如突出顯示的行所示:
import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';
在您鍵入時,Android Studio會爲您提供有關庫導入的建議。 而後它將呈現灰色的導入字符串,讓您知道導入的庫還沒有使用(到目前爲止)。
4.使用英文單詞包來生成文本,而不是使用字符串「Hello World」。
提示:「Pascal case」(也稱爲「上駱駝案例」)意味着字符串中的每一個單詞(包括第一個單詞)都以大寫字母開頭。 因此,「uppercamelcase」變成「UpperCamelCase」。
進行如下更改,以下所示:
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'), // Replace the highlighted text...
child: new Text(wordPair.asPascalCase), // With this highlighted text.
),
),
);
}
}
5.若是應用程序正在運行,請使用熱從新加載按鈕(閃電圖標)更新正在運行的應用程序。 每次單擊熱從新加載或保存項目時,都會在正在運行的應用程序中隨機選擇不一樣的單詞對。 這是由於配對這個詞是在構建方法內部生成的,每次MaterialApp須要渲染時或者在Flutter Inspector中切換平臺時都會運行。
問題?
若是您的應用程序運行不正常,請查找錯別字。 若是須要,請使用如下連接中的代碼從新開始正軌。
無狀態小部件是不可變的,這意味着它們的屬性不能改變 - 全部的值都是最終的。
有狀態的小部件保持在小部件的生命週期中可能改變的狀態。 實現一個有狀態的小部件至少須要兩個類:1)一個StatefulWidget類,它建立一個2)一個State類的實例。 StatefulWidget類自己是不可變的,但State類在整個構件的生命週期中保持不變。
在這一步中,您將添加一個有狀態的小部件RandomWords,它建立其狀態類RandomWordsState。 State類將最終維護小部件的建議和最喜歡的單詞對。
1.將有狀態的RandomWords小部件添加到main.dart。 它能夠在MyApp以外的文件中的任何位置使用,但解決方案將它放在文件的底部。 RandomWords小部件除了建立State類以外幾乎沒有其餘任何東西:
class RandomWords extends StatefulWidget { @override createState() => new RandomWordsState(); }
2.添加RandomWordsState類。 該應用的大部分代碼都駐留在該類中,該類保持RandomWords小部件的狀態。 這個類將保存隨着用戶滾動而無限增加的生成的單詞對,以及最喜歡的單詞對,由於用戶經過切換心臟圖標來將它們從列表中添加或刪除。
你會一點一點地創建這個類。 首先,經過添加突出顯示的文本建立一個最小類:
class RandomWordsState extends State<RandomWords> { }
3.在添加狀態類後,IDE會抱怨該類缺乏構建方法。 接下來,您將添加一個基本構建方法,該方法經過將單詞生成代碼從MyApp移動到RandomWordsState來生成單詞對。
將構建方法添加到RandomWordState中,如突出顯示的文本所示:
class RandomWordsState extends State<RandomWords> {
@override
Widget build(BuildContext context) {
final wordPair = new WordPair.random();
return new Text(wordPair.asPascalCase);
}
}
4.經過下面突出顯示的更改從MyApp中刪除單詞生成代碼:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
final wordPair = new WordPair.random();// Delete this linereturn 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), // Change the highlighted text to...
child: new RandomWords(), // ... this highlighted text
),
),
);
}
}
從新啓動應用程序。 若是您嘗試從新加載熱點,則可能會看到一條警告:
Reloading... Not all changed program elements ran during view reassembly; consider restarting.
這多是誤報,但考慮從新啓動以確保您的更改反映在應用的用戶界面中。
應用程序應該像之前同樣運行,每次熱從新加載或保存應用程序時都會顯示一個字對。
問題?
若是您的應用程序運行不正常,則可使用如下連接中的代碼從新進入正軌。
在這一步中,您將展開RandomWordsState以生成並顯示單詞配對列表。 當用戶滾動時,ListView小部件中顯示的列表將無限增加。 ListView的builder工廠構造函數容許您根據須要懶惰地構建列表視圖。
1.將一個_suggestions列表添加到RandomWordsState類,以保存建議的詞對。 該變量如下劃線(_)開頭 - 在前面加上一個帶有下劃線的標識符能夠強化Dart語言的隱私。
此外,添加一個largerFont變量來使字體變大。
class RandomWordsState extends State<RandomWords> { final _suggestions = <WordPair>[]; final _biggerFont = const TextStyle(fontSize: 18.0); ... }
2.將一個_buildSuggestions()函數添加到RandomWordsState類。 此方法構建顯示建議詞對的ListView。
ListView類提供了一個構建器屬性itemBuilder,一個指定爲匿名函數的工廠構建器和回調函數。 兩個參數傳遞給函數 - BuildContext和行迭代器,i 迭代器從0開始,每次調用該函數時遞增,每次建議的單詞配對一次。 該模型容許建議的列表在用戶滾動時無限增加。
添加下面突出顯示的行:
class RandomWordsState extends State<RandomWords> {
...
Widget _buildSuggestions() {
return new ListView.builder(
padding: const EdgeInsets.all(16.0),
// The itemBuilder callback is called once per suggested word pairing,
// and places each suggestion into a ListTile row.
// For even rows, the function adds a ListTile row for the word pairing.
// For odd rows, the function adds a Divider widget to visually
// separate the entries. Note that the divider may be difficult
// to see on smaller devices.
itemBuilder: (context, i) {
// Add a one-pixel-high divider widget before each row in theListView.
if (i.isOdd) return new Divider();// The syntax "i ~/ 2" divides i by 2 and returns an integer result.
// For example: 1, 2, 3, 4, 5 becomes 0, 1, 1, 2, 2.
// This calculates the actual number of word pairings in the ListView,
// minus the divider widgets.
final index = i ~/ 2;
// If you've reached the end of the available word pairings...
if (index >= _suggestions.length) {
// ...then generate 10 more and add them to the suggestions list.
_suggestions.addAll(generateWordPairs().take(10));
}
return _buildRow(_suggestions[index]);
}
);
}
}
3._buildSuggestions函數每一個詞對調用_buildRow一次。 這個函數在ListTile中顯示每一個新對,這容許您在下一步中使行更具吸引力。
向RandomWordsState添加_buildRow函數:
class RandomWordsState extends State<RandomWords> {
...Widget _buildRow(WordPair pair) {
return new ListTile(
title: new Text(
pair.asPascalCase,
style: _biggerFont,
),
);
}
}
4.更新RandomWordsState的build 方法以使用_buildSuggestions(),而不是直接調用單詞生成庫。 進行突出顯示的更改:
class RandomWordsState extends State<RandomWords> {
...
@override
Widget build(BuildContext context) {
final wordPair = new WordPair.random();// Delete these two lines.
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實例。 這些將由RandomWordsState管理,這使得用戶在下一步中從一個屏幕導航到另外一個屏幕時,能夠更輕鬆地更改應用欄中的路由名稱。
用下面突出顯示的構建方法替換原始方法:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Startup Name Generator',
home: new RandomWords(),
);
}
}
從新啓動應用程序。 你應該看到一個單詞配對清單。 儘量向下滾動,您將繼續看到新的單詞配對。
問題?
若是您的應用程序運行不正常,則可使用如下連接中的代碼從新進入正軌。
在這一步中,您將爲每一行添加可點擊的心臟圖標。 當用戶點擊列表中的條目,切換其「收藏」狀態時,該詞語配對被添加或從一組保存的收藏夾中移除。
1.將一個_saved集添加到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檢查以確保單詞配對還沒有添加到收藏夾。
Widget _buildRow(WordPair pair) {
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.從新啓動應用程序。 你如今應該在每一行看到開放的心,但它們尚未互動。
5.在_buildRow函數中讓心靈可點擊。 若是單詞條目已被添加到收藏夾中,再次點擊它將其從收藏夾中刪除。 小心臟被輕敲時,函數調用setState()來通知框架狀態已經改變。
添加突出顯示的行:
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的反應風格框架中,調用setState()會觸發對State對象的build()方法的調用,從而致使UI的更新。
熱從新加載應用程序。 你應該可以點擊任何一行以得到最喜歡的,或不適合的入口。 請注意,點擊一行會生成從心臟圖標發出的隱式墨跡飛濺動畫。
問題?
若是您的應用程序運行不正常,則可使用如下連接中的代碼從新進入正軌。
在這一步中,您將添加一個顯示收藏夾的新屏幕(在Flutter中稱爲路由)。 您將學習如何在主路由和新路由之間導航。
在Flutter中,導航器管理包含應用程序路由的堆棧。 將路由推入導航器的堆棧,將顯示更新爲該路由。 從導航器的堆棧中彈出路由,將顯示返回到前一個路由。
1.向RandomWordsState的構建方法中的AppBar添加列表圖標。 當用戶點擊列表圖標時,包含收藏夾項目的新路線被推送到導航器,顯示該圖標。
提示:某些小部件屬性採用單個小部件(子級),而其餘屬性(如操做)則採用小部件(子級)數組,如方括號([])所示。
將該圖標及其相應的操做添加到構建方法中:
class RandomWordsState extends State<RandomWords> {
...
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('Startup Name Generator'),
actions: <Widget>[
new IconButton(icon: new Icon(Icons.list), onPressed: _pushSaved),
],
),
body: _buildSuggestions(),
);
}
...
}
2.將一個_pushSaved()函數添加到RandomWordsState類。
class RandomWordsState extends State<RandomWords> {
...
void _pushSaved() {
}
}
熱從新加載應用程序。 列表圖標出如今應用程序欄中。 點擊它什麼也沒作,由於_pushSaved函數是空的。
3.當用戶點擊應用欄中的列表圖標時,創建一條路由並將其推送到導航器的堆棧。 此操做會更改屏幕以顯示新路由。
新頁面的內容是使用匿名函數在MaterialPageRoute的builder屬性中構建的。
將呼叫添加到Navigator.push,如突出顯示的代碼所示,將路由推送到導航器的堆棧。
void _pushSaved() {
Navigator.of(context).push(
);
}
4.添加MaterialPageRoute及其構建器。 如今,添加生成ListTile行的代碼。 ListTile的divideTiles()方法在每一個ListTile之間添加水平間距。 變量divided保存最後的行,經過便利函數toList()轉換爲列表。
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();
},
),
);
}
5.builder屬性返回一個Scaffold,其中包含名爲「Saved Suggestions」的新路由的應用欄。新路由的主體由包含ListTiles行的ListView組成; 每行由一個分隔符分隔。
添加下面突出顯示的代碼:
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),
);
},
),
);
}
6.熱從新加載應用程序。 最喜歡的一些選擇,並點擊應用欄中的列表圖標。 新路線顯示包含收藏夾。 請注意,導航器會在應用欄中添加一個「返回」按鈕。 你沒必要顯式實現Navigator.pop。 點擊後退按鈕返回到主頁路由。
問題?
若是您的應用程序運行不正常,則可使用如下連接中的代碼從新進入正軌。
在最後一步中,您將使用該應用的主題。 主題控制你的應用的外觀和感受。 您可使用默認主題,該主題取決於物理設備或模擬器,也能夠自定義主題以反映品牌。
1.您能夠經過配置ThemeData類輕鬆更改應用程序的主題。 您的應用程序目前使用默認主題,但您將更改主要顏色爲白色。
將突出顯示的代碼添加到MyApp,將應用程序的主題更改成白色:
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(),
);
}
}
2.熱從新加載應用程序。 請注意,整個背景是白色的,甚至是應用欄。
3.做爲讀者的練習,使用ThemeData來改變UI的其餘方面。 材質庫中的Colors類提供了許多可使用的顏色常量,而熱重載使得用戶界面的實驗變得快速而簡單。
問題?
若是您的應用程序運行不正常,則可使用如下連接中的代碼從新進入正軌。
您已經編寫了一個在iOS和Android上運行的交互式Flutter應用程序。 在這個codelab中,你有: