Dart4Flutter - 01 – 變量、類型和函數git
Dart4Flutter – 02 –控制流 和異常github
Dart4Flutter - 拾遺01 - flutter-dart環境搭建windows
Flutter 入門-本地訪問-MethodChannelless
Flutter 實例 - 從本地到Flutter通訊 - Event Channels
自從10年前Android和iOS出現,在移動開發界,跨平臺開發一直是一個目標。一個跨平臺的應用能夠幫助公司和團隊節省大量的時間和精力。
過去這些年,已經發布了不少跨平臺的開發工具,包括來自adobe基於web的PhoneGap,來自Microsoft的Xamarin,來自Facebook的React Native。
最近進入跨平臺界的工具是來自google的flutter,如今已經發布了第一個預覽版。flutter擁有開發效率高、UI渲染快,更容易的自定義組件、和在兩個平臺上和本地應用同樣的性能。
flutter應用是用dart語言開發的,Dart和其餘語言,例如Kotlin Swift同樣,擁有不少共同的語言特性,並且能夠轉化爲JavaScript代碼。
做爲一個跨平臺框架,flutter很是像React Native,例如flutter支持響應式和聲明是編程。可是和React Native不同的是,flutter不須要JavaScript橋接,這樣可提高整個應用的性能和應用啓動的時間。Dart是經過AOT技術取得了上述的表現。
另外一方面,擁有JIT的Flutter,支持在開發期間,不須要從頭構建整個應用,就能夠刷新UI,提供開發效率。
正如本教程將要展現的,flutter框架是創建在組件思想之上的。在flutter中,組件不僅僅指應用的視圖,而是整個界面甚至是真個應用。
除了開發跨Android和iOS應用,學習flutter將給你一個在Fuchsia平臺開發的超前開始。Fuchsia是google開發中的實驗性的操做系統。
本教程,咱們構建一個app,首先從github api中獲取一個組織的成員信息,而後將成員信息展現在一個滾動列表中。
當開發app時,但是使用Android模擬器、iOS模擬器或者同時使用。
在構建app時,咱們將學習flutter一下內容:
Flutter開發能夠在macOS,Linux或Windows上完成。 雖然您能夠在Flutter工具鏈中使用任何編輯器,但IntelliJ IDEA,Android Studio和Visual Studio Code的IDE插件能夠簡化開發工做。 咱們將在本教程中使用Android Studio。
您能夠在這找到配置flutter開發環境的說明,具體步驟因平臺而異,但主要步驟是:
flutter doctor
命令,他會安裝flutter框架,包括dart,並且提示你任何其餘須要安裝的依賴。flutter doctor
發現的任何問題。File->new->new Flutter Project
Flutter Application
,點擊next
.
在Android Studio中,您會在左側看到一個顯示項目結構的面板。 有iOS和Android的文件夾,以及包含main.dart的lib文件夾。 您將本教程中的只在lib文件夾中工做。
import 'package:flutter/material.dart';
void main() => runApp(new GHFlutterApp());
class GHFlutterApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'GHFlutter',
home: new Scaffold(
appBar: new AppBar(
title: new Text('GHFlutter'),
),
body: new Center(
child: new Text('GHFlutter'),
),
),
);
}
}
複製代碼
頂部附近的main()函數使用=>運算符,爲了單行函數運行應用程序。 您有一個名爲GHFlutterApp的應用程序。 你在這裏看到你的應用程序自己是一個StatelessWidget(無狀態組件)。Flutter應用程序中的大多數實體都是組件,不管是無狀態的仍是有狀態的。 您重寫組件的build()方法來建立您的應用的組件。 您正在使用MaterialApp 組件,該組件提供了大量遵循Material Design規範應用組件。
對於本入門教程,經過選擇它並敲擊Delete鍵,從項目中刪除test
文件夾中的測試文件widget_test.dart
。
你能夠在macOS上,啓動iOS模擬器。 您也能夠在macOS,Linux或Windows上使用Android模擬器。
您看到的慢速模式banner表示應用程序正在以調試模式運行。
您能夠經過單擊Android Studio窗口頂部工具欄右側的中止按鈕來中止正在運行的應用程序:
flutter支持Hot Reload 功能。flutter中的Hot reload和Android Studio的Instant Run相似。 構建並運行應用程序,使其在模擬器或模擬器上運行:
appBar: new AppBar(
title: new Text('GHFlutter App'),
),
複製代碼
如今單擊工具欄上的熱從新加載按鈕:
因爲Flutter處於開發階段,熱從新加載功能可能沒法正常工做,但整體而言,在構建UI時節省時間。
您不須要將全部的Dart代碼保存在一個main.dart文件中,而是但願可以從您建立的其餘類中導入。 如今您將看到一個用於導入字符串的示例,這將有助於您應用的本地化。 在lib文件夾中右鍵單擊並選擇New File建立一個名爲strings.dart的文件: 在新建的文件中添加以下類:
class Strings {
static String appTitle = "GHFlutter";
}
複製代碼
在main.dart的頂部添加以下引入:
import 'strings.dart';
複製代碼
以下更新應用的title:
return new MaterialApp(
title: Strings.appTitle,
複製代碼
更新其餘的字符串,使用新的字符串文件,因此最終GHFlutterApp類以下:
class GHFlutterApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: Strings.appTitle,
home: new Scaffold(
appBar: new AppBar(
title: new Text(Strings.appTitle),
),
body: new Center(
child: new Text(Strings.appTitle),
),
),
);
}
}
複製代碼
構建並運行應用程序,您應該看不到任何變化,但如今您正在使用字符串文件中的字符串。
幾乎Flutter應用程序的每一個元素都是一個組件。組件被設計成不可變的,由於使用不可變組件有助於保持應用程序UI的輕量級。
您將使用兩種基本類型的組件:
Stateless: 僅依賴於本身的配置信息的組件,例如圖像視圖中的靜態圖像 Stateful: 須要維護動態信息的組件,並經過與狀態對象交互來實現 無狀態和有狀態的組件在每一個幀的Flutter應用程序中重繪,不一樣之處在於有狀態組件將其配置委託給狀態對象。
要開始製做本身的組件,請在main.dart的底部建立一個新類:
class GHFlutter extends StatefulWidget {
@override
createState() => new GHFlutterState();
}
複製代碼
你已經建立了一個StatefulWidget子類,而且你重寫了createState()方法來建立它的狀態對象。 如今在GHFlutter上添加一個GHFlutterState類:
class GHFlutterState extends State<GHFlutter> {
}
複製代碼
GHFlutterState使用GHFlutter的參數擴展狀態。
製做新組件時的主要任務是重寫在組件呈如今屏幕上時調用的build()方法。
重寫GHFlutterState 的build()方法:
@override
Widget build(BuildContext context) {
}
複製代碼
最終以下:
@override
Widget build(BuildContext context) {
return new Scaffold (
appBar: new AppBar(
title: new Text(Strings.appTitle),
),
body: new Text(Strings.appTitle),
);
}
複製代碼
Scaffold 是material design組件的容器。 它充當組件層次結構的根。 您已將AppBar和一個body添加到scaffold中,而且每一個都包含一個Text組件
更新GHFlutterApp,以便它使用新的GHFlutter組件做爲其home屬性,而不是構建默認的scaffold:
class GHFlutterApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: Strings.appTitle,
home: new GHFlutter(),
);
}
}
複製代碼
構建並運行應用程序,您將看到新的組件正在運行:
以前,您將本身的strings.dart文件導入到項目中。 您能夠一樣導入Flutter框架的其餘庫或者Dart的庫。
例如,您如今將使用框架中的庫來進行HTTP網絡調用,並將生成的JSON響應解析爲Dart對象。 在main.dart的頂部添加兩個新的引入:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'strings.dart';
複製代碼
您會看到當前未使用的指標在引入上。
Dart應用程序是單線程的,但Dart支持在其餘線程上運行代碼以及運行異步代碼,經過使用async/await
模式,從而不會阻止UI線程。
您將進行異步網絡調用以檢索GitHub組織成員列表。 在GHFlutterState頂部添加一個空List做爲屬性,並添加一個屬性來保存文本樣式(_biggerFont
):
var _members = [];
final _biggerFont = const TextStyle(fontSize: 18.0);
複製代碼
名字如下劃線開頭的變量表示類的私有成員變量。 要進行異步HTTP調用,請在GHFlutterState中添加一個_loadData()
方法:
_loadData() async {
String dataURL = "https://api.github.com/orgs/raywenderlich/members";
http.Response response = await http.get(dataURL);
setState(() {
_members = JSON.decode(response.body);
});
}
複製代碼
您已將async關鍵字添加到_loadData()
中,以告訴Dart它是異步的,可是http.get()的await關鍵字表示阻塞執行。 經過dataUrl
設置API的請求地址。
當HTTP調用完成時,您將回調傳遞給在UI線程上同步運行的setState()。 這時,您解碼JSON響應並將其賦值給_members變量。 在GHFlutterState中重寫initState方法,在initState中調用_loadData() 方法。當組件狀態初始化時,_loadData()會被調用,加載數據。
@override
void initState() {
super.initState();
_loadData();
}
複製代碼
如今您已經有了成員列表,您須要一種方法在列表UI中顯示它們。 Dart提供了一個ListView組件,可以讓您在列表中顯示數據。 ListView的做用相似於Android上的RecyclerView和iOS上的UITableView,經過重複回收利用之後的view,使列表滾動更加順滑。 在GHFlutterState中添加_buildRow()
方法:
Widget _buildRow(int i) {
return new ListTile(
title: new Text("${_members[i]["login"]}", style: _biggerFont)
);
}
複製代碼
您將返回一個ListTile組件,該組件顯示第iJSON成員的"login"值,還使用您以前建立的文本樣式(_biggerFont
)。
更新build方法,使body屬性值爲ListView.builder:
body: new ListView.builder(
padding: const EdgeInsets.all(16.0),
itemCount: _members.length,
itemBuilder: (BuildContext context, int position) {
return _buildRow(position);
}),
複製代碼
您已添加padding,將itemCount設置爲成員數量,設置itemBuilder調用_buildRow(),傳遞postion變量。
您能夠嘗試熱從新加載,但可能會收到"Full restart may be required"的消息。 若是是這樣,重啓:
發起網絡請求,解析數據,並在列表中顯示結果是很容易的!
爲了在列表中增長分割線,須要翻倍list的itemCount屬性,而後list中的position爲偶數時返回Divider組件。
body: new ListView.builder(
itemCount: _members.length * 2,
itemBuilder: (BuildContext context, int position) {
if (position.isOdd) return new Divider();
final index = position ~/ 2;
return _buildRow(index);
}),
複製代碼
必定要itemCount翻倍,由於增長了分割線,因此刪除padding。在itemBuilder中,返回Divider組件,或者經過整數除法計算一個新的index,利用_buildRow(index)返回一個組件。
嘗試從新加載,你應該在列表中看到分割線:
Widget _buildRow(int i) {
return new Padding(
padding: const EdgeInsets.all(16.0),
child: new ListTile(
title: new Text("${_members[i]["login"]}", style: _biggerFont)
)
);
}
複製代碼
ListTile如今是Padding的子部件。 熱加載以查看行上的padding而不是分隔符上的Padding。
在上一節中,JSON解析器將JSON響應中的每一個成員都做爲Dart Map類型實例添加到_members列表中,這至關於Kotlin中的Map或Swift中的Dictionary。 可是,若是你想使用自定義類。 在main.dart文件中添加Member類:
class Member {
final String login;
Member(this.login) {
if (login == null) {
throw new ArgumentError("login of Member cannot be null. "
"Received: '$login'");
}
}
}
複製代碼
member有一個login屬性,和一個構造函數,在構造函數中當login值爲null時,會拋出一個error。 更新GHFlutterState類中的_members變量聲明,因此如今他就是一個Member對象的list
var _members = <Member>[];
複製代碼
更新_buildRow()方法使用Member對象的login屬性而不是map的"login"key的值。
title: new Text("${_members[i].login}", style: _biggerFont)
複製代碼
如今更新在_loadData()中的傳遞給setState()的回調,將map解析爲Member對象,添加到member對象list中。
setState(() {
final membersJSON = JSON.decode(response.body);
for (var memberJSON in membersJSON) {
final member = new Member(memberJSON["login"]);
_members.add(member);
}
});
複製代碼
若是你熱加載,有可能看到一個錯誤,這時你從新啓動。
每一個member都有一個頭像url.你將添加頭像到Member類中,而後在app中顯示頭像。
在Member類中添加avatarUrl屬性,這個屬性不能爲null。
class Member {
final String login;
final String avatarUrl;
Member(this.login, this.avatarUrl) {
if (login == null) {
throw new ArgumentError("login of Member cannot be null. "
"Received: '$login'");
}
if (avatarUrl == null) {
throw new ArgumentError("avatarUrl of Member cannot be null. "
"Received: '$avatarUrl'");
}
}
}
複製代碼
更新_buildRow()方法,經過NetworkImage 和 the CircleAvatar 組件顯示頭像。
Widget _buildRow(int i) {
return new Padding(
padding: const EdgeInsets.all(16.0),
child: new ListTile(
title: new Text("${_members[i].login}", style: _biggerFont),
leading: new CircleAvatar(
backgroundColor: Colors.green,
backgroundImage: new NetworkImage(_members[i].avatarUrl)
),
)
);
}
複製代碼
設置頭像爲ListTile的leading屬性,頭像將顯示在title的前面。 更新_loadData()方法,使在建立一個新的Member時使用map的"avatar_url"值。
final member = new Member(memberJSON["login"], memberJSON["avatar_url"]);
複製代碼
構建運行,你將能看到每個行的頭像。
大部分代碼如今位於main.dart文件中。 爲了使代碼更清晰一點,你能夠重構組件和你已經添加到他們本身的文件中的類。 在lib文件夾中建立名爲member.dart和ghflutter.dart的文件。 將Member類移入member.dart,並將GHFlutterState和GHFlutter類轉移到ghflutter.dart。 您不須要member.dart中的任何導入語句,但flutter.dart中的導入應以下所示:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'member.dart';
import 'strings.dart';
複製代碼
您還須要更新main.dart中的導入,因此整個文件包含如下內容:
import 'package:flutter/material.dart';
import 'ghflutter.dart';
import 'strings.dart';
void main() => runApp(new GHFlutterApp());
class GHFlutterApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: Strings.appTitle,
home: new GHFlutter(),
);
}
}
複製代碼
經過構建並運行應用程序,您應該看不到任何更改,但代碼如今更清晰一些
您能夠經過將主題屬性添加到您在main.dart中建立的MaterialApp來輕鬆地將主題添加到應用程序:
lass GHFlutterApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: Strings.appTitle,
theme: new ThemeData(primaryColor: Colors.green.shade800),
home: new GHFlutter(),
);
}
}
複製代碼
您正在使用綠色陰影做爲Material Design主題顏色值。
構建並運行應用程序,以查看新的主題:
大部分應用截圖都來自Android模擬器。 您也能夠在iOS模擬器中運行最終的主題應用程序: