Flutter_Weather今日熱點模塊實現(xiàn),效果圖如下:

首頁布局實現(xiàn)
代碼如下:
@override
Widget build(BuildContext context) {
return _buildTabController();
}
Widget _buildTabController(){
if(tabs.length == 0){
return ProgressView();
}else {
return DefaultTabController(
length: tabs.length,
child: Scaffold(
appBar: AppBar(
title: buildSearch(context),
bottom: TabBar(
isScrollable: true,
tabs: tabs.map<Widget>((dynamic title) {
return Tab(
text: title.toString(),
);
}).toList(),
),
),
body: TabBarView(
children: tabs.map<Widget>((dynamic title) {
return Container(
child: new NewListPage(type: title.toString(),));
}).toList()),
));
}
}
因為TabBar的標題是網(wǎng)絡(luò)加載,所以要有個一加載ProgressView,加載成功后顯示布局。
使用DefaultTabController控制tabBar和tabBarView聯(lián)動
搜索框?qū)崿F(xiàn)

橫向排列的3個widget,所以使用Row進行包裹。使用Container的BoxDecoration設(shè)置圓角屬性。使用Expanded填充中間的空白區(qū)域,使熱搜顯示在最右邊。
最后別忘了添加點擊事件,跳轉(zhuǎn)到搜索頁面。代碼如下
//搜索框
Widget buildSearch(BuildContext context) {
return GestureDetector(
onTap: (){
//通過路由進行頁面跳轉(zhuǎn)
Application.router.navigateTo(context, Routes.searchPage);
},
child: Container(
//添加圓角屬性
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(10))),
padding: EdgeInsets.all(8),
height: 40,
child: Row(
//設(shè)置子widget居中對齊
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Icon(
Icons.search,
color: Colors.grey,
size: 20,
),
SizedBox(
width: 5,
),
//填充空間
Expanded(
child: Text("搜你想要的",
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 14, color: Colors.black54))),
Text(
"熱搜",
style: TextStyle(color: Colors.blue, fontSize: 15),
)
],
),
),
);
}
加載數(shù)據(jù)并解析
使用http加載數(shù)據(jù)
//加載tabbar數(shù)據(jù)
loadData() async {
// final response =
// await http.post(Api.NEWS_TITLE_JS, body: {'appkey': Api.APPKEY_JS});
//由于接口有次數(shù)限制,所以直接把數(shù)據(jù)拿過來,節(jié)約接口次數(shù)
String data = '{"status":0,"msg":"ok","result":["頭條","新聞","國內(nèi)","國際","政治","財經(jīng)","體育","娛樂","軍事","教育","科技","NBA","股票","星座","女性","健康","育兒"]}';
Map map = json.decode(data);
//result返回一個String數(shù)組,可以直接解析
var list = map['result'];
setState(() {
tabs.addAll(list);
});
}
加載完成以后,調(diào)用setState()刷新widget。
下面我們來看一下TabBarView中的NewListPage的實現(xiàn)
消息列表的實現(xiàn)
布局
布局很簡單,就是一個ListView,因為有刷新的存在,所以我們要在ListView的外面套上RefreshIndicator,實現(xiàn)刷新。代如下:
@override
Widget build(BuildContext context) {
return new RefreshIndicator(
child: buildListView(),
onRefresh: _handlerRefresh,
);
}
- onRefresh:觸發(fā)刷新的回調(diào)
- child : 返回一個listview
listview實現(xiàn)
List<NewsItem> list = [null];
//listview
Widget buildListView(){
return new ListView.builder(
itemCount: list.length,
itemBuilder: (context, position) {
if(list[position] == null){
//顯示加載更多
return Container(
alignment: Alignment.center,
height: 40,child:SizedBox(height : 30,width :30 ,
child: CircularProgressIndicator(strokeWidth: 2,),));
}else{
return getRow(position);
}
},
controller: _scrollController,
);
}
我們在初始化數(shù)據(jù)源list的時候給list初始化了一個null作為標記位置(不推薦設(shè)置為null)List<NewsItem> list = [null];這樣就可以在listview中根據(jù)list[position] == null來判斷是不是到了標記位,如果到了就顯示加載的widget。當然也可以在這個地方做加載更多的數(shù)據(jù)請求。
加載更多
我把加載更多放到_scrollController中去了
@override
void initState() {
// TODO: implement initState
super.initState();
_scrollController.addListener((){
//滑到最底部
if(_scrollController.position.pixels == _scrollController.position.maxScrollExtent){
//加載更多
print("滑動到底部");
start = list.length - 1;
loadData();
}else{
}
});
loadData();
}
下拉刷新回調(diào)
_handlerRefresh為刷新是的回調(diào),代碼如下
//刷新
Future<Null> _handlerRefresh() async{
start = 0;
loadData();
return null;
}
加載數(shù)據(jù)
//加載數(shù)據(jù)
loadData() async {
final response = await http.post(Api.NEWS_LIST_JS+"?channel=$type&appkey=${Api.APPKEY_JS}&start=$start}",);
//刷新
setState(() {
print("response list:" + response.body);
NewsBean newsBean = NewsBean.fromJson(json.decode(response.body));
if(newsBean.status == "0"){
//表示刷新
if(start == 0 && list.length > 0){
list.clear();
list.add(null);
}
NewResult result = newsBean.result;
//把null放到最后一位
list.insertAll(list.length-1,result.list);
}
});
}
listview的item布局

- 紅色是最外層是一個column,里面包裹內(nèi)容和下劃線。
- 黃色是內(nèi)容里面用一個row包裹
- 藍色是文字,是一個column。
最后在使用GestureDetector包裹整個布局,給item添加點擊事件。
代碼如下:
Widget getRow(int index) {
NewsItem item = list[index];
return GestureDetector(
child: Container(
margin: EdgeInsets.only(left: 10, top: 10, right: 10),
child: Column(
children: <Widget>[
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
//文字
new Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
item.title,
style: TextStyle(
fontSize: 18,
color: Colors.black
),
overflow: TextOverflow.ellipsis,
maxLines: 2,
),
SizedBox(height: 5,),
Text(item.src + " " + TimeUtil.getNewsTime(item.time),
style: TextStyle(color: Colors.grey,fontSize: 12),),
],
)),
//圖片
Image.network(
item.pic,
height: 65,
width: 95,
fit: BoxFit.cover,
),
],
),
//分割線
Padding(
padding: EdgeInsets.only(top: 10),
child: SizedBox(height: 1, child: Container(color: Color(0xffE8E8E8),)),
)
],
),
),
onTap: (){
Application.router.navigateTo(context,
'${Routes.webViewPage}?title=${Uri.encodeComponent(item.title)}&url=${Uri.encodeComponent(item.url)}');
},
);
}
WebView實現(xiàn)
class WebViewPage extends StatefulWidget{
final String url;
final String title;
WebViewPage(this.url,this.title);
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return _PageState();
}
}
class _PageState extends State<WebViewPage>{
@override
Widget build(BuildContext context) {
print("WebViewPage url:" + widget.url + " title:" + widget.title);
// TODO: implement build
return new WebviewScaffold(
appBar: new AppBar(
backgroundColor: Color(0xff333333),
title: Text(widget.title),
),
url: widget.url,
withZoom: false,
withLocalStorage: true,
withJavascript: true,
initialChild: Center(child: Text("初始化"),),
);
}
}
android使用WebView的時候要在Android/../manifest 配置文件中的application中添加
android:usesCleartextTraffic="true",不然報http異常。