Flutter中为Stateful Widget添加下拉显隐动画及移除多余间距
问题解决:为Flutter下拉容器添加动画并移除多余间距
核心需求
- 为控制显隐的Container添加类似下拉菜单的平滑过渡动画
- 移除Divider与Container之间的多余间距
- 当前使用GetX的
obs状态管理,仅了解AnimatedContainer,不确定具体实现方式
解决方案
1. 实现下拉显隐动画
AnimatedContainer完全适用,结合GetX的状态监听即可实现自然的下拉动画:
- 隐藏时设置高度为0,显示时设为
double.infinity让容器自适应内容高度 - 添加
clipBehavior: Clip.hardEdge避免内容溢出 - 使用
Curves.easeInOut曲线实现流畅的过渡效果
2. 移除多余间距
原布局的间隙来自Widget默认的布局间距,只需两步即可消除:
- 给Divider设置
height: 1,取消其默认的上下留白 - 给Container设置
margin: EdgeInsets.only(top: 0),彻底消除与Divider的间距
修改后的完整代码
主Widget代码片段
Padding( padding: const EdgeInsets.only(left: 22.0, right: 22.0, top: 15.0, bottom: 5.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Job details ', style: GoogleFonts.poppins( fontSize: 16, fontWeight: FontWeight.w600, color: Color(0xff34495E)), ), IconButton( onPressed: controller.toggleDropDownVisibility, icon: Obx(() => Icon( controller.isDropDownVisible.value ? Icons.expand_less : Icons.expand_more, color: Color(0xff3498DB), )), ) ], ), ), Divider( color: Color(0xff34495E).withOpacity(0.1), thickness: 2, height: 1, // 消除Divider默认上下间距 ), Obx(() => AnimatedContainer( duration: Duration(milliseconds: 300), curve: Curves.easeInOut, height: controller.isDropDownVisible.value ? double.infinity : 0, clipBehavior: Clip.hardEdge, margin: EdgeInsets.only(top: 0), color: Color(0xffF5F5F5), child: Padding( padding: const EdgeInsets.only(left: 22.0, right: 22), child: Table( border: TableBorder( horizontalInside: BorderSide( strokeAlign: 10, color: Color(0xff34495E).withOpacity(0.1), ), ), defaultColumnWidth: const IntrinsicColumnWidth(), children: [ TableRow( children: [ Padding( padding: EdgeInsets.all(8.0), child: Text( 'Title', style: GoogleFonts.poppins( fontSize: 10, fontWeight: FontWeight.w400, fontStyle: FontStyle.italic, color: Color(0xff34495E).withOpacity(0.8)), ), ), Padding( padding: EdgeInsets.all(8.0), child: Text( 'Asoebi Gown', style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w400, color: Color(0xff34495E).withOpacity(0.8)), ), ), ], ), TableRow( children: [ Padding( padding: EdgeInsets.all(8.0), child: Text( 'Quantity', style: GoogleFonts.poppins( fontSize: 10, fontWeight: FontWeight.w400, fontStyle: FontStyle.italic, color: Color(0xff34495E).withOpacity(0.8)), ), ), Padding( padding: EdgeInsets.all(8.0), child: Text( '12', style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w400, color: Color(0xff34495E).withOpacity(0.8)), ), ), ], ), TableRow( children: [ Padding( padding: EdgeInsets.all(8.0), child: Text( 'Description', style: GoogleFonts.poppins( fontSize: 10, fontWeight: FontWeight.w400, fontStyle: FontStyle.italic, color: Color(0xff34495E).withOpacity(0.8)), ), ), Padding( padding: EdgeInsets.all(8.0), child: Text( 'Praesent vitae neque porta, hendrerit enim sed, temps ante. Suspendisse vitae massa neque. Praesent vitae neque porta, hendrerit enim sed, tempus ante.', style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w400, color: Color(0xff34495E).withOpacity(0.8)), ), ), ], ), TableRow( children: [ Padding( padding: EdgeInsets.all(8.0), child: Text( 'Phone', style: GoogleFonts.poppins( fontSize: 10, fontWeight: FontWeight.w400, fontStyle: FontStyle.italic, color: Color(0xff34495E).withOpacity(0.8)), ), ), Padding( padding: EdgeInsets.all(8.0), child: Text( '09077338833', style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w400, color: Color(0xff34495E).withOpacity(0.8)), ), ), ], ), TableRow( children: [ Padding( padding: EdgeInsets.all(8.0), child: Text( 'Date to complete', style: GoogleFonts.poppins( fontSize: 10, fontWeight: FontWeight.w400, fontStyle: FontStyle.italic, color: Color(0xff34495E).withOpacity(0.8)), ), ), TableCell( verticalAlignment: TableCellVerticalAlignment.fill, child: Row( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Padding( padding: EdgeInsets.all(8.0), child: Text( '05-03-22', style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w400, color: Color(0xff34495E) .withOpacity(0.8)), ), ), Container( decoration: BoxDecoration( color: Color.fromRGBO( 50, 151, 219, 0.09), borderRadius: BorderRadius.circular(20), ), child: Padding( padding: const EdgeInsets.all(8.0), child: Text( '15 days', style: GoogleFonts.poppins( fontSize: 10, fontWeight: FontWeight.w400, fontStyle: FontStyle.italic, color: Color(0xff34495E)), ), ), ), ], )), ], ), ], ), ), )),
GetX控制器代码(无需修改)
final isDropDownVisible = false.obs; void toggleDropDownVisibility() { isDropDownVisible.value = !isDropDownVisible.value; }
关键优化点说明
- 移除冗余的
setState,直接通过GetX的Obx监听状态更新UI - 将两个IconButton合并为一个,动态切换图标简化代码
- 通过Divider的
height和Container的margin彻底消除布局间隙 - 利用
AnimatedContainer的高度过渡实现原生下拉动画效果
内容的提问来源于stack exchange,提问作者tesseract
相关产品推荐
相关产品推荐

