如何在Flutter中点击按钮创建底部滑起半面板?
在Flutter中实现点击触发滑起式底部组件
方案一:用官方showModalBottomSheet快速实现
这是最省心的方式,官方已经封装好滑入滑出动画,还能自定义内容样式,完全匹配动图里的效果。
核心代码示例
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: Scaffold( appBar: AppBar(title: const Text('滑起组件示例')), body: Center( child: ElevatedButton( onPressed: () { showModalBottomSheet( context: context, // 设置顶部圆角,和动图效果对齐 shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(16)), ), // 支持拖拽调整高度或关闭 isScrollControlled: true, builder: (context) { // 自定义滑起组件的内部内容 return Container( padding: const EdgeInsets.all(16), height: 300, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 顶部拖拽指示器 Center( child: Container( width: 40, height: 4, decoration: BoxDecoration( color: Colors.grey[300], borderRadius: BorderRadius.circular(2), ), ), ), const SizedBox(height: 16), const Text( '操作选项', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), const SizedBox(height: 12), Expanded( child: ListView( children: const [ ListTile(title: Text('选项1')), ListTile(title: Text('选项2')), ListTile(title: Text('选项3')), ListTile(title: Text('选项4')), ], ), ), ], ), ); }, ); }, child: const Text('弹出滑起组件'), ), ), ), ); } }
关键参数说明
shape: 配置顶部圆角,还原动图里的圆角样式isScrollControlled: 设置为true后,组件可拖拽调整高度,也支持内容滚动builder: 自定义滑起组件的所有内容,表单、列表、按钮都能加
方案二:自定义动画实现个性化效果
如果需要更灵活的动画(比如调整滑入速度、自定义曲线),可以用AnimationController和SlideTransition自己实现:
核心代码示例
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 CustomBottomSlidePage()); } } class CustomBottomSlidePage extends StatefulWidget { const CustomBottomSlidePage({super.key}); @override State<CustomBottomSlidePage> createState() => _CustomBottomSlidePageState(); } class _CustomBottomSlidePageState extends State<CustomBottomSlidePage> with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation<Offset> _slideAnimation; bool _isSheetShow = false; @override void initState() { super.initState(); _controller = AnimationController( duration: const Duration(milliseconds: 350), vsync: this, ); _slideAnimation = Tween<Offset>( begin: const Offset(0, 1), end: Offset.zero, ).animate(CurvedAnimation( parent: _controller, curve: Curves.easeOutCubic, )); } @override void dispose() { _controller.dispose(); super.dispose(); } void _toggleSheet() { setState(() { _isSheetShow = !_isSheetShow; }); _isSheetShow ? _controller.forward() : _controller.reverse(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('自定义滑起组件')), body: Stack( children: [ Center( child: ElevatedButton( onPressed: _toggleSheet, child: const Text('弹出滑起组件'), ), ), // 点击遮罩关闭组件 if (_isSheetShow) GestureDetector( onTap: _toggleSheet, child: Container(color: Colors.black.withOpacity(0.5)), ), // 滑起组件本体 SlideTransition( position: _slideAnimation, child: _isSheetShow ? Container( height: 300, decoration: const BoxDecoration( color: Colors.white, borderRadius: BorderRadius.vertical(top: Radius.circular(16)), ), child: Column( children: [ const SizedBox(height: 8), Center( child: Container( width: 40, height: 4, decoration: BoxDecoration( color: Colors.grey[300], borderRadius: BorderRadius.circular(2), ), ), ), Expanded( child: ListView( children: const [ ListTile(title: Text('自定义选项1')), ListTile(title: Text('自定义选项2')), ListTile(title: Text('自定义选项3')), ], ), ), ], ), ) : const SizedBox.shrink(), ), ], ), ); } }
自定义方案优势
- 完全控制动画的时长、曲线,做出更独特的滑入效果
- 自定义遮罩层的透明度、点击逻辑
- 组件显示/隐藏的逻辑更灵活,可结合业务需求调整
内容的提问来源于stack exchange,提问作者Sunken In Time
相关产品推荐
相关产品推荐

