Flutter 自定義特殊形狀的tabbar

先看效果!
自定義形狀TabBar
實現

成員變量
List<Widget> pages = List<Widget>();用來存放tabs分別對應的頁面。
tabbar作為內容的容器 CustomerNotification用來容器內部Widget來通知tabbar做出改變。

NotificationListener的’ child‘是頁面顯示內容,NotificationListener用來監聽CustomerNotification。

看設計,TabBar是懸浮的設計,不能用官方庫提供的TabBar。TabBar和后面的內容是堆疊的,所以用Stack widget。
Stack: 取代線性布局,Stack允許子 widget 堆疊, 你可以使用 Positioned 來定位他們相對于Stack的上下左右四條邊的位置。Stacks是基于Web開發中的絕對定位(absolute positioning )布局模型設計的。
Positioned,用Positioned絕對定位懸浮的TabBar。 bottom: 15, left: 20, right: 20(top這里不需要設置)分別設計和容器的邊距。

Positioned的child就是你自己需要定制的tabbar形狀。

全部代碼如下:

/*
 * File: /Users/ottpay/Git/fyh_transfer/lib/Account/accountHomePage.dart
 * Created Date: 2019-09-27 4:13:15
 * Author: 木穆  pengxuan.li@ott.ca
 * Copyright (c) 2019 OTT Pay HK
 */



import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:fyh_transfer/Send/sendHomePage.dart';
import 'package:fyh_transfer/Order/orderHomePage.dart';
import 'package:fyh_transfer/Account/accountHomePage.dart';

class FYHTabbar extends StatefulWidget {
  @override
  FYHTabbarState createState() => FYHTabbarState();
}

class CustomerNotification extends Notification {
  CustomerNotification(this.enable);
  final bool enable;
}

class FYHTabbarState extends State<FYHTabbar>
     {
  List<Widget> pages = List<Widget>();

  bool sendeEnable = false;
  SendHomePage _sendHomePage = SendHomePage();

  int _activeIndex = 1; //激活項
  double _height = 48.0; //導航欄高度
  double _floatRadius = 30; //懸浮圖標半徑

  List _navs = [
    Icons.search,
    Icons.ondemand_video,
    Icons.music_video,
  ]; //導航項

  @override
  void initState() {
    super.initState();
    pages..add(OrderHomePage())..add(_sendHomePage)..add(AccountHomePage());
  }

  @override
  Widget build(BuildContext context) {
    double width = MediaQuery.of(context).size.width - 40;
    _floatRadius = _activeIndex == 1 ? 40 : 30;
    return NotificationListener<CustomerNotification>(
        onNotification: (notification) {
          setState(() {
            sendeEnable = notification.enable;
          });
        },
        child: Container(
          child: Stack(children: [
            pages[_activeIndex],//tabbar要存放的頁面
            Positioned(//用Positioned絕對定位懸浮的TabBar
              bottom: 15,
              left: 20,
              right: 20,
              child: Container(
                padding: EdgeInsets.fromLTRB(0, 10, 0, 0),
                width: width,
                height: 58,
                child: Stack(
                  overflow: Overflow.visible,
                  children: <Widget>[
                    //所有圖標
                    DecoratedBox(
                      decoration: BoxDecoration(
                        color: Color(0xFFf2f4f5), // 底色
                        shape: BoxShape.rectangle, // 默認值也是矩形
                        borderRadius:
                            new BorderRadius.circular(_height / 2), // 圓角度
                        boxShadow: [
                          BoxShadow(
                              color: Color(0xffc1c1c1),
                              offset: Offset(2.0, 2.0),
                              blurRadius: 50.0,
                              spreadRadius: 2.0)
                        ],
                      ),
                      child: SizedBox(
                        height: _height,
                        child: Row(
                          mainAxisAlignment: MainAxisAlignment.spaceAround,
                          crossAxisAlignment: CrossAxisAlignment.center,
                          children: _navs
                              .asMap()
                              .map((i, v) => MapEntry(
                                  i,
                                  GestureDetector(
                                    child: Container(
                                      color: Color(0xFFf2f4f5),
                                      margin: EdgeInsets.all(0),
                                      child: Icon(v,
                                          color: _activeIndex == i
                                              ? Colors.pink
                                              : Colors.grey),
                                      width: width / 3 - 30,
                                      height: _height - 10,
                                    ),
                                    onTap: () {
                                      _switchNav(i);
                                    },
                                  )))
                              .values
                              .toList(),
                        ),
                      ),
                    ),
                    //浮動圖標
                    Positioned(
                      bottom: 4,
                      left: (width) / 2 - _floatRadius,
                      child: DecoratedBox(
                        decoration:
                            ShapeDecoration(shape: CircleBorder(), shadows: [
                          BoxShadow(
                              blurRadius: 2,
                              offset: Offset(0, 2),
                              spreadRadius: 0,
                              color: _activeIndex == 1
                                  ? Colors.pink
                                  : Colors.grey),
                        ]),
                        child: GestureDetector(
                          child: CircleAvatar(
                              radius: _floatRadius, //浮動圖標和圓弧之間設置8pixel間隙
                              backgroundColor: Color(0xfff2f4f5),
                              child: Icon(_navs[1],
                                  color: sendeEnable && _activeIndex == 1
                                      ? Colors.pink
                                      : Colors.black38)),
                          onTap: () {
                            _clickMiddleBtn();
                          },
                        ),
                      ),
                    ),
                  ],
                ),
              ),
            )
          ]),
        ));
    ;
  }

  //中間按鈕
  _clickMiddleBtn() {
    if (_activeIndex == 1) {
      if (sendeEnable) {
        print('send');
      }
    } else {
      _switchNav(1);
    }
  }

  //切換導航
  _switchNav(int newIndex) {
    print(newIndex);
    double oldPosition = _activeIndex.toDouble();
    double newPosition = newIndex.toDouble();
    if (oldPosition != newPosition) {
      setState(() {
        _activeIndex = newIndex;
      });
    }
  }

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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 前言 本文的目的是為了讓讀者掌握不同布局類Widget的布局特點,分享一些在實際使用過程遇到的一些問題,在《Flu...
    xqqlv閱讀 5,297評論 0 18
  • 項目演示github地址 Flutter 的界面基本由Widgets組成,widget需要位于MaterialAp...
    wayDevelop閱讀 2,686評論 1 1
  • 站在長江大橋上, 看著那渾濁的水流,仿佛水的 深度就在表面。 那大小的船只,有的 在逆流行駛,有的 在順流行駛,漸...
    長港居士閱讀 312評論 0 3
  • 長長的路,慢慢地走 雖然是在假期,但每天早上依然是五點準時起床,梳洗完畢,去大學里跑步,順手拿起...
    bai素心若雪閱讀 1,311評論 6 26
  • 今天分享一個普通圖片轉化為日式漫畫的效果,主要運用圖層蒙版、混合樣式、色彩調整、以及濾鏡等效果。 先發一張效果圖鎮...
    合小沫閱讀 3,133評論 41 88