Flutter自定义DrawerButton的onTap页面跳转无效问题求助
解决Flutter Stack嵌套抽屉中自定义按钮无法跳转的问题
问题核心
你遇到的跳转失效问题,本质是自定义DrawerButton使用的context未关联到正确的Navigator上下文。由于抽屉是通过Stack嵌套实现的,DrawerButton所在的Widget上下文属于抽屉页面,不在根Navigator的上下文树中,导致Navigator.push无法找到路由栈。
解决方案(保留缩放动画)
以下两种方案都不会改动按钮的缩放动画逻辑,仅修复跳转的上下文问题:
方案1:使用全局NavigatorKey
通过全局Key直接获取根NavigatorState,彻底绕开context的限制:
- 在
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(), ); } }
- 修改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
相关产品推荐
相关产品推荐

