Flutter Web:创建独立无状态类的右侧半屏自定义BottomSheet
实现从右侧滑入的自定义BottomSheet(Flutter Web)
核心思路
默认BottomSheet无法满足右侧滑入需求,我们通过Overlay+动画组件实现:
- 用
OverlayEntry在页面顶层插入自定义弹窗 - 通过
AnimatedPositioned控制弹窗从屏幕右侧滑入/滑出的动画 - 弹窗宽度设为屏幕宽度的50%,覆盖右侧半屏
完整代码实现
1. 自定义右侧滑入BottomSheet组件
import 'package:flutter/material.dart'; class RightSlideBottomSheet extends StatelessWidget { final Widget child; final bool isVisible; final VoidCallback onClose; const RightSlideBottomSheet({ super.key, required this.child, required this.isVisible, required this.onClose, }); @override Widget build(BuildContext context) { final screenWidth = MediaQuery.of(context).size.width; final sheetWidth = screenWidth * 0.5; return GestureDetector( // 点击遮罩关闭弹窗 onTap: onClose, child: Stack( children: [ // 半透明遮罩层 Visibility( visible: isVisible, child: Container( width: double.infinity, height: double.infinity, color: Colors.black.withOpacity(0.5), ), ), // 右侧滑入的弹窗主体 AnimatedPositioned( duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, right: isVisible ? 0 : -sheetWidth, top: 0, bottom: 0, width: sheetWidth, child: GestureDetector( // 阻止弹窗内部点击触发遮罩关闭 onTap: () {}, child: Material( elevation: 8, child: child, ), ), ), ], ), ); } }
2. 集成InkWell触发逻辑(示例页面)
class DemoPage extends StatefulWidget { const DemoPage({super.key}); @override State<DemoPage> createState() => _DemoPageState(); } class _DemoPageState extends State<DemoPage> { bool _isSheetVisible = false; void _toggleSheet() { setState(() { _isSheetVisible = !_isSheetVisible; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text("右侧滑入BottomSheet示例")), body: Stack( children: [ // 页面主体内容 Center( child: InkWell( onTap: _toggleSheet, child: Container( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), decoration: BoxDecoration( color: Colors.blue, borderRadius: BorderRadius.circular(8), ), child: const Text( "点击打开右侧弹窗", style: TextStyle(color: Colors.white), ), ), ), ), // 插入自定义BottomSheet if (_isSheetVisible) RightSlideBottomSheet( isVisible: _isSheetVisible, onClose: _toggleSheet, child: Padding( padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( "右侧弹窗内容", style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), ), const SizedBox(height: 20), const Text("这里可以添加任意组件"), const Spacer(), Align( alignment: Alignment.bottomRight, child: TextButton( onPressed: _toggleSheet, child: const Text("关闭"), ), ), ], ), ), ), ], ), ); } }
关键说明
- 弹窗宽度通过
MediaQuery获取屏幕宽度后乘以0.5,适配不同尺寸的Web屏幕 - 动画使用
AnimatedPositioned实现平滑的滑入/滑出效果,时长300ms - 遮罩层点击可关闭弹窗,弹窗内部点击不会触发关闭,避免误操作
- 弹窗使用
Material组件包裹,保证Web端的视觉层级和阴影效果
内容的提问来源于stack exchange,提问作者farhan khan
相关产品推荐
相关产品推荐

