How to change AppBar height dynamically during runtime based on the AppBar content in Flutter?
Asked Answered
S

3

12

I'm trying to implement a flutter_tagging inside the AppBar. I managed to add the tagged TextField inside the AppBar and to resize the AppBar using PreferredSize widget using the following code:

return PreferredSize(
    preferredSize: Size.fromHeight(110),
    child: AppBar(
      backgroundColor: HexColor('171551'),
      leading: const BackButton(),
      flexibleSpace: Padding(
        padding: const EdgeInsets.only(top: 48.0, left: 10, right: 10),
        child: buildTaggedSearch(),
      ),
      title: Container(),
      actions: _buildActions(),
    ),
  );

And this is the result:

enter image description here

The problem I can't manage to solve is what happens when user enter too many tags and the tags go to the second line, this is what happens:

enter image description here

I'm still new to Flutter and maybe I missing something, but how would I solve this issue and make the AppBar resize based on the content of the tags. I went over most of the questions here which talk about the AppBar resize, all of them use the PreferredSize widget which I use here. So I wonder if there is another option?

Smelt answered 30/1, 2020 at 21:43 Comment(0)
I
8

There is no easy way to change the AppBar height at run-time. Yes, you can set it to any (fixed) height with PreferredSize, but once it's set, you normally cannot change it.

Even if you make your own class to extend PreferredSize, it will end up like this:

class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
  @override
  Size get preferredSize => Size.fromHeight(100); // fixed custom height

  @override
  Widget build(BuildContext context) {...}
}

This preferredSize getter needs to return a fixed size. Its parent widget (Scaffold) really wants to know the app bar height, so it knows where to start rendering its body.

And if you change the above "CustomAppBar" to Stateful, you will quickly realize the preferredSize you must override, is part of the Widget not the State.

And if you do some sort of hack, like using a global variable to "trick" it:

Size get preferredSize => Size.fromHeight(myAppBarHeight); // global variable

After changing the value in myAppBarHeight, the app bar still remains at its old height. You must call setState on widget with the Scaffold in order to redraw the app bar, and more importantly, redraw Scaffold body at a different position.

So really, the solution maybe is to control app bar height at the Scaffold level.

Or perhaps you should look into SliverAppBar.

Or do not try to change the app bar height at run-time, for example, use a ListView to horizontally scroll your chips.

Or build your own widget and don't use the app bar.

Implode answered 3/7, 2020 at 5:19 Comment(1)
myAppBarHeight not global variable on meMiscellanea
B
1

I was looking for this problem, I find a solution with NestedScrollView and a plugin SliverStickyHeader

Here is how I did this,

Scaffold(
  body: SafeArea(
    child: NestedScrollView(
      headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
        return <Widget>[
          SliverStickyHeader(
            sticky: false,
            header: Padding(
              padding: const EdgeInsets.all(8.0),
              child: Column(
                children: [
                  TextField(
                    decoration: InputDecoration(
                        hintText: 'Search', prefixIcon: Icon(Icons.search)),
                  ),
                  Container(
                    height: kToolbarHeight,
                    child: ListView.builder(
                      scrollDirection: Axis.horizontal,
                      itemBuilder: (context, i) {
                        return Center(
                          child: Container(
                            padding: EdgeInsets.all(8.0),
                            margin: EdgeInsets.symmetric(horizontal: 4.0),
                            decoration: BoxDecoration(
                              color: Theme.of(context)
                                  .primaryColor
                                  .withOpacity(0.5),
                              borderRadius: BorderRadius.circular(25.0),
                            ),
                            child: Row(
                              children: [
                                Text(
                                  'Tag $i',
                                  style: TextStyle(
                                    color: Colors.white,
                                  ),
                                ),
                                SizedBox(width: 8.0),
                                CircleAvatar(
                                  radius: 25.0 / 2,
                                  child: Icon(Icons.close),
                                )
                              ],
                            ),
                          ),
                        );
                      },
                    ),
                  ),
                ],
              ),
            ),
          )
        ];
      },
      body: ListView.builder(itemBuilder: (context, i) {
        return ListTile(
          leading: CircleAvatar(child: Text('$i')),
          title: Text('Appbar with dynamic height'),
        );
      }),
    ),
  ),
);

and it is fulfilling my requirement, Plugin also support fully Sliver functionality, but I use it with as header only. I hope it will be helpful.

Bennie answered 19/11, 2021 at 5:11 Comment(0)
I
0

I have faced the same problem where I want to show search along with filter which is Not Scroll with body content i.e it's fixed in the top and the result is scrollable.

Instead using AppBar i done following,

Scaffold(
  body: Column(
    children: [
      _buildSearchHeader(),
      Expanded(
        child: ListView(
          children: [
              _buildSearchProductResult()
          ],
        ),
      ),
    ],
  )

Inside _buildSearchHeader() i declare my app bar content.

SafeArea(
  child: Container(
    margin: EdgeInsets.fromLTRB(
        homePageMargin, homePageMargin, homePageMargin, 0),
    width: double.infinity,
    child: Column(
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      children: [
        Align(child: SectionTitle("Search", fontSize: 20),
            alignment: Alignment.centerLeft),
        SizedBox(height: 22),
        _buildSearchBox(), // search bar
        if(showSortSection) _buildSortSection() // sort section/ or any other 
      ],
    ),
  ),
);

Now I am able to create dynamic appear and scroll option perfectly.

Note: This may not perfect way to do it. If any dev knows a better way let me know here.

Illailladvised answered 14/4, 2021 at 11:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.