欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Flutter TabBar、TabBarView的使用

程序员文章站 2022-06-01 18:35:35
...

效果:

Flutter TabBar、TabBarView的使用Flutter TabBar、TabBarView的使用

说实话,这个效果实现起来并没有我想象中的那么简单,demo跟实际开发差的还是蛮大的,一方面是学习成本,要查看源码,另一方面就是页面的布局,层级嵌套的太深了。。


简介

TabBarTabBarView到底是什么关系呢,简而言之,TabBar就是导航栏,TabBarView就是导航栏当前所对应的内容区。


TabBar

简单看一下TabBar的参数,重要或常用的会有注释

  const TabBar({
    Key key,
    @required this.tabs,//子标签
    this.controller,//控制器
    this.isScrollable = false,//能否滑动,false:tab宽度则等比,true:tab宽度则包裹item
    this.indicatorColor,//指示器颜色
    this.indicatorWeight = 2.0,
    this.indicatorPadding = EdgeInsets.zero,
    this.indicator,
    this.indicatorSize,//TabBarIndicatorSize.label:indicator与文字同宽,TabBarIndicatorSize.tab:与tab同宽
    this.labelColor,//选中标签颜色
    this.labelStyle,//选中标签样式
    this.labelPadding,
    this.unselectedLabelColor,//未选中标签颜色
    this.unselectedLabelStyle,
    this.dragStartBehavior = DragStartBehavior.down,
    this.onTap,//点击事件
  })

参数挺多,但是没什么复杂的,都是一些基本属性。


TabBarView

简单看一下TabBarView的参数

  const TabBarView({
    Key key,
    @required this.children,//子widget
    this.controller,//控制器
    this.physics,
    this.dragStartBehavior = DragStartBehavior.down,
  })

TabBarView的参数就更少了,因为它只是一个容器而已


TabBar和TabBarView联动

TabBar和TabBarView联动主要是控制器controller,上面可以看到,两个widget都有一个共同的参数controller,它就是控制二者联动的。

系统提供了一个DefaultTabController,只需要把TabBar和TabBarView包裹起来就能实现联动

  const DefaultTabController({
    Key key,
    @required this.length,
    this.initialIndex = 0,
    @required this.child,
  })

示例:

    return new DefaultTabController(
      length: _datas.length,
      child: new Scaffold(
        appBar: new TabBar(
          ...
        ),
        body: new TabBarView(
          ...
        ),
      ),
    );

当然,我们也可以自定义Controller

  • 首先添加SingleTickerProviderStateMixin
class _ProjectPageState extends State<ProjectPage> with SingleTickerProviderStateMixin { }
  • 定义我们的Controller并初始化
class _ProjectPageState extends State<ProjectPage> with SingleTickerProviderStateMixin {

  TabController controller;//tab控制器
  int _currentIndex = 0; //选中下标

  @override
  void initState() {
    super.initState();
    //初始化controller并添加监听
    controller = TabController(length: _datas.length, vsync: this);
    controller.addListener(() => _onTabChanged());
  }
}
  • 重新配置TabBar和TabBarView
    return new Scaffold(
      appBar: new TabBar(
        controller: controller,//控制器
        ...
      ),
      body: new TabBarView(
        controller: controller,
        ...
      ), 
    );

点击更新

在开发实战中,数据都是动态的,即每次点击tab标签,页面数据是要刷新的,

获取数据是一样的,只是tab下标在变,所以要在事件触发之后就更新下标。

上面我们在自定义的controller中添加了一个Listener监听tab变化,

  _onTabChanged() {
    if (_controller.index.toDouble() == _controller.animation.value) { 
      //赋值 并更新数据
      this.setState(() {
        _currentIndex = controller.index;
      });
      getDetail();
    } 
  }

这个_currentIndex就是标识当前选中的下标,默认选中第一个为0,
在事件中把改变的下标赋值给_currentIndex,然后根据下标重新请求刷新数据源,然后页面会重绘,即刷新。


完整代码

class ProjectPage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return new _ProjectPageState();
  }
}

class _ProjectPageState extends State<ProjectPage> with SingleTickerProviderStateMixin {

  TabController controller;//tab控制器
  int _currentIndex = 0; //选中下标

  List<ProjectData> _datas = new List();//tab集合
  List<ProjectListDataData> _listDatas = new List();//内容集合

  @override
  void initState() {
    super.initState();
    getHttp();
  }

  void getHttp() async {
    try {
      var response = await HttpUtil().get(Api.PROJECT);
      Map userMap = json.decode(response.toString());
      var projectEntity = new ProjectEntity.fromJson(userMap);

      setState(() {
        if (_datas.length != null) _datas = projectEntity.data;
        _currentIndex = 0;
      });

      getDetail();

      //初始化controller并添加监听
      controller = TabController(length: _datas.length, vsync: this);
      controller.addListener(() => _onTabChanged());

    } catch (e) {
      print(e);
    }
  }

  ///
  /// tab改变监听
  /// 
  _onTabChanged() {
    if (_controller.index.toDouble() == _controller.animation.value) {
      //赋值 并更新数据
      this.setState(() {
        _currentIndex = _controller.index;
      });
      getDetail();
    }
  }

  ///
  /// 根据tab下标获取数据
  ///
  void getDetail() async {
    try {
      var data = {"cid": _datas[_currentIndex].id};
      var response = await HttpUtil().get(Api.PROJECT_LIST, data: data);
      Map userMap = json.decode(response.toString());
      var projectListEntity = new ProjectListEntity.fromJson(userMap);

      setState(() {
        _listDatas = projectListEntity.data.datas;
      });
    } catch (e) {
      print(e);
    }
  }

  @override
  Widget build(BuildContext context) {
    return new DefaultTabController(
      length: _datas.length,
      child: new Scaffold(
        appBar: new TabBar(
          controller: controller,//控制器
          labelColor: YColors.colorPrimaryDark, //选中的颜色
          labelStyle: TextStyle(fontSize: 16), //选中的样式
          unselectedLabelColor: YColors.color_666, //未选中的颜色
          unselectedLabelStyle: TextStyle(fontSize: 14), //未选中的样式
          indicatorColor: YColors.colorPrimary, //下划线颜色
          isScrollable: true, //是否可滑动
          //tab标签
          tabs: _datas.map((ProjectData choice) {
            return new Tab(
              text: choice.name,
            );
          }).toList(),
          //点击事件
          onTap: (int i) {
            print(i);
          },
        ),
        body: new TabBarView(
          controller: controller,
          children: _datas.map((ProjectData choice) {
            return new ListView.builder(
                itemCount: _listDatas.length,
                itemBuilder: (BuildContext context, int position) {
                  return getRow(position);
                });
          }).toList(),
        ),
      ),
    );
  }

  Widget getRow(int i) {
    return new GestureDetector(
      child: new Container(
        alignment: Alignment.topLeft,
        padding: EdgeInsets.all(10),
        child: new Card(
          elevation: 5,
          shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))),
          color: Colors.white,
          child: Padding(
            padding: EdgeInsets.all(10),
            child: Row(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                new Expanded(
                  flex: 2,
                  child: new Image.network(_listDatas[i].envelopePic),
                ),
                new Expanded(
                  flex: 5,
                  child: new Column(
                    mainAxisSize: MainAxisSize.max,
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: <Widget>[
                      new Padding(
                        padding: EdgeInsets.symmetric(horizontal: 10),
                        child: new Text(
                          _listDatas[i].title,
                          style: TextStyle(
                              fontSize: 16, fontWeight: FontWeight.bold),
                          maxLines: 3,
                          overflow: TextOverflow.ellipsis,
                        ),
                      ),
                      new Padding(
                        padding: EdgeInsets.all(10),
                        child: new Text(
                          _listDatas[i].desc,
                          style:
                              TextStyle(fontSize: 14, color: YColors.color_666),
                          maxLines: 3,
                          overflow: TextOverflow.ellipsis,
                        ),
                      ),
                      new Container(
                        alignment: Alignment.bottomLeft,
                        child: Row(
                          children: <Widget>[
                            new Expanded(
                              flex: 1,
                              child: new Padding(
                                padding: EdgeInsets.all(10),
                                child: new Text(_listDatas[i].niceDate,
                                    style: TextStyle(fontSize: 14)),
                              ),
                            ),
                            new Padding(
                              padding: EdgeInsets.all(10),
                              child: new Text(
                                _listDatas[i].author,
                                style: TextStyle(fontSize: 14),
                                textAlign: TextAlign.right,
                              ),
                            )
                          ],
                        ),
                      ),
                    ],
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
      onTap: () {
        //点击item跳转到详情
        Navigator.push(
          context,
          new MaterialPageRoute(
              builder: (context) => new ArticleDetail(
                  title: _listDatas[i].title, url: _listDatas[i].link)),
        );
      },
    );
  }

  @override
  void dispose() {
    super.dispose();
    controller.dispose();
  }
}

github:https://github.com/yechaoa/wanandroid_flutter/blob/master/lib/pages/projectPage.dart


官方文档:https://api.flutter.dev/flutter/material/TabBar-class.html