Flutter 中 FloatingActionButton与 BottomAppBar 的使用详解 | Flutter Widgets

这是我参与更文挑战的第18天,活动详情查看: 更文挑战

上一篇我们聊了底部导航栏 BottomNavigationBar 的使用详解,这篇我们聊聊 FloatingActionButton 与 BottomAppBar的使用,为啥要放到一起呢?只有这两个 Widget 组合在一起才可以出效果,单独聊没有太大的价值,因为使用都比较简单。

比如这些奇怪的导航,我们要怎么实现?

凹凸菜单 圆角导航 波浪导航
image.png image.png image.png

FloatingActionButton

实现上面的效果前,我们先聊聊 FloatingActionButton (中间蓝色加号按钮),然后再与下面的 BottomAppBar 结合即可

先看整体结构

Scaffold(
  body:[bodyWidget],
  // 这里设置悬浮按钮
  floatingActionButton: FloatingActionButton(
    // 设置一个➕按钮
    child: Icon(Icons.add),
    // 添加点击事件
    onPressed: () {

一般我们使用它就是在 Scaffold 中设置即可,当然他是一个 Widget 我们在任何地方都可以使用他。

backgroundColor: Colors.orangeforegroundColor: Colors.orangesplashColor: Colors.orange
image.pngimage.png01.gif
elevation: 0elevation: 6(默认)highlightElevation: 12
image.pngimage.pngimage.png
mini: trueshape: RoundedRectangleBordershape: ContinuousRectangleBorder
image.pngimage.pngimage.png
FloatingActionButton(
  child: Icon(Icons.add),
  // backgroundColor: Colors.orange,
  // foregroundColor: Colors.orange,
  // splashColor: Colors.orange,
  // elevation: 6,
  // highlightElevation: 12,
  // mini: true,
  // shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
  // shape: ContinuousRectangleBorder(
  //   side: BorderSide(
  //     width: 4,
  //     color: Colors.orange,
  //   ),
  //   borderRadius: BorderRadius.circular(12),
  // ),
  // shape: BeveledRectangleBorder(
  //   side: BorderSide(
  //     width: 4,
  //     color: Colors.orange,
  //   ),
  //   borderRadius: BorderRadius.circular(12),
  // ),
  onPressed: () {

上面是写基本的设置的属性,有时我们还有一些特殊的需求,比如

你会发现除了刚才设置的 mini 和默认大小外,没有属性可以设置大小,那么我们就在外层包一层 SizedBox 即可。

SizedBox(
  width: 80,
  height: 80,
  child: FloatingActionButton(...),

比如我们做上下排列的效果,那么我们只需要添加一个 Column 即可,里面放置多个 FloatingActionButton 即可

Column(
  mainAxisSize: MainAxisSize.min,
  children: [
    FloatingActionButton(
      child: Icon(Icons.ac_unit_rounded),
      backgroundColor: Colors.orange,
      onPressed: () {},
    SizedBox(height: 10),
    FloatingActionButton(
      child: Icon(Icons.adb_sharp),
      backgroundColor: Colors.green,
      onPressed: () {},
    SizedBox(height: 10),
    FloatingActionButton(
      child: Icon(Icons.more_horiz_outlined),
      onPressed: () {},

FloatingActionButton 还有个 heroTag 属性我们没有聊,这个是干嘛用的呢?这个就是 Hero widget 的标签,如果此时我们设置点击跳转到一个新的页面,那么会报错,因为在 material 的设计规范里,每个屏幕只能有一个悬浮按钮,如果多个的话,在路由导航是就会出现标签冲突(不设置都是默认标签对象),解决版本就是设置不同的 heroTag 即可。

Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          FloatingActionButton(
            child: Icon(Icons.ac_unit_rounded),
            backgroundColor: Colors.orange,
            // 设置 tag1
            heroTag: 'tag1',
            onPressed: () {},
          SizedBox(height: 10),
          FloatingActionButton(
            child: Icon(Icons.adb_sharp),
            backgroundColor: Colors.green,
            // 设置 tag2
            heroTag: 'tag2',
            onPressed: () {},
          SizedBox(height: 10),
          FloatingActionButton(
            child: Icon(Icons.more_horiz_outlined),
            // 设置 tag3
            heroTag: 'tag3',
            onPressed: () {},

02.gif
如上图我们可以通过 ScaffoldfloatingActionButtonLocation 属性来设置 FloatingActionButton 的位置,为了更好的展示,我们增加一个 BottomAppBar 来帮助我们理解各个属性的调配

BottomAppBar

上一篇我们聊过 BottomNavigationBar ,这里我们将 bottomNavigationBar 属性改为 BottomAppBar ,因为 Flutter 中所有的控件都是 Widget

Scaffold(
  // 设置位置,中心停靠
  floatingActionButtonLocation:FloatingActionButtonLocation.centerDocked,
  // 设置 BottomAppBar
  bottomNavigationBar: BottomAppBar(
    child: Row(
      mainAxisAlignment: MainAxisAlignment.spaceAround,
      children: [
        TextButton.icon(
          icon: Icon(Icons.home),
          label: Text('首页'),
          onPressed: () {},
        SizedBox(),
        TextButton.icon(
          icon: Icon(Icons.people),
          label: Text('我的'),
          onPressed: () {},
  • 非 mini
  • 小表展示了各种位置效果,觉得有用记得点赞支持我哦

    startFloatcenterFloatendFloat
    image.pngimage.pngimage.png
    startDockedcenterDockedendDocked
    image.pngimage.pngimage.png
    startTopcenterTopendTop
    image.pngimage.pngimage.png
  • mini 类型
  • 这个需要配合 FloatingActionButton 中的 mini 属性来使用,并且位置属性加了前缀 mini,如 FloatingActionButtonLocation.miniCenterDocked ,位置与上面是一致的。

    区别在于 BottomAppBar 设置缺口 shape 的时候,缺口的半径不同

    centerDockedminiCenterDocked
    image.pngimage.png

    多形状 Shape

    在 Flutter 很多 Widget 都是可以设置 Shape 来自定义形状的,从输入框的边框线条到背景都是如此。

  • 圆形缺口矩形
  • BottomAppBar(
      /// 圆形缺口矩形
      shape: CircularNotchedRectangle(),
      child: [childWidget],
    
    BottomAppBar(
      /// 自动缺口形状
      shape: AutomaticNotchedShape(
        // 斜角矩形
        BeveledRectangleBorder(
          borderRadius: BorderRadius.circular(20),
      child: [childWidget],
    
    BottomAppBar(
      /// 自动缺口形状
      shape: AutomaticNotchedShape(
        // 圆角矩形
        RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(40),
      child: [childWidget],
    
  • 自定义形状
  • 如果上面的一些效果也不能满足我们的需求,我们可以选择自定义 Shape,具体怎么自定义可以看下面的代码,当然如果你阅读 Flutter 的源码会更好。

    BottomAppBar(
      /// 自定义形状
      shape: MyShape(),
      child: [childWidget],
    
    /// 自定义Shape
    class MyShape extends NotchedShape {
      @override
      Path getOuterPath(Rect host, Rect? guest) {
        var path = Path();
        int wallCount = 10;
        double step = host.width / wallCount;
        double wall = host.height / 4;
        for (var i = 0; i < wallCount; i++) {
          // 上下起伏的锯齿状
          path.relativeLineTo(step, i.isEven ? -wall : wall);
        // 分别连接到右下角、左下角、闭合左上角
          ..lineTo(host.right, host.bottom)
          ..lineTo(host.left, host.bottom)
          ..close();
        return path;
    /// 自定义Shape
    class MyShape extends NotchedShape {
      @override
      Path getOuterPath(Rect host, Rect? guest) {
        var path = Path();
        int wallCount = 10;
        double step = host.width / wallCount;
        double wall = host.height / 4;
        for (var i = 0; i < wallCount; i++) {
          // 圆角波浪
          path.relativeArcToPoint(
            Offset(step, i.isEven ? -wall : wall),
            radius: Radius.circular(20),
        // 分别连接到右下角、左下角、闭合左上角
          ..lineTo(host.right, host.bottom)
          ..lineTo(host.left, host.bottom)
          ..close();
        return path;
    

    基于 Flutter 🔥 最新版本

  • Flutter Widgets 仓库
  • FloatingActionButton (Flutter Widget of the Week)
  • Flutter-FloatingActionButton
  • Flutter-BottomAppBar
  • 此文章已收录到下面👇 的专栏,可以直接关注
  • 更多文章继续阅读|系列文章持续更新
  • 👏 欢迎点赞➕收藏➕关注,有任何问题随时在下面👇评论,我会第一时间回复哦