Flutter 解决系统BottomNavigationBar的水波纹问题

起因

Flutter 系统自带的BottomNavigationBar,在点击时item会有一个水波纹效果,产品并不想要这个(实际上这个水波纹有的时候还会卡住无法消失)。
网上暂时没有找到现成的,所以就自己撸一个。

PS:通过继承InteractiveInkFeature,也可以去除一些widget自带的水波纹(使用方法可以参考demo里的NoInkWellFactory类文件),不过bottom nav这里没法使用。

Step.1

首先整体结构,我们参照系统的,子Item的状态我们通过Provider进行管理,先创建一个BottomNavBarNoInk.

代码如下(说明我尽量写在注释里方便阅读):
class BottomNavBarNoInk extends StatefulWidget{

  IndexModel indexModel;

  final width;
  final height;
  List items;

  int currentIndex;
  ValueChanged onTap;

  BottomNavBarNoInk({@required this.width
    ,@required this.
    height,@required this.items,
    this.currentIndex,this.onTap}) : indexModel = IndexModel(currentIndex);


  @override
  State createState() {

    return BottomNavBarNoInkState();
  }
}

class BottomNavBarNoInkState extends State {

  List barItems = [];

  @override
  void initState() {
    // TODO: implement initState
    super.initState();

    transfer2Widget();
  }
    //根据items创建对应的widget
  transfer2Widget(){
    for(int i=0; i< widget.items.length;i++){
      barItems.add(GestureDetector(
        onTap: (){
          widget.onTap(i);
          //更新model的值
          model?.setIndex(i);
        },
        child: BottomNoInkBarItem(item: widget.items[i],index: i,),
      ));
    }
  }
  //用于保存当前第几个item被点击
  IndexModel model ;

  @override
  Widget build(BuildContext context) {
    //通过provider 保存IndexModel,
    //子widget可以共享这个model并根据内部数据的变更自动刷新
    return ChangeNotifierProvider(
      create: (ctx){
        model = IndexModel(widget.currentIndex);
        return model;
      },
      child:Container(
        width: widget.width,
        height: widget.height,
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: barItems,
        ),
      ) ,
    ) ;
  }



这里我们依然使用系统的BottomNavigationBarItem对item进行封装.

Step.2

创建一个IndexModel对状态进行保存

代码如下

class IndexModel extends ChangeNotifier{
  int selectIndex;

  IndexModel(@required this.selectIndex);

  get index => selectIndex;

  setIndex(int index){
    selectIndex = index;
    notifyListeners();
  }

}

Step.3

在子Widget(BottomNoInkBarItem)中我们通过Consumer来获取到Provider管理的对象,并且根据这个对象的值来构造子widget,如果Provider的值变动,子widget也会同步刷新。

代码如下:

class BottomNoInkBarItem extends StatefulWidget{

  int index;
  BottomNavigationBarItem item;


  BottomNoInkBarItem({this.item,this.index});

  @override
  State createState() {
    // TODO: implement createState
    return BottomNoInkBarItemState();
  }

}

class BottomNoInkBarItemState extends State {
  @override
  Widget build(BuildContext context) {

    return Consumer(
      builder: (ctx,model,child){
        
        return Container(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.spaceAround,
            children: [
              Stack(
                children: [
                  //未激活状态
                  Offstage(
                    offstage: model.index == widget.index,
                    child: widget.item.icon,
                  ),
                  Offstage(
                    offstage: model.index != widget.index,
                    child: widget.item.activeIcon,
                  ),
                ],
              ),
              ///title
              widget.item.title
            ],
          ),
        );
      },
    );
  }
}

结束

至此功能就完成了,因为我的项目用到了Provider,所以这里便直接使用了。如果不想用Provider,也可以使用stream来实现。
有其他骚操作的,请评论区告诉我,大家一起交流。

Demo地址

你可能感兴趣的:(flutter,android,前端,android,ios,flutter,javascript,css)