在这里插入图片描述

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

🎯 欢迎来到 Flutter for OpenHarmony 社区!本文将深入讲解 Flutter 中 FloatingActionButton 浮动按钮的使用方法,带你从基础到精通,掌握这一 Material Design 标志性的交互组件。


一、FloatingActionButton 组件概述

在 Flutter for OpenHarmony 应用开发中,FloatingActionButton(浮动按钮,简称 FAB)是一种悬浮在页面内容之上的圆形按钮,用于表示页面最主要的操作。它是 Material Design 设计语言中最具标志性的组件之一,通常放置在屏幕右下角,点击时会触发页面的主要操作。

📋 FloatingActionButton 类型

类型 说明 适用场景
标准 FAB 56x56dp 的圆形按钮 单一主要操作
小型 FAB 40x40dp 的圆形按钮 空间受限时
大型 FAB 96x96dp 的圆形按钮 需要突出显示
扩展 FAB 带文字的宽按钮 需要文字说明
速度拨 FAB 展开多个子按钮 多个相关操作

💡 使用场景:FloatingActionButton 常用于新建、编辑、添加、分享、导航等页面的主要操作。


二、FloatingActionButton 基础用法

FloatingActionButton 通常作为 Scaffold 的 floatingActionButton 属性使用。

2.1 最简单的 FloatingActionButton

Scaffold(
  appBar: AppBar(title: const Text('FAB 示例')),
  floatingActionButton: FloatingActionButton(
    onPressed: () {
      print('FAB 被点击');
    },
    child: const Icon(Icons.add),
  ),
  body: const Center(
    child: Text('点击右下角的浮动按钮'),
  ),
)

代码解析:

  • onPressed:点击回调,设为 null 时按钮禁用
  • child:按钮内显示的内容,通常是 Icon

2.2 完整示例

下面是一个完整的可运行示例,展示 FloatingActionButton 的基本使用:

class FabExample extends StatefulWidget {
  const FabExample({super.key});

  
  State<FabExample> createState() => _FabExampleState();
}

class _FabExampleState extends State<FabExample> {
  int _counter = 0;

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('FloatingActionButton 示例')),
      body: Center(
        child: Text(
          '计数器: $_counter',
          style: const TextStyle(fontSize: 32),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          setState(() {
            _counter++;
          });
        },
        child: const Icon(Icons.add),
      ),
    );
  }
}

三、FloatingActionButton 常用属性

3.1 onPressed - 点击回调

设置按钮的点击事件,设为 null 时按钮禁用。

FloatingActionButton(
  onPressed: () {
    // 执行操作
  },
  child: const Icon(Icons.add),
)

FloatingActionButton(
  onPressed: null,  // 禁用状态
  child: const Icon(Icons.add),
)

3.2 child - 子组件

设置按钮内显示的内容,通常是图标。

FloatingActionButton(
  onPressed: () {},
  child: const Icon(Icons.edit),
)

FloatingActionButton(
  onPressed: () {},
  child: const Text('添加'),
)

3.3 backgroundColor - 背景颜色

设置按钮的背景颜色。

FloatingActionButton(
  onPressed: () {},
  backgroundColor: Colors.blue,
  child: const Icon(Icons.add),
)

3.4 foregroundColor - 前景颜色

设置按钮内图标/文字的颜色。

FloatingActionButton(
  onPressed: () {},
  backgroundColor: Colors.blue,
  foregroundColor: Colors.white,
  child: const Icon(Icons.add),
)

3.5 heroTag - Hero 动画标签

用于页面切换时的 Hero 动画标识,多个 FAB 需要设置不同的 heroTag。

FloatingActionButton(
  onPressed: () {},
  heroTag: 'fab_1',
  child: const Icon(Icons.add),
)

3.6 mini - 小型按钮

设置为 true 时显示小型 FAB(40x40dp)。

FloatingActionButton(
  onPressed: () {},
  mini: true,
  child: const Icon(Icons.add),
)

3.7 shape - 形状

设置按钮的形状。

FloatingActionButton(
  onPressed: () {},
  shape: RoundedRectangleBorder(
    borderRadius: BorderRadius.circular(16),
  ),
  child: const Icon(Icons.add),
)

3.8 elevation - 阴影高度

设置按钮的阴影高度。

FloatingActionButton(
  onPressed: () {},
  elevation: 12,
  child: const Icon(Icons.add),
)

3.9 highlightElevation - 按下时阴影

设置按钮按下时的阴影高度。

FloatingActionButton(
  onPressed: () {},
  elevation: 6,
  highlightElevation: 12,
  child: const Icon(Icons.add),
)

3.10 tooltip - 长按提示

设置长按时显示的提示文字。

FloatingActionButton(
  onPressed: () {},
  tooltip: '添加新项目',
  child: const Icon(Icons.add),
)

📊 FloatingActionButton 属性速查表

属性 类型 默认值 说明
onPressed VoidCallback? - 点击回调
child Widget? - 子组件
backgroundColor Color? - 背景颜色
foregroundColor Color? - 前景颜色
heroTag Object? - Hero 动画标签
mini bool false 是否小型
shape ShapeBorder? CircleBorder 形状
elevation double? 6 阴影高度
highlightElevation double? 12 按下时阴影
tooltip String? - 长按提示

四、FloatingActionButton 位置控制

4.1 floatingActionButtonLocation

通过 Scaffold 的 floatingActionButtonLocation 属性控制 FAB 的位置。

Scaffold(
  floatingActionButton: FloatingActionButton(
    onPressed: () {},
    child: const Icon(Icons.add),
  ),
  floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
)

常用位置:

位置 说明
FloatingActionButtonLocation.endFloat 右下角浮动(默认)
FloatingActionButtonLocation.centerFloat 底部居中浮动
FloatingActionButtonLocation.startFloat 左下角浮动
FloatingActionButtonLocation.endDocked 右下角停靠
FloatingActionButtonLocation.centerDocked 底部居中停靠
FloatingActionButtonLocation.startDocked 左下角停靠
FloatingActionButtonLocation.endTop 右上角
FloatingActionButtonLocation.startTop 左上角

4.2 自定义位置

class CustomFabLocation extends FloatingActionButtonLocation {
  final double offsetX;
  final double offsetY;

  CustomFabLocation({required this.offsetX, required this.offsetY});

  
  Offset getOffset(ScaffoldPrelayoutGeometry scaffoldGeometry) {
    return Offset(offsetX, offsetY);
  }
}

Scaffold(
  floatingActionButton: FloatingActionButton(
    onPressed: () {},
    child: const Icon(Icons.add),
  ),
  floatingActionButtonLocation: CustomFabLocation(offsetX: 100, offsetY: 200),
)

五、FloatingActionButton 扩展类型

5.1 FloatingActionButton.extended - 扩展 FAB

带有图标和文字的宽按钮。

FloatingActionButton.extended(
  onPressed: () {},
  icon: const Icon(Icons.add),
  label: const Text('添加'),
)

扩展 FAB 属性:

属性 类型 说明
icon Widget? 图标
label Widget 文字标签
extendedTextStyle TextStyle? 文字样式
extendedIconLabelSpacing double? 图标与文字间距
extendedPadding EdgeInsetsGeometry? 内边距

5.2 FloatingActionButton.large - 大型 FAB

更大的浮动按钮(96x96dp)。

FloatingActionButton.large(
  onPressed: () {},
  child: const Icon(Icons.add),
)

5.3 FloatingActionButton.small - 小型 FAB

更小的浮动按钮(40x40dp)。

FloatingActionButton.small(
  onPressed: () {},
  child: const Icon(Icons.add),
)

六、FloatingActionButton 与 BottomAppBar 配合

FloatingActionButton 可以与 BottomAppBar 配合,创建凹槽效果。

6.1 基本配合

Scaffold(
  floatingActionButton: FloatingActionButton(
    onPressed: () {},
    child: const Icon(Icons.add),
  ),
  floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
  bottomNavigationBar: BottomAppBar(
    shape: const CircularNotchedRectangle(),
    notchMargin: 8,
    child: Row(
      mainAxisAlignment: MainAxisAlignment.spaceAround,
      children: [
        IconButton(
          icon: const Icon(Icons.home),
          onPressed: () {},
        ),
        const SizedBox(width: 48),
        IconButton(
          icon: const Icon(Icons.settings),
          onPressed: () {},
        ),
      ],
    ),
  ),
)

6.2 完整底部导航示例

class BottomAppBarDemo extends StatefulWidget {
  const BottomAppBarDemo({super.key});

  
  State<BottomAppBarDemo> createState() => _BottomAppBarDemoState();
}

class _BottomAppBarDemoState extends State<BottomAppBarDemo> {
  int _selectedIndex = 0;

  
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Text('页面 $_selectedIndex'),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          ScaffoldMessenger.of(context).showSnackBar(
            const SnackBar(content: Text('FAB 被点击')),
          );
        },
        child: const Icon(Icons.add),
      ),
      floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
      bottomNavigationBar: BottomAppBar(
        shape: const CircularNotchedRectangle(),
        notchMargin: 8,
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: [
            IconButton(
              icon: Icon(
                Icons.home,
                color: _selectedIndex == 0 ? Colors.blue : Colors.grey,
              ),
              onPressed: () => setState(() => _selectedIndex = 0),
            ),
            IconButton(
              icon: Icon(
                Icons.search,
                color: _selectedIndex == 1 ? Colors.blue : Colors.grey,
              ),
              onPressed: () => setState(() => _selectedIndex = 1),
            ),
            const SizedBox(width: 48),
            IconButton(
              icon: Icon(
                Icons.favorite,
                color: _selectedIndex == 2 ? Colors.blue : Colors.grey,
              ),
              onPressed: () => setState(() => _selectedIndex = 2),
            ),
            IconButton(
              icon: Icon(
                Icons.person,
                color: _selectedIndex == 3 ? Colors.blue : Colors.grey,
              ),
              onPressed: () => setState(() => _selectedIndex = 3),
            ),
          ],
        ),
      ),
    );
  }
}

七、SpeedDial 速度拨按钮

当需要多个相关操作时,可以使用展开式的 SpeedDial 按钮。

7.1 实现方式

class SpeedDialDemo extends StatefulWidget {
  const SpeedDialDemo({super.key});

  
  State<SpeedDialDemo> createState() => _SpeedDialDemoState();
}

class _SpeedDialDemoState extends State<SpeedDialDemo> {
  bool _isExpanded = false;

  void _toggleExpanded() {
    setState(() {
      _isExpanded = !_isExpanded;
    });
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('SpeedDial 示例')),
      body: const Center(
        child: Text('点击右下角的浮动按钮'),
      ),
      floatingActionButton: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          if (_isExpanded) ...[
            _buildSpeedDialItem(Icons.email, '邮件', Colors.blue, () {}),
            const SizedBox(height: 12),
            _buildSpeedDialItem(Icons.message, '消息', Colors.green, () {}),
            const SizedBox(height: 12),
            _buildSpeedDialItem(Icons.share, '分享', Colors.orange, () {}),
            const SizedBox(height: 12),
          ],
          FloatingActionButton(
            onPressed: _toggleExpanded,
            child: AnimatedRotation(
              turns: _isExpanded ? 0.125 : 0,
              duration: const Duration(milliseconds: 200),
              child: const Icon(Icons.add),
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildSpeedDialItem(
    IconData icon,
    String label,
    Color color,
    VoidCallback onTap,
  ) {
    return Row(
      mainAxisSize: MainAxisSize.min,
      children: [
        Container(
          padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
          decoration: BoxDecoration(
            color: Colors.grey[800],
            borderRadius: BorderRadius.circular(4),
          ),
          child: Text(
            label,
            style: const TextStyle(color: Colors.white),
          ),
        ),
        const SizedBox(width: 12),
        FloatingActionButton.small(
          heroTag: label,
          backgroundColor: color,
          onPressed: () {
            _toggleExpanded();
            onTap();
          },
          child: Icon(icon),
        ),
      ],
    );
  }
}

八、实际应用场景

8.1 列表添加操作

class ListWithFab extends StatefulWidget {
  const ListWithFab({super.key});

  
  State<ListWithFab> createState() => _ListWithFabState();
}

class _ListWithFabState extends State<ListWithFab> {
  final List<String> _items = [];

  void _addItem() {
    setState(() {
      _items.add('项目 ${_items.length + 1}');
    });
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('列表')),
      body: _items.isEmpty
          ? const Center(child: Text('点击 + 添加项目'))
          : ListView.builder(
              itemCount: _items.length,
              itemBuilder: (context, index) {
                return ListTile(
                  title: Text(_items[index]),
                  trailing: IconButton(
                    icon: const Icon(Icons.delete),
                    onPressed: () {
                      setState(() {
                        _items.removeAt(index);
                      });
                    },
                  ),
                );
              },
            ),
      floatingActionButton: FloatingActionButton(
        onPressed: _addItem,
        child: const Icon(Icons.add),
      ),
    );
  }
}

8.2 编辑操作

class EditPage extends StatelessWidget {
  const EditPage({super.key});

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('编辑')),
      body: const Padding(
        padding: EdgeInsets.all(16),
        child: TextField(
          maxLines: 10,
          decoration: InputDecoration(
            hintText: '输入内容...',
            border: OutlineInputBorder(),
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          ScaffoldMessenger.of(context).showSnackBar(
            const SnackBar(content: Text('已保存')),
          );
        },
        child: const Icon(Icons.save),
      ),
    );
  }
}

8.3 导航操作

class NavigationPage extends StatelessWidget {
  const NavigationPage({super.key});

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('导航')),
      body: const Center(
        child: Text('点击按钮跳转到下一页'),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          Navigator.push(
            context,
            MaterialPageRoute(builder: (context) => const NextPage()),
          );
        },
        child: const Icon(Icons.arrow_forward),
      ),
    );
  }
}

class NextPage extends StatelessWidget {
  const NextPage({super.key});

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('下一页')),
      body: const Center(child: Text('这是下一页')),
    );
  }
}

8.4 分享操作

class SharePage extends StatelessWidget {
  const SharePage({super.key});

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('分享')),
      body: const Center(
        child: Text('点击按钮分享内容'),
      ),
      floatingActionButton: FloatingActionButton.extended(
        onPressed: () {
          showModalBottomSheet(
            context: context,
            builder: (context) {
              return SafeArea(
                child: Column(
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    ListTile(
                      leading: const Icon(Icons.chat),
                      title: const Text('微信'),
                      onTap: () => Navigator.pop(context),
                    ),
                    ListTile(
                      leading: const Icon(Icons.message),
                      title: const Text('短信'),
                      onTap: () => Navigator.pop(context),
                    ),
                    ListTile(
                      leading: const Icon(Icons.email),
                      title: const Text('邮件'),
                      onTap: () => Navigator.pop(context),
                    ),
                  ],
                ),
              );
            },
          );
        },
        icon: const Icon(Icons.share),
        label: const Text('分享'),
      ),
    );
  }
}

九、完整示例代码

下面是一个完整的 Flutter 应用示例,展示 FloatingActionButton 组件的各种用法。

import 'package:flutter/material.dart';

void main() {
  runApp(const FloatingActionButtonDemo());
}

class FloatingActionButtonDemo extends StatelessWidget {
  const FloatingActionButtonDemo({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'FloatingActionButton 组件演示',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        colorScheme: ColorScheme.light(
          primary: const Color(0xFF6366F1),
          secondary: const Color(0xFF8B5CF6),
          surface: const Color(0xFFE8EAF6),
          background: const Color(0xFFF8F9FF),
          brightness: Brightness.light,
        ),
        useMaterial3: true,
      ),
      home: const FloatingActionButtonPage(),
    );
  }
}

class FloatingActionButtonPage extends StatefulWidget {
  const FloatingActionButtonPage({super.key});

  
  State<FloatingActionButtonPage> createState() => _FloatingActionButtonPageState();
}

class _FloatingActionButtonPageState extends State<FloatingActionButtonPage> {
  int _counter = 0;
  int _selectedTab = 0;
  bool _speedDialExpanded = false;

  final List<String> _items = [];

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('FloatingActionButton 组件演示'),
        centerTitle: true,
        elevation: 0,
      ),
      body: Container(
        decoration: const BoxDecoration(
          gradient: LinearGradient(
            begin: Alignment.topCenter,
            end: Alignment.bottomCenter,
            colors: [
              Color(0xFFE8F4FF),
              Color(0xFFF8F9FF),
            ],
          ),
        ),
        child: SafeArea(
          child: _buildBody(),
        ),
      ),
      floatingActionButton: _buildFab(),
      floatingActionButtonLocation: _selectedTab == 2
          ? FloatingActionButtonLocation.centerFloat
          : FloatingActionButtonLocation.endFloat,
      bottomNavigationBar: _selectedTab == 3
          ? null
          : BottomNavigationBar(
              currentIndex: _selectedTab,
              onTap: (index) => setState(() => _selectedTab = index),
              selectedItemColor: const Color(0xFF6366F1),
              unselectedItemColor: Colors.grey,
              backgroundColor: Colors.white,
              type: BottomNavigationBarType.fixed,
              items: const [
                BottomNavigationBarItem(
                  icon: Icon(Icons.home),
                  label: '基础',
                ),
                BottomNavigationBarItem(
                  icon: Icon(Icons.list),
                  label: '列表',
                ),
                BottomNavigationBarItem(
                  icon: Icon(Icons.edit),
                  label: '扩展',
                ),
                BottomNavigationBarItem(
                  icon: Icon(Icons.more),
                  label: '速度拨',
                ),
              ],
            ),
    );
  }

  Widget _buildBody() {
    switch (_selectedTab) {
      case 0:
        return _buildBasicTab();
      case 1:
        return _buildListTab();
      case 2:
        return _buildExtendedTab();
      case 3:
        return _buildSpeedDialTab();
      default:
        return _buildBasicTab();
    }
  }

  Widget _buildBasicTab() {
    return SingleChildScrollView(
      padding: const EdgeInsets.all(20),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          _buildHeader(),
          const SizedBox(height: 24),
          _buildCounterCard(),
          const SizedBox(height: 16),
          _buildFabTypesCard(),
        ],
      ),
    );
  }

  Widget _buildHeader() {
    return Container(
      padding: const EdgeInsets.all(24),
      decoration: BoxDecoration(
        gradient: const LinearGradient(
          begin: Alignment.topLeft,
          end: Alignment.bottomRight,
          colors: [
            Color(0xFF6366F1),
            Color(0xFF8B5CF6),
            Color(0xFFEC4899),
          ],
        ),
        borderRadius: BorderRadius.circular(24),
        boxShadow: [
          BoxShadow(
            color: const Color(0xFF6366F1).withOpacity(0.3),
            blurRadius: 20,
            offset: const Offset(0, 8),
          ),
        ],
      ),
      child: const Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(
            '🔘 FloatingActionButton',
            style: TextStyle(
              fontSize: 28,
              fontWeight: FontWeight.bold,
              color: Colors.white,
            ),
          ),
          SizedBox(height: 8),
          Text(
            'Material Design 标志性的浮动操作按钮',
            style: TextStyle(
              fontSize: 16,
              color: Colors.white,
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildCounterCard() {
    return Card(
      elevation: 0,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(16),
        side: BorderSide(color: Colors.grey.withOpacity(0.2)),
      ),
      child: Padding(
        padding: const EdgeInsets.all(24),
        child: Column(
          children: [
            const Text(
              '计数器示例',
              style: TextStyle(
                fontSize: 18,
                fontWeight: FontWeight.w600,
              ),
            ),
            const SizedBox(height: 16),
            Text(
              '$_counter',
              style: const TextStyle(
                fontSize: 64,
                fontWeight: FontWeight.bold,
                color: Color(0xFF6366F1),
              ),
            ),
            const SizedBox(height: 16),
            const Text(
              '点击右下角的 FAB 增加计数',
              style: TextStyle(color: Colors.grey),
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildFabTypesCard() {
    return Card(
      elevation: 0,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(16),
        side: BorderSide(color: Colors.grey.withOpacity(0.2)),
      ),
      child: Padding(
        padding: const EdgeInsets.all(20),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            const Text(
              'FAB 类型',
              style: TextStyle(
                fontSize: 18,
                fontWeight: FontWeight.w600,
              ),
            ),
            const SizedBox(height: 16),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              children: [
                Column(
                  children: [
                    FloatingActionButton.small(
                      heroTag: 'small',
                      onPressed: () {},
                      child: const Icon(Icons.add, size: 20),
                    ),
                    const SizedBox(height: 8),
                    const Text('小型', style: TextStyle(fontSize: 12)),
                  ],
                ),
                Column(
                  children: [
                    FloatingActionButton(
                      heroTag: 'standard',
                      onPressed: () {},
                      child: const Icon(Icons.add),
                    ),
                    const SizedBox(height: 8),
                    const Text('标准', style: TextStyle(fontSize: 12)),
                  ],
                ),
                Column(
                  children: [
                    FloatingActionButton.large(
                      heroTag: 'large',
                      onPressed: () {},
                      child: const Icon(Icons.add, size: 32),
                    ),
                    const SizedBox(height: 8),
                    const Text('大型', style: TextStyle(fontSize: 12)),
                  ],
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildListTab() {
    return _items.isEmpty
        ? const Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Icon(Icons.inbox, size: 64, color: Colors.grey),
                SizedBox(height: 16),
                Text(
                  '列表为空',
                  style: TextStyle(fontSize: 18, color: Colors.grey),
                ),
                SizedBox(height: 8),
                Text(
                  '点击 FAB 添加项目',
                  style: TextStyle(color: Colors.grey),
                ),
              ],
            ),
          )
        : ListView.builder(
            padding: const EdgeInsets.all(16),
            itemCount: _items.length,
            itemBuilder: (context, index) {
              return Card(
                margin: const EdgeInsets.only(bottom: 8),
                child: ListTile(
                  leading: CircleAvatar(
                    backgroundColor: const Color(0xFF6366F1),
                    child: Text('${index + 1}'),
                  ),
                  title: Text(_items[index]),
                  trailing: IconButton(
                    icon: const Icon(Icons.delete, color: Colors.red),
                    onPressed: () {
                      setState(() {
                        _items.removeAt(index);
                      });
                      ScaffoldMessenger.of(context).showSnackBar(
                        const SnackBar(content: Text('已删除')),
                      );
                    },
                  ),
                ),
              );
            },
          );
  }

  Widget _buildExtendedTab() {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          const Text(
            '扩展 FAB 示例',
            style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
          ),
          const SizedBox(height: 32),
          FloatingActionButton.extended(
            heroTag: 'extended1',
            onPressed: () {},
            icon: const Icon(Icons.add),
            label: const Text('添加项目'),
          ),
          const SizedBox(height: 16),
          FloatingActionButton.extended(
            heroTag: 'extended2',
            onPressed: () {},
            backgroundColor: Colors.green,
            icon: const Icon(Icons.share),
            label: const Text('分享'),
          ),
          const SizedBox(height: 16),
          FloatingActionButton.extended(
            heroTag: 'extended3',
            onPressed: () {},
            backgroundColor: Colors.orange,
            icon: const Icon(Icons.edit),
            label: const Text('编辑'),
          ),
        ],
      ),
    );
  }

  Widget _buildSpeedDialTab() {
    return const Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Icon(Icons.touch_app, size: 64, color: Colors.grey),
          SizedBox(height: 16),
          Text(
            '速度拨示例',
            style: TextStyle(fontSize: 18, color: Colors.grey),
          ),
          SizedBox(height: 8),
          Text(
            '点击 FAB 展开更多选项',
            style: TextStyle(color: Colors.grey),
          ),
        ],
      ),
    );
  }

  Widget _buildFab() {
    if (_selectedTab == 3) {
      return _buildSpeedDialFab();
    }

    if (_selectedTab == 2) {
      return FloatingActionButton.extended(
        onPressed: () {
          ScaffoldMessenger.of(context).showSnackBar(
            const SnackBar(content: Text('扩展 FAB 被点击')),
          );
        },
        icon: const Icon(Icons.check),
        label: const Text('保存'),
      );
    }

    if (_selectedTab == 1) {
      return FloatingActionButton(
        onPressed: () {
          setState(() {
            _items.add('项目 ${_items.length + 1}');
          });
          ScaffoldMessenger.of(context).showSnackBar(
            const SnackBar(content: Text('已添加')),
          );
        },
        child: const Icon(Icons.add),
      );
    }

    return FloatingActionButton(
      onPressed: () {
        setState(() {
          _counter++;
        });
      },
      child: const Icon(Icons.add),
    );
  }

  Widget _buildSpeedDialFab() {
    return Column(
      mainAxisSize: MainAxisSize.min,
      crossAxisAlignment: CrossAxisAlignment.end,
      children: [
        if (_speedDialExpanded) ...[
          _buildSpeedDialItem(
            Icons.email,
            '发送邮件',
            Colors.blue,
          ),
          const SizedBox(height: 12),
          _buildSpeedDialItem(
            Icons.message,
            '发送消息',
            Colors.green,
          ),
          const SizedBox(height: 12),
          _buildSpeedDialItem(
            Icons.share,
            '分享',
            Colors.orange,
          ),
          const SizedBox(height: 12),
        ],
        FloatingActionButton(
          onPressed: () {
            setState(() {
              _speedDialExpanded = !_speedDialExpanded;
            });
          },
          child: AnimatedRotation(
            turns: _speedDialExpanded ? 0.125 : 0,
            duration: const Duration(milliseconds: 200),
            child: const Icon(Icons.add),
          ),
        ),
      ],
    );
  }

  Widget _buildSpeedDialItem(IconData icon, String label, Color color) {
    return Row(
      mainAxisSize: MainAxisSize.min,
      children: [
        Container(
          padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
          decoration: BoxDecoration(
            color: Colors.grey[800],
            borderRadius: BorderRadius.circular(4),
          ),
          child: Text(
            label,
            style: const TextStyle(color: Colors.white),
          ),
        ),
        const SizedBox(width: 12),
        FloatingActionButton.small(
          heroTag: label,
          backgroundColor: color,
          onPressed: () {
            setState(() {
              _speedDialExpanded = false;
            });
            ScaffoldMessenger.of(context).showSnackBar(
              SnackBar(content: Text('$label 被点击')),
            );
          },
          child: Icon(icon),
        ),
      ],
    );
  }
}

十、总结

FloatingActionButton 是 Flutter for OpenHarmony 应用开发中 Material Design 风格的重要组件。通过本文的学习,我们掌握了:

  1. 基础用法:FloatingActionButton 的基本属性和使用方式
  2. 常用属性:onPressed、child、backgroundColor、mini 等
  3. 位置控制:floatingActionButtonLocation 的各种位置选项
  4. 扩展类型:extended、large、small 等不同尺寸的 FAB
  5. 配合使用:与 BottomAppBar 配合创建凹槽效果
  6. 速度拨按钮:展开式多操作按钮的实现
  7. 实际应用:列表添加、编辑保存、导航跳转、分享操作等场景

💡 开发建议:使用 FloatingActionButton 时应注意:

  • 每个页面只使用一个主 FAB
  • FAB 代表页面的最主要操作
  • 合理选择 FAB 的尺寸和位置
  • 使用 heroTag 避免页面切换时的动画冲突
  • 配合 BottomAppBar 创建更丰富的底部导航
Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐