Flutter实现Android原生TabLayout + ViewPager 页面切换以及切换监听

方式一:

   使用Flutter自带的Widget(TabBar + TabBarView)实现,如下

   

List _tabs = ["A", "B", "C"];



@override
  void initState() {
    super.initState();
    _tabController = TabController(length: _tabs.length, vsync: this);

    _tabController.addListener(() {
      bool indexIsChanging = _tabController.indexIsChanging;
      if (!indexIsChanging) {
        logx("index : " + _tabController.index.toString());
      }
    });

  }



// build方法里面返回
return Column(
      children: [
        Container(
          child: TabBar(
            tabs: _tabs
                .map((e) => Tab(
                      text: e,
                    ))
                .toList(),
            controller: _tabController,
            unselectedLabelColor: XColor.colorBlackStyle,
            labelColor: XColor.colorStyle,
            indicatorSize: TabBarIndicatorSize.label,
          ),
        ),
        Expanded(
          child: TabBarView(
            children: _tabs.map((e) => Container(child: Text(e + "的内容"),)).toList(),
            controller: _tabController,
          ),
        )
      ],
    )

最开始我是不想用这种方法的,所以才有了下面第二种方法,我不想用这种方式的一个原因是一开始我没找到TabBar或者TabBarView中类似于PageView中onPageChanged的监听,不知道当前页面的index是多少,我要做到切换到第二个或者后面的时候,第一个页面中的轮播图定时器取消,让它不再自动滚动。

既然TabBar或者TabBarView中没有参数去监听,我就注意到了TabController,然后我就看了一下它里面的方法,有一个addListener方法,顾名思义,就是添加一个监听。具体监听什么呢?打印一下日志

print("listener");

结果每次滑动的时候都会调用一下,再去看TabController里面的方法或者变量,里面有

index 和 indexIsChanging

前一个应该就是我所需要的index了,后面的字面意思就是index是否改变中(这个暂且不谈是干嘛的),然后打印一下index,

print(_tabController.index);

结果滑动页面的时候就打印当前页面对应的index了。这里有一个小问题,就是手指滑动页面的时候到打印出index有明显间隔。

后来我再点击上面的TabBar来切换页面,这时候index日志会打印两次,第一次打印是在点击的时候打印的,第二次就和上面有延时的打印时间差不多间隔。因为才学Flutter几天,具体原理不太清除,这时候我想起还有 

indexIsChanging  这个变量,一起和index打印一下,

结果indexIsChanging自然也是打印了两遍,第一次为true,第二次为false。从结果来看,第一次为true时,是因为点击Tab,index正好处于切换状态中,至于第二次为false,是因为页面已经切换完成(具体原因感兴趣的同学可以去看源码)。

所以由indexIsChanging可以来排除第一次返回的index,具体代码如下

_tabController.addListener(() {
      bool indexIsChanging = _tabController.indexIsChanging;
      if (!indexIsChanging) {
        print(_tabController.index);
     
      }
    });

这样就可以实现滑动页面+Tab的效果了,缺点只有在监听页面切换的时候,获取index的时间要稍微延时一会,总体来说问题不大。

*******************************************************************************************************************************************************************************************

方式二:

在我之前没有办法获取tab页面切换监听的时候我用到了第二种方式,当时的想法是既然TabBarView没有onPageChanged监听,我可以使用PageView的啊。然后上网查找,找到一位开发者的替换方式,先贴代码。

开始就自定义了新的TabBarView,如下:

import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';

class FixTabBarView extends StatefulWidget {
  const FixTabBarView({
    Key key,
    @required this.children,
    @required this.tabController,
    @required this.pageController,
    this.physics,
    this.dragStartBehavior = DragStartBehavior.start,
    this.onTabIndexChanged,
  })  : assert(children != null),
        assert(dragStartBehavior != null),
        super(key: key);
  final TabController tabController;
  final PageController pageController;
  final List<Widget> children;
  final ScrollPhysics physics;
  final DragStartBehavior dragStartBehavior;

  final ValueChanged<int> onTabIndexChanged;

  @override
  _FixTabBarViewState createState() => _FixTabBarViewState();
}

class _FixTabBarViewState extends State<FixTabBarView> {
  @override
  void dispose() {
    super.dispose();
    widget.tabController.dispose();
    widget.pageController.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return PageView(
      dragStartBehavior: widget.dragStartBehavior,
      physics: widget.physics,
      controller: widget.pageController,
      children: widget.children,
      onPageChanged: (index) {
        widget.onTabIndexChanged(index);
        widget.tabController.animateTo(index);
      },
    );
  }
}

最开始是没有onTabIndexChanged这个监听的,是因为我个人需要后面自己加的。你们也可以根据自己需求来修改

使用方法如下:

List _tabs = ["A", "B", "C"];

@override
  void initState() {
    super.initState();
    _tabController = TabController(length: _tabs.length, vsync: this);

    _pageController = PageController();
  }


return Column(
      children: [
        Container(
          child: TabBar(
            tabs: _tabs
                .map((e) => Tab(
                      text: e,
                    ))
                .toList(),
            controller: _tabController,
            unselectedLabelColor: XColor.colorBlackStyle,
            labelColor: XColor.colorStyle,
            indicatorSize: TabBarIndicatorSize.label,
            onTap: (index) {
              _pageController.jumpToPage(index);
            },
          ),
        ),
        Expanded(
           child: FixTabBarView(
             tabController: _tabController,
             pageController: _pageController,
             children: _tabs.map((e) => Container(child: Text(e + "的内容"),)).toList(),
             onTabIndexChanged: (index){
               logx("onTabIndexChanged: " + index.toString());
             },
           ),
        )
      ],
    )

先说一下优点:

1:在我没发现第一种方法里面获取监听的方式之前,我觉得全是优点,QAQ

现在再说一下缺点:

1:构造相对复杂,页面切换的监听在FixTabBarView里面的onTabIndexChanged中,相对方式一,这里还要再加一个PageController来进行绑定,并且在点击顶部tab的时候还要在TabBar的onTab事件里面手动添加

_pageController.jumpToPage(index);

切换页面的方法。

2:手动在FixTabBarView滑动的时候,如果页面没有切换完成,顶部的指示器是不会移动做出改变的,只有在页面切换完成,tab的指示器才会在最后的时候滑动到相应的index,这样显得有点突兀。

3:点击顶部tab去切换的时候,底部的页面没有左右滑动的切换效果,会有点突兀。

对比看来,还是第一种方法更好用,而第二种方法也是在学习过程中的成果,在此记录一下,希望可以帮助需要的朋友。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值