美文网首页Flutter圈子
Flutter 之列表和头部 (ListView + Heade

Flutter 之列表和头部 (ListView + Heade

作者: 刘bowen | 来源:发表于2018-10-25 16:27 被阅读27次

    上一篇介绍Banner的开发。在大多数应用场景中。banner和ListView通常是一起显示的。 并且能够共同滑动。例如如下界面:


    421896775.jpg

    要实现上图的界面,直接想到是ListView添加Header。但在Flutter中,ListView 组件相当于RecyclerView,所以添加Header也用RecyclerView的原理:

    封装ListPage组件,list_page.dart

    import 'package:flutter/material.dart';
    
    typedef HeaderWidgetBuild = Widget Function(BuildContext context, int position);
    
    typedef ItemWidgetBuild = Widget Function(BuildContext context, int position);
    
    class ListPage extends StatefulWidget {
      List headerList;
      List listData;
      ItemWidgetBuild itemWidgetCreator;
      HeaderWidgetBuild headerCreator;
    
      ListPage(List this.listData,
          {Key key,
          List this.headerList,
          ItemWidgetBuild this.itemWidgetCreator,
          HeaderWidgetBuild this.headerCreator})
          : super(key: key);
    
      @override
      ListPageState createState() {
        return new ListPageState();
      }
    }
    
    class ListPageState extends State<ListPage> {
      @override
      Widget build(BuildContext context) {
        return Container(
          child: new ListView.builder(
              itemBuilder: (BuildContext context, int position) {
                return buildItemWidget(context, position);
              },
              itemCount: _getListCount()),
        );
      }
    
      int _getListCount() {
        int itemCount = widget.listData.length;
        return getHeaderCount() + itemCount;
      }
    
      int getHeaderCount() {
        int headerCount = widget.headerList != null ? widget.headerList.length : 0;
        return headerCount;
      }
    
      Widget _headerItemWidget(BuildContext context, int index) {
        if (widget.headerCreator != null) {
          return widget.headerCreator(context, index);
        } else {
          return new GestureDetector(
            child: new Padding(
                padding: new EdgeInsets.all(10.0),
                child: new Text("Header Row $index")),
            onTap: () {
              print('header click $index --------------------');
            },
          );
        }
      }
    
      Widget buildItemWidget(BuildContext context, int index) {
        if (index < getHeaderCount()) {
          return _headerItemWidget(context, index);
        } else {
          int pos = index - getHeaderCount();
          return _itemBuildWidget(context, pos);
        }
      }
    
      Widget _itemBuildWidget(BuildContext context, int index) {
        if (widget.itemWidgetCreator != null) {
          return widget.itemWidgetCreator(context, index);
        } else {
          return new GestureDetector(
            child: new Padding(
                padding: new EdgeInsets.all(10.0), child: new Text("Row $index")),
            onTap: () {
              print('click $index --------------------');
            },
          );
        }
      }
    }
    
    

    使用及测试:异步加载网络数据使用

    import 'package:demonewsapp/app_constance.dart';
    import 'package:demonewsapp/common/banner_widget.dart';
    import 'package:demonewsapp/common/str_util.dart';
    import 'package:demonewsapp/page/list_page.dart';
    import 'package:flutter/material.dart';
    import 'package:http/http.dart' as http;
    import 'dart:convert';
    import 'package:transparent_image/transparent_image.dart';
    
    class NewsListPage extends StatefulWidget {
      @override
      NewsListPageState createState() {
        return NewsListPageState();
      }
    }
    
    class NewsListPageState extends State<NewsListPage> {
      List<BannerItem> bannerList = [];
    
      List<NewsItem> newsList = [];
    
      @override
      void initState() {
        super.initState();
        _getBannerData();
        _testNewsData();
        _getNewsListData();
      }
    
      _testNewsData() {
        for (int i = 0; i < 10; i++) {
          NewsItem news = new NewsItem();
          news.nid = i;
          news.nodeTitle =
              "$i$i$i$i$i$i$i$i$i$i$i$i$i$i$i$i$i$i$i$i$i$i$i$i$i$i$i$i$i$i$i$i$i$i$i$i";
          news.node_created = 12345;
          news.total_count = '1111';
          news.node_type = 'news';
          news.thumbPath =
              '''http://img.mukewang.com/user/584ff8bf0001609c01000100-100-100.jpg''';
          news.news_category = 'test';
    
          newsList.add(news);
        }
      }
    
      _getNewsListData() async {
        String url = 'http://www.wsrtv.com.cn/services/sevice_news_list.json';
        var res = await http.get(url);
        List resList = json.decode(res.body);
        List<NewsItem> tempList = [];
        for (var item in resList) {
          NewsItem news = new NewsItem();
          news.nid = int.parse(item['nid']);
          news.nodeTitle = item['node_title'];
          news.node_created = int.parse(item['node_created']);
          news.total_count = item['totalcount'];
          news.node_type = item['node_type'];
          news.thumbPath =
              StringUtil.getSrcImagePath(item['field_news_video_thumb_app']);
          var field_news_category = item['field_news_category'];
          if (field_news_category is List) {
            news.news_category = field_news_category[0].toString();
          } else {
            news.news_category = field_news_category.toString();
          }
          print(news.news_category);
          tempList.add(news);
        }
    
        setState(() {
          newsList = tempList;
        });
      }
    
      _getBannerData() async {
        String url =
            AppConstance.makeUrl('services/service_news_slideshow.json', null);
        var res = await http.get(url);
        List list = json.decode(res.body);
        List<BannerItem> temp = [];
        for (var item in list) {
          String imagePath = item['field_new_app_slideshow'];
          imagePath = StringUtil.getSrcImagePath(imagePath);
          String text = item['node_title'];
          temp.add(BannerItem.defaultBannerItem(imagePath, text));
        }
    
        print('temp ==== $temp');
        bannerList = temp;
        setState(() {});
      }
    
      @override
      Widget build(BuildContext context) {
        return new Scaffold(
          appBar: new AppBar(
            title: Text('news list and banner'),
          ),
          body: new ListPage(
            newsList,
            headerList: [1, 2],
            itemWidgetCreator: getItemWidget,
            headerCreator: (BuildContext context, int position) {
              if(position == 0) {
                return new BannerWidget(180.0, bannerList);
              }else {
                return new Padding(padding: EdgeInsets.all(10.0), child: 
                  Text('$position -----header------- '),);
              }
            },
          ),
        );
      }
    
      _onItemClick(int pos) {
        if (newsList != null && newsList.length > pos) {
          print('click $pos ==== ${newsList[pos].nid}');
        }
      }
    
      Widget getItemWidget(BuildContext context, int pos) {
        return new GestureDetector(
          onTap: () {
            _onItemClick(pos);
          },
          child: IntrinsicHeight(
            child: Container(
              height: 80.0,
              padding: EdgeInsets.all(7.0),
              decoration: UnderlineTabIndicator(
                  borderSide: BorderSide(color: Color(0Xfff1f1f1), width: 1.0)),
              child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: <Widget>[
                  AspectRatio(
                    aspectRatio: 118 / 66,
                    child: FadeInImage.memoryNetwork(
                      placeholder: kTransparentImage,
                      image: newsList[pos].thumbPath,
                      fit: BoxFit.cover,
                    ),
                  ),
                  Expanded(
                    flex: 1,
                    child: Padding(
                      padding: EdgeInsets.only(left: 7.0),
                      child: Column(
                        mainAxisSize: MainAxisSize.max,
                        mainAxisAlignment: MainAxisAlignment.spaceBetween,
                        children: <Widget>[
                          Expanded(
                              child: Text(
                            newsList[pos].nodeTitle,
                            style: new TextStyle(
                              color: Colors.black,
                              fontSize: 15.0,
                              decoration: TextDecoration.none,
                            ),
                            softWrap: true,
                            maxLines: 2,
                            overflow: TextOverflow.ellipsis,
                          )),
                          Container(child: getItemBottomWidget(pos)),
                        ],
                      ),
                    ),
                  )
                ],
              ),
            ),
          ),
        );
      }
    
      Widget getItemBottomWidget(int pos) {
        return IntrinsicHeight(
          child: Row(
            mainAxisSize: MainAxisSize.max,
            mainAxisAlignment: MainAxisAlignment.spaceAround,
            children: <Widget>[
              Expanded(
                  child: Text(
                newsList[pos].news_category,
                style: TextStyle(
                    color: Color(0xff979797),
                    fontSize: 12.0,
                    decoration: TextDecoration.none),
              )),
              Container(
                padding: EdgeInsets.all(3.0),
                decoration: ShapeDecoration(
                    shape: RoundedRectangleBorder(
                        side: BorderSide(color: Color(0xfff1f1f1)),
                        borderRadius: BorderRadius.circular(3.0)),
                    color: Color(0xfff1f1f1)),
                child: Text(
                  '${newsList[pos].total_count}浏览',
                  style: TextStyle(
                      color: Color(0xff979797),
                      fontSize: 12.0,
                      decoration: TextDecoration.none),
                ),
              ),
            ],
          ),
        );
      }
    }
    
    class NewsItem {
      int nid;
      String nodeTitle;
      String total_count;
      int node_created;
      String node_type;
      String thumbPath;
      String news_category;
    }
    
    

    相关文章

      网友评论

        本文标题:Flutter 之列表和头部 (ListView + Heade

        本文链接:https://www.haomeiwen.com/subject/szlctqtx.html