在現在的 Flutter 大潮下,本系列是讓你看完會安心的文章。本系列將完整講述:如何快速從0開發一個完整的 Flutter APP,配套高完成度 Flutter 開源項目 GSYGithubAppFlutter。同時也會提供一些Flutter的開發細節技巧,並針對開發過程當中可能遇到的問題進行填坑。前端
系列文章分爲三篇,第一部分是基礎篇(針對Dart語言和Flutter基礎),第二部分是App快速開發實戰篇,第三部分是細節填坑篇。java
筆者相繼開發過 Flutter、React Native 、Weex 等主流跨平臺框架項目,其中 Flutter 的跨平臺兼容性無疑最好。前期開發調試徹底在 Android 端進行的狀況下,第一次在 IOS 平臺運行竟然沒有任何錯誤,而且還沒出現UI兼容問題,相信對於經歷過跨平臺開發的猿們而言,這是多麼的難以想象畫面。而且 Fluuter 的 HotLoad 相比較其餘兩個平臺,也是絲滑的讓人沒法相信。吹爆了!git
這些特色其實這得益於Flutter Engine 和 Skia ,若是有興趣的能夠看看筆者以前的《移動端跨平臺開發的深度解析》。好了,感慨那麼多,讓咱們進入正題吧。github
本篇主要涉及:環境搭建、Dart語言、Flutter的基礎。面試
Flutter 的環境搭建十分省心,特別對應 Android 開發者而言,只是在 Android Stuido 上安裝插件,並下載flutter Sdk到本地,配置在環境變量便可。其實中文網的搭建Futter開發環境 已經很貼心詳細,從平臺指引開始安裝基本都不會遇到問題。數組
這裏主要是須要注意,由於某些不可抗力的緣由,國內的用戶須要配置 Flutter 的代理,而且國內用戶在搜索 Flutter 第三方包時,也是在 pub.flutter-io.cn 內查找,下方是須要配置到環境變量的地址。(ps Android Studio下運行 IOS 也是蠻有意思的(◐‿◑))bash
///win直接配置到環境編輯便可,mac配置到bash_profile
export PUB_HOSTED_URL=https://pub.flutter-io.cn //國內用戶須要設置
export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn //國內用戶須要設置
複製代碼
在跨平臺開領域被 JS 一統天下的今天,Dart 語言的出現無疑是一股清流。做爲後來者,Dart語言有着很多Java、kotlin 和 JS 的影子,因此對於 Android 原生開發者、前端開發者而言無疑是很是友好的。閉包
官方也提供了包括IOS開發者,React Native 等開發者遷移到 Flutter 上的文檔,因此請不要擔憂,Dart語言不會是你掌握 Flutter 的門檻。甚至做爲開發者,就算你不懂 Dart 也能夠看着代碼摸索。app
Come on,下面主要經過對比,簡單講述下 Dart 的一些特性,主要涉及的是 Flutter 下使用。框架
var 能夠定義變量,如 var tag = "666"
,這和 JS 、 Kotlin 等語言相似,同時 Dart 屬於僞動態強類型語言,支持閉包。
Dart 中 number 類型分爲 int
和 double
,其中 java 中的 long 對應的也是 Dart 中的 int 類型。Dart 中沒有 float 類型。
Dart 下只有 bool 型能夠用於 if 等判斷,不一樣於 JS 這種使用方式是不合法的 var g = "null"; if(g){}
。
DART中,switch 支持 String 類型。
Dart 不須要給變量設置 setter getter
方法, 這和 kotlin 等相似。Dart 中全部的基礎類型、類等都繼承 Object ,默認值是 NULL, 自帶 getter 和 setter ,而若是是 final 或者 const 的話,那麼它只有一個 getter 方法。
Dart 中 final 和 const 表示常量,好比 final name = 'GSY'; const value= 1000000;
同時 static const
組合表明了靜態常量。其中 const 的值在編譯期肯定,final 的值要到運行時才肯定。(ps Flutter 在 Release 下是 AOT 模式。)
Dart 下的數值,在做爲字符串使用時,是須要顯式指定的。好比:int i = 0; print("aaaa" + i);
這樣並不支持,須要 print("aaaa" + i.toString());
這樣使用。這和 Java 與 JS 存在差別。因此在使用動態類型時,須要注意不要把 number 類型當作 String 使用。
DART 中數組等於列表,因此 var list = [];
和 List list = new List()
能夠簡單看作同樣。
Dart 下 ??
、??=
屬於操做符,如: AA ?? "999"
表示若是 AA 爲空,返回999;AA ??= "999"
表示若是 AA 爲空,給 AA 設置成 999。
Dart 方法能夠設置 參數默認值 和 指定名稱 。好比: getDetail(Sting userName, reposName, {branch = "master"}){}
方法,這裏 branch 不設置的話,默認是 「master」 。參數類型 能夠指定或者不指定。調用效果: getRepositoryDetailDao(「aaa", "bbbb", branch: "dev");
Dart 不像 Java ,沒有關鍵詞 public 、private 等修飾符,_
下橫向直接表明 private ,可是有 @protected
註解。
Dart 中多構造函數,能夠經過以下代碼實現的。默認構造方法只能有一個,而經過Model.empty()
方法能夠建立一個空參數的類,其實方法名稱隨你喜歡。而變量初始化值時,只須要經過 this.name
在構造方法中指定便可:
class ModelA {
String name;
String tag;
//默認構造方法,賦值給name和tag
ModelA(this.name, this.tag);
//返回一個空的ModelA
ModelA.empty();
//返回一個設置了name的ModelA
ModelA.forName(this.name);
}
複製代碼
Flutter 中支持 async
/await
。這一點和 ES7 很像,以下代碼所示,只是定義的位置不一樣。同時異步操做也和 ES6 中的Promise
很像,只是 Flutter 中返回的是 Future
對象,經過 then
能夠執行下一步。若是返回的仍是 Future
即可以 then().then.()
的流式操做了 。
///模擬等待兩秒,返回OK
request() async {
await Future.delayed(Duration(seconds: 1));
return "ok!";
}
///獲得"ok!"後,將"ok!"修改成"ok from request"
doSomeThing() async {
String data = await request();
data = "ok from request";
return data;
}
///打印結果
renderSome() {
doSomeThing().then((value) {
print(value);
///輸出ok from request
});
}
複製代碼
Flutter 中 setState
頗有 React Native 的既視感,Flutter 中也是經過 state 跨幀實現管理數據狀態的,這個後面會詳細講到。
Flutter 中一切皆 Widget 呈現,經過 build
方法返回 Widget,這也是和 React Native 中,經過 render
函數返回須要渲染的 component 同樣的模式。
在 Flutter 中,一切的顯示都是 Widget 。Widget 是一切的基礎,做爲響應式的渲染,屬於 MVVM 的實現機制。咱們能夠經過修改數據,再用setState
設置數據,Flutter 會自動經過綁定的數據更新 Widget 。 因此你須要作的就是實現 Widget 界面,而且和數據綁定起來。
Widget 分爲 有狀態 和 無狀態 兩種,在 Flutter 中每一個頁面都是一幀。無狀態就是保持在那一幀。而有狀態的 Widget 當數據更新時,實際上是繪製了新的 Widget,只是 State 實現了跨幀的數據同步保存。
這裏有個小 Tip ,當代碼框裏輸入
stl
的時候,能夠自動彈出建立無狀態控件的模板選項,而輸入stf
的時,就會彈出建立有狀態 Widget 的模板選項。代碼格式化的時候,括號內外的逗號都會影響格式化時換行的位置。
若是以爲默認換行的線過短,能夠在設置-Editor-Code Style-Dart-Wrapping and Braces-Hard wrap at 設置你接受的數值。
直接進入主題,下方代碼是無狀態 Widget 的簡單實現。
繼承 StatelessWidget,經過 build
方法返回一個佈局好的控件。可能如今你還對 Flutter 的內置控件不熟悉,but Don't worry , take it easy ,後面咱們就會詳細介紹。這裏你只須要知道,一個無狀態的 Widget 就是這麼簡單。
Widget 和 Widget 之間經過 child:
進行嵌套。其中有的 Widget 只能有一個 child,好比下方的 Container
;有的 Widget 能夠多個 child ,也就是children:
,好比` Column 佈局。下方代碼即是 Container Widget 嵌套了 Text Widget。
import 'package:flutter/material.dart';
class DEMOWidget extends StatelessWidget {
final String text;
//數據能夠經過構造方法傳遞進來
DEMOWidget(this.text);
@override
Widget build(BuildContext context) {
//這裏返回你須要的控件
//這裏末尾有沒有的逗號,對於格式化代碼而已經是不同的。
return Container(
//白色背景
color: Colors.white,
//Dart語法中,?? 表示若是text爲空,就返回尾號後的內容。
child: Text(text ?? "這就是無狀態DMEO"),
);
}
}
複製代碼
繼續直插主題,以下代碼,是有狀態的widget的簡單實現。
你須要建立管理的是主要是 State
, 經過 State 的 build
方法去構建控件。在 State 中,你能夠動態改變數據,這相似 MVVM 實現,在 setState
以後,改變的數據會觸發 Widget 從新構建刷新。而下方代碼中,是經過延兩秒以後,讓文本顯示爲 "這就變了數值"。
以下代碼還能夠看出,State 中主要的聲明週期有 :
看到沒,Flutter 其實就是這麼簡單!你的關注點只要在:建立你的 StatelessWidget
或者 StatefulWidget
而已。你須要的就是在 build
中堆積你的佈局,而後把數據添加到 Widget 中,最後經過 setState
改變數據,從而實現畫面變化。
import 'dart:async';
import 'package:flutter/material.dart';
class DemoStateWidget extends StatefulWidget {
final String text;
////經過構造方法傳值
DemoStateWidget(this.text);
///主要是負責建立state
@override
_DemoStateWidgetState createState() => _DemoStateWidgetState(text);
}
class _DemoStateWidgetState extends State<DemoStateWidget> {
String text;
_DemoStateWidgetState(this.text);
@override
void initState() {
///初始化,這個函數在生命週期中只調用一次
super.initState();
///定時2秒
new Future.delayed(const Duration(seconds: 1), () {
setState(() {
text = "這就變了數值";
});
});
}
@override
void dispose() {
///銷燬
super.dispose();
}
@override
void didChangeDependencies() {
///在initState以後調 Called when a dependency of this [State] object changes.
super.didChangeDependencies();
}
@override
Widget build(BuildContext context) {
return Container(
child: Text(text ?? "這就是有狀態DMEO"),
);
}
}
複製代碼
Flutter 中擁有須要將近30種內置的 佈局Widget,其中經常使用有 Container、Padding、Center、Flex、Stack、Row、Column、ListView 等,下面簡單講解它們的特性和使用。
類型 | 做用特色 |
---|---|
Container | 只有一個子 Widget。默認充滿,包含了padding、margin、color、寬高、decoration 等配置。 |
Padding | 只有一個子 Widget。只用於設置Padding,經常使用於嵌套child,給child設置padding。 |
Center | 只有一個子 Widget。只用於居中顯示,經常使用於嵌套child,給child設置居中。 |
Stack | 能夠有多個子 Widget。 子Widget堆疊在一塊兒。 |
Column | 能夠有多個子 Widget。垂直佈局。 |
Row | 能夠有多個子 Widget。水平佈局。 |
Expanded | 只有一個子 Widget。在 Column 和 Row 中充滿。 |
ListView | 能夠有多個子 Widget。本身意會吧。 |
child:
,支持配置 padding,margin,color,寬高,decoration(通常配置邊框和陰影)等配置,在 Flutter 中,不是全部的控件都有 寬高、padding、margin、color 等屬性,因此纔會有 Padding、Center 等 Widget 的存在。new Container(
///四周10大小的maring
margin: EdgeInsets.all(10.0),
height: 120.0,
width: 500.0,
///透明黑色遮罩
decoration: new BoxDecoration(
///弧度爲4.0
borderRadius: BorderRadius.all(Radius.circular(4.0)),
///設置了decoration的color,就不能設置Container的color。
color: Colors.black,
///邊框
border: new Border.all(color: Color(GSYColors.subTextColor), width: 0.3)),
child:new Text("666666"));
複製代碼
//主軸方向,Column的豎向、Row個人橫向
mainAxisAlignment: MainAxisAlignment.start,
//默認是最大充滿、仍是根據child顯示最小大小
mainAxisSize: MainAxisSize.max,
//副軸方向,Column的橫向、Row個人豎向
crossAxisAlignment :CrossAxisAlignment.center,
複製代碼
flex
屬性決定比例。new Column(
///主軸居中,便是豎直向居中
mainAxisAlignment: MainAxisAlignment.center,
///大小按照最小顯示
mainAxisSize : MainAxisSize.min,
///橫向也居中
crossAxisAlignment : CrossAxisAlignment.center,
children: <Widget>[
///flex默認爲1
new Expanded(child: new Text("1111"), flex: 2,),
new Expanded(child: new Text("2222")),
],
);
複製代碼
接下來咱們來寫一個複雜一些的控件。首先咱們建立一個私有方法_getBottomItem
,返回一個 Expanded Widget
,由於後面咱們須要將這個方法返回的 Widget 在 Row 下平均充滿。
如代碼中註釋,佈局內主要是現實一個居中的Icon圖標和文本,中間間隔5.0的 padding:
///返回一個居中帶圖標和文本的Item
_getBottomItem(IconData icon, String text) {
///充滿 Row 橫向的佈局
return new Expanded(
flex: 1,
///居中顯示
child: new Center(
///橫向佈局
child: new Row(
///主軸居中,便是橫向居中
mainAxisAlignment: MainAxisAlignment.center,
///大小按照最大充滿
mainAxisSize : MainAxisSize.max,
///豎向也居中
crossAxisAlignment : CrossAxisAlignment.center,
children: <Widget>[
///一個圖標,大小16.0,灰色
new Icon(
icon,
size: 16.0,
color: Colors.grey,
),
///間隔
new Padding(padding: new EdgeInsets.only(left:5.0)),
///顯示文本
new Text(
text,
//設置字體樣式:顏色灰色,字體大小14.0
style: new TextStyle(color: Colors.grey, fontSize: 14.0),
//超過的省略爲...顯示
overflow: TextOverflow.ellipsis,
//最長一行
maxLines: 1,
),
],
),
),
);
}
複製代碼
接着咱們把上方的方法,放到新的佈局裏。以下流程和代碼:
Container
包含了Card
,用於快速簡單的實現圓角和陰影。FlatButton
實現了點擊,經過Padding實現了邊距。Column
垂直包含了兩個子Widget,一個是Container
、一個是Row
。_getBottomItem
方法返回的 Widget ,效果以下圖。@override
Widget build(BuildContext context) {
return new Container(
///卡片包裝
child: new Card(
///增長點擊效果
child: new FlatButton(
onPressed: (){print("點擊了哦");},
child: new Padding(
padding: new EdgeInsets.only(left: 0.0, top: 10.0, right: 10.0, bottom: 10.0),
child: new Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
///文本描述
new Container(
child: new Text(
"這是一點描述",
style: TextStyle(
color: Color(GSYColors.subTextColor),
fontSize: 14.0,
),
///最長三行,超過 ... 顯示
maxLines: 3,
overflow: TextOverflow.ellipsis,
),
margin: new EdgeInsets.only(top: 6.0, bottom: 2.0),
alignment: Alignment.topLeft),
new Padding(padding: EdgeInsets.all(10.0)),
///三個平均分配的橫向圖標文字
new Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
_getBottomItem(Icons.star, "1000"),
_getBottomItem(Icons.link, "1000"),
_getBottomItem(Icons.subject, "1000"),
],
),
],
),
))),
);
}
複製代碼
Flutter 中,你的佈局不少時候就是這麼一層一層嵌套出來的,固然還有其餘更高級的佈局方式,這裏就先不展開了。
Flutter 中除了佈局的 Widget,還有交互顯示的 Widget 和完整頁面呈現的Widget。其中常見的有 MaterialApp、Scaffold、Appbar、Text、Image、FlatButton等。下面簡單介紹這些 Wdiget,並完成一個頁面。
類型 | 做用特色 |
---|---|
MaterialApp | 通常做爲APP頂層的主頁入口,可配置主題,多語言,路由等 |
Scaffold | 通常用戶頁面的承載Widget,包含appbar、snackbar、drawer等material design的設定。 |
Appbar | 通常用於Scaffold的appbar ,內有標題,二級頁面返回按鍵等,固然不止這些,tabbar等也會須要它 。 |
Text | 顯示文本,幾乎都會用到,主要是經過style設置TextStyle來設置字體樣式等。 |
RichText | 富文本,經過設置TextSpan ,能夠拼接出富文本場景。 |
TextField | 文本輸入框 :new TextField(controller: //文本控制器, obscureText: "hint文本"); |
Image | 圖片加載: new FadeInImage.assetNetwork( placeholder: "預覽圖", fit: BoxFit.fitWidth, image: "url"); |
FlatButton | 按鍵點擊: new FlatButton(onPressed: () {},child: new Container()); |
那麼再次直插主題實現一個簡單完整的頁面試試。以下方代碼:
DemoPage
。build
建立了一個Scaffold
。AppBar
和一個ListView
。title
爲 Text Widget。ListView
,返回了20個以前咱們建立過的 DemoItem Widget。import 'package:flutter/material.dart';
import 'package:gsy_github_app_flutter/test/DemoItem.dart';
class DemoPage extends StatefulWidget {
@override
_DemoPageState createState() => _DemoPageState();
}
class _DemoPageState extends State<DemoPage> {
@override
Widget build(BuildContext context) {
///一個頁面的開始
///若是是新頁面,會自帶返回按鍵
return new Scaffold(
///背景樣式
backgroundColor: Colors.blue,
///標題欄,固然不只僅是標題欄
appBar: new AppBar(
///這個title是一個Widget
title: new Text("Title"),
),
///正式的頁面開始
///一個ListView,20個Item
body: new ListView.builder(
itemBuilder: (context, index) {
return new DemoItem();
},
itemCount: 20,
),
);
}
}
複製代碼
最後咱們建立一個StatelessWidget做爲入口文件,實現一個MaterialApp
將上方的DemoPage
設置爲home頁面,經過main
入口執行頁面。
import 'package:flutter/material.dart';
import 'package:gsy_github_app_flutter/test/DemoPage.dart';
void main() {
runApp(new DemoApp());
}
class DemoApp extends StatelessWidget {
DemoApp({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return new MaterialApp(home: DemoPage());
}
}
複製代碼
好吧,第一部分終於完了,這裏主要講解都是一些簡單基礎的東西,適合安利入坑,後續還有更多實戰,敬請期待喲!( ̄^ ̄)ゞ