美文网首页
Flutter Go 源码分析(三)

Flutter Go 源码分析(三)

作者: Hedgehog___ | 来源:发表于2019-06-06 00:26 被阅读0次
    - (6)FirstPage--首页
    1. DisclaimerMsgState--免责声明弹窗
      首先我们看一下FirstPageStateinitState方法中做了什么:
    void initState() {
        super.initState();
        if (key == null) {
           key = GlobalKey<DisclaimerMsgState>();
           // key = const Key('__RIKEY1__');
          //获取sharePre 
           _unKnow = _prefs.then((SharedPreferences prefs) {
             //查询是否需要自动弹出
             return (prefs.getBool('disclaimer::Boolean') ?? false);
           });
    
          /// 判断是否需要弹出免责声明,已经勾选过不在显示,就不会主动弹
          _unKnow.then((bool value) {
             new Future.delayed(const Duration(seconds: 1),(){
               if (!value) {
                key.currentState.showAlertDialog(context);
               }
             });
          });
        }
      }
    

    弹出免责声明showAlertDialog:

    void showAlertDialog(BuildContext context) {
        showDialog<void>(
          context: context,
          barrierDismissible: false, // user must tap button!
          builder: (BuildContext context) {
            return AlertDialog(
              //title: Text('免责声明'),
              content: SingleChildScrollView(
                child: ListBody(
                  children: <Widget>[
                    Container(
                        padding: EdgeInsets.fromLTRB(5.0, 5.0, 10.0, 10.0),
                        //width: 100,
                        height: 35,
                        child: Text('免责声明',
                            style: TextStyle(
                                fontSize: 18, fontWeight: FontWeight.w700)),
                        decoration: BoxDecoration(
                          //color: Colors.blue,
                          image: DecorationImage(
                              fit: BoxFit.fitWidth,
                              image: AssetImage('assets/images/paimaiLogo.png')),
                          borderRadius: BorderRadius.all(
                            Radius.circular(10.0),
                          ),
                          //alignment: Alignment.bottomRight,
                        )),
                    SizedBox(height: 20),
                    Text(disclaimerText1),
                    Text(disclaimerText2),
                  ],
                ),
              ),
              shape: RoundedRectangleBorder(
                  borderRadius: new BorderRadius.circular(20.0)), // 圆角
    
              actions: <Widget>[
                new Container(
                  width: 250,
                  child: _create(),
                )
              ],
            );
          },
        );
      }
    

    这里涉及到了AlertDialog组件大家可自行去官网学习,这里不过多阐述,我们来看_create函数:

    Row _create() {
        //已读
        if (_readed) {
          return Row(
            mainAxisAlignment: MainAxisAlignment.end,
            children: <Widget>[
              FlatButton(
                padding: EdgeInsets.symmetric(horizontal: 20.0),
                child: Text('已阅读知晓',
                    style: TextStyle(fontSize: 16, color: Colors.white)),
                //可点击
                color: Theme.of(context).primaryColor,
                onPressed: () {
                  Navigator.of(context).pop();
                },
              ),
              SizedBox(
                width: 10.0,
              )
            ],
          );
        }
    
        //第一次读取
        return Row(mainAxisAlignment: MainAxisAlignment.spaceAround,
            //crossAxisAlignment:CrossAxisAlignment.start,
            children: <Widget>[
              Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  Checkbox(
                      activeColor: Theme.of(context).primaryColor,
                      tristate: false,
                      value: _valBool,
                      onChanged: (bool bol) {
                        if(mounted) {
                          setState(() {
                            _valBool = bol;
                          });
                        }
                        Navigator.of(context).pop(); // here I pop to avoid multiple Dialogs
                        showAlertDialog(context); //here i call the same function
                      }),
                  Text('不再自动提示', style: TextStyle(fontSize: 14)),
                ],
              ),
              FlatButton(
                child: Text('知道了',
                    style: TextStyle(fontSize: 16, color: Colors.white)),
                //可点击
                color: _valBool
                    ? Theme.of(context).primaryColor
                    : Theme.of(context).primaryColor.withAlpha(800),
                onPressed: () {
                  // if (_valBool) {
                  refs(_valBool);//存储已读结果
                  Navigator.of(context).pop();
                  // }
                },
              ),
            ]);
      }
    

    build函数:没啥可值得说的,自己看代码吧。

    1. ListRefresh首页主要widget
      build函数:
    Widget build(BuildContext context) {
        return new RefreshIndicator(
          child: ListView.builder(
            itemCount: items.length + 1,
            itemBuilder: (context, index) {
              if (index == 0 && index != items.length) {
                if(widget.headerView is Function){
                  return widget.headerView();//头部轮播图
                }else {
                  return Container(height: 0);
                }
              }
              if (index == items.length) {
                //return _buildLoadText();
                return _buildProgressIndicator();//底部加载更多提示
              } else {
                //print('itemsitemsitemsitems:${items[index].title}');
                //return ListTile(title: Text("Index${index}:${items[index].title}"));
                if (widget.renderItem is Function) {//列表项
                  return widget.renderItem(index, items[index]);
                }
              }
            },
            controller: _scrollController,
          ),
          onRefresh: _handleRefresh,
        );
      }
    

    大概分为三部分headerView_buildProgressIndicatorrenderItem

    • 1)headerView(头部轮播+免责声明)
    headerView(){
        return
          Column(
            children: <Widget>[
            Stack(
            //alignment: const FractionalOffset(0.9, 0.1),//方法一
            children: <Widget>[
                Pagination(),//轮播图组件
                Positioned(//方法二
                top: 10.0,
                left: 0.0,
                child: DisclaimerMsg(key:key,pWidget:this)
                ),
              ]),
            SizedBox(height: 1, child:Container(color: Theme.of(context).primaryColor)),
            SizedBox(height: 10),
            ],
          );
    
      }
    

    headerView的build函数构建主要是_pageSelector

    List<Widget> _pageSelector(BuildContext context) {
        List<Widget> list = [];//widget列表
        List<StoryModel> bannerStories = [];//数据列表
        /// super.initState();
        arr.forEach((item) {
          //StoryModel将数据<StoryModel>装到数组里面
          bannerStories.add(StoryModel.fromJson(item));
        });
    
    
        if (arr.length > 0) {
          //将widget<HomeBanner>装入数组
          list.add(HomeBanner(bannerStories, (story) {
            _launchURL('${story.url}');
          }));
        }
        return list;
      }
    

    StoryModel就是一个数据模型不多说,主要来看HomeBanner组件,构造方法:

    final List<StoryModel> bannerStories;//数据源
      final OnTapBannerItem onTap;//点击回调
    
      HomeBanner(this.bannerStories, this.onTap, {Key key})
          :super(key: key);
    

    _BannerStatebuild方法:

    Widget build(BuildContext context) {
        return Container(
          height: 226.0,//轮播图高度
          child: Stack(
              alignment: Alignment.bottomCenter,
              children: <Widget>[
                PageView(
                  controller: controller,
                  onPageChanged: _onPageChanged,
                  children: _buildItems(),),//轮播item
                _buildIndicator(), // 下面的小点
              ]),
        );
      }
    
    • _buildItems:
      返回一个装有wedget 的列表,列表项wedget通过_buildItem创建:
    Widget _buildItem(StoryModel story) {
        return GestureDetector(//手势
          onTap: () { // 按下
            if (widget.onTap != null) {
              widget.onTap(story);
            }
          },
          child: Stack(
            fit: StackFit.expand,
            children: <Widget>[
             Image.network(story.image, fit: BoxFit.cover),//图片
              _buildItemTitle(story.title), // 内容文字,大意
            ],),);
      }
    

    Image是用来展示网络图片,_buildItemTitle是构建渐变遮罩和标题文字的。没有什么难点,不阐述了。

    • _buildIndicator
      构造方法(没有复杂点):
    Widget _buildIndicator() {
        List<Widget> indicators = [];
        for (int i = 0; i < widget.bannerStories.length; i++) {
          indicators.add(Container(
              width: 6.0,
              height: 6.0,
              margin: EdgeInsets.symmetric(horizontal: 1.5, vertical: 10.0),
              decoration: BoxDecoration(
                  shape: BoxShape.circle,
                  color: i == virtualIndex ? Colors.white : Colors.grey)));
        }
        return Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: indicators);
      }
    

    最后再来看计时器的初始化以及响应方法:

    void initState() {
        super.initState();
        controller = PageController(initialPage: realIndex);
        timer = Timer.periodic(Duration(seconds: 5), (timer) { // 自动滚动
          /// print(realIndex);
          controller.animateToPage(realIndex + 1,
              duration: Duration(milliseconds: 300),
              curve: Curves.linear);
        });
      }
    

    这代码的意思是每隔5秒进行一次动画变化,并响应_onPageChanged方法模拟无限循环:

    _onPageChanged(int index) {
        realIndex = index;
        int count = widget.bannerStories.length;
        if (index == 0) {
          virtualIndex = count - 1;
          controller.jumpToPage(count);
        } else if (index == count + 1) {
          virtualIndex = 0;
          controller.jumpToPage(1);
        } else {
          virtualIndex = index - 1;
        }
        setState(() {});
      }
    
    • 1)makeCard首页列表项
      构造方法:
    Widget makeCard(index,FirstPageItem item){
    
        var myTitle = '${item.title}';
        var myUsername = '${'👲'}: ${item.username} ';
        var codeUrl = '${item.detailUrl}';
        return new ListViewItem(itemUrl:codeUrl,itemTitle: myTitle,data: myUsername,);
      }
    

    返回的是ListViewItem实例对象没啥好说的。

    • 1)getIndexListData首页数据获取
    Future<Map> getIndexListData([Map<String, dynamic> params]) async {
        const juejin_flutter = 'https://timeline-merger-ms.juejin.im/v1/get_tag_entry?src=web&tagId=5a96291f6fb9a0535b535438';
        var pageIndex = (params is Map) ? params['pageIndex'] : 0;
        final _param  = {'page':pageIndex,'pageSize':20,'sort':'rankIndex'};
        var responseList = [];
        var  pageTotal = 0;
    
        try{
          var response = await NetUtils.get(juejin_flutter, params: _param);
          responseList = response['d']['entrylist'];
          pageTotal = response['d']['total'];
          if (!(pageTotal is int) || pageTotal <= 0) {
            pageTotal = 0;
          }
        }catch(e){
    
        }
        pageIndex += 1;
        List resultList = new List();
        for (int i = 0; i < responseList.length; i++) {
          try {
            FirstPageItem cellData = new FirstPageItem.fromJson(responseList[i]);
            resultList.add(cellData);
          } catch (e) {
            // No specified type, handles all
          }
        }
        Map<String, dynamic> result = {"list":resultList, 'total':pageTotal, 'pageIndex':pageIndex};
        return result;
      }
    

    NetUtils为网络请求工具,内部通过Dio三方进行网络请求并返回数据。
    最后ListRefresh通过 _getMoreData方法进行加载数据和下拉加载更多逻辑,进行数据显示

    // list探底,执行的具体事件
      Future _getMoreData() async {
        if (!isLoading && _hasMore) {
          // 如果上一次异步请求数据完成 同时有数据可以加载
          if (mounted) {
            setState(() => isLoading = true);
          }
          //if(_hasMore){ // 还有数据可以拉新
          List newEntries = await mokeHttpRequest();
          //if (newEntries.isEmpty) {
          _hasMore = (_pageIndex <= _pageTotal);
          if (this.mounted) {
            setState(() {
              items.addAll(newEntries);
              isLoading = false;
            });
          }
          backElasticEffect();
        } else if (!isLoading && !_hasMore) {
          // 这样判断,减少以后的绘制
          _pageIndex = 0;
          backElasticEffect();
        }
      }
    // 伪装吐出新数据
      Future<List> mokeHttpRequest() async {
        if (widget.requestApi is Function) {
          final listObj = await widget.requestApi({'pageIndex': _pageIndex});
          _pageIndex = listObj['pageIndex'];
          _pageTotal = listObj['total'];
          return listObj['list'];
        } else {
          return Future.delayed(Duration(seconds: 2), () {
            return [];
          });
        }
      }
    

    首页布局已经全部完成。

    相关文章

      网友评论

          本文标题:Flutter Go 源码分析(三)

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