創(chuàng)建一個(gè)簡(jiǎn)單的、基于模板的Flutter應(yīng)用程序,按照創(chuàng)建您的第一個(gè)Flutter應(yīng)用中的指南的步驟, 然后將項(xiàng)目命名為startup_namer(而不是myapp),接下來你將會(huì)修改這個(gè)應(yīng)用來完成最終的APP。
在這個(gè)示例中,你將主要編輯Dart代碼所在的 lib/main.dart 文件,
提示: 將代碼粘貼到應(yīng)用中時(shí),縮進(jìn)可能會(huì)變形。您可以使用Flutter工具自動(dòng)修復(fù)此問題:
flutter format <filename>
.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'),
),
),
);
}
}
分析
在這一步中,您將開始使用一個(gè)名為english_words的開源軟件包 ,其中包含數(shù)千個(gè)最常用的英文單詞以及一些實(shí)用功能.
您可以 在pub.dartlang.org上找到english_words軟件包以及其他許多開源軟件包
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^0.1.0
english_words: ^3.1.0
flutter packages get
Running "flutter packages get" in startup_namer...
Process finished with exit code 0
import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';
在您輸入時(shí),Android Studio會(huì)為您提供有關(guān)庫導(dǎo)入的建議。然后它將呈現(xiàn)灰色的導(dǎo)入字符串,讓您知道導(dǎo)入的庫尚未使用(到目前為止)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),
),
),
);
}
}
Stateless widgets 是不可變的, 這意味著它們的屬性不能改變 - 所有的值都是最終的.
Stateful widgets 持有的狀態(tài)可能在widget生命周期中發(fā)生變化. 實(shí)現(xiàn)一個(gè) stateful widget 至少需要兩個(gè)類:
在這一步中,您將添加一個(gè)有狀態(tài)的widget-RandomWords,它創(chuàng)建其State類RandomWordsState。State類將最終為widget維護(hù)建議的和喜歡的單詞對(duì)。
class RandomWords extends StatefulWidget {
@override
createState() => new RandomWordsState();
}
class RandomWordsState extends State<RandomWords> {
}
class RandomWordsState extends State<RandomWords> {
@override
Widget build(BuildContext context) {
final wordPair = new WordPair.random();
return new Text(wordPair.asPascalCase);
}
}
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(),
),
),
);
}
}
重新啟動(dòng)應(yīng)用程序。如果您嘗試熱重載,則可能會(huì)看到一條警告:
Reloading...
Not all changed program elements ran during view reassembly; consider
restarting.
這可能是誤報(bào),但考慮到重新啟動(dòng)可以確保您的更改在應(yīng)用界面中生效。
應(yīng)用程序應(yīng)該像之前一樣運(yùn)行,每次熱重載或保存應(yīng)用程序時(shí)都會(huì)顯示一個(gè)單詞對(duì)。
遇到問題?
如果您的應(yīng)用程序運(yùn)行不正常,可以使用下面鏈接中的代碼來對(duì)比更正。
在這一步中,您將擴(kuò)展(繼承)RandomWordsState類,以生成并顯示單詞對(duì)列表。 當(dāng)用戶滾動(dòng)時(shí),ListView中顯示的列表將無限增長(zhǎng)。 ListView的builder工廠構(gòu)造函數(shù)允許您按需建立一個(gè)懶加載的列表視圖。
class RandomWordsState extends State<RandomWords> {
final _suggestions = <WordPair>[];
final _biggerFont = const TextStyle(fontSize: 18.0);
...
}
class RandomWordsState extends State<RandomWords> {
...
Widget _buildSuggestions() {
return new ListView.builder(
padding: const EdgeInsets.all(16.0),
// 對(duì)于每個(gè)建議的單詞對(duì)都會(huì)調(diào)用一次itemBuilder,然后將單詞對(duì)添加到ListTile行中
// 在偶數(shù)行,該函數(shù)會(huì)為單詞對(duì)添加一個(gè)ListTile row.
// 在奇數(shù)行,該函數(shù)會(huì)添加一個(gè)分割線widget,來分隔相鄰的詞對(duì)。
// 注意,在小屏幕上,分割線看起來可能比較吃力。
itemBuilder: (context, i) {
// 在每一列之前,添加一個(gè)1像素高的分隔線widget
if (i.isOdd) return new Divider();
// 語法 "i ~/ 2" 表示i除以2,但返回值是整形(向下取整),比如i為:1, 2, 3, 4, 5
// 時(shí),結(jié)果為0, 1, 1, 2, 2, 這可以計(jì)算出ListView中減去分隔線后的實(shí)際單詞對(duì)數(shù)量
final index = i ~/ 2;
// 如果是建議列表中最后一個(gè)單詞對(duì)
if (index >= _suggestions.length) {
// ...接著再生成10個(gè)單詞對(duì),然后添加到建議列表
_suggestions.addAll(generateWordPairs().take(10));
}
return _buildRow(_suggestions[index]);
}
);
}
}
class RandomWordsState extends State<RandomWords> {
...
Widget _buildRow(WordPair pair) {
return new ListTile(
title: new Text(
pair.asPascalCase,
style: _biggerFont,
),
);
}
}
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(),
);
}
...
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Startup Name Generator',
home: new RandomWords(),
);
}
}
重新啟動(dòng)應(yīng)用程序。你應(yīng)該看到一個(gè)單詞對(duì)列表。盡可能地向下滾動(dòng),您將繼續(xù)看到新的單詞對(duì)。
遇到問題?
如果你的應(yīng)用沒有正常運(yùn)行,你可以使用一下鏈接中的代碼對(duì)比更正。
在這一步中,您將為每一行添加一個(gè)可點(diǎn)擊的心形 ?? 圖標(biāo)。當(dāng)用戶點(diǎn)擊列表中的條目,切換其“收藏”狀態(tài)時(shí),將該詞對(duì)添加到或移除出“收藏夾”。
class RandomWordsState extends State<RandomWords> {
final _suggestions = <WordPair>[];
final _saved = new Set<WordPair>();
final _biggerFont = const TextStyle(fontSize: 18.0);
...
}
Widget _buildRow(WordPair pair) {
final alreadySaved = _saved.contains(pair);
...
}
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,
),
);
}
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的響應(yīng)式風(fēng)格的框架中,調(diào)用setState()
會(huì)為State對(duì)象觸發(fā)build()
方法,從而導(dǎo)致對(duì)UI的更新
熱重載你的應(yīng)用。你就可以點(diǎn)擊任何一行收藏或移除。請(qǐng)注意,點(diǎn)擊一行時(shí)會(huì)生成從心形 ?? 圖標(biāo)發(fā)出的水波動(dòng)畫
遇到了問題?
如果您的應(yīng)用沒有正常運(yùn)行,請(qǐng)查看下面鏈接處的代碼,對(duì)比更正。
在這一步中,您將添加一個(gè)顯示收藏夾內(nèi)容的新頁面(在Flutter中稱為路由(route))。您將學(xué)習(xí)如何在主路由和新路由之間導(dǎo)航(切換頁面)。
在Flutter中,導(dǎo)航器管理應(yīng)用程序的路由棧。將路由推入(push)到導(dǎo)航器的棧中,將會(huì)顯示更新為該路由頁面。 從導(dǎo)航器的棧中彈出(pop)路由,將顯示返回到前一個(gè)路由。
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(),
);
}
...
}
class RandomWordsState extends State<RandomWords> {
...
void _pushSaved() {
}
}
熱重載應(yīng)用,列表圖標(biāo)將會(huì)出現(xiàn)在導(dǎo)航欄中?,F(xiàn)在點(diǎn)擊它不會(huì)有任何反應(yīng),因?yàn)?nbsp;_pushSaved 函數(shù)還是空的。void _pushSaved() {
Navigator.of(context).push(
);
}
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();
},
),
);
}
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)用不能正常工作,請(qǐng)參考下面鏈接處的代碼,對(duì)比并更正。
在這最后一步中,您將會(huì)使用主題。主題控制您應(yīng)用程序的外觀和風(fēng)格。您可以使用默認(rèn)主題,該主題取決于物理設(shè)備或模擬器,也可以自定義主題以適應(yīng)您的品牌。
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(),
);
}
}
更多建議: