flutter,跟著官網一步一步創建第一個flutter應用
- 2020 年 9 月 27 日
- 筆記
創建第一個flutter
編輯器: vscode
一、創建flutter項目
1.啟動vscode
2.按ctrl+shift+p打開命令面板
3.輸入flutter 選擇Flutter: New Project
4.輸入創建項目名稱
5.指定存放項目地址。自動創建flutter基礎項目結構 顯示main.dart文件
二、運行項目
1.選擇運行設備
2.按 F5 鍵或調用Debug>Start Debugging 運行項目
三、體驗熱重載
1.把lib/main.dart
文件中 'You have pushed the button this many times:'
改為 'You have clicked the button this many times:'
2.調用保存自動熱更新
三、創建 Flutter app
1.替換 lib/main.dart 刪除lib / main.dart中的所有程式碼,然後替換為下面的程式碼,它將在螢幕的中心顯示「Hello World」.
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
- 使用外部包(package) 打開pubspec.yaml文件添加 english_words到依賴列表
3.添加後編輯器會自動拉取依賴
4.在 lib/main.dart 中, 引入 english_words
, 如高亮顯示的行所示:
5.使用 English words 包生成文本來替換字元串「Hello World」.
tips:果應用程式正在運行,請使用熱重載按鈕 () 更新正在運行的應用程式。每次單擊熱重載或保存項目時,都會在正在運行的應用程式中隨機選擇不同的單詞對。 這是因為單詞對是在
build
方法內部生成的。每次MaterialApp需要渲染時或者在Flutter Inspector中切換平台時 build
都會運行.
四、添加有狀態的部件(Stateful widget)
- 添加有狀態的 RandomWords widget 到 main.dart。 它也可以在MyApp之外的文件的任何位置使用,但是本示例將它放到了文件的底部。RandomWords widget除了創建State類之外幾乎沒有其他任何東西
- 添加 RandomWordsState 類.該應用程式的大部分程式碼都在該類中, 該類持有RandomWords widget的狀態。這個類將保存隨著用戶滾動而無限增長的生成的單詞對, 以及喜歡的單詞對,用戶通過重複點擊心形 ❤️ 圖標來將它們從列表中添加或刪除。
你會一步一步地建立這個類。首先,通過添加高亮顯示的程式碼創建一個最小類
- 在添加狀態類後,IDE會提示該類缺少build方法。接下來,您將添加一個基本的build方法,該方法通過將生成單詞對的程式碼從MyApp移動到RandomWordsState來生成單詞對。
將build方法添加到RandomWordState中,如下面高亮程式碼所示
- 通過下面高亮顯示的程式碼,將生成單詞對代的碼從MyApp移動到RandomWordsState中
五、創建一個無限滾動listView
- 向RandomWordsState類中添加一個
_suggestions
列表以保存建議的單詞對。 該變數以下劃線(_)開頭,在Dart語言中使用下劃線前綴標識符,會強制其變成私有的。
另外,添加一個biggerFont
變數來增大字體大小
class RandomWordsState extends State<RandomWords> {
final _suggestions = <WordPair>[];
final _biggerFont = const TextStyle(fontSize: 18.0);
...
}
- 向RandomWordsState類添加一個
_buildSuggestions()
函數. 此方法構建顯示建議單詞對的ListView。
ListView類提供了一個builder屬性,itemBuilder
值是一個匿名回調函數, 接受兩個參數- BuildContext和行迭代器i
。迭代器從0開始, 每調用一次該函數,i
就會自增1,對於每個建議的單詞對都會執行一次。該模型允許建議的單詞對列表在用戶滾動時無限增長。
添加如下高亮的行:
class RandomWordsState extends State<RandomWords> {
...
Widget _buildSuggestions() {
return new ListView.builder(
padding: const EdgeInsets.all(16.0),
// 對於每個建議的單詞對都會調用一次itemBuilder,然後將單詞對添加到ListTile行中
// 在偶數行,該函數會為單詞對添加一個ListTile row.
// 在奇數行,該函數會添加一個分割線widget,來分隔相鄰的詞對。
// 注意,在小螢幕上,分割線看起來可能比較吃力。
itemBuilder: (context, i) {
// 在每一列之前,添加一個1像素高的分隔線widget
if (i.isOdd) return new Divider();
// 語法 "i ~/ 2" 表示i除以2,但返回值是整形(向下取整),比如i為:1, 2, 3, 4, 5
// 時,結果為0, 1, 1, 2, 2, 這可以計算出ListView中減去分隔線後的實際單詞對數量
final index = i ~/ 2;
// 如果是建議列表中最後一個單詞對
if (index >= _suggestions.length) {
// ...接著再生成10個單詞對,然後添加到建議列表
_suggestions.addAll(generateWordPairs().take(10));
}
return _buildRow(_suggestions[index]);
}
);
}
}
- 對於每一個單詞對,
_buildSuggestions
函數都會調用一次_buildRow
。 這個函數在ListTile中顯示每個新詞對,這使您在下一步中可以生成更漂亮的顯示行
在RandomWordsState中添加一個_buildRow
函數 :
class RandomWordsState extends State<RandomWords> {
...
Widget _buildRow(WordPair pair) {
return new ListTile(
title: new Text(
pair.asPascalCase,
style: _biggerFont,
),
);
}
}
-
更新RandomWordsState的build方法以使用
_buildSuggestions()
,而不是直接調用單詞生成庫。 更改後如下面高亮部分:class RandomWordsState extends State<RandomWords> { ...
- 更新MyApp的build方法。從MyApp中刪除Scaffold和AppBar實例。 這些將由RandomWordsState管理,這使得用戶在下一步中從一個螢幕導航到另一個螢幕時, 可以更輕鬆地更改導航欄中的的路由名稱。
用下面高亮部分替換最初的build方法:
class MyApp extends StatelessWidget {
六、添加交互
在這一步中,您將為每一行添加一個可點擊的心形 ❤️ 圖標。當用戶點擊列表中的條目,切換其「收藏」狀態時,將該詞對添加到或移除出「收藏夾」。
-
添加一個
_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); ... }
-
在
_buildRow
方法中添加alreadySaved
來檢查確保單詞對還沒有添加到收藏夾中。Widget _buildRow(WordPair pair) { final alreadySaved = _saved.contains(pair); ... }
- 同時在
_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,
),
);
}
- 重新啟動應用。你現在可以在每一行看到心形❤️圖標️,但它們還沒有交互。
- 在
_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中稱為路由(route))。您將學習如何在主路由和新路由之間導航(切換頁面)。
在Flutter中,導航器管理應用程式的路由棧。將路由推入(push)到導航器的棧中,將會顯示更新為該路由頁面。 從導航器的棧中彈出(pop)路由,將顯示返回到前一個路由。
- 在RandomWordsState的build方法中為AppBar添加一個列表圖標。當用戶點擊列表圖標時,包含收藏夾的新路由頁面入棧顯示。
提示: 某些widget屬性需要單個widget(child),而其它一些屬性,如action,需要一組widgets(children),用方括弧[]表示。
將該圖標及其相應的操作添加到build方法中:
class RandomWordsState extends State<RandomWords> {
...
-
向RandomWordsState類添加一個
_pushSaved()
方法.class RandomWordsState extends State<RandomWords> { ... void _pushSaved() { } }
熱重載應用,列表圖標將會出現在導航欄中。現在點擊它不會有任何反應,因為 _pushSaved
函數還是空的。
- 當用戶點擊導航欄中的列表圖標時,建立一個路由並將其推入到導航管理器棧中。此操作會切換頁面以顯示新路由。
新頁面的內容在在MaterialPageRoute的builder
屬性中構建,builder
是一個匿名函數。
添加Navigator.push調用,這會使路由入棧(以後路由入棧均指推入到導航管理器的棧)
void _pushSaved() {
Navigator.of(context).push(
);
}
-
添加MaterialPageRoute及其builder。 現在,添加生成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」的新路由的應用欄。 新路由的body由包含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),
);
},
),
);
}
- 熱重載應用程式。收藏一些選項,並點擊應用欄中的列表圖標,在新路由頁面中顯示收藏的內容。 請注意,導航器會在應用欄中添加一個「返回」按鈕。你不必顯式實現Navigator.pop。點擊後退按鈕返回到主頁路由。
七、使用主題更改UI