【问题标题】:Is there a way to achieve a dynamic height in bottom widget of SliverAppBar有没有办法在 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,
                      ),
                    ),
                  ],
                ),
              ],
            )));
  }

【问题讨论】:

    标签: layout flutter appbar bottombar flutter-sliver


    【解决方案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()),
                  ],
                ),
              ),
            );
          }
        }
        

        【讨论】:

          猜你喜欢
          • 2021-05-23
          • 1970-01-01
          • 1970-01-01
          • 2017-04-27
          • 1970-01-01
          • 2019-06-03
          • 2018-10-14
          • 2020-04-15
          • 2012-04-17
          相关资源
          最近更新 更多