You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Flutter中创建位于AppBar与底部TabNavigation间的自定义Drawer?

在Flutter中实现AppBar与底部Tab之间的自定义抽屉

要实现这种夹在AppBar和底部导航栏之间的抽屉,核心是利用Scaffold的结构特性——它的body区域天然处于AppBar下方、底部导航栏上方,我们只需要在body里嵌套Stack来放置主内容和抽屉组件即可,无需给整个页面套Stack。

实现步骤

  • 基础结构搭建:用Scaffold定义AppBar和底部导航栏,将body设为Stack,内部包含主内容容器和抽屉组件。
  • 抽屉定位与动画:用AnimatedPositioned控制抽屉的位置,通过状态变量切换抽屉的展开/收起状态,实现平滑滑动效果。
  • 交互增强:给主内容添加遮罩,点击遮罩可关闭抽屉;支持手势滑动或按钮触发抽屉状态切换。

完整代码示例

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: const CustomDrawerPage(),
      theme: ThemeData(primarySwatch: Colors.blue),
    );
  }
}

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

  @override
  State<CustomDrawerPage> createState() => _CustomDrawerPageState();
}

class _CustomDrawerPageState extends State<CustomDrawerPage> {
  bool _isDrawerOpen = false;
  final double _drawerWidth = 280;

  // 切换抽屉状态
  void _toggleDrawer() {
    setState(() {
      _isDrawerOpen = !_isDrawerOpen;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('自定义抽屉示例'),
        leading: IconButton(
          icon: const Icon(Icons.menu),
          onPressed: _toggleDrawer,
        ),
      ),
      bottomNavigationBar: BottomNavigationBar(
        items: const [
          BottomNavigationBarItem(icon: Icon(Icons.home), label: '首页'),
          BottomNavigationBarItem(icon: Icon(Icons.settings), label: '设置'),
        ],
      ),
      body: Stack(
        children: [
          // 主内容区域
          GestureDetector(
            onTap: _isDrawerOpen ? _toggleDrawer : null,
            child: Container(
              color: Colors.white,
              child: const Center(
                child: Text('主内容区域'),
              ),
            ),
          ),
          // 抽屉遮罩(仅抽屉展开时显示)
          if (_isDrawerOpen)
            GestureDetector(
              onTap: _toggleDrawer,
              child: Container(
                color: Colors.black.withOpacity(0.5),
              ),
            ),
          // 自定义抽屉
          AnimatedPositioned(
            duration: const Duration(milliseconds: 300),
            curve: Curves.easeInOut,
            left: _isDrawerOpen ? 0 : -_drawerWidth,
            top: 0,
            bottom: 0,
            width: _drawerWidth,
            child: Container(
              color: Colors.blue[50],
              child: ListView(
                padding: EdgeInsets.zero,
                children: [
                  const DrawerHeader(
                    decoration: BoxDecoration(color: Colors.blue),
                    child: Text('抽屉头部', style: TextStyle(color: Colors.white, fontSize: 20)),
                  ),
                  ListTile(
                    leading: const Icon(Icons.person),
                    title: const Text('个人中心'),
                    onTap: _toggleDrawer,
                  ),
                  ListTile(
                    leading: const Icon(Icons.history),
                    title: const Text('历史记录'),
                    onTap: _toggleDrawer,
                  ),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }
}

关键细节说明

  • 层级控制:Stack中组件的顺序决定了显示层级,遮罩和抽屉要放在主内容之后,这样才能覆盖主内容。
  • 动画效果:AnimatedPositioned通过left属性的变化实现抽屉滑入滑出,搭配curve参数让动画更自然。
  • 交互逻辑:主内容区域在抽屉展开时,点击会触发关闭;遮罩层专门用来处理点击关闭抽屉的操作,提升用户体验。

内容的提问来源于stack exchange,提问作者José Ferreira

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 03:32:47