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

Flutter自定义DrawerButton的onTap页面跳转无效问题求助

解决Flutter Stack嵌套抽屉中自定义按钮无法跳转的问题

问题核心

你遇到的跳转失效问题,本质是自定义DrawerButton使用的context未关联到正确的Navigator上下文。由于抽屉是通过Stack嵌套实现的,DrawerButton所在的Widget上下文属于抽屉页面,不在根Navigator的上下文树中,导致Navigator.push无法找到路由栈。

解决方案(保留缩放动画)

以下两种方案都不会改动按钮的缩放动画逻辑,仅修复跳转的上下文问题:

方案1:使用全局NavigatorKey

通过全局Key直接获取根NavigatorState,彻底绕开context的限制:

  1. 在main.dart中定义全局Key并绑定到MaterialApp:
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      navigatorKey: navigatorKey, // 绑定全局导航Key
      title: 'Drawer Demo',
      home: const HomePage(),
    );
  }
}
  1. 修改DrawerButton的onTap回调:
DrawerButton(
  title: "跳转页面A",
  onTap: () {
    // 直接通过全局Key触发跳转
    navigatorKey.currentState?.push(
      MaterialPageRoute(builder: (_) => const PageA()),
    );
  },
),

方案2:用Builder获取正确上下文

如果不想用全局Key,可在DrawerButton外层包裹Builder组件,获取包含Navigator的上下文:

// 抽屉页面中的按钮部分
Column(
  children: [
    Builder(
      builder: (ctx) { // 这里的ctx是关联到Navigator的上下文
        return DrawerButton(
          title: "跳转页面A",
          onTap: () {
            Navigator.push(ctx, MaterialPageRoute(builder: (_) => const PageA()));
          },
        );
      },
    ),
    Builder(
      builder: (ctx) {
        return DrawerButton(
          title: "跳转页面B",
          onTap: () {
            Navigator.of(ctx).push(MaterialPageRoute(builder: (_) => const PageB()));
          },
        );
      },
    ),
  ],
),

完整示例代码

自定义DrawerButton(保留缩放动画)

class DrawerButton extends StatefulWidget {
  final String title;
  final VoidCallback? onTap;

  const DrawerButton({super.key, required this.title, this.onTap});

  @override
  State<DrawerButton> createState() => _DrawerButtonState();
}

class _DrawerButtonState extends State<DrawerButton> with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _scaleAnimation;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: const Duration(milliseconds: 150),
      vsync: this,
    );
    _scaleAnimation = Tween<double>(begin: 1.0, end: 0.95).animate(
      CurvedAnimation(parent: _controller, curve: Curves.easeInOut),
    );
  }

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

  void _handleTap() {
    _controller.forward().then((_) {
      _controller.reverse();
      widget.onTap?.call();
    });
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTapDown: (_) => _controller.forward(),
      onTapUp: (_) => _controller.reverse(),
      onTapCancel: () => _controller.reverse(),
      onTap: _handleTap,
      child: ScaleTransition(
        scale: _scaleAnimation,
        child: Container(
          width: double.infinity,
          padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 20),
          child: Text(
            widget.title,
            style: const TextStyle(fontSize: 18),
          ),
        ),
      ),
    );
  }
}

主页Stack抽屉实现

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

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  bool _isDrawerOpen = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: [
          // 抽屉页面
          Positioned.fill(
            child: Container(
              color: Colors.grey[100],
              padding: const EdgeInsets.only(top: 80),
              child: Column(
                children: [
                  // 用方案2的Builder方式
                  Builder(
                    builder: (ctx) {
                      return DrawerButton(
                        title: "跳转页面A",
                        onTap: () {
                          Navigator.push(ctx, MaterialPageRoute(builder: (_) => const PageA()));
                          // 跳转后关闭抽屉
                          setState(() => _isDrawerOpen = false);
                        },
                      );
                    },
                  ),
                  Builder(
                    builder: (ctx) {
                      return DrawerButton(
                        title: "跳转页面B",
                        onTap: () {
                          Navigator.of(ctx).push(MaterialPageRoute(builder: (_) => const PageB()));
                          setState(() => _isDrawerOpen = false);
                        },
                      );
                    },
                  ),
                ],
              ),
            ),
          ),
          // 主页面(可偏移)
          AnimatedPositioned(
            duration: const Duration(milliseconds: 300),
            left: _isDrawerOpen ? 280 : 0,
            right: _isDrawerOpen ? -280 : 0,
            top: 0,
            bottom: 0,
            child: Scaffold(
              appBar: AppBar(
                leading: IconButton(
                  icon: const Icon(Icons.menu),
                  onPressed: () => setState(() => _isDrawerOpen = !_isDrawerOpen),
                ),
                title: const Text("主页"),
              ),
              body: const Center(child: Text("主页内容")),
            ),
          ),
        ],
      ),
    );
  }
}

// 示例跳转页面
class PageA extends StatelessWidget {
  const PageA({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(appBar: AppBar(title: const Text("页面A")), body: const Center(child: Text("这是页面A")));
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(appBar: AppBar(title: const Text("页面B")), body: const Center(child: Text("这是页面B")));
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 09:01:35