發表日期:2019-01 文章編輯:小燈 瀏覽次數:1945
最近Flutter一直比較火,我也它也是非常感興趣,看了下官網的基礎教程后我決定直接上手做一個App,一是這樣學的比較快印象更加深刻,二是可以記錄其中遇到的一些坑,幫助大家少走一些彎路.本篇文章我會盡可能詳細的講到每一個點上.
下載項目后報錯是因為沒有添加依賴,在pubspec.yaml文件中點擊Packages get下載依賴,有時候會在這里出現卡死的情況,可以配置一下環境變量,詳情請看修改Flutter環境變量.
先看看效果圖吧.
iOS效果圖
Android效果圖
怎么搭建Flutter環境我就不多說了,官網上講的很詳細,還沒有搭建開發環境的可以看看這個Flutter中文網.
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官網示例
樣式一
這種布局的大概結構如下
注意這里圖片是緊貼著右邊屏幕的,所以這里需要用到
Expanded
控件,用于自動填充子控件.
樣式二
這個樣式的控件布局就很簡單了,結構如下
樣式三
這個和樣式二差不多,只不過最上面多了一塊.
這里需要注意的是,那個你猜這個圖片是堆疊在整個大圖上面的,所以需要用到
Stack
這個控件,其中Stack中有個屬性const FractionalOffset(double dx, double dy)
用于表示子控件相對于父控件的位置
樣式四
這種樣式稍微復雜一點,結構如下
用青花瓷抓取了好奇心數據.青花瓷使用教程
首先在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); }
好了,寫完后會報錯,因為FeedFromJson
和FeedToJson
沒有找到,這個時候在控制到輸入flutter packages pub run build_runner build
指令后會自動生成一個moded.g.dart
文件,于是在網絡請求下來數據后就可以用Feed feed = Feed.fromJson(data)
這個方法來將Json中數據轉換保存在Feed這個實例中了.在model類中還有些復雜的Json嵌套,但是也都很簡單,大家看一眼應該就會了,哈哈.JSON和序列化具體教程
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, ),
StatefulWidget
,因為需要動態更新數據和列表.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.
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, ), ); }
由于最上面有一個輪播圖,最下面有加載框,所以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), ); }, ), ); }
這種的話也稍微復雜一點,有兩種樣式.并且到滑到最右邊的時候可以繼續請求并加載數據.
首先來分析一下數據
那么思路就很清晰了,在請求獲得數據后遍歷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和最左邊一直有條線
FlutterWebviewPlugin.m
文件中的- (void)navigate:(FlutterMethodCall*)call
方法中的最后一排,將[self.webview loadRequest:request]
方法改為[self.webview loadHTMLString:url baseURL:nil]
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"); }
/assets/app3
開頭的,所以需要替換成絕對路徑,所以要用到這個方法htmlBody.replaceAll( '/assets/app3','http://app3.qdaily.com/assets/app3')
在點擊橫向滑動列表的總標題的時候,會進入到相關欄目的詳情頁,如圖
這個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
日期:2018-10 瀏覽次數:7266
日期:2018-12 瀏覽次數:4342
日期:2018-07 瀏覽次數:4889
日期:2018-12 瀏覽次數:4186
日期:2018-09 瀏覽次數:5513
日期:2018-12 瀏覽次數:9935
日期:2018-11 瀏覽次數:4821
日期:2018-07 瀏覽次數:4594
日期:2018-05 瀏覽次數:4867
日期:2018-12 瀏覽次數:4334
日期:2018-10 瀏覽次數:5149
日期:2018-12 瀏覽次數:6226
日期:2018-11 瀏覽次數:4478
日期:2018-08 瀏覽次數:4602
日期:2018-11 瀏覽次數:12653
日期:2018-09 瀏覽次數:5593
日期:2018-12 瀏覽次數:4847
日期:2018-10 瀏覽次數:4200
日期:2018-11 瀏覽次數:4540
日期:2018-12 瀏覽次數:6076
日期:2018-06 瀏覽次數:4017
日期:2018-08 瀏覽次數:5449
日期:2018-10 瀏覽次數:4466
日期:2018-12 瀏覽次數:4545
日期:2018-07 瀏覽次數:4370
日期:2018-12 瀏覽次數:4509
日期:2018-06 瀏覽次數:4398
日期:2018-11 瀏覽次數:4385
日期:2018-12 瀏覽次數:4258
日期:2018-12 瀏覽次數:5296
Copyright ? 2013-2018 Tadeng NetWork Technology Co., LTD. All Rights Reserved.