阿里 Flutter-go 项目拆解笔记(三)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ITxiaodong/article/details/88374155

Flutter-go 项目地址是:https://github.com/alibaba/flutter-go

上文 我们分析了home.dart文件,这个文件主要承载了底部四个Tab页面,以及顶部的搜索功能。今天时间来不拆解搜索功能,下篇文章再进行拆解。

这篇文章主要拆解first_page.dart的文件,也就是第一个Tab页的实现。首页文件的路径如下:
'package:flutter_go/views/first_page/first_page.dart';

从项目的演示效果上可以看出第一个Tab页主要包含
图片说明来的明确些.png

免责声明实现

显示时机

源码initState方法可以看出这里使用了 SharedPreferences库,用于记录是否查看过免责声明。

如果查看过免责声明则下次启动不在弹出,否则弹出。

/// _unKnow 是 SP 存储的 Boolean 值,判断是否需要弹出免责声明,已经勾选过不在显示,就不会主动弹
_unKnow.then((bool value) {
 new Future.delayed(const Duration(seconds: 1),(){
   if (!value) {
      key.currentState.showAlertDialog(context);
   }
 });
});

弹窗实现

实现弹窗的文件路径在:package:flutter_go/components/disclaimer_msg.dart

弹窗的实现是通过AlertDialog去实现的。

image.png

这里分为第一次 查看 以及之后在 Banner 左上角点击后查看。

第一次查看:底部按钮显示 不再自动提示 文字和 知道了 文字
之后查看:底部按钮显示 已阅读知晓 文字

关闭弹窗的方法:Navigator.of(context).pop();

信息流实现

实现信息流的文件路径在:package:flutter_go/components/list_refresh.dart

image.png

从上图可以看出ListRefresh是实现列表的关键,它通过构造函数传递了 数据集合、卡片布局、banner参数过去。而 数据集合、卡片布局、banner 是在first_page.dart文件中获取

数据集合的实现
getIndexListData方法中通过接口参数的拼接,然后通过NetUtils工具去获取数据,NetUtils工具的实现是使用了网络请求库Dio。获取到返回的json数据之后,通过FirstPageItem去解析数据(FirstPageItem 中定义了数据Bean ,FirstPageItem的路径是:package:flutter_go/views/first_page/first_page_item.dart),然后将结果添加到集合中,最后返回该结果。

卡片 Item 实现

路径是:package:flutter_go/components/list_view_item.dart,构造方法中接收了文章地址,文章标题,作者名字

通过查看源码可以看到卡片 Item使用的是Card Widget包裹ListTile Widget实现,利用了Card Widget提供的圆角、阴影功能,以及ListTile提供的title、subtitle、trailing来实现子布局。
具体效果可如下:
首页卡片布局

下拉刷新

使用的是 RefreshIndicator组件

// 下拉加载的事件,清空之前list内容,取前X个
// 其实就是列表重置
  Future<Null> _handleRefresh() async {
// mokeHttpRequest 发起网络接口请求
    List newEntries = await mokeHttpRequest();
// this.mounted 确保能够刷新
    if (this.mounted) {
      setState(() {
        items.clear();
        items.addAll(newEntries);
        isLoading = false;
        _hasMore = true;
        return null;
      });
    }
  }

上拉加载

通过ScrollController监听 RefreshIndicator组件是否滑动到最后一条触发加载操作。在触发加载操作时还进一步判断是否有更多数据返回,没有更多数据则显示数据没有更多了!!!,如果有更多数据则显示稍等片刻更精彩..后刷新数据。

 _scrollController.addListener(() {
      // 如果下拉的当前位置到scroll的最下面
      if (_scrollController.position.pixels ==
          _scrollController.position.maxScrollExtent) {
        _getMoreData();
      }
    });

Banner 实现

headerView方法中的children: <Widget>中的Pagination实现了轮播图效果,Pagination文件的路径在:package:flutter_go/components/pagination.dart

Pagination中的_pageSelector中构建了HomeBanner组件,该组件构造方法中接收了Banner要显示的数据,以及点击的操作。

HomeBanner组件的路径在:package:flutter_go/components/home_banner.dart
它利用PageView + PageController组件实现翻页效果,利用Timer.periodic实现了自动滚动,在实现 无限滚动 的时候使用的技巧是在 第一个item 的前面添加上最后一条 item 的数据,在最后一条 item 的前面添加上第一条一条 item 的数据 ,代码如下:

List<Widget> _buildItems() { // 排列轮播数组
    List<Widget> items = [];
    if (widget.bannerStories.length > 0) {
      // 头部添加一个尾部Item,模拟循环
      items.add(
          _buildItem(widget.bannerStories[widget.bannerStories.length - 1]));
      // 正常添加Item
      items.addAll(
          widget.bannerStories.map((story) => _buildItem(story)).toList(
              growable: false));
      // 尾部
      items.add(
          _buildItem(widget.bannerStories[0]));
    }
    return items;
  }

Banner实现源码中,为了适配图片是白色和标题问题白色的问题,源码中还实现背景渐变的功能;

  Widget _buildItemTitle(String title) {
    return Container(
      decoration: BoxDecoration( /// 背景的渐变色
        gradient: LinearGradient(
          begin: Alignment.bottomCenter,
          end: const Alignment(0.0, -0.8),
          colors: [const Color(0xa0000000), Colors.transparent],
        ),
      ),
      alignment: Alignment.bottomCenter,
      child: Container(
        margin: EdgeInsets.symmetric(vertical: 22.0, horizontal: 16.0),
        child: Text(
          title, style: TextStyle(color: Colors.white, fontSize: 18.0),),),
    );
  }

轮播图小圆点 的实现,以及页面滑动的时候,对item的处理
由于之前做了无限轮播,所以这里的坐标需要加一层判断

小圆点的实现
  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);
  }

// 页面滑动监听
 _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(() {});
  }

这里点击之后是调用系统的浏览器打开该条Item对应的链接,实现如下;

  void _launchURL(String url) async {
    if (await canLaunch(url)) {
// launch(url) 启动默认浏览器打开该 URL
      await launch(url);
    } else {
      throw 'Could not launch $url';
    }
  }

一些小发现

widget 指代什么?

// 伪装吐出新数据
  Future<List> mokeHttpRequest() async {
    if (widget.requestApi is Function) {
     ...
    } else {
    ...
    }
  }

比如widget.requestApi中的widget是从哪来的呢?
通过查看源码,发现widget的介绍是这样的

abstract class State<T extends StatefulWidget> extends Diagnosticable {
  /// The current configuration.
  ///
  /// A [State] object's configuration is the corresponding [StatefulWidget]
  /// instance. This property is initialized by the framework before calling
  /// [initState]. If the parent updates this location in the tree to a new
  /// widget with the same [runtimeType] and [Widget.key] as the current
  /// configuration, the framework will update this property to refer to the new
  /// widget and then call [didUpdateWidget], passing the old configuration as
  /// an argument.
  T get widget => _widget;
  T _widget;

我的理解应该是继承了StatefulWidget的组件后,就可以在组件中通过widget去调用组件中的定义的参数。相当于Android 中 this 指代的上下文吧

养成好习惯

在页面销毁的时候处理掉不用的资源

  @override
  void dispose() {
    super.dispose();
    _scrollController.dispose();
  

本篇完~

猜你喜欢

转载自blog.csdn.net/ITxiaodong/article/details/88374155