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

Flutter单页面能否实现两个从右侧滑出的End Drawer?

Flutter实现第二个右侧滑出抽屉的方案

Flutter的Scaffold默认仅支持一个左侧抽屉(drawer)和一个右侧抽屉(endDrawer),要实现第二个从右侧滑出的抽屉,需要自定义滑出逻辑,以下提供两种可行方案:

方案一:使用DrawerController控制额外Drawer

利用Flutter自带的DrawerController组件,直接管理第二个Drawer的显示状态,快速实现右侧滑出效果。

代码示例

import 'package:flutter/material.dart';

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

  @override
  State<DoubleEndDrawerPage> createState() => _DoubleEndDrawerPageState();
}

class _DoubleEndDrawerPageState extends State<DoubleEndDrawerPage> {
  final scaffoldKey = GlobalKey<ScaffoldState>();
  // 第二个右侧抽屉的控制器Key
  final secondEndDrawerKey = GlobalKey<DrawerControllerState>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: scaffoldKey,
      appBar: AppBar(
        title: const Text("双右侧抽屉示例"),
        actions: [
          IconButton(
            onPressed: () {
              scaffoldKey.currentState?.openEndDrawer();
            },
            icon: const Icon(Icons.filter_alt_outlined),
          ),
        ],
      ),
      // 第一个右侧抽屉
      endDrawer: Drawer(
        child: Container(
          color: Colors.lightBlue.shade50,
          child: const Center(child: Text("第一个右侧抽屉")),
        ),
      ),
      body: Stack(
        children: [
          // 原页面主体内容
          Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Center(
                child: IconButton(
                  icon: const Icon(Icons.sort, size: 32),
                  onPressed: () {
                    // 触发打开第二个右侧抽屉
                    secondEndDrawerKey.currentState?.open();
                  },
                ),
              ),
              const Text("点击按钮打开第二个右侧抽屉"),
            ],
          ),
          // 第二个右侧抽屉
          DrawerController(
            key: secondEndDrawerKey,
            // 设置抽屉从右侧滑出
            alignment: Alignment.centerRight,
            child: Drawer(
              child: Container(
                width: MediaQuery.of(context).size.width * 0.7,
                color: Colors.orange.shade50,
                child: Column(
                  children: [
                    AppBar(
                      automaticallyImplyLeading: false,
                      actions: [
                        IconButton(
                          icon: const Icon(Icons.close),
                          onPressed: () {
                            secondEndDrawerKey.currentState?.close();
                          },
                        ),
                      ],
                    ),
                    const Expanded(child: Center(child: Text("第二个右侧抽屉"))),
                  ],
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

方案二:自定义SlideTransition实现滑出动画

如果需要更灵活的动画效果(比如调整动画时长、曲线),可以使用SlideTransition结合AnimationController实现自定义右侧滑出抽屉,还能自由添加遮罩层。

代码示例

import 'package:flutter/material.dart';

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

  @override
  State<CustomEndDrawerPage> createState() => _CustomEndDrawerPageState();
}

class _CustomEndDrawerPageState extends State<CustomEndDrawerPage> with SingleTickerProviderStateMixin {
  final scaffoldKey = GlobalKey<ScaffoldState>();
  late AnimationController _animationController;
  late Animation<Offset> _slideAnimation;
  bool _isSecondDrawerOpen = false;

  @override
  void initState() {
    super.initState();
    // 初始化动画控制器
    _animationController = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 300),
    );
    // 定义从右侧滑入的动画轨迹
    _slideAnimation = Tween<Offset>(
      begin: const Offset(1, 0), // 初始状态在屏幕右侧外部
      end: Offset.zero, // 滑入屏幕内的目标位置
    ).animate(CurvedAnimation(
      parent: _animationController,
      curve: Curves.easeInOut,
    ));
  }

  // 打开第二个抽屉
  void _openSecondDrawer() {
    setState(() => _isSecondDrawerOpen = true);
    _animationController.forward();
  }

  // 关闭第二个抽屉
  void _closeSecondDrawer() {
    _animationController.reverse().then((_) {
      setState(() => _isSecondDrawerOpen = false);
    });
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: scaffoldKey,
      appBar: AppBar(
        title: const Text("自定义右侧抽屉"),
        actions: [
          IconButton(
            onPressed: () {
              scaffoldKey.currentState?.openEndDrawer();
            },
            icon: const Icon(Icons.filter_alt_outlined),
          ),
        ],
      ),
      endDrawer: Drawer(
        child: Container(
          color: Colors.lightBlue.shade50,
          child: const Center(child: Text("第一个右侧抽屉")),
        ),
      ),
      body: Stack(
        children: [
          // 原页面主体内容
          Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Center(
                child: IconButton(
                  icon: const Icon(Icons.sort, size: 32),
                  onPressed: _openSecondDrawer,
                ),
              ),
              const Text("点击按钮打开第二个右侧抽屉"),
            ],
          ),
          // 遮罩层:抽屉打开时点击空白处关闭
          if (_isSecondDrawerOpen)
            GestureDetector(
              onTap: _closeSecondDrawer,
              child: Container(
                color: Colors.black54,
                width: double.infinity,
                height: double.infinity,
              ),
            ),
          // 第二个右侧抽屉
          SlideTransition(
            position: _slideAnimation,
            child: Container(
              width: MediaQuery.of(context).size.width * 0.75,
              color: Colors.white,
              child: Column(
                children: [
                  AppBar(
                    backgroundColor: Colors.orange.shade100,
                    automaticallyImplyLeading: false,
                    actions: [
                      IconButton(
                        icon: const Icon(Icons.close),
                        onPressed: _closeSecondDrawer,
                      ),
                    ],
                    title: const Text("第二个抽屉"),
                  ),
                  const Expanded(child: Center(child: Text("自定义动画的右侧抽屉"))),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }
}

内容的提问来源于stack exchange,提问作者Leong

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 17:15:34