有没有办法在 SliverAppBar 的底部小部件中实现动态高度

     2023-02-23     162

关键词:

【中文标题】有没有办法在 SliverAppBar 的底部小部件中实现动态高度【英文标题】:Is there a way to achieve a dynamic height in bottom widget of SliverAppBar 【发布时间】:2018-08-22 09:50:12 【问题描述】:

SliverAppBar 有一个 bottom 属性,该属性必须具有 preferredSize。

现在我让它返回常量值:

  ...
  new SliverAppBar(
    expandedHeight: _kFlexibleSpaceMaxHeight,
    flexibleSpace: new FlexibleSpaceBar(.....)
    ...                   
    bottom: new BottomBar(...), // has to have preferredSize
  ),
  ...

class BottomBar extends StatelessWidget implements PreferredSizeWidget 
    ...
    @override
      Size get preferredSize 
        return new Size.fromHeight(my_constant_height_value);
      

    ...
    

我想在这个底部的Widget里面放一个Text,不知道里面的文字要多长。

如何实现底部小部件的动态高度?

有没有办法在布局之前测量小部件的高度?

编辑 25/04/2018

最后,我按照 Thibault 的指示做了这个:

// 'as rendering' to avoid conflict with 'package:intl/intl.dart'
import 'package:flutter/rendering.dart' as rendering; 

...

// this is the function that returns the height of a Text widget
// given the text
double getHeight(String text, BuildContext context, bool isTitle) 
  var rp = rendering.RenderParagraph(
    new TextSpan(
        style: isTitle
            ? Theme.of(context).primaryTextTheme.title
            : Theme.of(context).primaryTextTheme.subhead,
        text: text,
        children: null,
        recognizer: null),

    // important as the user can have increased text on his device
    textScaleFactor: MediaQuery.of(context).textScaleFactor, 

    textDirection: rendering.TextDirection.ltr,
  );
  var horizontalPaddingSum = 20; // optional 
  var width = MediaQuery.of(context).size.width - horizontalPaddingSum;
  // if your Text widget has horizontal padding then you have to 
  // subtract it from available width to get the needed results
  var ret = rp.computeMinIntrinsicHeight(width);
  return ret;


...


  _kPreferredBBTextHeight =
      getHeight(mTitle ?? "", context, true);

  var verticalPaddingSum = 10;
  _kPreferredBBSubTextHeight = getHeight(mSubtitle ?? "", context,false) + verticalPaddingSum;

  _kPreferredBottomBarSize =
      _kPreferredBBTextHeight + _kPreferredBBSubTextHeight + 48;

  _kFlexibleSpaceMaxHeight =
      _kPreferredBottomBarSize + _kPreferredBottomBarSize + kToolbarHeight;

  _backgroudBottomPadding = _kPreferredBottomBarSize;

...
new CustomSliverAppBar(
                pinned: true,
                automaticallyImplyLeading: false,
                primary: true,
                expandedHeight: _kFlexibleSpaceMaxHeight,
                flexibleSpace: new FlexibleSpaceBar(
                  background: new Padding(
                      padding:
                          new EdgeInsets.only(bottom: _backgroudBottomPadding),
                      child: new Image(
                        image: new NetworkImage(mImageUrl),
                        fit: BoxFit.cover,
                      )),
                ),
                bottom: new BottomBar(
                  fixedHeight: _kPreferredBottomBarSize,
                ),
              ),

...

class BottomBar extends StatelessWidget implements PreferredSizeWidget 
  final double fixedHeight;

  BottomBar(this.fixedHeight);

  @override
  Size get preferredSize 
    return new Size.fromHeight(this.fixedHeight);
  

  @override
  Widget build(BuildContext context) 
    // https://github.com/flutter/flutter/issues/3782
    return new Container(
        height: this.fixedHeight,
        child: new Material(
            color: Theme.of(context).primaryColor,
            child: new Column(
              children: <Widget>[
                new Row(
                  children: <Widget>[
                    new IconButton(
                      icon: new Icon(Icons.arrow_back, color: Colors.white),
                      onPressed: () 
                        Navigator.of(context).pop();
                      ,
                    ),
                    new Expanded(
                      child: new Container(),
                    ),
                    new IconButton(
                      icon: new Icon(Icons.share, color: Colors.white),
                      onPressed: () 
                        print("share pressed");
                      ,
                    )
                  ],
                ),
                new Column(
                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                  children: <Widget>[
                    new Padding(
                        padding: new EdgeInsets.only(left: 10.0, right: 10.0),
                        child: new Container(
                          child: new Container(
                            alignment: Alignment.centerLeft,
                            child: new Text(
                              mTitle ?? "",
                              style: Theme.of(context).primaryTextTheme.title,
                            ),
                          ),
                        )),
                    new Container(
                      padding: new EdgeInsets.only(
                          left: 10.0, right: 10.0, top: 5.0, bottom: 5.0),
                      alignment: Alignment.centerLeft,
                      child: new Text(
                        mSubtitle ?? "",
                        style: Theme.of(context).primaryTextTheme.subhead,
                      ),
                    ),
                  ],
                ),
              ],
            )));
  

【问题讨论】:

【参考方案1】:

PreferredSizeWidget 的全部意义在于,您不能动态调整此小部件的大小。

其背后的原因是Scaffold 使用该首选大小进行一些计算。如果在渲染之前 appbar 的大小是未知的,这将是不可能的。

您必须相应地重新考虑您的 UI。

【讨论】:

如果我能在给定宽度和文本样式的情况下确定 Text 小部件的高度,这将非常有用【参考方案2】:

有没有办法在布局之前测量小部件的高度?

通常您可以在 build() 方法中构建 UI 时使用 LayoutBuilder,但在这种情况下它可能对您没有帮助。

在这里,您可以尝试 RenderParagraph 来渲染您的文本并在构建脚手架之前对其进行测量。您可以使用屏幕宽度作为宽度约束、布局 RenderParagraph、检索高度并将其用作首选尺寸。

也就是说,如果您的文本在 Scaffold 的生命周期内发生变化,您以后将无法更改首选高度。

【讨论】:

【参考方案3】:

我使用下面的代码来解决这个问题。 toolbarHeight 是文字高度(是动态的)。

注意:此页面呈现两次。

  var toolbarHeight;
  BuildContext? renderBoxContext;

  @override
  void initState() 
    super.initState();

    WidgetsBinding.instance?.addPostFrameCallback((timeStamp) 
      var renderBox = renderBoxContext?.findRenderObject() as RenderBox;
      toolbarHeight = renderBox.size.height;
      setState(() );
    );
  

@override
  Widget build(BuildContext context) 
    
    return Material(
          child: getBody(context),
        );
  


getBody(BuildContext context) 
  var mediaQuery = MediaQuery.of(context).size;
  state.toolbarHeight ??= mediaQuery.height;

  return SizedBox(
    width: mediaQuery.width,
    height: mediaQuery.height,
    child: CustomScrollView(
      slivers: <Widget>[

        SliverAppBar(
          pinned: false,
          floating: true,
          snap: false,
          backwardsCompatibility: true,
          centerTitle: true,
          bottom: PreferredSize(
            preferredSize: Size(mediaQuery.width, state.toolbarHeight),
            child: Builder(
              builder: (ctx)
                state.renderBoxContext = ctx;

                return Align(
                  alignment: Alignment.topLeft,
                  child: ColoredBox(
                    color: Colors.green,
                    child: Text('line1\nline2\nline3'),
                  ),
                );
              ,
            ),
          ),
          flexibleSpace: FlexibleSpaceBar(
            title: Text('FlexibleSpaceBar'),
            centerTitle: true,
            collapseMode: CollapseMode.pin,
          ),
        ),

        SliverPadding(
          padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
          sliver: SliverFixedExtentList(
            itemExtent: 110,
            delegate: SliverChildBuilderDelegate(
                  (context, index) 
                    return Text('   item  $index');
                  ,
              childCount: 10,
            ),
          ),
        ),
      ],
    ),
  );

【讨论】:

【参考方案4】:

您可以使用此小部件作为解决此问题的方法。

class DynamicSliverAppBar extends StatefulWidget 
  final Widget child;
  final double maxHeight;

  DynamicSliverAppBar(
    @required this.child,
    @required this.maxHeight,
    Key key,
  ) : super(key: key);

  @override
  _DynamicSliverAppBarState createState() => _DynamicSliverAppBarState();


class _DynamicSliverAppBarState extends State<DynamicSliverAppBar> 
  final GlobalKey _childKey = GlobalKey();
  bool isHeightCalculated = false;
  double height;

  @override
  Widget build(BuildContext context) 
    WidgetsBinding.instance.addPostFrameCallback((timeStamp) 
      if (!isHeightCalculated) 
        isHeightCalculated = true;
        setState(() 
          height = (_childKey.currentContext.findRenderObject() as RenderBox)
              .size
              .height;
        );
      
    );

    return SliverAppBar(
      expandedHeight: isHeightCalculated ? height : widget.maxHeight,
      flexibleSpace: FlexibleSpaceBar(
        background: Column(
          children: [
            Container(
              key: _childKey,
              child: widget.child,
            ),
            Expanded(child: SizedBox.shrink()),
          ],
        ),
      ),
    );
  

【讨论】:

有没有办法在身体的顶部、左侧、底部和右侧创建具有锯齿形效果的背景?

】有没有办法在身体的顶部、左侧、底部和右侧创建具有锯齿形效果的背景?【英文标题】:Isthereawaytocreateabackgroundwithazig-zageffectonthetop,left,bottom,andrightsideofabody?【发布时间】:2020-11-2921:20:53【问题描述】:我曾尝试创建多个容... 查看详情

有没有办法阻止底部导航在应用程序存在之前返回到每个访问过的选项卡?

】有没有办法阻止底部导航在应用程序存在之前返回到每个访问过的选项卡?【英文标题】:Isthereawaytopreventthebottomnavigationfromgettingbacktoeveryvisitedtabbeforeexistingtheapp?【发布时间】:2019-12-0911:25:41【问题描述】:我有一个4标签底部... 查看详情

有没有办法在集合视图控制器中启用底部工具栏?

】有没有办法在集合视图控制器中启用底部工具栏?【英文标题】:IsthereawaytoenablethebottomtoolbarinaCollectionViewController?【发布时间】:2013-03-2612:00:24【问题描述】:我制作并实现了一个集合视图控制器,现在我希望添加一个底部工... 查看详情

有没有办法从底部而不是从顶部在 RecyclerView 中堆叠 CardView?

】有没有办法从底部而不是从顶部在RecyclerView中堆叠CardView?【英文标题】:IsthereawaytostackaCardViewwithinaRecyclerViewfromthebottominsteadoffromthetop?【发布时间】:2021-07-2307:03:10【问题描述】:我是Android编码的新手,所以请多多包涵。在... 查看详情

有没有办法不显示分组 UITableView 中每个部分的顶部和底部单元格的圆角?

】有没有办法不显示分组UITableView中每个部分的顶部和底部单元格的圆角?【英文标题】:isthereawaytonotshowtheroundedcornerforthetopandbottomcellofeachsectioningroupedUITableView?【发布时间】:2012-04-1522:28:22【问题描述】:我想将它们改为直矩... 查看详情

有没有办法自动滚动到 UICollectionView 的底部

】有没有办法自动滚动到UICollectionView的底部【英文标题】:IsthereawaytoautomaticallyscrolltothebottomofaUICollectionView【发布时间】:2013-06-0607:07:06【问题描述】:所以我目前正在开发一个项目,该项目有一个将单元格添加到UICollectionView... 查看详情

如何使 UINavigationController 的导航栏显示在底部?

...layatthebottom?【发布时间】:2011-08-1712:00:51【问题描述】:有没有办法让UINavigationController的默认导航栏显示在底部而不是顶部?我知道有一个UIToolbar可以显示在底部,但是那个是特定 查看详情

滚动时如何从 SliverAppBar 淡入/淡出小部件?

】滚动时如何从SliverAppBar淡入/淡出小部件?【英文标题】:Howtofadein/outawidgetfromSliverAppBarwhilescrolling?【发布时间】:2018-12-2115:20:08【问题描述】:当用户在屏幕上滚动时,我想从SliverAppBar中“淡入”和“淡出”一个小部件。这是... 查看详情

始终在底部滚动文本框[重复]

...ttom[duplicate]【发布时间】:2010-09-1505:08:12【问题描述】:有没有办法让多行文本框的滚动保持在底部?类似vb6的东西txtfoo.selstart=len(txtfoo.text)我正在尝试使用txtfoo.selectionstart=txtfoo.text.length没有成功。问候。【问题讨论】:您有两... 查看详情

如何使用 tabBar 实现 sliverAppBar

】如何使用tabBar实现sliverAppBar【英文标题】:howtoimplementasliverAppBarwithatabBar【发布时间】:2018-11-1709:34:01【问题描述】:flutter文档显示一个demoforSliverAppBar+TabBar+TabBarViewwithListView使用NestedScrollView,而且有点复杂,不知道有没有简... 查看详情

强制按钮停留在手机屏幕底部

...只将它放在布局的其余部分下方,而不是屏幕的最底部。有没有办法解决这个问题?【问题讨论】:我猜你是用listVie 查看详情

如何在 SwiftUI 中屏蔽/剪辑图像的底部?

...pShape(RoundedRectangle(cornerRadius:20,style:.continuous))甚至.mask()。有没有办法通 查看详情

我如何制作一个类似于google新闻的sliverappbar?

我想在应用启动时使用标准的AppBar,但是,一旦用户开始滚动,我希望应用栏向上滑动并移出屏幕。但是,发生这种情况时,状态栏(在iOS上)没有背景。我不想使用flutter_statusbarcolor之类的东西直接在状态栏上设置恒定的背景... 查看详情

将视图放置在程序中 ListView 的底部

...时,我会更改它的颜色,但我也想将它放在ListView的底部有没有办法将给定的View从ListView放置到ListView的底部?【问题讨论】:当然。你试过什么?Abit不知道怎么做,试图用Brin 查看详情

小部件中的 EditText 替代品?必须有办法

...herehastobeaway【发布时间】:2011-05-2501:30:29【问题描述】:有没有人在小部件中成功实现过EditText?我意识到Android不支持这个,但是,HTC设备上的“朋友流”支持...这是由于HTC功能吗?我可以在HTC上实现这个吗?任何人有任何疯狂... 查看详情

有没有办法在 Xamarin.Forms Shell 顶部选项卡中显示图标?

】有没有办法在Xamarin.FormsShell顶部选项卡中显示图标?【英文标题】:IsthereawaytoshowIconinXamarin.FormsShelltoptab?【发布时间】:2019-09-0608:41:59【问题描述】:我正在尝试使用Xamarin.Forms(Shell导航)开发UI。我有底部标签,在每个底部... 查看详情

wxPython 和 STC 边距 - 顶部和底部

...ns-TopandBottom【发布时间】:2013-01-1613:20:07【问题描述】:有没有办法在我的程序的顶部和底部为3和4实现填充?或者有没有像添加\\n之类的解决方法......感觉好像行号和Styled文本离顶部和底部太近了。【问题讨论】:【参考方案1... 查看详情

如何修改 Sliverappbar 以便在向上滚动时可以看到带有 FlexibleSpaceBar 的背景图像?

】如何修改Sliverappbar以便在向上滚动时可以看到带有FlexibleSpaceBar的背景图像?【英文标题】:HowtomakemodifySliverappbarsoIcanseethebackgroundimagewithFlexibleSpaceBarwhenIscrollup?【发布时间】:2021-05-0319:57:22【问题描述】:如何修改Sliverappbar以... 查看详情