Flutter_Weather今日熱點模組實現

正義啊發表於2019-07-31

Flutter_Weather今日熱點模組實現,效果圖如下:

Flutter_Weather今日熱點模組實現

首頁佈局實現

佈局
程式碼如下:

  @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的標題是網路載入,所以要有個一載入ProgressView,載入成功後顯示佈局。 使用DefaultTabController控制tabBar和tabBarView聯動

搜尋框實現

在這裡插入圖片描述
橫向排列的3個widget,所以使用Row進行包裹。使用Container的BoxDecoration設定圓角屬性。使用Expanded填充中間的空白區域,使熱搜顯示在最右邊。

最後別忘了新增點選事件,跳轉到搜尋頁面。程式碼如下

  //搜尋框
  Widget buildSearch(BuildContext context) {
    return  GestureDetector(
      onTap: (){
      	//通過路由進行頁面跳轉
          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(
        //設定子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),
            )
          ],
        ),
      ),
    );
  }
複製程式碼
載入資料並解析

使用http載入資料

  //載入tabbar資料
  loadData() async {
//    final response =
//        await http.post(Api.NEWS_TITLE_JS, body: {'appkey': Api.APPKEY_JS});
//由於介面有次數限制,所以直接把資料拿過來,節約介面次數
  String data = '{"status":0,"msg":"ok","result":["頭條","新聞","國內","國際","政治","財經","體育","娛樂","軍事","教育","科技","NBA","股票","星座","女性","健康","育兒"]}';
    Map map = json.decode(data);
    //result返回一個String陣列,可以直接解析
    var list = map['result'];
    setState(() {
      tabs.addAll(list);
    });
  }
複製程式碼

載入完成以後,呼叫setState()重新整理widget。

下面我們來看一下TabBarView中的NewListPage的實現

訊息列表的實現

佈局

佈局很簡單,就是一個ListView,因為有重新整理的存在,所以我們要在ListView的外面套上RefreshIndicator,實現重新整理。代如下:

  @override
  Widget build(BuildContext context) {
  
    return new RefreshIndicator(
        child: buildListView(),
        onRefresh: _handlerRefresh,
    );
  }
複製程式碼
  • onRefresh:觸發重新整理的回撥
  • child : 返回一個listview
listview實現
  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,
        );
  }
複製程式碼

我們在初始化資料來源list的時候給list初始化了一個null作為標記位置(不推薦設定為null)List<NewsItem> list = [null];這樣就可以在listview中根據list[position] == null來判斷是不是到了標記位,如果到了就顯示載入的widget。當然也可以在這個地方做載入更多的資料請求。

載入更多

我把載入更多放到_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();
  }
複製程式碼
下拉重新整理回撥

_handlerRefresh為重新整理是的回撥,程式碼如下

  //重新整理
  Future<Null> _handlerRefresh() async{
    start = 0;
    loadData();
    return null;
  }

複製程式碼
載入資料
  //載入資料
  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,裡面包裹內容和下劃線。
  • 黃色是內容裡面用一個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實現

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異常。

專案完整地址:github.com/Zhengyi66/F…

相關文章