Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SliverAppBar flexibleSpace with height depending on children #18345

Open
adriancmurray opened this issue Jun 10, 2018 · 56 comments
Open

SliverAppBar flexibleSpace with height depending on children #18345

adriancmurray opened this issue Jun 10, 2018 · 56 comments
Labels
c: new feature Nothing broken; request for a new capability c: proposal A detailed proposal for a change to Flutter customer: crowd Affects or could affect many people, though not necessarily a specific customer. f: material design flutter/packages/flutter/material repository. f: scrolling Viewports, list views, slivers, etc. framework flutter/packages/flutter repository. See also f: labels. P2 Important issues not at the top of the work list team-design Owned by Design Languages team triaged-design Triaged by Design Languages team

Comments

@adriancmurray
Copy link

I have a SliverAppBar with a flexibleSpace that holds information that will change in height. I simply want my AppBar to determine it's height at runtime rather than the static expandedHeight option. Do I really need to calculate the height of this widget before I pass it to the flexibleSpace? Or is there a built in method to allow the SliverAppBar to determine that on it's own?

@zoechi zoechi added the framework flutter/packages/flutter repository. See also f: labels. label Aug 26, 2018
@noordawod
Copy link
Contributor

Have you managed to achieve this somehow, @adriancmurray?

@adriancmurray
Copy link
Author

@noordawod I did a complete redesign of my app since I wanted to implement this feature so I ended up not needing it.

@noordawod
Copy link
Contributor

Alright, cool, thanks.

@goderbauer goderbauer added f: material design flutter/packages/flutter/material repository. f: scrolling Viewports, list views, slivers, etc. labels Dec 29, 2018
@zoechi zoechi added this to the Goals milestone Feb 14, 2019
@vinicioslc
Copy link

I would like if there was an option to set a fixed height for the AppBar.

@zoechi zoechi added the c: new feature Nothing broken; request for a new capability label Feb 19, 2019
@zoechi
Copy link
Contributor

zoechi commented Feb 19, 2019

Did you try to get an answer on StackOverflow?

@vinicioslc
Copy link

vinicioslc commented Feb 19, 2019

Yes, but when I'm using a SliverAppBar, I can not set the height of it to a fixed size, or to use SliverAppBar's children's (title) size.

And the PreferredSize used inside a Sliver does not force the child (SliverAppBar) to stick to the height I want.

(Wont work Sliver -> Preferred -> SliverAppBar && Sliver -> SliverAppBar -> FlexibleHeight -> Container)

@javierforero
Copy link

javierforero commented May 4, 2019

@vinicioslc @noordawod did you guys figure out a way to do this? I have a text widget that could be very long. I am setting it as the background so I need the expandableHeight to be whatever the height the text widget is.

@noordawod
Copy link
Contributor

No, unfortunately.

@vinicioslc
Copy link

@javierforero @noordawod im using this package for now https://pub.dev/packages/flutter_sticky_header and its highly customizable

@vinicioslc
Copy link

@javierforero see my comment

@karansingla007
Copy link

@vinicioslc https://pub.dev/packages/flutter_sticky_header
this library use Flutter SDK version >=1.5.9-pre.94 <2.0.0, but i don't want to downgrade version. so, what can i do?

@noordawod
Copy link
Contributor

@singlakaran

Learn about dependency overrides: https://dart.dev/tools/pub/pubspec#dependencies

@scopendo
Copy link

scopendo commented Jan 10, 2020

I've been able to get something that works for me by using SliverAppBar with a combination FlexibleSpaceBar with title padding and a bottom widget to set the overall height.

SliverAppBar(
  pinned: true,
  flexibleSpace: FlexibleSpaceBar(
    centerTitle: true,
    titlePadding: EdgeInsets.only(top: 70),
    title: Column(
      children: <Widget>[
        Text("Row 1", style: Theme.of(context).textTheme.display3),
        Text("Row 2", style: Theme.of(context).textTheme.display2),
        Text("Row 3", style: Theme.of(context).textTheme.display1),
      ],
    ),
  ),
  bottom: PreferredSize(
    preferredSize: Size.fromHeight(200),
    child: Container(),
  ),
);

But obviously this is using known heights.

@VladyslavBondarenko VladyslavBondarenko added customer: crowd Affects or could affect many people, though not necessarily a specific customer. and removed customer: crowd Affects or could affect many people, though not necessarily a specific customer. labels Mar 25, 2020
@kf6gpe kf6gpe added the P2 Important issues not at the top of the work list label May 29, 2020
@clocksmith clocksmith added this to Backlog in Material Flutter - Sprint 36 via automation Jun 18, 2020
@Pomis
Copy link

Pomis commented Jul 8, 2020

Really need this one, any updates?

@Hixie Hixie removed this from the None. milestone Aug 17, 2020
@drriguz
Copy link

drriguz commented Aug 20, 2020

Need this feature +1

@clocksmith clocksmith moved this from Backlog to Current Sprint in Material Flutter - Sprint 36 Sep 1, 2020
@perclasson perclasson self-assigned this Sep 1, 2020
@clocksmith clocksmith moved this from Current Sprint to In progress in Material Flutter - Sprint 36 Sep 10, 2020
@pablopantaleon
Copy link

we are still waiting for this 😞

@LeGoffMael
Copy link

It is May 2022 and this is still an issue.

The best solution I found is to calculate the exact height of the flexibleSpace by adding up all the widgets it contains (widget height, padding, SizedBox, ...).

For example to calculate the height of a Text widget : https://stackoverflow.com/a/60065737

The problem is that every time you add a new widget in the flexibleSpace, you have to update the sum result.

@JavierPerezLavadie
Copy link

up

@SynSzakala
Copy link

Current SliverAppBar API depends on flexibleSpace being a PreferredSizeWidget so I don't think It will be possible to redesign it in a non-intrusive way to support this case.
However, SliverAppBar is built upon SliverPersistentHeader which can be extended to accommodate such (and, possibly, any) use-case. Below, a simple implementation that uses child's intrinsic height as maxExtent.

class CustomSliverPersistentHeader extends SingleChildRenderObjectWidget {
  const CustomSliverPersistentHeader({super.key, required super.child});

  @override
  RenderObject createRenderObject(BuildContext context) => _RenderCustomSliverPersistentHeader();
}

class _RenderCustomSliverPersistentHeader extends RenderSliverPinnedPersistentHeader {
  @override
  double get maxExtent => child!.getMaxIntrinsicHeight(constraints.crossAxisExtent);

  @override
  double get minExtent => 0;
}

And a dartpad with a working example. Notice, that CustomSliverPersistentHeader needs to be paired with OverflowBox and ClipRect to achieve the desired layout behavior.

This approach can be used to implement basically any layout requirements (which shows the amazing flexibility of Flutter layout architecture!). Unfortunately, implementing your own RenderObjects is quite error-prone and may require a deeper understanding of what can go wrong (above implementation is quite naive and I'm sure there's a ton of edge-cases when it'll go bonkers).

The requirements here can be drastically different, so i think that creating a Widget-level solution won't be doable without introducing unreasonable complexity. Maybe documenting RenderSliver*PersistentHeader and including an example similar to the above would be enough. It should be also linked in the SliverAppBar/SliverPersistentHeader docs to increase discoverability.

@vishalpatel1327
Copy link

vishalpatel1327 commented Jul 27, 2022

It has been 4 years that we are looking for this kind of feature .
so is it impossible to make it for google ?

@JeanPSF
Copy link

JeanPSF commented Sep 5, 2022

up

@ChiPhanTlm
Copy link

Any updates?

@rutajdash
Copy link

up

1 similar comment
@caihua
Copy link

caihua commented Nov 16, 2022

up

@lanrehnics
Copy link

Up

@VaporGraphic
Copy link

Almost 5 years and still waiting

@rubik-crypto
Copy link

Up

@lartie
Copy link

lartie commented Dec 19, 2022

up?

@AlexandrErmolaenko
Copy link

up

@milezabaleta
Copy link

milezabaleta commented Jan 17, 2023

Hi guys! I was struggling with the same issue and discovered this adapter (SliverToBoxApdater) that allows you to convert any widget into a sliver, so you can use this instead of SliverAppBar and achieve the dynamic height.

@DhavalRKansara
Copy link

+1

@sunnykinger
Copy link

I used the extended_sliver package's ExtendedSliverAppbar widget. It helped me with dynamic height
ExtendedSliver

@sgehrman

This comment was marked as abuse.

@flutter-triage-bot flutter-triage-bot bot added multiteam-retriage-candidate team-design Owned by Design Languages team triaged-design Triaged by Design Languages team labels Jul 8, 2023
@Letalus
Copy link

Letalus commented Jul 9, 2023

late answer, but I also had the same requirement for a dynamic height sliver app bar. I finally found a workaround which was working for nearly every situation I needed by faking a sliverappbar, calculating its size and then displaying a real sliverapp bar (so I get access to its cool animations). Enjoy:


import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:weighlos/ui/shared/app_bar/invisible_expanded_header.dart';

class DynamicSliverAppBar extends StatefulWidget {
  const DynamicSliverAppBar({
    this.flexibleSpace,
    super.key,
    this.leading,
    this.automaticallyImplyLeading = true,
    this.title,
    this.actions,
    this.bottom,
    this.elevation,
    this.scrolledUnderElevation,
    this.shadowColor,
    this.surfaceTintColor,
    this.forceElevated = false,
    this.backgroundColor,
    this.backgroundGradient,
    this.foregroundColor,
    this.iconTheme,
    this.actionsIconTheme,
    this.primary = true,
    this.centerTitle,
    this.excludeHeaderSemantics = false,
    this.titleSpacing,
    this.collapsedHeight,
    this.expandedHeight,
    this.floating = false,
    this.pinned = false,
    this.snap = false,
    this.stretch = false,
    this.stretchTriggerOffset = 100.0,
    this.onStretchTrigger,
    this.shape,
    this.toolbarHeight = kToolbarHeight + 20,
    this.leadingWidth,
    this.toolbarTextStyle,
    this.titleTextStyle,
    this.systemOverlayStyle,
    this.forceMaterialTransparency = false,
    this.clipBehavior,
    this.appBarClipper,
  });

  final Widget? flexibleSpace;
  final Widget? leading;
  final bool automaticallyImplyLeading;
  final Widget? title;
  final List<Widget>? actions;
  final PreferredSizeWidget? bottom;
  final double? elevation;
  final double? scrolledUnderElevation;
  final Color? shadowColor;
  final Color? surfaceTintColor;
  final bool forceElevated;
  final Color? backgroundColor;

  /// If backgroundGradient is non null, backgroundColor will be ignored
  final LinearGradient? backgroundGradient;
  final Color? foregroundColor;
  final IconThemeData? iconTheme;
  final IconThemeData? actionsIconTheme;
  final bool primary;
  final bool? centerTitle;
  final bool excludeHeaderSemantics;
  final double? titleSpacing;
  final double? expandedHeight;
  final double? collapsedHeight;
  final bool floating;
  final bool pinned;
  final ShapeBorder? shape;
  final double toolbarHeight;
  final double? leadingWidth;
  final TextStyle? toolbarTextStyle;
  final TextStyle? titleTextStyle;
  final SystemUiOverlayStyle? systemOverlayStyle;
  final bool forceMaterialTransparency;
  final Clip? clipBehavior;
  final bool snap;
  final bool stretch;
  final double stretchTriggerOffset;
  final AsyncCallback? onStretchTrigger;
  final CustomClipper<Path>? appBarClipper;

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

class _DynamicSliverAppBarState extends State<DynamicSliverAppBar> {
  final GlobalKey _childKey = GlobalKey();

  // As long as the height is 0 instead of the sliver app bar a sliver to box adapter will be used
  // to calculate dynamically the size for the sliver app bar
  double _height = 0;

  @override
  void initState() {
    super.initState();
    _updateHeight();
  }

  @override
  void didUpdateWidget(covariant DynamicSliverAppBar oldWidget) {
    super.didUpdateWidget(oldWidget);
    _updateHeight();
  }

  void _updateHeight() {
    // Gets the new height and updates the sliver app bar. Needs to be called after the last frame has been rebuild
    // otherwise this will throw an error
    WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
      if (_childKey.currentContext == null) return;
      setState(() {
        _height = (_childKey.currentContext!.findRenderObject()! as RenderBox).size.height;
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    //Needed to lay out the flexibleSpace the first time, so we can calculate its intrinsic height
    if (_height == 0) {
      return SliverToBoxAdapter(
        child: Stack(
            children: [
              Padding(
                // Padding which centers the flexible space within the app bar
                padding: EdgeInsets.symmetric(vertical: MediaQuery.paddingOf(context).top / 2),
                child: Container(key: _childKey, child: widget.flexibleSpace ?? SizedBox(height: kToolbarHeight)),
              ),
              Positioned.fill(
                // 10 is the magic number which the app bar is pushed down within the sliver app bar. Couldnt find exactly where this number
                // comes from and found it through trial and error.
                top: 10,
                child: Align(
                  alignment: Alignment.topCenter,
                  child: AppBar(
                    backgroundColor: Colors.transparent,
                    elevation: 0,
                    leading: widget.leading,
                    actions: widget.actions,
                  ),
                ),
              )
            ],
          ),
      );
    }

    return SliverAppBar(
      leading: widget.leading,
      automaticallyImplyLeading: widget.automaticallyImplyLeading,
      title: widget.title,
      actions: widget.actions,
      bottom: widget.bottom,
      elevation: widget.elevation,
      scrolledUnderElevation: widget.scrolledUnderElevation,
      shadowColor: widget.shadowColor,
      surfaceTintColor: widget.surfaceTintColor,
      forceElevated: widget.forceElevated,
      backgroundColor: widget.backgroundColor,
      foregroundColor: widget.foregroundColor,
      iconTheme: widget.iconTheme,
      actionsIconTheme: widget.actionsIconTheme,
      primary: widget.primary,
      centerTitle: widget.centerTitle,
      excludeHeaderSemantics: widget.excludeHeaderSemantics,
      titleSpacing: widget.titleSpacing,
      collapsedHeight: widget.collapsedHeight,
      floating: widget.floating,
      pinned: widget.pinned,
      snap: widget.snap,
      stretch: widget.stretch,
      stretchTriggerOffset: widget.stretchTriggerOffset,
      onStretchTrigger: widget.onStretchTrigger,
      shape: widget.shape,
      toolbarHeight: widget.toolbarHeight,
      expandedHeight: _height,
      leadingWidth: widget.leadingWidth,
      toolbarTextStyle: widget.toolbarTextStyle,
      titleTextStyle: widget.titleTextStyle,
      systemOverlayStyle: widget.systemOverlayStyle,
      forceMaterialTransparency: widget.forceMaterialTransparency,
      clipBehavior: widget.clipBehavior,
      flexibleSpace: FlexibleSpaceBar(background: widget.flexibleSpace),
    );
  }
}

@orangeyong
Copy link

late answer, but I also had the same requirement for a dynamic height sliver app bar. I finally found a workaround which was working for nearly every situation I needed by faking a sliverappbar, calculating its size and then displaying a real sliverapp bar (so I get access to its cool animations). Enjoy:


import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:weighlos/ui/shared/app_bar/invisible_expanded_header.dart';

class DynamicSliverAppBar extends StatefulWidget {
  const DynamicSliverAppBar({
    this.flexibleSpace,
    super.key,
    this.leading,
    this.automaticallyImplyLeading = true,
    this.title,
    this.actions,
    this.bottom,
    this.elevation,
    this.scrolledUnderElevation,
    this.shadowColor,
    this.surfaceTintColor,
    this.forceElevated = false,
    this.backgroundColor,
    this.backgroundGradient,
    this.foregroundColor,
    this.iconTheme,
    this.actionsIconTheme,
    this.primary = true,
    this.centerTitle,
    this.excludeHeaderSemantics = false,
    this.titleSpacing,
    this.collapsedHeight,
    this.expandedHeight,
    this.floating = false,
    this.pinned = false,
    this.snap = false,
    this.stretch = false,
    this.stretchTriggerOffset = 100.0,
    this.onStretchTrigger,
    this.shape,
    this.toolbarHeight = kToolbarHeight + 20,
    this.leadingWidth,
    this.toolbarTextStyle,
    this.titleTextStyle,
    this.systemOverlayStyle,
    this.forceMaterialTransparency = false,
    this.clipBehavior,
    this.appBarClipper,
  });

  final Widget? flexibleSpace;
  final Widget? leading;
  final bool automaticallyImplyLeading;
  final Widget? title;
  final List<Widget>? actions;
  final PreferredSizeWidget? bottom;
  final double? elevation;
  final double? scrolledUnderElevation;
  final Color? shadowColor;
  final Color? surfaceTintColor;
  final bool forceElevated;
  final Color? backgroundColor;

  /// If backgroundGradient is non null, backgroundColor will be ignored
  final LinearGradient? backgroundGradient;
  final Color? foregroundColor;
  final IconThemeData? iconTheme;
  final IconThemeData? actionsIconTheme;
  final bool primary;
  final bool? centerTitle;
  final bool excludeHeaderSemantics;
  final double? titleSpacing;
  final double? expandedHeight;
  final double? collapsedHeight;
  final bool floating;
  final bool pinned;
  final ShapeBorder? shape;
  final double toolbarHeight;
  final double? leadingWidth;
  final TextStyle? toolbarTextStyle;
  final TextStyle? titleTextStyle;
  final SystemUiOverlayStyle? systemOverlayStyle;
  final bool forceMaterialTransparency;
  final Clip? clipBehavior;
  final bool snap;
  final bool stretch;
  final double stretchTriggerOffset;
  final AsyncCallback? onStretchTrigger;
  final CustomClipper<Path>? appBarClipper;

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

class _DynamicSliverAppBarState extends State<DynamicSliverAppBar> {
  final GlobalKey _childKey = GlobalKey();

  // As long as the height is 0 instead of the sliver app bar a sliver to box adapter will be used
  // to calculate dynamically the size for the sliver app bar
  double _height = 0;

  @override
  void initState() {
    super.initState();
    _updateHeight();
  }

  @override
  void didUpdateWidget(covariant DynamicSliverAppBar oldWidget) {
    super.didUpdateWidget(oldWidget);
    _updateHeight();
  }

  void _updateHeight() {
    // Gets the new height and updates the sliver app bar. Needs to be called after the last frame has been rebuild
    // otherwise this will throw an error
    WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
      if (_childKey.currentContext == null) return;
      setState(() {
        _height = (_childKey.currentContext!.findRenderObject()! as RenderBox).size.height;
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    //Needed to lay out the flexibleSpace the first time, so we can calculate its intrinsic height
    if (_height == 0) {
      return SliverToBoxAdapter(
        child: Stack(
            children: [
              Padding(
                // Padding which centers the flexible space within the app bar
                padding: EdgeInsets.symmetric(vertical: MediaQuery.paddingOf(context).top / 2),
                child: Container(key: _childKey, child: widget.flexibleSpace ?? SizedBox(height: kToolbarHeight)),
              ),
              Positioned.fill(
                // 10 is the magic number which the app bar is pushed down within the sliver app bar. Couldnt find exactly where this number
                // comes from and found it through trial and error.
                top: 10,
                child: Align(
                  alignment: Alignment.topCenter,
                  child: AppBar(
                    backgroundColor: Colors.transparent,
                    elevation: 0,
                    leading: widget.leading,
                    actions: widget.actions,
                  ),
                ),
              )
            ],
          ),
      );
    }

    return SliverAppBar(
      leading: widget.leading,
      automaticallyImplyLeading: widget.automaticallyImplyLeading,
      title: widget.title,
      actions: widget.actions,
      bottom: widget.bottom,
      elevation: widget.elevation,
      scrolledUnderElevation: widget.scrolledUnderElevation,
      shadowColor: widget.shadowColor,
      surfaceTintColor: widget.surfaceTintColor,
      forceElevated: widget.forceElevated,
      backgroundColor: widget.backgroundColor,
      foregroundColor: widget.foregroundColor,
      iconTheme: widget.iconTheme,
      actionsIconTheme: widget.actionsIconTheme,
      primary: widget.primary,
      centerTitle: widget.centerTitle,
      excludeHeaderSemantics: widget.excludeHeaderSemantics,
      titleSpacing: widget.titleSpacing,
      collapsedHeight: widget.collapsedHeight,
      floating: widget.floating,
      pinned: widget.pinned,
      snap: widget.snap,
      stretch: widget.stretch,
      stretchTriggerOffset: widget.stretchTriggerOffset,
      onStretchTrigger: widget.onStretchTrigger,
      shape: widget.shape,
      toolbarHeight: widget.toolbarHeight,
      expandedHeight: _height,
      leadingWidth: widget.leadingWidth,
      toolbarTextStyle: widget.toolbarTextStyle,
      titleTextStyle: widget.titleTextStyle,
      systemOverlayStyle: widget.systemOverlayStyle,
      forceMaterialTransparency: widget.forceMaterialTransparency,
      clipBehavior: widget.clipBehavior,
      flexibleSpace: FlexibleSpaceBar(background: widget.flexibleSpace),
    );
  }
}

this worked very well!

@ccosnean
Copy link

ccosnean commented Jan 5, 2024

ExtendedSliver

Thx @SynSzakala This was really helpful. <3

I did some small adjustments for it to not break when over-scrolling on iOS, and Written this:

import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';

enum SliverToBoxPersistentHeaderBehaviour {
  pinned,
  floating,
}

class SliverToBoxPersistentHeader extends SingleChildRenderObjectWidget {
  final SliverToBoxPersistentHeaderBehaviour scrollBehaviour;

  const SliverToBoxPersistentHeader({
    super.key,
    required super.child,
    this.scrollBehaviour = SliverToBoxPersistentHeaderBehaviour.floating,
  });

  @override
  RenderObject createRenderObject(BuildContext context) {
    switch (scrollBehaviour) {
      case SliverToBoxPersistentHeaderBehaviour.pinned:
        return _RenderSliverPinnedPersistentHeader();
      case SliverToBoxPersistentHeaderBehaviour.floating:
        return _RenderSliverFloatingPersistentHeader(
          showOnScreenConfiguration:
              const PersistentHeaderShowOnScreenConfiguration(),
        );
    }
  }
}

class _RenderSliverPinnedPersistentHeader
    extends RenderSliverPinnedPersistentHeader {
  @override
  double get maxExtent =>
      child!.getMaxIntrinsicHeight(constraints.crossAxisExtent);

  @override
  double get minExtent => maxExtent;
}

class _RenderSliverFloatingPersistentHeader
    extends RenderSliverFloatingPersistentHeader {
  _RenderSliverFloatingPersistentHeader({
    required super.showOnScreenConfiguration,
  });

  @override
  double get maxExtent =>
      child!.getMaxIntrinsicHeight(constraints.crossAxisExtent);

  @override
  double get minExtent => maxExtent;
}

There is also the RenderSliverScrollingPersistentHeader that has a different stretch behaviour... but for this usecase it is no different than RenderSliverFloatingPersistentHeader.

@arthurgiani
Copy link

ExtendedSliver

Thx @SynSzakala This was really helpful. <3

I did some small adjustments for it to not break when over-scrolling on iOS, and Written this:

import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';

enum SliverToBoxPersistentHeaderBehaviour {
  pinned,
  floating,
}

class SliverToBoxPersistentHeader extends SingleChildRenderObjectWidget {
  final SliverToBoxPersistentHeaderBehaviour scrollBehaviour;

  const SliverToBoxPersistentHeader({
    super.key,
    required super.child,
    this.scrollBehaviour = SliverToBoxPersistentHeaderBehaviour.floating,
  });

  @override
  RenderObject createRenderObject(BuildContext context) {
    switch (scrollBehaviour) {
      case SliverToBoxPersistentHeaderBehaviour.pinned:
        return _RenderSliverPinnedPersistentHeader();
      case SliverToBoxPersistentHeaderBehaviour.floating:
        return _RenderSliverFloatingPersistentHeader(
          showOnScreenConfiguration:
              const PersistentHeaderShowOnScreenConfiguration(),
        );
    }
  }
}

class _RenderSliverPinnedPersistentHeader
    extends RenderSliverPinnedPersistentHeader {
  @override
  double get maxExtent =>
      child!.getMaxIntrinsicHeight(constraints.crossAxisExtent);

  @override
  double get minExtent => maxExtent;
}

class _RenderSliverFloatingPersistentHeader
    extends RenderSliverFloatingPersistentHeader {
  _RenderSliverFloatingPersistentHeader({
    required super.showOnScreenConfiguration,
  });

  @override
  double get maxExtent =>
      child!.getMaxIntrinsicHeight(constraints.crossAxisExtent);

  @override
  double get minExtent => maxExtent;
}

There is also the RenderSliverScrollingPersistentHeader that has a different stretch behaviour... but for this usecase it is no different than RenderSliverFloatingPersistentHeader.

You saved my life.

@martinsellergren
Copy link

martinsellergren commented Feb 1, 2024

I've created a package that can help with this very well: https://pub.dev/packages/intrinsic_size_builder

Greatly simplifies:

  • SliverAppBars with dynamically sized flexibleSpace
  • SliverAppBars with dynamically sized bottom
  • Including when the flexibleSpace has a widget which can resize itself, like an Image.network, that has a different size when loading

Checkout the examples, they specifically target the issue OP describes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c: new feature Nothing broken; request for a new capability c: proposal A detailed proposal for a change to Flutter customer: crowd Affects or could affect many people, though not necessarily a specific customer. f: material design flutter/packages/flutter/material repository. f: scrolling Viewports, list views, slivers, etc. framework flutter/packages/flutter repository. See also f: labels. P2 Important issues not at the top of the work list team-design Owned by Design Languages team triaged-design Triaged by Design Languages team
Projects
None yet
Development

No branches or pull requests