Flutter 实战:撸半个知乎日报~HomePage

作者: jzhu085 | 来源:发表于2018-06-23 14:48 被阅读385次

    fir_release_Android包下载地址

    GIF:

    homepage.gif

    UI如图:

    homepage.png

    需求分析:

    • 主页显示动态名字且居中,跳转到抽屉的图标
    • 图片展示,手动切换,指示器,自动轮播,点击可以跳转
    • 整体滑动 ,根据滑动的位置改变主页名字(参考最后演示的GIF)

    UI拆解并实现:

    • AppBar:

         appBar: new AppBar(
            title: new Text('$_title'),//动态改变title
            centerTitle: true, // 居中
          ), //头部的标题AppBar
    
    • Banner:

    拆解1:图片展示
     Widget _buildItem(BuildContext context, int index) {
        HotNewsTopStoriesModel item = _fakeList[index];
        return new GestureDetector(
          onTap: () {
            RouteUtil.route2Detail(context, '${item.id}'); // 通过路由跳转到详情
          },
          child: new FadeInImage.memoryNetwork(
              placeholder: kTransparentImage,
              image: item.image,
              height: widget._homeBannerHeight,
              fit: BoxFit.fitWidth),
        );
      }
    
    
    拆解2:手动滑动
    new PageView.builder(
              controller: _pageController,
              itemBuilder: (BuildContext context, int index) {
                return _buildItem(context, index);
              },
              itemCount: _fakeList.length,
              onPageChanged: (index) {
                _changePage(index);
              },
            )
    
    拆解3:轮播(向左向右都可以“无限”滚动)
      //用于做banner循环
      _initFakeList() {
        for (int i = 0; i < fakeLength; i++) {
          _fakeList.addAll(widget.topList);
        }
      }
    
    拆解4:自动轮播,以及手动与自动的判定
       //通过时间timer做轮询,达到自动播放的效果
      initTimer() {
        _timer = new Timer.periodic(_bannerDuration, (timer) {
          if(_isEndScroll){
            _pageController.animateToPage(_curPageIndex + 1,
                duration: _bannerAnimationDuration, curve: Curves.linear);
          }
        });
      }
    
    //检查手指和自动播放的是否冲突,如果滚动停止开启自动播放,反之停止自动播放
        return new NotificationListener(
            onNotification: (ScrollNotification scrollNotification) {
              if (scrollNotification is ScrollEndNotification || scrollNotification is UserScrollNotification) {
                _isEndScroll = true;
              } else {
                _isEndScroll = false;
              }
              return false;
            },
            ........
            ........
    
    拆解5:创建指示器
     //创建指示器
      Widget _buildIndicators() {
        _initIndicators();
        return new Align(
          alignment: Alignment.bottomCenter,
          child: new Container(
              color: Colors.black45,
              height: 20.0,
              child: new Center(
                child: new SizedBox(
                  width: widget.topList.length * 16.0,
                  height: 5.0,
                  child: new Row(
                    children: _indicators,
                    mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                  ),
                ),
              )),
        );
      }
    
     _initIndicators() {
        _indicators.clear();
        for (int i = 0; i < widget.topList.length; i++) {
          _indicators.add(new SizedBox(
            width: 5.0,
            height: 5.0,
            child: new Container(
              color: i == _curIndicatorsIndex ? Colors.white : Colors.grey,
            ),
          ));
        }
      }
    
    拆解6:完成组件整合
      Widget _buildBanner() {
        return new Container(
          height: widget._homeBannerHeight,
          //指示器覆盖在pagerview上,所以用Stack
          child: new Stack(
            children: <Widget>[
              _buildPagerView(),
              _buildIndicators(),
            ],
          ),
        );
      }
    
    
    • ListView:

      拆解1:基础Item
     Widget _buildNormalItem(HotNewsStoriesModel item) {
        final String imgUrl = item.images[0];
        final String title = item.title;
        final int id = item.id;
        return new InkWell(
            onTap: () {
              RouteUtil.route2Detail(context, '$id');
            },
            child: new Padding(
                padding: const EdgeInsets.only(left: 12.0, right: 12.0),
                child: new SizedBox(
                  height: Constant.normalItemHeight,
                  child: new Column(
                    children: <Widget>[
                      new Row(
                        children: <Widget>[
                          new Expanded(
                            child: new Text(
                              title,
                              style: new TextStyle(
                                  fontSize: 16.0, fontWeight: FontWeight.w300),
                            ),
                          ),
                          new Padding(
                            padding: const EdgeInsets.all(8.0),
                            child: new SizedBox(
                              height: 80.0,
                              width: 80.0,
                              child: new Image.network(imgUrl),
                            ),
                          )
                        ],
                      ),
                      new Expanded(
                        child: new Align(
                          alignment: Alignment.bottomCenter,
                          child: CommonDivider.buildDivider(),
                        ),
                      ),
                    ],
                  ),
                )));
      }
    
    拆解2:日期Item
    Widget _buildDateTimeItem(HotNewsStoriesModel item) {
       final String dateTime = item.curDate;
    
       return new Container(
         color: Colors.blue,
         height: Constant.dateTimeItemHeight,
         child: new Center(
           child: new Text(
             dateTime,
             style: new TextStyle(
                 fontSize: 16.0, fontWeight: FontWeight.w300, color: Colors.white),
           ),
         ),
       );
     }
    
    
    拆解3:ListView组装items
     //根据type组装数据
      Widget _buildItem(BuildContext context, int index) {
        final HotNewsStoriesModel item = _normalDatas[index];
    
        Widget widget;
    
        switch (item.itemType) {
          case HotNewsStoriesModel.itemTypeBanner:
            widget = new HomeBanner(_topDatas, Constant.bannerHeight);
            break;
          case HotNewsStoriesModel.itemTypeNormal:
            widget = _buildNormalItem(item);
            break;
          case HotNewsStoriesModel.itemTypeDate:
            widget = _buildDateTimeItem(item);
            break;
        }
        return widget;
      }
    
     content = new ListView.builder(
            //设置physics属性总是可滚动
            physics: AlwaysScrollableScrollPhysics(),
            controller: _scrollController,
            itemCount: _normalDatas.length,
            itemBuilder: _buildItem,
          );
    
    
    拆解4:刷新
     var _refreshIndicator = new NotificationListener(
          onNotification: _onNotification,
          child: new RefreshIndicator(
            key: _refreshIndicatorKey,
            onRefresh: _refreshData,
            child: content,
          ),
        );
    
    拆解5:加载更多
      void _scrollListener() {
        //滑到最底部刷新
        if (_scrollController.position.pixels ==
            _scrollController.position.maxScrollExtent) {
          _loadData();
        }
      }
      
    
    拆解5: 动态改变title
    //由于暂时没找到监听滑动到某个具体Item的方法,所以用了个很原始方法计算 ,根据位移和Items的高度进行判断
      _computeShowtTitle(double offset) {
        //滑动监听改变title
        if (_dateTimeOffsetList.isNotEmpty) {
          if (offset.round() < _dateTimeOffsetList[0]) {
            _title = Constant.todayHot;
            setState(() {});
            return;
          }
    
          for (int i = 0; i < _dateTimeOffsetList.length; i++) {
            if (i != _dateTimeOffsetList.length - 1) {
              if (offset.round() >= _dateTimeOffsetList[i].round() &&
                  _dateTimeOffsetList[i + 1].round() >= offset.round()) {
                String dateTime = DateUtil.formatDateWithWeek(
                    _dateTimeList[i].subtract(new Duration(days: 1)));
    
                if (dateTime != _title) {
                  setState(() {
                    _title = dateTime;
                  });
                }
    
                break;
              }
            } else {
              if (offset.round() >= _dateTimeOffsetList[i].round() &&
                  _dateTimeOffsetList[i].round() >= offset.round()) {
                String dateTime = DateUtil.formatDateWithWeek(
                    _dateTimeList[i].subtract(new Duration(days: 1)));
    
                if (dateTime != _title) {
                  setState(() {
                    _title = dateTime;
                  });
                }
                break;
              }
            }
    
            if (i != _dateTimeOffsetList.length - 1) {
              if (offset.round() < _dateTimeOffsetList[i + 1].round() &&
                  offset.round() > _dateTimeOffsetList[i].round()) {
                String dateTime = DateUtil.formatDateWithWeek(_dateTimeList[i]);
    
                if (dateTime != _title) {
                  setState(() {
                    _title = dateTime;
                  });
                }
                break;
              }
            } else {
              if (offset.round() < _dateTimeOffsetList[i].round() &&
                  offset.round() > _dateTimeOffsetList[i].round()) {
                String dateTime = DateUtil.formatDateWithWeek(_dateTimeList[i]);
    
                if (dateTime != _title) {
                  setState(() {
                    _title = dateTime;
                  });
                }
                break;
              }
            }
          }
        }
      }
    

    知乎日报Flutter版代码已经上传到我的GITHUB ZhihuDailyPurifyByFlutter

    基础学习过程中的代码都放在GITHUB Flutter_Study

    每天学一点,学到Flutter发布正式版!

    相关文章

      网友评论

        本文标题:Flutter 实战:撸半个知乎日报~HomePage

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