Flutter 状态栏与 FloatingActionButton 的布局问题?
flutter 状态栏问题当我设置0的时候 floatingActionButton 显示正常当我设置一点高度为什么会这样?
这是什么原因导致的?
希望知道原因 我想 floatingActionButton 可以在Appbar下边
建议:
不推荐下面的写法,因为AppBar已经实现了PreferredSizeWidget(Widgets like AppBar implement a PreferredSizeWidget, so that this PreferredSize widget is not necessary for them)
PreferredSize( preferredSize: const Size.fromHeight(1), child: AppBar( centerTitle: true, title: const Text('状态栏与FloatingActionButton 的布局问题?'), backgroundColor: Colors.yellow, ), ),
可以像下面这样自定义的AppBar,参考这个例子
PreferredSize( preferredSize: const Size.fromHeight(1), child: YourCustomWigde( child: child, ), ),
如果非要要preferredSize的child是一个AppBar可以像下面这样,也可以把ConstrainedBox 换成SizedBox(height:0);其实就是 Size.fromHeight(0)的效果,把0换成其他的数字也是可以的
PreferredSize( preferredSize: const Size.fromHeight(1), child: ConstrainedBox( constraints: const BoxConstraints(maxHeight: 0), child: AppBar( centerTitle: true, title: const Text('状态栏与FloatingActionButton 的布局问题?'), backgroundColor: Colors.yellow, ), ), ),
为什么要添加ConstrainedBox、SizedBox :
下面是我的Build方法实现,和你上面的不太一样;如果把ConstrainedBox去掉就会出现类似你那种的情况
Widget build(BuildContext context) {
return Scaffold(
appBar: PreferredSize(
preferredSize: const Size.fromHeight(1),
child: ConstrainedBox(
constraints: const BoxConstraints(maxHeight: 0),
child: AppBar(
centerTitle: true,
title: const Text('状态栏与FloatingActionButton 的布局问题?'),
backgroundColor: Colors.yellow,
),
),
),
body: const Center(
child: Text('This is Content'),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerTop,
floatingActionButton: const ColoredBox(
color: Colors.red,
child: Column(
children: [
Text('A'),
Text('B'),
Text('C'),
Text('DDDDDDDDDDDDDDDDD'),
Text('E'),
Text(
'F',
style: TextStyle(color: Colors.yellow),
),
],
),
),
);
}
- 现在的出现的问题是FloatingActionButton位置不对,内容被部分遮挡了,第一想到就是添加SafeArea避免被遮挡,但是没有效果;(我猜想Scaffold类似一个Stack组件而FloatingActionButton是一个Position组件)
- 打开Android Studio的Flutter Inspector查看FloatingActionButton布局后的数据也就是查看top,bottom,width,height;但没有找到top,bootom的数据,只有ColoredBox的Size(width,height)(
如果你知道的话请告知🙏
) 接下来打断点,跟着代码运行一下看看FloatingActionButton布局时的top数据; 下面是scaffold.dart文件中 performLayout方法部分实现,我的flutter版本:3.19.5
if (hasChild(_ScaffoldSlot.floatingActionButton)) { final Size fabSize = layoutChild(_ScaffoldSlot.floatingActionButton, looseConstraints); // To account for the FAB position being changed, we'll animate between // the old and new positions. final ScaffoldPrelayoutGeometry currentGeometry = ScaffoldPrelayoutGeometry( bottomSheetSize: bottomSheetSize, contentBottom: contentBottom, /// [appBarHeight] should be used instead of [contentTop] because /// ScaffoldPrelayoutGeometry.contentTop must not be affected by [extendBodyBehindAppBar]. contentTop: appBarHeight, floatingActionButtonSize: fabSize, minInsets: minInsets, scaffoldSize: size, snackBarSize: snackBarSize, materialBannerSize: materialBannerSize, textDirection: textDirection, minViewPadding: minViewPadding, ); final Offset currentFabOffset = currentFloatingActionButtonLocation.getOffset(currentGeometry); final Offset previousFabOffset = previousFloatingActionButtonLocation.getOffset(currentGeometry); final Offset fabOffset = floatingActionButtonMotionAnimator.getOffset( begin: previousFabOffset, end: currentFabOffset, progress: floatingActionButtonMoveAnimationProgress, ); positionChild(_ScaffoldSlot.floatingActionButton, fabOffset); floatingActionButtonRect = fabOffset & fabSize; }
其中floatingActionButton的位置由fabOffset和fabSize决定;而fabSize只有宽度和高度; 我门还需要fabOffset才能确定floatingActionButton的位置;previousFloatingActionButtonLocation、currentFloatingActionButtonLocation是为了计算之前和现在位置的差值进行动画用的;例子中的floatingActionButton的位置是固定的;所以currentFloatingActionButtonLocation 数值等于previousFloatingActionButtonLocation 并且等于 fabOffset(根据显示的数据能判断是相等的,内部实现代码省略); 所以获取currentFloatingActionButtonLocation就可以获取floatingActionButton的top值
; 下面代码是getOffset的实现;位于floting_action_button_location.dart 文件中
Offset getOffset(ScaffoldPrelayoutGeometry scaffoldGeometry) {
final double adjustment = isMini() ? kMiniButtonOffsetAdjustment : 0.0;
return Offset(
getOffsetX(scaffoldGeometry, adjustment),
getOffsetY(scaffoldGeometry, adjustment),
);
}
因为例子中floatingActionButton的X轴方向布局是没有问题的,所以下面代码中只关心 getOffsetY(scaffoldGeometry, adjustment)的实现,此时 scaffoldGeometry.contentTop = 25,scaffoldGeometry.minViewPadding.top = 24;scaffoldGeometry.floatingActionButtonSize.height = 737.45454545;所以 scaffoldGeometry.contentTop - fabHalfHeight = -343.7
所以 currentFloatingActionButtonLocation 这个偏移量top值就是-343.7;也就是floatingActionButton的top值等于 -343.7, 这就造成了floatingActionButton显示不完全
mixin FabTopOffsetY on StandardFabLocation {
/// Calculates y-offset for [FloatingActionButtonLocation]s floating over
/// the transition between the [Scaffold.appBar] and the [Scaffold.body].
@override
double getOffsetY(ScaffoldPrelayoutGeometry scaffoldGeometry, double adjustment) {
if (scaffoldGeometry.contentTop > scaffoldGeometry.minViewPadding.top) {
final double fabHalfHeight = scaffoldGeometry.floatingActionButtonSize.height / 2.0;
return scaffoldGeometry.contentTop - fabHalfHeight;
}
// Otherwise, ensure we are placed within the bounds of a safe area.
return scaffoldGeometry.minViewPadding.top;
}
}
看一下上面scaffold.dart文件中 performLayout方法部分实现中scaffoldGeometry.contentTop的值等于appBarHeight;appBarHeight的定义如下:
double appBarHeight = 0.0;
if (hasChild(_ScaffoldSlot.appBar)) {
appBarHeight = layoutChild(_ScaffoldSlot.appBar, fullWidthConstraints).height;
contentTop = extendBodyBehindAppBar ? 0.0 : appBarHeight;
positionChild(_ScaffoldSlot.appBar, Offset.zero);
}
上面layoutChild这个方法会布局appBar,appBar会循环布局里面的子组件,然后返回最后的高度和宽度;这里的appBarHeight等于appBar布局后的高度,这里是25;而不是1;所以才想到添加ConstrainedBox或者SizedBox 强制约束
来强制设置AppBar高度;
AppBar布局后高度为什么是25尼? 😂😂
上面最最最有价值的地方是解决问题的思路
,一定要慢慢培养
- 经过验证的有效解决办法
- 自己的经验指引,对解决问题有帮助
- 遵循 Markdown 语法排版,代码语义正确
- 询问内容细节或回复楼层
- 与题目无关的内容
- “赞”“顶”“同问”“看手册”“解决了没”等毫无意义的内容