Flutter Go 源码分析(三)

- (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 [];
      });
    }
  }

首页布局已经全部完成。

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

推荐阅读更多精彩内容