轻松 Flutter 入门,秒变大前端
本文作者:dickma,騰訊 IEG 前端開發工程師
本文不是Flutter的教程,只是對 Flutter 的技術特性,做了一些略全面的入門級的介紹,如果你聽說過Flutter,想去了解他,但是又不想去翻厚厚的API,那么本文就是為你準備的。
隨著純客戶端到Hybrid技術,到RN&Weex,再到如今的Flutter技術,客戶端實現技術不斷前進。在之前的一個APP項目中,因為歷史原因當時選擇了weex,隨著使用的不斷深入,我們逐漸發現了weex的渲染性能問題已經成為一個隱患和瓶頸。隨著Flutter技術的不斷成熟和流行,Flutter的良好的跨平臺性和高性能優點,不斷吸引著我們,他是否可以幫助我們解決這些問題呢?因此,才有本文這篇技術嘗試。
(本文包含以下內容,閱讀完需要約18分鐘)
1.Flutter是啥玩意兒?
2.移動端跨平臺技術對比
2.1 H5+原生APP
2.2 RN&Weex
2.3 Flutter
3.Dart語言
4.環境配置
5.Hello World
5.1 創建項目
5.2 項目結構
5.3 啟動模擬器
5.4 啟動項目APP
5.5 簡化版的Hello World
5.6 給頁面加上狀態
5.7 小結一下
6.路由
6.1 單個頁面的跳轉
6.2 更多頁面跳轉使用路由表
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 復雜一些的動畫:放大縮小
10.http請求
10.1 HttpClient
10.2 http
10.3 Dio
11.吐吐槽
11.1 墻
11.2 組件過度設計
11.3 嵌套太多不適應
11.4 布局修改會導致嵌套關系修改
11.5 Dart語言升級
11.6 不能熱更新
12.結語
1.Flutter是啥玩意兒?
Flutter是谷歌的移動UI框架,可以快速在iOS和Android上構建高質量的原生用戶界面。
具有跨平臺開發特性,支持IOS、Android、Web三端。
熱重載特性大大提高了開發效率
自繪UI引擎和編譯成原生代碼的方式,使得系統的運行時的高性能成為了可能
使用Dart語言,目前已經支持同時編譯成Web端代碼,
到底值不值得跟進Flutter技術呢?還是看下Flutter,Weex,ReactNative的搜索指數對比,大概就知道這個行業趨勢了。
藍色是Flutter,可以看出上升勢頭非常強勁。苦逼的前端就是這樣,你不跟潮流,潮流就會把你拋棄。
2.移動端跨平臺技術對比
為啥會有Flutter這種東西? 他的原理是什么?他是怎么做到高性能的?要明白這些問題,我們不得不從幾種移動端跨平臺技術的對比講起。
2.1 H5+原生APP
圖片來源于網絡技術門檻最低,接入速度最快,熱更新最方便的,自然就是H5方式。APP中提供一個Webview使用H5頁面的Http直連。APP和H5可以相互獨立開發,JS使用Bridge與原生進行數據通信,顯示界面依賴Webview的瀏覽器渲染。但是帶來的問題也很明顯,因為是需要遠程直連,那么初次打開H5頁面,會有瞬間的白屏,并且Webview本身會有至少幾十M的內存消耗。
當然,作為前端開發人員,在H5方式可以使用SPA單頁面、懶加載、離線H5等各種前端優化手段進行性能優化,以使得H5的表現更接近原生。但是首次的瞬間白屏和內存,Bridge的通信效率低下,始終是被技術框架給局限住了。
2.2 RN&Weex
圖片來源于網絡由于H5的那些弊端,愛折騰的前端工程師,祭出了RN、Weex兩個大殺器, 使用原生去解析RN、Weex的顯示配置,顯示層、邏輯層都直接與原生數據通信。因為拋棄了瀏覽器,自然渲染性能、執行性能都提升了一大截。
但是,每次遇到顯示的變更,JS都還會通過Bridge和原生轉一道再做渲染的調整,所以Bridge就最后成為了性能的瓶頸。在實際項目中,特別是做一些大量復雜動畫處理的時候,由于渲染部分需要頻繁通信,性能問題變得尤為突出。有興趣的同學可以去看看BindingX,里面有關于動畫中數據通信效率低下導致動畫幀率低下的詳細說明。
2.3 Flutter
圖片來源于網絡不得不佩服Google開發人員的想象力,為了達到極致性能,Flutter更前進了一步,Flutter代碼編譯完成以后,直接就是原生代碼,并且使用自繪UI引擎原生方式做渲染。Flutter依賴一個Skia 2D圖形化引擎。Skia也是Android平臺和Chrome的底層渲染引擎,所以性能方面完全不用擔心。因為使用Dart做AOT編譯成原生,自然也比使用解釋性的JS在V8引擎中執行性能更快,并且因為去掉Bridge,沒有了繁瑣的數據通信和交互,性能就更前進了一步。
3.Dart語言
學習Flutter,得先了解Dart。Dart語言曾經雄心勃勃的要替換Javascript, 但是發布的時機正好遇到JS的飛速發展,于是就逐漸沉寂,直到配合Flutter的發布,才又重新煥發了生機。
在最近2019年9月的一次Google開發者大會中,伴隨著Flutter1.9的發布,目前的Dart也同時更新到了2.5版本, 提供了機器學習和對C跨平臺調用的能力。總體來說,Dart語法,對于前端同學,上手還是很容易的,風格很像。
關于Dart語法,請移步傳送門:https://dart.dev/samples
4.環境配置
無論學什么新語言,首先都是環境配置。由于Flutter出自Google,所以,,如果在公司內安裝,你還需要一個方便的代理切換工具, 比如:Proxifier 。
安裝教程,參照官網:https://flutter.dev/docs/get-started/install
Flutter支持多種編輯器如:Android Studio , XCode。但是既然作為支持跨雙端的開發,個人還是推薦使用 VSCode。
VSCode安裝完成后,需要安裝Flutter插件,和Dart插件. 在擴展窗口里,搜索Flutter,和Dart,點擊“Install”即可,非常方便。
如果安裝不上去,記得開啟下代理,代理配置如下:
5.Hello World
作為一個偉大的程序員,第一行代碼總是從Hello World開始。^_^
5.1 創建項目:
方法1:直接使用命令創建:
flutter?create?projectname方法2:使用VSCode創建:
View -> Command Palette -> Flutter:New Project 即可
注意請先打開代理,否則你的創建進度,會一直被卡住。
5.2 項目結構
將項目先拖入VSCode,看下目錄結構。自動創建完成的項目中,我們看到已經自帶了Android,IOS相關的運行環境。
入口主文件是main.dart. 可以打開來先熟悉下,暫時不了解沒關系,后面再講。
還有一個重要的文件是pubspec.yaml ,是項目的配置文件,這個后續也會做修改。
5.3 啟動模擬器
點擊VSCode右下角的模擬器,啟動模擬器。(VSCode會自動找到Android環境、IOS環境下的模擬器,以及真機環境)
5.4 啟動項目APP
選中Main.dart, 點擊Debug-> Start Debugging , 項目就會啟動調試,并在模擬器里運行。
5.5 簡化版的Hello World
講道理,Flutter一上來就用StatefulWidget做一個自增的Demo,其實是對新手不太友好。我還是喜歡循序漸進,先刪掉那些復雜的自增邏輯,我們基于StatelessWidget 只做一個最簡單的靜態頁面顯示。(什么是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',)));}?? }在上面的代碼中,可以清楚看到,最簡單的頁面的層級關系:
MaterialApp -> MyHomePage -> Scaffold -> body -> Center -> Text
Scaffold是啥?他是Flutter的頁面腳手架,你可以當HTML頁面一樣去理解,不同的是,他除了Body以外,還提供appBar頂部TitleBar、bottomNavigationBar底部導航欄等屬性。
顯示效果:
這是最簡單的頁面,沒有交互,只有顯示,但是實際業務場景中,是不太可能都是這種頁面的,頁面上的數據一般都是來自接口返回,然后再在頁面上進行動態的渲染。此時,就需要使用使用帶狀態的StatefulWidget了
5.6 給頁面加上狀態
給自己一個需求,按鈕點擊時,修改頁面上顯示的文字“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默認文字@overrideWidget?build(BuildContext?context)?{return?Scaffold(appBar:?AppBar(title:?Text("我是Title"),),body:?Center(child:Column(children:<Widget>[Text(msg),?//根據變量值,顯示文字FlatButton(color:?Colors.blue,textColor:?Colors.white,//點擊按鈕,修改msg的文字onPressed:?()?{setState(()?{this.msg="You?Click?ME";});},child:?Text("Click?ME",style:?TextStyle(fontSize:?20.0),),)])));}??}執行效果:
上面最關鍵的一段代碼就是這個:
?onPressed:?()?{setState(()?{this.msg="You?Click?ME";});},相信寫過小程序的同學,對這個 setState 還是很眼熟的 ^_^
5.7 小結一下
StatelessWidget:無狀態變更,UI靜態固化的Widget, 頁面渲染性能更高。
StatefulWidget:因狀態變更可以導致UI變更的的Widget,涉及到數據渲染場景,都使用StatefulWidget。
為啥要分兩個?StatelessWidget擁有的功能,StatefulWidget都有了啊?
答案只有一個:性能、性能、性能
在StatefulWidget里,因為要維護狀態,他的生命周期比StatelessWidget更復雜,每次執行setState,都會觸發
使用過小程序的同學在這點上應該有體會,在小程序的官方文檔中,會強烈建議減少setData的使用頻率,以避免性能的下降。只不過flutter更是激進,推出了StatelessWidget,并直接在該Widget里砍掉了setState的使用。
頁面結構關系如下:
6.路由
實際的項目,是有多個不同的頁面的,頁面之間的跳轉,就要用到路由了。我們增加一個list頁面,點擊Home頁的“Click Me”按鈕,跳轉到列表頁list。
6.1 單個頁面的跳轉
增加list.dart
import?'package:flutter/material.dart';class?ListPage?extends?StatelessWidget?{@overrideWidget?build(BuildContext?context)?{//定義列表widget的listList<Widget>?list=<Widget>[];//Demo數據定義var?data=[{"id":1,"title":"測試數據AAA","subtitle":"ASDFASDFASDF"},{"id":2,"title":"測試數據bbb","subtitle":"ASDFASDFASDF"},{"id":3,"title":"測試數據ccc","subtitle":"ASDFASDFASDF"},{"id":4,"title":"測試數據eee","subtitle":"ASDFASDFASDF"},];//根據Demo數據,構造列表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跳轉
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)
跳轉示例:
6.2 更多頁面跳轉使用路由表
在MaterialApp中,有一個屬性是routes,我們可以對路由進行命名,這樣跳轉的時候,只需要使用對應的路由名字即可,如:Navigator.pushNamed(context, RouterName)。點擊兩個不同的按鈕,分別跳轉到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:?()?{//根據命名路由做跳轉Navigator.pushNamed(context,?"ListPage");},),RaisedButton(child:?Text("Click?to?Page2"?),onPressed:?()?{//根據命名路由做跳轉Navigator.pushNamed(context,?"Page2");},)])));}??}示例:
當我們有了路由以后,就可以開始在一個項目里用不同的頁面,去學習不同的功能了。
6.3 路由傳參
列表頁跳轉到詳情頁,需要路由傳參,這個在flutter體系里,又是怎么做的呢?
首先,在main.dart里,增加詳情頁DedailPage的路由配置
//路由表定義routes:{"ListPage":(context)=>?ListPage(),"Page2":(context)=>?Page2(),"DetailPage":(context)=>?DetailPage(),?//增加詳情頁的路由配置},并修改ListPage里ListTile的點擊事件,增加路由跳轉傳參,這里是將整個item數據對象傳遞
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:(){//點擊的時候,進行路由跳轉傳參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提供了很多默認的組件,而每個組件的都繼承自widget 。在Flutter眼里:一切都是widget。這句看起來是不是很熟悉?還記得在webpack里,一切都是module嗎?類似的還有java的一切都是對象。貌似任何一個技術,最后都是用哲學作為指導思想。
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的縮進的方法,看起來確實有點丑陋,習慣寫CSS的前端同學,可以看看下面的風格:
Text(?"Hello?world",?style:?TextStyle(?fontSize:?50,fontWeight:?FontWeight.bold,color:Color(0xFF0000ff)?)?)寫成一行,是不是就順眼多了?這算前端惡習嗎?^_^
7.2 Button
對于flutter來說,Button就提供了很多種,我們來看看他們的區別:
RaisedButton: 凸起的按鈕
FlatButton:扁平化按鈕
OutlineButton:帶邊框按鈕
IconButton:帶圖標按鈕
按鈕測試頁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,他一般是用作一個容器。我們先來看看他的基礎屬性,順便可以想想他像HTML里的啥?
基礎屬性: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
我們也可以不設置寬高,用padding在內部撐開增加留白:
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做一些變化,比如,旋轉一個角度
Container(color:?Colors.blue,padding:?EdgeInsets.all(30),child:?Text("Hello?Container?",style:TextStyle(fontSize:?20,color:?Colors.white)),transform:?Matrix4.rotationZ(0.5))看到這里,好多前端同學要說了,好熟悉啊。對,他就是很像Html里的一個東西:DIV,你確實可以對應的去加強理解。
7.4 Image
網絡圖片加載
使用NetworkImage,可以做網絡圖片的加載:
child:Image(image:?NetworkImage("https://mat1.gtimg.com/pingjs/ext2020/qqindex2018/dist/img/qq_logo_2x.png"),width:?200.0,)??本地圖片加載
加載本地圖片,就稍微復雜一些,首先要把圖片的路徑配置,加入到之前說過的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.布局
我們已經了解了這么多組件,那么怎么繪制一個完整的頁面呢?這就到了頁面布局的部分了。
8.1 Row & Column & Center 行列軸布局
字面意義也很好理解,行布局、列布局、居中布局,這些布局對于Flutter來說,也都是一個個的widget。
區別在于,row、column 是有多個children的widget, 而Center是只有 1個child的 widget。
?Row(children:<Widget>[])?Column(children:<Widget>[])????Center(child:Text("Hello"))8.2 Align 角定位布局
我們常常在Container里,需要顯示的內容在左上角,左下角,右上角,右下角。在html時代,使用CSS可以很容易的實現,但是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 絕對定位
當然還有絕對定位的需求,這在css里,使用position:absolute就搞定了,但是在flutter里,需要借助stack+ positioned兩個widget一起組合使用。
Stack: 支持元素堆疊
Positioned:支持絕對定位
child:Stack(children:?<Widget>[Image.network("https://ossweb-img.qq.com/upload/adw/image/20191022/627bdf586e0de8a29d5a48b86700a790.jpeg"),Positioned(top:?20,right:?10,child:Image.asset("assets/images/logo.png",width:200.0))],)8.4 Flex & Expanded 流式布局
Flex流式布局作為前端同學都熟悉,之前講過的Row,Column,其實都是繼承自Flex,也屬于流式布局。
如果軸向不確定,使用Flex,通過修改direction的值設定軸向
如果軸向已確定,使用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,包括動畫實現,也是一個Widget。我們還是看一個示例
9.1 簡單動畫:淡入淡出:
使用flutter提供的現成的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 復雜一些的動畫:放大縮小
當寫復雜一些動畫的時候,沒有對應的widget組件,就需要自己使用Animation,和AnimationController,以及Tween來組合。
Animation: 保存動畫的值和狀態
AnimationController: 控制動畫,包含:啟動forward()、停止stop()、反向播放reverse()等方法
Tween: 提供begin,end作為動畫變化的取值范圍
Curve:設置動畫使用曲線變化,如非勻速動畫,先加速,后減速等的設定。
動畫示例:
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);//使用彈性曲線,數據變化從0到300animation?=?CurvedAnimation(parent:?controller,?curve:?Curves.bounceIn);animation?=?Tween(begin:?0.0,?end:?300.0).animate(animation)..addListener(()?{setState(()?{});});//啟動動畫(正向執行)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();}}很重要的一點,在路由銷毀的時候,需要釋放動畫資源,否則容易導致內存泄漏。
顯示Demo:
10.http請求
做業務邏輯,總離不開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返回的數據,是字符串String?responseBody?=?await?response.transform(utf8.decoder).join();//關閉httpClientclient.close();//字符串需要轉化為JSONvar?json=?jsonDecode(responseBody);return?json;}?總的看起來,代碼還是挺繁瑣的,使用起來并不方便。
10.2 http
這是Dart.dev提供的第三方類庫,地址:https://pub.dev/packages/http
需要先在pubspec.yaml里添加類庫應用
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);//接口的返回值轉化為JSONvar?json?=?jsonDecode(response.body);?return?json; }這種寫法,比上面的httpClient簡潔了許多。
10.3 Dio
國內使用最廣泛的,還是flutterchina在github上提供的Dio第三方庫,目前Star達到了5800多個。
官網地址: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);//返回值轉化為JSONvar?json=jsonDecode(response.data);return?json; }接口調用也是比httpclient簡單很多,可能由于fluterchina在他的官方教程里,極力推薦這個dio庫,所以目前這個第三方庫的使用情況最為廣泛。和Dart.dev的http不同的是,他需要new一個Dio的實例,在創建實例的時候,還可以傳入更多的擴展配置參數。
BaseOptions?options?=?new?BaseOptions(baseUrl:?"https://www.xx.com/api",connectTimeout:?5000,receiveTimeout:?3000, ); Dio?dio?=?new?Dio(options);11.吐吐槽
學習Flutter的過程中,其實還是有很多坎坷和需要吐槽的地方。
11.1 墻
因為有墻在,所以在配置flutter,或者下載flutter插件和第三方庫的時候,需要墻內外來回切換。
11.2 組件過度設計
提供的各種widget組件很多,但是真正核心的組件、常用的組件,也就哪些。比如Flex 和column、row的關系,比如,Tween 與IntTween,ColorTween,SizeTween等20多個Tween子類之間的關系,你需要花很大的精力,去看每個具體子類的實現差別。
11.3 嵌套太多不適應
因為嵌套層級很多,而且布局、動畫、功能都在一起,第一次上手Flutter和Dart,這種嵌套關系讓人很暈菜,這個只能去慢慢克服。另外,多開發自定義的組件,可以讓嵌套關系看起來清晰一些。
11.4 布局修改會導致嵌套關系修改
前端的html+css分離世界里,不改變嵌套關系,修改CSS就可以調整布局。但是在Flutter里因為布局也是嵌套關系,這就導致必須去改變嵌套關系。要讓嵌套更簡單變動影響更小,頁面拆分成子組件變得尤為重要。
11.5 Dart語言升級
沒錯,語言升級也會導致學習的困擾,外面的資料新舊都有,比如有些是 new Text() ,有些直接是Text() ,新手上路會很暈菜。其實這都是Dart語言升級導致的,記住Dart升級2.X以后,都不使用new了。感興趣的可以自己去看下Dart的升級變更說明。
11.6 不能熱更新
年中的時候,Google官方宣布flutter暫不官方支持熱更新,但是閑魚團隊已經有了自己的熱更新方案。關于熱更新,只能靜觀其變了。性能、開發效率、熱更新,總是要有取舍的。即使是閑魚團隊,熱更新也是付出了一點點性能下降的代價的,這是你選擇flutter的初衷嗎?還是那句話:權衡得失。
12.結語
這不是一篇教程,只是自己在學習Flutter過程中的一點體驗和經歷,也因為時間關系,研究并不深入,如有疏漏,還請不吝賜教。
隨著Flutter1.9的發布,以及flutter for web的發布,Flutter的組件化思路,使得一份代碼跨三端變成可能,相信Flutter的未來會更加廣闊,也歡迎大家一起交流Flutter。
總結
以上是生活随笔為你收集整理的轻松 Flutter 入门,秒变大前端的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 开发一个爆款 VS Code 插件这么简
- 下一篇: 是什么能让 APP 快速精准定位到我们的