<address id="r9vd9"><address id="r9vd9"><listing id="r9vd9"></listing></address></address>

      歡迎您光臨深圳塔燈網絡科技有限公司!
      電話圖標 余先生:13699882642

      網站百科

      為您解碼網站建設的點點滴滴

      Flutter實戰詳解--高仿好奇心日報

      發表日期:2019-01 文章編輯:小燈 瀏覽次數:2002

      前言

      最近Flutter一直比較火,我也它也是非常感興趣,看了下官網的基礎教程后我決定直接上手做一個App,一是這樣學的比較快印象更加深刻,二是可以記錄其中遇到的一些坑,幫助大家少走一些彎路.本篇文章我會盡可能詳細的講到每一個點上.

      項目地址

      Github,如果覺得不錯,歡迎Star

      下載項目后報錯是因為沒有添加依賴,在pubspec.yaml文件中點擊Packages get下載依賴,有時候會在這里出現卡死的情況,可以配置一下環境變量,詳情請看修改Flutter環境變量.
      先看看效果圖吧.

      • iOS效果圖


        iOS效果圖.gif
      • Android效果圖


        Android效果圖.gif

      正題

      怎么搭建Flutter環境我就不多說了,官網上講的很詳細,還沒有搭建開發環境的可以看看這個Flutter中文網.

      1導航欄Tabbar


      這里我用到了DefaultTabController這個控件,使用DefaultTabController包裹需要用到Tab的頁面即可,它的child為Scaffold,Scaffold有個appBar屬性,在AppBar中設置具體的樣式,大家看代碼會更加清楚.相關注釋也都寫上了.

       home: new DefaultTabController( length: titleList.length, child: new Scaffold( appBar: new AppBar( elevation: 0.0,//導航欄下面那根線 title: new TabBar( isScrollable: false,//是否可滑動 unselectedLabelColor: Colors.black26,//未選中按鈕顏色 labelColor: Colors.black,//選中按鈕顏色 labelStyle: TextStyle(fontSize: 18),//文字樣式 indicatorSize: TabBarIndicatorSize.label,//滑動的寬度是根據內容來適應,還是與整塊那么大(label表示根據內容來適應) indicatorWeight: 4.0,//滑塊高度 indicatorColor: Colors.yellow,//滑動顏色 indicatorPadding: EdgeInsets.only(bottom: 1),//與底部距離為1 tabs: titleList.map((String text) {//tabs表示具體的內容,是一個數組 return new Tab( text: text, ); }).toList(), ), ), //body表示具體展示的內容 body:TabBarView(children: [News(url: 'http://app3.qdaily.com/app3/homes/index_v2/'),News(url: 'http://app3.qdaily.com/app3/papers/index/')]) , ), ), 

      大家也可以看看官網的示例Flutter官網示例

      2. 不同樣式的item

      • 樣式一



        這種布局的大概結構如下


      注意這里圖片是緊貼著右邊屏幕的,所以這里需要用到Expanded控件,用于自動填充子控件.

      • 樣式二



        這個樣式的控件布局就很簡單了,結構如下


      • 樣式三



        這個和樣式二差不多,只不過最上面多了一塊.

      這里需要注意的是,那個你猜這個圖片是堆疊在整個大圖上面的,所以需要用到Stack這個控件,其中Stack中有個屬性const FractionalOffset(double dx, double dy)用于表示子控件相對于父控件的位置

      • 樣式四



        這種樣式稍微復雜一點,結構如下


      3數據抓取

      用青花瓷抓取了好奇心數據.青花瓷使用教程

      image.png
      簡單分析一下,has_more表示是否可以加載更多,last_key用于上拉加載的時候請求用的,feeds就是每一條數據,banners就是輪播圖的信息,columns就是橫向滾動的ListView的相關數據,這個后面講.接下來就做json序列化相關的了.

      4.Json序列化

      首先在pubspec.yaml中導入

      dependencies:
      json_annotation: ^2.0.0
      dev_dependencies:
      build_runner: ^1.0.0
      json_serializable: ^2.0.0

      創建一個model.dart文件
      引入文件

      import 'package:json_annotation/json_annotation.dart';
      part 'model.g.dart';

      其中這個model.g.dart等會兒會自動生成.這里需要掌握兩個知識點

      1.@JsonSerializable() 這是表示告訴編譯器這個類是需要生成Model類的
      2,@JsonKey 由于服務器返回的部分數據名稱在Dart語言中是不被允許的,比如has_more,Dart中命名不能出現下劃線,所以就需要用到@JsonKey來告訴編譯器這個參數對于json中的哪個字段

      @JsonSerializable() class Feed { String image; int type; @JsonKey(name: 'index_type') int indexType; Post post; @JsonKey(name: 'news_list') List<News> newsList; Feed(this.image,this.type,this.post,this.indexType,this.newsList); factory Feed.fromJson(Map<String,dynamic> json) => _$FeedFromJson(json); Map<String, dynamic> toJson() => _$FeedToJson(this); } 

      好了,寫完后會報錯,因為FeedFromJsonFeedToJson沒有找到,這個時候在控制到輸入flutter packages pub run build_runner build指令后會自動生成一個moded.g.dart文件,于是在網絡請求下來數據后就可以用Feed feed = Feed.fromJson(data)這個方法來將Json中數據轉換保存在Feed這個實例中了.在model類中還有些復雜的Json嵌套,但是也都很簡單,大家看一眼應該就會了,哈哈.JSON和序列化具體教程

      5.輪播圖

      Flutter中的輪播圖我用到了Fluuter_Swiper這個組件,這里設置小圓點屬性的時候稍微麻煩了點,網上好像也沒有講到,我這里講一下.
      首先要創建DotSwiperPaginationBuilder

       DotSwiperPaginationBuilder builder = DotSwiperPaginationBuilder( color: Colors.white,//未選中圓點顏色 activeColor: Colors.yellow,//選中圓點顏色 size:7,//未選中大小 activeSize: 7,//選中圓點大小 space: 5//圓點間距 ); 

      然后在Swiper中的pagination屬性中設置它

      pagination: new SwiperPagination( builder: builder, ), 
      1. 網絡請求
        首先,展示頁面要繼承自StatefulWidget,因為需要動態更新數據和列表.
        網絡請求插件我用的Dio,非常好用.
        initState方法中請求數據表示剛加載頁面的時候進行網絡請求,請求數據方法如下
      void getData()async{ if (lastKey == '0'){ dataList = [];//下拉刷新的時候將DataList制空 } Dio dio = new Dio(); Response response = await dio.get("$url$lastKey.json"); Reslut reslut = Reslut.fromJson(response.data); if(!reslut.response.hasMore){ return;//如果沒有數據就不繼續了 } if(reslut.response.columns != null) { columnList = reslut.response.columns; } lastKey = reslut.response.lastKey;//更新lastkey setState(() { if (reslut.response.banners != null){ banners = reslut.response.banners;//給輪播圖賦值 } dataList.addAll(reslut.response.feeds);//給數據源賦值 }); } 

      因為用到了setState()方法,所以在該方法中改變了的數據會對其相應的地方進行刷新,比如設置了ListView的itemCount個數為dataList.length,如果在SetState方法中dataList.length改變了,那么ListView的itemCount樹也會自動改變并刷新ListView.

      7. 上拉刷新與加載

      Flutter中有RefreshIndicator用于下拉刷新,它有個onRefresh閉包方法,表示下拉的時候執行的方法,一般用于網絡請求.onRefresh方法如下

       Future<void> _handleRefresh() { final Completer<void> completer = Completer<void>(); Timer(const Duration(seconds: 1), () { completer.complete(); }); return completer.future.then<void>((_) { lastKey = '0'; getData(); }); } 

      下拉加載的話需要初始化一個ScrollController,將它設為ListView的controller,并對其進行監聽,當滑動到最底部的時候進行網絡請求.

      @override void initState() { url = widget.url; getData(); _scrollController.addListener(() { ///判斷當前滑動位置是不是到達底部,觸發加載更多回調 if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent) { getData(); } }); } final ScrollController _scrollController = new ScrollController(); 

      上拉加載loading框用到了flutter_spinkit插件,提供了大量的加載樣式.


      代碼如下

      ///上拉加載更多 Widget _buildProgressIndicator() { ///是否需要顯示上拉加載更多的loading Widget bottomWidget = new Row(mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ ///loading框 new SpinKitThreeBounce(color: Color(0xFF24292E)), new Container( width: 5.0, ), ]); return new Padding( padding: const EdgeInsets.all(20.0), child: new Center( child: bottomWidget, ), ); } 

      8. ListView賦值

      由于最上面有一個輪播圖,最下面有加載框,所以ListView的itemCount個數為dataList.length+2,又因為每個item之間都有一個淺灰色的風格線,所以需要用到ListView.separated,具體代碼如下:

       Widget build(BuildContext context) { return RefreshIndicator( onRefresh:(()=> _handleRefresh()), color: Colors.yellow,//刷新控件的顏色 child: ListView.separated( physics: const AlwaysScrollableScrollPhysics(), itemCount: _getListCount(),//item個數 controller: _scrollController,//用于監聽是否滑到最底部 itemBuilder: (context,index){ if(index == 0){ return SwiperWidget(context, banners);//如果是第一個,則展示banner }else if(index < dataList.length + 1){ return WidgetUtils.GetListWidget(context, dataList[index - 1]);//展示數據 }else { return _buildProgressIndicator();//展示加載loading框 } }, separatorBuilder: (context,idx){//分割線 return Container( height: 5, color: Color.fromARGB(50,183, 187, 197), ); }, ), ); } 

      9. ListView嵌套橫向滑動ListView

      這種的話也稍微復雜一點,有兩種樣式.并且到滑到最右邊的時候可以繼續請求并加載數據.




      首先來分析一下數據



      這個colunmns就是橫向滑動列表的重要數據.

      里面的id是請求參數,show_type表示列表的樣式,location表示插入的位置.而且通過抓取接口發現,當橫向列表快要展示出來的時候,才會去請求橫向列表的具體接口.

      那么思路就很清晰了,在請求獲得數據后遍歷colunmns,根據每個colunmn的location插入一個Map,如下

      data.insert(colunm.location,{'id':colunm.id,'showType':colunm.showType}); 

      ,再創建一個ColumnsListWidget類,繼承自StatefulWidget,是一個新item,在滑動到該列表的位置的時候,會將該Map數據傳給ColumnsListWidget,這個時候ColumnsListWidget就會加載數據并展示出來了,滑到最右邊的時候加載和滑到最底部加載的方法一樣,就不多說了.具體可以查看源碼,關鍵代碼如下:

      static Widget GetListWidget(BuildContext context, dynamic data) { Widget widget; if(data.runtimeType == Feed) { if (data.indexType != null) { widget = NewsListWidget(context, data); } else if (data.type == 2) { widget = ListImageTop(context, data); } else if (data.type == 0) { widget = ActivityWidget(context, data); } else if (data.type == 1) { widget = ListImageRight(context, data); } }else{ widget = ColumnsListWidget(id: data['id'],showType: data['showType'],); } 

      1.橫向ListView外需要用Flexible包裹,Flexible組件可以使Row、Column、Flex等子組件在主軸方向有填充可用空間的能力(例如,Row在水平方向,Column在垂直方向),但是它與Expanded組件不同,它不強制子組件填充可用空間。
      2.ListView初始位置用到padding: new EdgeInsets.symmetric(horizontal: 12.0),用padding: EdgeInsets.only(left: 12)的話會讓ListView和最左邊一直有條線

      10.webview加載復雜的Html字段


      獲取到網頁詳情的數據發現是Html字段,并且其中的css是url地址,試了很多Flutter加載Html的插件發現樣式都不正確,最后決定使用原生和Flutter混編,這時候發現flutter_webview_plugin這個插件是使用原生網頁的,不過它只支持加載url,于是就需要做一些修改.

      • iOS
        FlutterWebviewPlugin.m文件中的- (void)navigate:(FlutterMethodCall*)call方法中的最后一排,將[self.webview loadRequest:request]方法改為[self.webview loadHTMLString:url baseURL:nil]
      • Android
        WebViewManager.java文件中webView.loadUrl(url)方法改為webView.loadData(url, "text/html", "UTF-8"),以及下面那排的void reloadUrl(String url) { webView.loadUrl(url); }改為void reloadUrl(String url) { webView.loadData(url, "text/html", "UTF-8"); }
        由于服務器端返回的Html中的css和js文件地址是/assets/app3開頭的,所以需要替換成絕對路徑,所以要用到這個方法htmlBody.replaceAll( '/assets/app3','http://app3.qdaily.com/assets/app3')
        好了,這下就可以呈現出漂亮的網頁了.

      11.ListView嵌套GridView

      在點擊橫向滑動列表的總標題的時候,會進入到相關欄目的詳情頁,如圖



      這個ListView包含上下兩部分.上面這部分為:



      結構如下

      下面就是一個GridView,不過有時候下面會是ListView,根據shouwType字段來判斷,GridView的代碼如下:

      Widget ColumnsDetailTypeTwo(BuildContext context,List<Feed> feesList){ return GridView.count( physics: NeverScrollableScrollPhysics(), crossAxisCount: 2, shrinkWrap: true, mainAxisSpacing: 10.0, crossAxisSpacing: 15.0, childAspectRatio: 0.612, padding: new EdgeInsets.symmetric(horizontal: 20.0), children: feesList.map((Feed feed) { returnColumnsTypeTwoTile(context, feed); }).toList()); } 

      其中 childAspectRatio表示寬高比.

      圓角頭像需要用到
      CircleAvatar(backgroundImage:NetworkImage(url),),這個控件

      總結

      做了這個項目最大的感受就是界面布局是真的很方便很簡單,因為做了一遍對很多知識點也理解的更深了.如果覺得有幫助到你的話,希望可以給個 Star

      項目地址

      Github


      本頁內容由塔燈網絡科技有限公司通過網絡收集編輯所得,所有資料僅供用戶學習參考,本站不擁有所有權,如您認為本網頁中由涉嫌抄襲的內容,請及時與我們聯系,并提供相關證據,工作人員會在5工作日內聯系您,一經查實,本站立刻刪除侵權內容。本文鏈接:http://www.webpost.com.cn/17689.html
      相關APP開發
       八年  行業經驗

      多一份參考,總有益處

      聯系深圳網站公司塔燈網絡,免費獲得網站建設方案及報價

      咨詢相關問題或預約面談,可以通過以下方式與我們聯系

      業務熱線:余經理:13699882642

      Copyright ? 2013-2018 Tadeng NetWork Technology Co., LTD. All Rights Reserved.    

      国产成人精品综合在线观看