Is there a way to achieve a dynamic height in bottom widget of SliverAppBar

Issue

The SliverAppBar has an attribute bottom which has to have preferredSize.

Right now I have it returning constant value:

  ...
  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);
      }

    ...
    }

I want to put a Text inside this bottom Widget and I don’t know how long the text inside it is going to be.

How can I achieve the dynamic height of the bottom widget ?

Is there a way to measure a widget’s height before it is layed out ?

EDIT 25/04/2018

Eventually, I followed Thibault’s instructions and ended up with this:

// '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,
                      ),
                    ),
                  ],
                ),
              ],
            )));
  }

Solution

Is there a way to measure a widget’s height before it is layed out ?

In general you can use LayoutBuilder when building the UI in the build() method, but it probably won’t help you in this case.

Here, you could try RenderParagraph to render your text and measure it before building the Scaffold. You can use the screen width as the width constraint, layout the RenderParagraph, retrieve the height, and use that as the preferred size.

That said, you won’t be able to change the preferred height later on if your text changes during the lifetime of your Scaffold.

Answered By – Thibault

Answer Checked By – Mildred Charles (FlutterFixes Admin)

Leave a Reply

Your email address will not be published. Required fields are marked *