一、搭建Flutter環(huán)境
Flutter官網(wǎng): https://flutterchina.club/
windows環(huán)境搭建過程
-
由于在國內(nèi)訪問Flutter有時可能會受到限制循狰,為了能正常獲取Flutter SDK以及之后第一次執(zhí)行 flutter doctor 能夠正常完成,可以先添加鏡像地址鳄哭,F(xiàn)lutter官方為中國開發(fā)者搭建了臨時鏡像趁俊。
環(huán)境變量加入到用戶環(huán)境變量中:
export PUB_HOSTED_URL=https://pub.flutter-io.cn
export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn
或者:
如果下載慢無法下載可以使用git下載:
git clone -b beta https:*//github.com/flutter/flutter.git
3.將安裝包zip解壓到你想安裝Flutter SDK的路徑 ,即為你的Flutter安裝目錄
4.在Flutter安裝目錄的flutter
文件下找到flutter_console.bat
糊探,雙擊運行并啟動flutter命令行
5.打開一個新的命令提示符或PowerShell窗口并運行以下命令以查看是否需要安裝任何依賴項來完成安裝:
運行 flutter doctor挎狸,第一次運行有點慢健提,需要點等待時間
flutter doctor: 下載它自己的依賴項并自行編譯
6.Android Studio安裝flutter插件,重啟
到此說明flutter環(huán)境已經(jīng)搭建完成
二伟叛、新建Flutter工程
如果首次創(chuàng)建項目如果一直卡在Creating Flutter project上,先打開項目文件脐嫂,如果里面已經(jīng)有了創(chuàng)建的文件统刮,那么從任務(wù)管理器關(guān)掉Android Studio,然后重新打開,點擊Open an existing Android Studio project,找到項目文件账千,打開就行了侥蒙。
工程的目錄結(jié)構(gòu):
官方文檔中:
在這個示例中,你將主要編輯Dart代碼所在的 lib/main.dart 文件,
我們可以修改main.dart文件來實現(xiàn)簡單的信息修改匀奏。
我們先運行看一下效果:
三鞭衩、熱重載
看一下main.dart文件
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.green,
// This makes the visual density adapt to the platform that you run
// the app on. For desktop platforms, the controls will be smaller and
// closer together (more dense) than on mobile platforms.
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
注釋中提示我們可以改變
primarySwatch: Colors.green,來修改app的主題顏色,我們來試著修改顏色并體驗 熱重載
反應(yīng)很快娃善,日志顯示1122ms完成了熱重載论衍。
四、使用外部包(package)
我們參考官網(wǎng)提供的步驟聚磺,使用 english_words 開源軟件包
flutter一些開源的軟件包:https://pub.dev/flutter/packages
1.pubspec文件管理Flutter應(yīng)用程序的assets(資源坯台,如圖片、package等) 瘫寝,所以我們可以到 pubspec.yaml 添加package的依賴
- 單擊右上角的 Packages get蜒蕾,這會將依賴包安裝到您的項目稠炬。您可以在控制臺中看到以下內(nèi)容:
-
lib/main.dart 中, 引入
english_words
import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';
4.使用詞庫
此時假如package,熱更新會報錯咪啡,所以需要先停止運行首启,在運行
之后每一次熱更新,MyHomePage的title都會隨機出現(xiàn)一個單詞撤摸。
以上主要是記錄軟件包package的使用
五毅桃、頁面跳轉(zhuǎn)
Flutter的路由和導(dǎo)航功能 ,管理多個頁面時有兩個核心概念和類:Route和 Navigator愁溜。 一個route是一個屏幕或頁面的抽象疾嗅,Navigator是管理route的Widget。Navigator可以通過route入棧和出棧來實現(xiàn)頁面之間的跳轉(zhuǎn)冕象。
1.簡單的頁面跳轉(zhuǎn)
我們先實現(xiàn)簡單的頁面跳轉(zhuǎn)代承,可以用 Navigator.push 和 Navigator.pop 實現(xiàn)
1.新建一個page,新建newpage.dart文件渐扮,繼承StatelessWidget论悴,可以看到需要復(fù)寫createState
如果不復(fù)寫,那么只能抽象墓律,或者noSuchMethod膀估,一會能跳轉(zhuǎn)了我們可以驗證一下noSuchMethod是什么意思。
在頁面顯示一串Text耻讽,并定義onPressed動作的事件察纯,使用pop返回到上一頁
@override
Widget build(BuildContext context) {
return Scaffold(appBar: AppBar(title: Text('New page'),),
body: Center(child: RaisedButton(
child: Text('點擊返回上一頁'),
onPressed: () {
Navigator.pop(context);
}),),);
}
2.main.dart找到懸浮按鈕的點擊事件,刪除setState针肥,添加跳轉(zhuǎn)動作
void _incrementCounter() {
Navigator.push(context,
MaterialPageRoute(builder: (context) => Newpage()));
}
3.熱重載饼记,查看并點擊懸浮按鈕,跳轉(zhuǎn)到了第二個頁面慰枕,點擊可以返回到上一頁
2.通過routes路徑方式跳轉(zhuǎn)
創(chuàng)建 MaterialApp
時可以指定 routes
參數(shù)具则,該參數(shù)是一個映射路由名稱和構(gòu)造器的 Map。
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.green,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
routes: {
"home": (context) => MyHomePage(),
"newpage": (context) => Newpage()
},
home: MyHomePage(title: wordPair.asPascalCase),
);
跳轉(zhuǎn)直接使用Navigator.pushNamed(context, "newpage");
Navigator.pushNamed(context, "newpage");
3.傳遞數(shù)據(jù)到下一個頁面
傳遞的方式有兩種:
- 在構(gòu)造方法中傳遞數(shù)據(jù)
- 在Route中傳遞數(shù)據(jù)給下一個頁面
分別來使用一下兩種方式
第一種:在跳轉(zhuǎn)的時候?qū)?shù)傳入具帮,在接受頁面使用ModalRoute.of(context).settings.arguments獲取
class User {
String name;
int age;
String toString(){
return ("name:" + this.name + " age:" + this.age.toString());
}
User({this.name, this.age});
}
void startNewPage() {
// Navigator.push(context,
// MaterialPageRoute(builder: (context) => Newpage()));
// Navigator.pushNamed(context, "newpage");
Navigator.pushNamed(context, "newpage", arguments: User(name: "GodV",age: 23));
}
接受數(shù)據(jù)博肋, ModalRoute.of(context).settings.arguments;方式獲取傳遞的數(shù)據(jù)
@override
Widget build(BuildContext context) {
//構(gòu)造獲取傳遞過來的參數(shù)user
final User user = ModalRoute.of(context).settings.arguments;
return Scaffold(appBar: AppBar(title: Text("第二頁"),),
body: Center(child: RaisedButton(
child: Text('傳遞的參數(shù):' + user.toString()),
onPressed: () {
Navigator.pop(context);
}),),);
}
第二種: onGenerateRoute定義了返回的目標頁面需要帶有參數(shù),另外目標頁面寫構(gòu)造參數(shù)
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.green,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
routes: {
"home": (context) => MyHomePage(),
},
onGenerateRoute: (settings) {
if (settings.name == "newpage") {
final User args = settings.arguments;
return MaterialPageRoute(builder: (context) {
return Newpage(
user: args,
);
});
}
},
home: MyHomePage(title: wordPair.asPascalCase),
);
發(fā)送數(shù)據(jù)的方法沒有改變:
Navigator.pushNamed(context, "newpage", arguments: User(name: "Zgg01",age: 23));
接受頁面:
final User user;
Newpage({this.user});
@override
Widget build(BuildContext context) {
//構(gòu)造獲取傳遞過來的參數(shù)user
return Scaffold(appBar: AppBar(title: Text("第二頁"),),
body: Center(child: RaisedButton(
child: Text('傳遞的參數(shù):' + user.toString()),
onPressed: () {
Navigator.pop(context);
}),),);
}
4.接收頁面返回值
定義接受值顯示在Text上
String reciveString = "接受的值:";
children: <Widget>[
Text(
'路由跳轉(zhuǎn)',
),
Text(
'$reciveString',
// style: Theme.of(context).textTheme.headline4,
),
],
打開頁面時使用then接受返回回來的參數(shù)蜂厅,需要使用setState來更新組件的值
Navigator.pushNamed(context, "newpage", arguments: User(name: "Zgg01",age: 23))
.then((value) => {
setState(() {
reciveString = value.toString();
})
});
在第二個頁面發(fā)送需要返回的值
@override
Widget build(BuildContext context) {
//構(gòu)造獲取傳遞過來的參數(shù)user
return Scaffold(appBar: AppBar(title: Text("第二頁"),),
body: Center(child: RaisedButton(
child: Text('傳遞的參數(shù):' + user.toString()),
onPressed: () {
Navigator.pop(context, User(name: "Forever",age: 20));
}),),);
}
第二個頁面點擊返回后效果:
六匪凡、添加一個 有狀態(tài)的部件(Stateful widget)
部件 分為狀態(tài)可變的和狀態(tài)不可變的
Stateless widgets 是不可變的, 這意味著它們的屬性不能改變 - 所有的值都是最終的.
Stateful widgets 持有的狀態(tài)可能在widget生命周期中發(fā)生變化. 實現(xiàn)一個 stateful widget 至少需要兩個類:
- 一個 StatefulWidget類。
- 一個 State類掘猿。 StatefulWidget類本身是不變的锹雏,但是 State類在widget生命周期中始終存在.
以官網(wǎng)的添加一個ListView為例
第一步:定義一個Page繼承StatefulWidget
class ListPage extends StatefulWidget {
final String title;
ListPage({Key key, this.title}) : super(key: key);
@override
State<StatefulWidget> createState() {
return ListWordsState();
}
}
第二步:定義ListView頁面,定義ListWordsState繼承State<ListPage>
class ListWordsState extends State<ListPage>{
final _suggestions = <WordPair>[];
final _biggerFont = const TextStyle(fontSize: 18.0);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("ListView"),),
body: new ListView.builder(
padding: const EdgeInsets.all(16.0),
// 對于每個建議的單詞對都會調(diào)用一次itemBuilder术奖,然后將單詞對添加到ListTile行中
// 在偶數(shù)行礁遵,該函數(shù)會為單詞對添加一個ListTile row.
// 在奇數(shù)行轻绞,該函數(shù)會添加一個分割線widget,來分隔相鄰的詞對佣耐。
// 注意政勃,在小屏幕上,分割線看起來可能比較吃力兼砖。
itemBuilder: (context, i) {
// 在每一列之前奸远,添加一個1像素高的分隔線widget
if (i.isOdd) return new Divider();
// 語法 "i ~/ 2" 表示i除以2,但返回值是整形(向下取整)讽挟,比如i為:1, 2, 3, 4, 5
// 時懒叛,結(jié)果為0, 1, 1, 2, 2, 這可以計算出ListView中減去分隔線后的實際單詞對數(shù)量
final index = i ~/ 2;
// 如果是建議列表中最后一個單詞對
if (index >= _suggestions.length) {
// ...接著再生成10個單詞對耽梅,然后添加到建議列表
_suggestions.addAll(generateWordPairs().take(10));
}
return _buildRow(_suggestions[index]);
}
),
);
}
//生成每一個Item的TextView
Widget _buildRow(WordPair pair) {
return new ListTile(
title: new Text(
pair.asPascalCase,
style: _biggerFont,
),
);
}
}
注意:復(fù)寫Widget build(BuildContext context)時薛窥,需要return一個以Scaffold為根布局的組件,之后再body里面定義ListView
padding:即對應(yīng)Android的padding眼姐,可以使用all修改全部的內(nèi)邊距诅迷,或者使用fromLTRB定義每一個方向的內(nèi)邊距
const EdgeInsets.all(double value)
: left = value,
top = value,
right = value,
bottom = value;
const EdgeInsets.fromLTRB(this.left, this.top, this.right, this.bottom);
itemBuilder: (context, i):生成item時的遍歷
_buildRow:定義每一個Item的內(nèi)容
第三步:添加交互
點擊收藏,顯示收藏圖標
1.添加一個 _saved Set(集合) 到RandomWordsState
class ListWordsState extends State<ListPage>{
final _suggestions = <WordPair>[];
final _biggerFont = const TextStyle(fontSize: 18.0);
//添加一個 _saved Set(集合) 到RandomWordsState
final _saved = new Set<WordPair>();
}
- 在
_buildRow
方法中添加alreadySaved
來檢查確保單詞對還沒有添加到收藏夾中众旗。 - 定義trailing: new Icon圖標罢杉,通過alreadySaved狀態(tài)來設(shè)置顏色
- 定義onTap方法,更新對應(yīng)item的收藏狀態(tài)
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);
}
});
},
);
}
效果:
關(guān)于Flutter和Android對應(yīng)的Api可以參考: https://flutterchina.club/flutter-for-android/#flutter%E5%92%8Candroid%E4%B8%AD%E7%9A%84view
以上Demo GitHub地址: https://github.com/heezier/Flutter
參考: