Flutter_Weather今日热点模块实现

Flutter_Weather今日热点模块实现,效果图如下:


image

首页布局实现

布局

代码如下:

  @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异常。

项目完整地址:https://github.com/Zhengyi66/Flutter_weather

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容