Flutter 状态栏与 FloatingActionButton 的布局问题?

作者站长头像
站长
· 阅读数 6

flutter 状态栏问题当我设置0的时候 floatingActionButton 显示正常Flutter 状态栏与 FloatingActionButton 的布局问题?当我设置一点高度为什么会这样?Flutter 状态栏与 FloatingActionButton 的布局问题?

这是什么原因导致的?

希望知道原因 我想 floatingActionButton 可以在Appbar下边

回复
1个回答
avatar
test
2024-06-19
建议:
  • 不推荐下面的写法,因为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),
            ),
          ],
        ),
      ),
    );
  }
  1. 现在的出现的问题是FloatingActionButton位置不对,内容被部分遮挡了,第一想到就是添加SafeArea避免被遮挡,但是没有效果;(我猜想Scaffold类似一个Stack组件而FloatingActionButton是一个Position组件)
  2. 打开Android Studio的Flutter Inspector查看FloatingActionButton布局后的数据也就是查看top,bottom,width,height;但没有找到top,bootom的数据,只有ColoredBox的Size(width,height)(如果你知道的话请告知🙏)
  3. 接下来打断点,跟着代码运行一下看看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尼? 😂😂
上面最最最有价值的地方是解决问题的思路,一定要慢慢培养
回复
likes
适合作为回答的
  • 经过验证的有效解决办法
  • 自己的经验指引,对解决问题有帮助
  • 遵循 Markdown 语法排版,代码语义正确
不该作为回答的
  • 询问内容细节或回复楼层
  • 与题目无关的内容
  • “赞”“顶”“同问”“看手册”“解决了没”等毫无意义的内容