代码之家  ›  专栏  ›  技术社区  ›  Jerzy Kiler

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

  •  5
  • Jerzy Kiler  · 技术社区  · 7 年前

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

    我想在这个底部小部件中放置一个文本,但我不知道其中的文本会有多长。

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

    有没有一种方法可以在布局之前测量小部件的高度?

    2018年4月25日编辑

    最终,我按照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,
                          ),
                        ),
                      ],
                    ),
                  ],
                )));
      }
    
    4 回复  |  直到 6 年前
        1
  •  4
  •   Rémi Rousselet    7 年前

    的全部要点 PreferredSizeWidget 是吗 ,无法动态调整此小部件的大小。

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

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

        2
  •  2
  •   Thibault    7 年前

    有没有一种方法可以在布局之前测量小部件的高度?

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

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

    也就是说,如果您的文本在脚手架使用期间发生变化,您将无法在以后更改首选高度。

        3
  •  0
  •   Ali Bagheri    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
  •  0
  •   mahdi shahbazi    3 年前

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

    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()),
              ],
            ),
          ),
        );
      }
    }
    
    推荐文章