腾讯技术团队整理,年度好文轻松彻底入门 Flutter,秒变大前端
原文作者:騰訊技術(shù)
原文鏈接:https://zhuanlan.zhihu.com/p/90836859
來源:知乎
本文真對 Flutter 的技術(shù)特性,做了一些略全面的入門級的介紹,如果你聽說過Flutter,想去了解它,但是又不想去翻厚厚的API,那么本文就是為你準(zhǔn)備的。
隨著純客戶端到Hybrid技術(shù),到RN&Weex,再到如今的Flutter技術(shù),客戶端實現(xiàn)技術(shù)不斷前進。 在之前的一個APP項目中,因為歷史原因當(dāng)時選擇了weex,隨著使用的不斷深入,我們逐漸發(fā)現(xiàn)了weex的渲染性能問題已經(jīng)成為一個隱患和瓶頸。 而Flutter技術(shù)的不斷成熟和流行,Flutter的良好的跨平臺性和高性能優(yōu)點,不斷吸引著我們。
(本文包含以下內(nèi)容,閱讀完需要約18分鐘)
- 1.Flutter是啥玩意兒?
- 2.移動端跨平臺技術(shù)對比
2.1 H5+原生APP
2.2 RN&Weex
2.3 Flutter - 3.Dart語言
- 4.環(huán)境配置
- 5.Hello World
5.1 創(chuàng)建項目
5.2 項目結(jié)構(gòu)
5.3 啟動模擬器
5.4 啟動項目APP
5.5 簡化版的Hello World
5.6 給頁面加上狀態(tài)
5.7 小結(jié)一下 - 6.路由
6.1 單個頁面的跳轉(zhuǎn)
6.2 更多頁面跳轉(zhuǎn)使用路由表
6.3 路由傳參 - 7.widget
7.1 Text
7.2 Button
7.3 Container
7.4 Image - 8.布局
8.1 Row & Column & Center 行列軸布局
8.2 Align 角定位布局
8.3 Stack & Positioned 絕對定位
8.4 Flex & Expanded 流式布局 - 9.動畫
9.1 簡單動畫:淡入淡出
9.2 復(fù)雜一些的動畫:放大縮小 - 10.http請求
10.1 HttpClient
10.2 http
10.3 Dio - 11.吐吐槽
11.1 墻
11.2 組件過度設(shè)計
11.3 嵌套太多不適應(yīng)
11.4 布局修改會導(dǎo)致嵌套關(guān)系修改
11.5 Dart語言升級
11.6 不能熱更新 - 12.結(jié)語
1.Flutter是啥玩意兒?
Flutter是谷歌的移動UI框架,可以快速在iOS和Android上構(gòu)建高質(zhì)量的原生用戶界面。
- 具有跨平臺開發(fā)特性,支持IOS、Android、Web三端。
- 熱重載特性大大提高了開發(fā)效率
- 自繪UI引擎和編譯成原生代碼的方式,使得系統(tǒng)的運行時的高性能成為了可能
- 使用Dart語言,目前已經(jīng)支持同時編譯成Web端代碼,
到底值不值得跟進Flutter技術(shù)呢? 還是看下Flutter,Weex,ReactNative的搜索指數(shù)對比,大概就知道這個行業(yè)趨勢了。
藍色是Flutter,可以看出上升勢頭非常強勁。苦逼的前端就是這樣,你不跟潮流,潮流就會把你拋棄。
2.移動端跨平臺技術(shù)對比
為啥會有Flutter這種東西? 他的原理是什么? 他是怎么做到高性能的? 要明白這些問題,我們不得不從幾種移動端跨平臺技術(shù)的對比講起。
2.1 H5+原生APP
技術(shù)門檻最低,接入速度最快,熱更新最方便的,自然就是H5方式。APP中提供一個Webview使用H5頁面的Http直連。APP和H5可以相互獨立開發(fā),JS使用Bridge與原生進行數(shù)據(jù)通信,顯示界面依賴Webview的瀏覽器渲染。 但是帶來的問題也很明顯,因為是需要遠程直連,那么初次打開H5頁面,會有瞬間的白屏,并且Webview本身會有至少幾十M的內(nèi)存消耗。
當(dāng)然,作為前端開發(fā)人員,在H5方式可以使用SPA單頁面、懶加載、離線H5等各種前端優(yōu)化手段進行性能優(yōu)化,以使得H5的表現(xiàn)更接近原生。但是首次的瞬間白屏和內(nèi)存,Bridge的通信效率低下,始終是被技術(shù)框架給局限住了。
2.2 RN&Weex
由于H5的那些弊端,愛折騰的前端工程師,祭出了RN、Weex兩個大殺器, 使用原生去解析RN、Weex的顯示配置,顯示層、邏輯層都直接與原生數(shù)據(jù)通信。 因為拋棄了瀏覽器,自然渲染性能、執(zhí)行性能都提升了一大截。
但是,每次遇到顯示的變更,JS都還會通過Bridge和原生轉(zhuǎn)一道再做渲染的調(diào)整,所以Bridge就最后成為了性能的瓶頸。在實際項目中,特別是做一些大量復(fù)雜動畫處理的時候,由于渲染部分需要頻繁通信,性能問題變得尤為突出。 有興趣的同學(xué)可以去看看BindingX,里面有關(guān)于動畫中數(shù)據(jù)通信效率低下導(dǎo)致動畫幀率低下的詳細說明。
2.3 Flutter
不得不佩服Google開發(fā)人員的想象力,為了達到極致性能,Flutter更前進了一步,Flutter代碼編譯完成以后,直接就是原生代碼,并且使用自繪UI引擎原生方式做渲染。 Flutter依賴一個Skia 2D圖形化引擎。Skia也是Android平臺和Chrome的底層渲染引擎,所以性能方面完全不用擔(dān)心。因為使用Dart做AOT編譯成原生,自然也比使用解釋性的JS在V8引擎中執(zhí)行性能更快,并且因為去掉Bridge,沒有了繁瑣的數(shù)據(jù)通信和交互,性能就更前進了一步。
3.Dart語言
學(xué)習(xí)Flutter,得先了解Dart。Dart語言曾經(jīng)雄心勃勃的要替換Javascript, 但是發(fā)布的時機正好遇到JS的飛速發(fā)展,于是就逐漸沉寂,直到配合Flutter的發(fā)布,才又重新煥發(fā)了生機。
在最近2019年9月的一次Google開發(fā)者大會中,伴隨著Flutter1.9的發(fā)布,目前的Dart也同時更新到了2.5版本, 提供了機器學(xué)習(xí)和對C跨平臺調(diào)用的能力。總體來說,Dart語法,對于前端同學(xué),上手還是很容易的,風(fēng)格很像。
關(guān)于Dart語法,請移步傳送門:https://dart.dev/samples
4.環(huán)境配置
無論學(xué)什么新語言,首先都是環(huán)境配置。由于Flutter出自Google,所以有一定門檻,如果在公司內(nèi)安裝,你還需要一個方便的代理切換工具, 比如:Proxifier?。
安裝教程,參照官網(wǎng):https://flutter.dev/docs/get-started/install
Flutter支持多種編輯器如:Android Studio , XCode。 但是既然作為支持跨雙端的開發(fā),個人還是推薦使用?VSCode。
VSCode安裝完成后,需要安裝Flutter插件,和Dart插件. 在擴展窗口里,搜索Flutter,和Dart,點擊“Install”即可,非常方便。
如果安裝不上去,記得開啟下代理。
5.Hello World
作為一個偉大的程序員,第一行代碼總是從Hello World開始。_
5.1 創(chuàng)建項目:
方法1:直接使用命令創(chuàng)建:
flutter create projectname方法2:使用VSCode創(chuàng)建:
View -> Command Palette -> Flutter:New Project 即可
注意請先打開代理,否則你的創(chuàng)建進度,會一直被卡住。
5.2 項目結(jié)構(gòu)
將項目先拖入VSCode,看下目錄結(jié)構(gòu)。自動創(chuàng)建完成的項目中,我們看到已經(jīng)自帶了Android,IOS相關(guān)的運行環(huán)境。
入口主文件是main.dart. 可以打開來先熟悉下,暫時不了解沒關(guān)系,后面再講。
還有一個重要的文件是pubspec.yaml ,是項目的配置文件,這個后續(xù)也會做修改。
5.3 啟動模擬器
點擊VSCode右下角的模擬器,啟動模擬器。(VSCode會自動找到Android環(huán)境、IOS環(huán)境下的模擬器,以及真機環(huán)境)
5.4 啟動項目APP
選中Main.dart, 點擊Debug-> Start Debugging?, 項目就會啟動調(diào)試,并在模擬器里運行。
5.5 簡化版的Hello World
講道理,Flutter一上來就用StatefulWidget做一個自增的Demo,其實是對新手不太友好。 我還是喜歡循序漸進,先刪掉那些復(fù)雜的自增邏輯,我們基于StatelessWidget 只做一個最簡單的靜態(tài)頁面顯示。(什么是StatefulWidget 和StatelessWidget?后面會說)
main.dart
import 'package:flutter/material.dart';void main() => runApp(MyApp());class MyApp extends StatelessWidget {// This widget is the root of your application.@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',home: MyHomePage(),);} }class MyHomePage extends StatelessWidget{@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("我是Title"),),body: Center(child: Text('Hello World',)));} }在上面的代碼中,可以清楚看到,最簡單的頁面的層級關(guān)系:
MaterialApp -> MyHomePage -> Scaffold -> body ->?Center -> Text
**Scaffold是啥?**他是Flutter的頁面腳手架,你可以當(dāng)HTML頁面一樣去理解,不同的是,他除了Body以外,還提供appBar頂部TitleBar、bottomNavigationBar底部導(dǎo)航欄等屬性。
顯示效果:
這是最簡單的頁面,沒有交互,只有顯示,但是實際業(yè)務(wù)場景中,是不太可能都是這種頁面的,頁面上的數(shù)據(jù)一般都是來自接口返回,然后再在頁面上進行動態(tài)的渲染。 此時,就需要使用使用帶狀態(tài)的StatefulWidget了
5.6 給頁面加上狀態(tài)
給自己一個需求,按鈕點擊時,修改頁面上顯示的文字“Hello World” 變成“You Click Me”
import 'package:flutter/material.dart';void main() => runApp(MyApp());class MyApp extends StatelessWidget {// This widget is the root of your application.@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',home: MyHomePage(),);} }class MyHomePage extends StatefulWidget{@overrideMyHomePageState createState() => MyHomePageState(); }class MyHomePageState extends State<MyHomePage>{var msg="Hello World"; //msg默認(rèn)文字@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("我是Title"),),body: Center(child:Column(children:<Widget>[Text(msg), //根據(jù)變量值,顯示文字FlatButton(color: Colors.blue,textColor: Colors.white,//點擊按鈕,修改msg的文字onPressed: () {setState(() {this.msg="You Click ME";});},child: Text("Click ME",style: TextStyle(fontSize: 20.0),),)])));} }執(zhí)行效果:
上面最關(guān)鍵的一段代碼就是這個:
onPressed: () {setState(() {this.msg="You Click ME";});},相信寫過小程序的同學(xué),對這個 setState 還是很眼熟的 _
5.7 小結(jié)一下
StatelessWidget:無狀態(tài)變更,UI靜態(tài)固化的Widget, 頁面渲染性能更高。
StatefulWidget:因狀態(tài)變更可以導(dǎo)致UI變更的的Widget,涉及到數(shù)據(jù)渲染場景,都使用StatefulWidget。
為啥要分兩個? StatelessWidget擁有的功能,StatefulWidget都有了啊?
答案只有一個:性能、性能、性能
在StatefulWidget里,因為要維護狀態(tài),他的生命周期比StatelessWidget更復(fù)雜,每次執(zhí)行setState,都會觸發(fā)
window.scheduleFrame() 導(dǎo)致整個頁面的widget被刷新,性能就會降低。
使用過小程序的同學(xué)在這點上應(yīng)該有體會,在小程序的官方文檔中,會強烈建議減少setData的使用頻率,以避免性能的下降。 只不過flutter更是激進,推出了StatelessWidget,并直接在該Widget里砍掉了setState的使用。
頁面結(jié)構(gòu)關(guān)系如下:
6.路由
實際的項目,是有多個不同的頁面的,頁面之間的跳轉(zhuǎn),就要用到路由了。 我們增加一個list頁面,點擊Home頁的“Click Me”按鈕,跳轉(zhuǎn)到列表頁list。
6.1 單個頁面的跳轉(zhuǎn)
增加list.dart
import 'package:flutter/material.dart';class ListPage extends StatelessWidget {@overrideWidget build(BuildContext context) {//定義列表widget的listList<Widget> list=<Widget>[];//Demo數(shù)據(jù)定義var data=[{"id":1,"title":"測試數(shù)據(jù)AAA","subtitle":"ASDFASDFASDF"},{"id":2,"title":"測試數(shù)據(jù)bbb","subtitle":"ASDFASDFASDF"},{"id":3,"title":"測試數(shù)據(jù)ccc","subtitle":"ASDFASDFASDF"},{"id":4,"title":"測試數(shù)據(jù)eee","subtitle":"ASDFASDFASDF"},];//根據(jù)Demo數(shù)據(jù),構(gòu)造列表ListTile組件listfor (var item in data) {print(item["title"]);list.add( ListTile( title: Text(item["title"],style: TextStyle(fontSize: 18.0) ),subtitle: Text(item["subtitle"]),leading: Icon( Icons.fastfood, color:Colors.orange ),trailing: Icon(Icons.keyboard_arrow_right)));}//返回整個頁面return Scaffold(appBar: AppBar(title: Text("List Page"),),body: Center(child: ListView(children: list,)),);} }在main.dart增加list頁面的引入
import 'list.dart';修改Home頁的按鈕事件,增加Navigator.push跳轉(zhuǎn)
FlatButton(color: Colors.blue,textColor: Colors.white,onPressed: () { Navigator.push(context, MaterialPageRoute(builder:(context) {return ListPage();}));},child: Text("Click ME",style: TextStyle(fontSize: 20.0) ),)核心方法就是:Navigator.push(context,MaterialPageRoute)
跳轉(zhuǎn)示例:
6.2 更多頁面跳轉(zhuǎn)使用路由表
在MaterialApp中,有一個屬性是routes,我們可以對路由進行命名,這樣跳轉(zhuǎn)的時候,只需要使用對應(yīng)的路由名字即可,如:Navigator.pushNamed(context, RouterName)。點擊兩個不同的按鈕,分別跳轉(zhuǎn)到ListPage,和Page2去。
Main.dart修改一下如下:
import 'package:flutter/material.dart'; import 'list.dart'; import 'page2.dart';void main() => runApp(MyApp());class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',//路由表定義routes:{"ListPage":(context)=> ListPage(),"Page2":(context)=> Page2(),},home: MyHomePage(),);} }class MyHomePage extends StatefulWidget{@overrideMyHomePageState createState() => MyHomePageState(); }class MyHomePageState extends State<MyHomePage>{@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("我是Title"),),body: Center(child:Column(children:<Widget>[RaisedButton(child: Text("Clikc to ListPage" ),onPressed: () {//根據(jù)命名路由做跳轉(zhuǎn)Navigator.pushNamed(context, "ListPage");},),RaisedButton(child: Text("Click to Page2" ),onPressed: () {//根據(jù)命名路由做跳轉(zhuǎn)Navigator.pushNamed(context, "Page2");},)])));} }示例:
當(dāng)我們有了路由以后,就可以開始在一個項目里用不同的頁面,去學(xué)習(xí)不同的功能了。
6.3 路由傳參
列表頁跳轉(zhuǎn)到詳情頁,需要路由傳參,這個在flutter體系里,又是怎么做的呢?
首先,在main.dart里,增加詳情頁DedailPage的路由配置
//路由表定義routes:{"ListPage":(context)=> ListPage(),"Page2":(context)=> Page2(),"DetailPage":(context)=> DetailPage(), //增加詳情頁的路由配置},并修改ListPage里L(fēng)istTile的點擊事件,增加路由跳轉(zhuǎn)傳參,這里是將整個item數(shù)據(jù)對象傳遞
ListTile( title: Text(item["title"],style: TextStyle(fontSize: 18.0) ),subtitle: Text(item["subtitle"]),leading: Icon( Icons.fastfood, color:Colors.orange ),trailing: Icon(Icons.keyboard_arrow_right),onTap:(){//點擊的時候,進行路由跳轉(zhuǎn)傳參Navigator.pushNamed(context, "DetailPage", arguments:item);},)詳情頁DetailPage里,獲取傳參并顯示
import 'package:flutter/material.dart'; class DetailPage extends StatelessWidget {@overrideWidget build(BuildContext context) {//獲取路由傳參final Map args = ModalRoute.of(context).settings.arguments;return Scaffold(appBar: AppBar(title: Text("Detail Page"),),body: new Column(children: <Widget>[Text("我是Detail頁面"),Text("id:${args['id']}" ),Text("id:${args['title']}"),Text("id:${args['subtitle']}")],));} }Demo效果:
7.widget
Flutter提供了很多默認(rèn)的組件,而每個組件的都繼承自widget 。 在Flutter眼里:一切都是widget。 這句看起來是不是很熟悉? 還記得在webpack里,一切都是module嗎? 類似的還有java的一切都是對象。貌似任何一個技術(shù),最后都是用哲學(xué)作為指導(dǎo)思想。
widget,作為可視化的UI組件,包含了顯示UI、功能交互兩部分。大的widget,也可以由多個小的widget組合而成。
常用的widget組件:
7.1 Text
Demo:
Text("Hello world",style: TextStyle(fontSize: 50,fontWeight: FontWeight.bold,color:Color(0xFF0000ff))),Text的樣式,來自另一個widget:TextStyle。 而TextStyle里的color,又是另一個widget Color的實例。
如果用flutter的縮進的方法,看起來確實有點丑陋,習(xí)慣寫CSS的前端同學(xué),可以看看下面的風(fēng)格:
Text( "Hello world", style: TextStyle( fontSize: 50,fontWeight: FontWeight.bold,color:Color(0xFF0000ff) ) )寫成一行,是不是就順眼多了?這算前端惡習(xí)嗎?_
7.2 Button
對于flutter來說,Button就提供了很多種,我們來看看他們的區(qū)別:
RaisedButton: 凸起的按鈕
FlatButton:扁平化按鈕
OutlineButton:帶邊框按鈕
IconButton:帶圖標(biāo)按鈕
按鈕測試頁dart:
import 'package:flutter/material.dart';class ButtonPage extends StatelessWidget {@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("Button Page"),),body: Column(children: <Widget>[RaisedButton(child: Text("我是 RaiseButton" ),onPressed: () {},),FlatButton(child: Text("我是 FlatButton" ),color: Colors.blue,onPressed: () {},),OutlineButton(child: Text("我是 OutlineButton" ),textColor: Colors.blue,onPressed: () {},),IconButton(icon: Icon(Icons.add),onPressed: () {},) ]));} }Demo:
項目中要用哪個,就各取所需吧~
7.3 Container
Container是非常常用的一個widget,他一般是用作一個容器。我們先來看看他的基礎(chǔ)屬性,順便可以想想他像HTML里的啥?
基礎(chǔ)屬性:width,height,color,child
body: Center(child: Container(color: Colors.blue,width: 200,height: 200,child: Text("Hello Container ",style:TextStyle(fontSize: 20,color: Colors.white)),))Padding
我們也可以不設(shè)置寬高,用padding在內(nèi)部撐開增加留白:
Container(color: Colors.blue,padding: EdgeInsets.all(30),child: Text("Hello Container ",style:TextStyle(fontSize: 20,color: Colors.white)),)Margin
我們還可以使用margin,在容器的外部撐開增加偏移量,
Container(color: Colors.blue,padding: EdgeInsets.all(30),margin: EdgeInsets.only(left: 150,top: 0,right: 0,bottom: 0),child: Text("Hello Container ",style:TextStyle(fontSize: 20,color: Colors.white)),)Transform
我們還可以給這個矩形,使用tansform做一些變化,比如,旋轉(zhuǎn)一個角度
Container(color: Colors.blue,padding: EdgeInsets.all(30),child: Text("Hello Container ",style:TextStyle(fontSize: 20,color: Colors.white)),transform: Matrix4.rotationZ(0.5))看到這里,好多前端同學(xué)要說了,好熟悉啊。 對,他就是很像Html里的一個東西:DIV,你確實可以對應(yīng)的去加強理解。
7.4 Image
網(wǎng)絡(luò)圖片加載
使用NetworkImage,可以做網(wǎng)絡(luò)圖片的加載:
child:Image(image: NetworkImage("https://mat1.gtimg.com/pingjs/ext2020/qqindex2018/dist/img/qq_logo_2x.png"),width: 200.0,)本地圖片加載
加載本地圖片,就稍微復(fù)雜一些,首先要把圖片的路徑配置,加入到之前說過的pubspec.yaml配置文件里去:
加載本地圖片時使用AssetImage:
child:Image(image: AssetImage("assets/images/logo.png"),width: 200.0,)也可以使用簡寫:
Image.asset("assets/images/logo.png",width:200.0)flutter提供的組件很多,這里就不一一舉例說明,有興趣的還是建議大家去看API:https://api.flutter.dev/
8.布局
我們已經(jīng)了解了這么多組件,那么怎么繪制一個完整的頁面呢? 這就到了頁面布局的部分了。
8.1 Row & Column & Center 行列軸布局
字面意義也很好理解,行布局、列布局、居中布局,這些布局對于Flutter來說,也都是一個個的widget。
區(qū)別在于,row、column 是有多個children的widget, 而Center是只有 1個child的 widget。
Row(children:<Widget>[]) Column(children:<Widget>[]) Center(child:Text("Hello"))8.2 Align 角定位布局
我們常常在Container里,需要顯示的內(nèi)容在左上角,左下角,右上角,右下角。 在html時代,使用CSS可以很容易的實現(xiàn),但是flutter里,必須依賴Align 這個定位的Widget
右下角定位示例:
child: Container(color: Colors.blue,width: 300,height: 200,child: Align(alignment: Alignment.bottomRight,child:Text("Hello Align ",style:TextStyle(fontSize: 20,color: Colors.white)),))顯示效果:
Alignment提供了多種定位供選擇,還算是很貼心的。
8.3 Stack & Positioned 絕對定位
當(dāng)然還有絕對定位的需求,這在css里,使用position:absolute就搞定了,但是在flutter里,需要借助stack+ positioned兩個widget一起組合使用。
Stack: 支持元素堆疊
Positioned:支持絕對定位
8.4 Flex & Expanded 流式布局
Flex流式布局作為前端同學(xué)都熟悉,之前講過的Row,Column,其實都是繼承自Flex,也屬于流式布局。
如果軸向不確定,使用Flex,通過修改direction的值設(shè)定軸向
如果軸向已確定,使用Row,Column,布局更簡潔,更有語義化
Flex測試頁:
class FlexPage extends StatelessWidget {@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("Flex Page"),),body: Flex(direction: Axis.horizontal,children: <Widget>[Container(width: 30,height: 100,color: Colors.blue,),Expanded(flex: 1,child: Container(height: 100.0,color: Colors.red,),),Expanded(flex: 1,child: Container(height: 100.0,color: Colors.green,),),],),);} }示例中,軸向橫向排列,最左邊一個固定寬度的Container,右邊兩個Expanded,各自占剩下的寬度的一半。
9.動畫
Flutter既然說了,一切都是Widget,包括動畫實現(xiàn),也是一個Widget。 我們還是看一個示例
9.1 簡單動畫:淡入淡出:
使用flutter提供的現(xiàn)成的Widget:
import 'package:flutter/material.dart';class AnimatePage extends StatefulWidget {_AnimatePage createState()=> _AnimatePage(); } class _AnimatePage extends State<AnimatePage> {bool _visible=true;@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("Animate Page"),),body: Center(child: Column(children: <Widget>[AnimatedOpacity(opacity: _visible ? 1.0:0.0,duration: Duration(milliseconds: 1000),child: Image.asset("assets/images/logo.png"),),RaisedButton(child: Text("顯示隱藏"),onPressed: (){setState(() {_visible=!_visible;});},),],),) );} }其中的AnimatedOpacity就是動畫透明度變化的的Widget,而被透明度控制變化的Image則是AnimatedOpacity的子元素。這個和以往前端寫動畫的方式,就完全不一樣了,需要改變一下思維方式。
Demo效果
9.2 復(fù)雜一些的動畫:放大縮小
當(dāng)寫復(fù)雜一些動畫的時候,沒有對應(yīng)的widget組件,就需要自己使用Animation,和AnimationController,以及Tween來組合。
Animation: 保存動畫的值和狀態(tài)
AnimationController: 控制動畫,包含:啟動forward()、停止stop()、反向播放reverse()等方法
Tween: 提供begin,end作為動畫變化的取值范圍
Curve:設(shè)置動畫使用曲線變化,如非勻速動畫,先加速,后減速等的設(shè)定。
動畫示例:
class AnimatePage2 extends StatefulWidget {_AnimatePage createState()=> _AnimatePage(); } class _AnimatePage extends State<AnimatePage2> with SingleTickerProviderStateMixin {Animation<double> animation;AnimationController controller;initState() {super.initState();controller = AnimationController(duration: Duration(seconds: 3), vsync: this);//使用彈性曲線,數(shù)據(jù)變化從0到300animation = CurvedAnimation(parent: controller, curve: Curves.bounceIn);animation = Tween(begin: 0.0, end: 300.0).animate(animation)..addListener(() {setState(() {});});//啟動動畫(正向執(zhí)行)controller.forward();}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("Animate Page"),),body: Center(child: Image.asset("assets/images/logo.png",width: animation.value, height: animation.value),) ); }dispose() {//路由銷毀時需要釋放動畫資源controller.dispose();super.dispose();}}很重要的一點,在路由銷毀的時候,需要釋放動畫資源,否則容易導(dǎo)致內(nèi)存泄漏。
顯示Demo:
10.http請求
做業(yè)務(wù)邏輯,總離不開http請求,接下來,就來看下flutter的http請求是如何做的。
10.1 HttpClient
httpClient在 dart:io庫中,不需要引入第三方庫就可以使用,示例代碼如下:
使用示例
import 'dart:convert'; import 'dart:io';Future _getByHttpClient() async{//接口地址const url="https://www.demo.com/api";//定義httpClientHttpClient client = new HttpClient();//定義requestHttpClientRequest request = await client.getUrl(Uri.parse(url));//定義reponseHttpClientResponse response = await request.close();//respinse返回的數(shù)據(jù),是字符串String responseBody = await response.transform(utf8.decoder).join();//關(guān)閉httpClientclient.close();//字符串需要轉(zhuǎn)化為JSONvar json= jsonDecode(responseBody);return json;}總的看起來,代碼還是挺繁瑣的,使用起來并不方便。
10.2 http
這是Dart.dev提供的第三方類庫,地址:https://pub.dev/packages/http
需要先在pubspec.yaml里添加類庫應(yīng)用
dependencies:flutter:sdk: flutterjson_annotation: ^2.0.0http: ^0.12.0+2使用示例:
Future _getByDartHttp() async {// 接口地址const url="https://www.demo.com/api";//獲取接口的返回值final response = await http.get(url);//接口的返回值轉(zhuǎn)化為JSONvar json = jsonDecode(response.body); return json; }這種寫法,比上面的httpClient簡潔了許多。
Dio
國內(nèi)使用最廣泛的,還是flutterchina在github上提供的Dio第三方庫,目前Star達到了5800多個。
官網(wǎng)地址:https://github.com/flutterchina/dio
使用Dio,因為是第三方庫,所以同樣要先在 pubspec.yaml 添加第三方庫引用。
dependencies:flutter:sdk: flutterjson_annotation: ^2.0.0dio: 2.1.16使用示例:
import 'package:dio/dio.dart';Future _getByDio() async{// 接口地址const url="https://www.demo.com/api";//定義 Dio實例Dio dio = new Dio();//獲取dio返回的ResponseResponse response = await dio.get(url);//返回值轉(zhuǎn)化為JSONvar json=jsonDecode(response.data);return json; }接口調(diào)用也是比httpclient簡單很多,可能由于fluterchina在他的官方教程里,極力推薦這個dio庫,所以目前這個第三方庫的使用情況最為廣泛。和Dart.dev的http不同的是,他需要new一個Dio的實例,在創(chuàng)建實例的時候,還可以傳入更多的擴展配置參數(shù)。
BaseOptions options = new BaseOptions(baseUrl: "https://www.xx.com/api",connectTimeout: 5000,receiveTimeout: 3000, ); Dio dio = new Dio(options);11.吐吐槽
學(xué)習(xí)Flutter的過程中,其實還是有很多坎坷和需要吐槽的地方。
11.1 墻
因為有墻在,所以在配置flutter,或者下載flutter插件和第三方庫的時候,需要墻內(nèi)外來回切換。
11.2 組件過度設(shè)計
提供的各種widget組件很多,但是真正核心的組件、常用的組件,也就哪些。 比如Flex 和column、row的關(guān)系,比如,Tween 與IntTween,ColorTween,SizeTween等20多個Tween子類之間的關(guān)系,你需要花很大的精力,去看每個具體子類的實現(xiàn)差別。
11.3 嵌套太多不適應(yīng)
因為嵌套層級很多,而且布局、動畫、功能都在一起,第一次上手Flutter和Dart,這種嵌套關(guān)系讓人很暈菜,這個只能去慢慢克服。 另外,多開發(fā)自定義的組件,可以讓嵌套關(guān)系看起來清晰一些。
11.4 布局修改會導(dǎo)致嵌套關(guān)系修改
前端的html+css分離世界里,不改變嵌套關(guān)系,修改CSS就可以調(diào)整布局。 但是在Flutter里因為布局也是嵌套關(guān)系,這就導(dǎo)致必須去改變嵌套關(guān)系。 要讓嵌套更簡單變動影響更小,頁面拆分成子組件變得尤為重要。
11.5 Dart語言升級
沒錯,語言升級也會導(dǎo)致學(xué)習(xí)的困擾,外面的資料新舊都有,比如有些是 new Text() ,有些直接是Text() ,新手上路會很暈菜。 其實這都是Dart語言升級導(dǎo)致的,記住Dart升級2.X以后,都不使用new了。感興趣的可以自己去看下Dart的升級變更說明。
11.6 不能熱更新
年中的時候,Google官方宣布flutter暫不官方支持熱更新,但是閑魚團隊已經(jīng)有了自己的熱更新方案。 關(guān)于熱更新,只能靜觀其變了。 性能、開發(fā)效率、熱更新,總是要有取舍的。即使是閑魚團隊,熱更新也是付出了一點點性能下降的代價的,這是你選擇flutter的初衷嗎?還是那句話:權(quán)衡得失。
12.結(jié)語
隨著 9 月谷歌發(fā)布 Flutter1.9 以及flutter for web,Flutter的組件化思路,使得一份代碼跨三端變成可能,相信Flutter的未來會更加廣闊。
這不是一篇教程,只是在學(xué)習(xí)Flutter過程中的一點體驗和經(jīng)歷,也因為時間關(guān)系,研究并不深入,如有疏漏,還請不吝賜教。
學(xué)習(xí)分享,共勉
題外話,畢竟我在三星小米工作多年,深知技術(shù)改革和創(chuàng)新的方向,Flutter作為跨平臺開發(fā)技術(shù)、Flutter以其美觀、快速、高效、開放等優(yōu)勢迅速俘獲人心,但很多FLutter興趣愛好者進階學(xué)習(xí)確實資料,今天我把我搜集和整理的這份學(xué)習(xí)資料分享給有需要的人,若有關(guān)Flutter學(xué)習(xí)進階可以與我在Flutter跨平臺開發(fā)終極之選交流群一起討論交流。下載地址:https://shimo.im/docs/yTD3t8Pjq3XJtGv8
下載地址:https://shimo.im/docs/yTD3t8Pjq3XJtGv8
總結(jié)
以上是生活随笔為你收集整理的腾讯技术团队整理,年度好文轻松彻底入门 Flutter,秒变大前端的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 二、网络编程
- 下一篇: x86、i386、IA-32、amd64