如何为布局变化导致位置变更的Flutter Widget添加位移动画
Flutter 键盘弹出/收起时动态偏移Widget的平滑动画实现方案
你的场景核心矛盾是无法提前确定位移终态,所以无需使用需要显式设置终态的显式动画组件(如SlideTransition),采用Flutter自带的隐式动画组件即可实现需求,它会自动监听属性变化、生成补间动画,完全适配动态计算的偏移场景。
实现思路
- 关闭Scaffold默认的键盘顶起行为,避免系统默认的生硬跳转
- 通过
MediaQuery.of(context).viewInsets.bottom实时获取当前键盘高度,无需手动计算偏移量 - 用隐式动画组件包裹目标Widget(放大镜+文字组),动态传入位置参数即可自动生成过渡动画
示例代码
import 'package:flutter/material.dart'; class SearchPage extends StatelessWidget { const SearchPage({super.key}); @override Widget build(BuildContext context) { // 实时获取当前键盘弹出高度,键盘收起时该值为0 final keyboardHeight = MediaQuery.of(context).viewInsets.bottom; return Scaffold( // 关闭系统默认的布局resize行为,避免生硬跳转 resizeToAvoidBottomInset: false, body: LayoutBuilder( builder: (context, constraints) { // 计算键盘弹出后的页面可用高度 final availableHeight = constraints.maxHeight - keyboardHeight; return Stack( children: [ // 顶部搜索框 const Positioned( top: 24, left: 16, right: 16, child: TextField( decoration: InputDecoration( hintText: '请输入搜索内容', border: OutlineInputBorder(), contentPadding: EdgeInsets.symmetric(horizontal: 12), ), ), ), // 目标动画组件:放大镜+文字组 AnimatedPositioned( // 动态计算垂直居中位置:(可用高度 - 组件高度) / 2 // 如果你不知道组件实际高度,可以通过GlobalKey在布局完成后动态获取 top: (availableHeight - 100) / 2, left: 0, right: 0, // 动画时长和系统键盘动画时长保持一致,体验更连贯 duration: const Duration(milliseconds: 250), curve: Curves.easeInOut, child: Column( mainAxisSize: MainAxisSize.min, children: const [ Icon(Icons.search, size: 48, color: Colors.grey), SizedBox(height: 12), Text( '搜索你感兴趣的内容', style: TextStyle( color: Colors.grey, fontSize: 14, ), ), ], ), ) ], ); }, ), ); } }
优化提示
- 若不需要嵌套Stack,也可以用
AnimatedPadding包裹目标组件,设置padding: EdgeInsets.only(bottom: keyboardHeight * 0.5),调整比例即可控制组件上移幅度,同样会自动生成过渡动画 - 若页面全量内容都需要跟随键盘平滑偏移,直接将整个body用
AnimatedPadding包裹,设置padding: EdgeInsets.only(bottom: keyboardHeight)即可实现全局平滑过渡效果 - 如需获取目标组件的实际高度,给组件绑定
GlobalKey,在WidgetsBinding.instance.addPostFrameCallback回调中通过key.currentContext?.size获取后再计算位置即可
内容的提问来源于stack exchange,提问作者Danny Allen
相关产品推荐
相关产品推荐

