Reappearing App Bar Titles

Stefan Matthias Aust
ICNH
Published in
3 min readMar 21, 2019

--

I find it difficult to create good looking teaser images

Flutter’s SliverAppBar makes collapsing headers easy to create. Here is a basic example with a header expanded to 500 points. Instead of displaying a purple color, the FlexibleSpaceBar could of course feature some beautiful imagery. When scrolling, the header shrinks to become the standard AppBar.

class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
drawer: Drawer(),
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
primary: true,
pinned: true,
expandedHeight: 500,
flexibleSpace: FlexibleSpaceBar(
background: Container(
color: Colors.purple,
),
),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return ListTile(
title: Text("List tile $index"),
);
},
childCount: 30,
),
),
],
),
);
}
}

The Problem We Want to Solve

Let’s assume we don’t want to display the title if the header is expanded because that would disturb our beautiful imagery. But if collapsed, the app bar should display it as it would normally. To my knowledge, Flutter has no such behavior by default.

We can either add a title to the FlexibleSpaceBar which is then displayed at the bottom of the expanded header and animated to the usual position or to the SliverAppBar which then is displayed without animation.

Demonstrating both title variants

Analyzing The Status Quo

I browsed the Flutter source code for inspiration how to create a solution that doesn’t require replacing or modifying large parts of the SliverAppBar source code.

Fortunately, each widget inside a Scrollable widget can get access to that widget’s scroll position and if adding a listener to that position, it gets notified each time when the user scrolls the Scrollable widget which is a CustomScrollView in our case.

Also, a SliverAppBar creates a FlexibleSpaceBarSettings contextual widget for its FlexibleSpaceBar to internally get access to the minimum, maximum, and current height. We can use those values to determine whether the app bar is collapsed or not.

Developing The Solution

I wrap the normal title widget with another widget that only displays the title if it detects a collapsed app bar and hides the title otherwise. I call this wrapper SABT which is short for sliver app bar title (I know, an awful name).

Because we want to track scrolling using a listener on the scrollable’s position, the SABT needs to be a stateful widget.

There’s one problem, though. We cannot add the listener in the usual initState method because we cannot access the context at this point of time. It looks like didChangeDependencies is the right method to override. As this method is also called if the widget tree is reconfigured, we need to first remove the listener as shown in the code below. Otherwise we might end up with multiple listeners.

Here is the complete source code:

import 'package:flutter/material.dart';class SABT extends StatefulWidget {
final Widget child;
const SABT({
Key key,
@required this.child,
}) : super(key: key);
@override
_SABTState createState() {
return new _SABTState();
}
}
class _SABTState extends State<SABT> {
ScrollPosition _position;
bool _visible;
@override
void dispose() {
_removeListener();
super.dispose();
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
_removeListener();
_addListener();
}
void _addListener() {
_position = Scrollable.of(context)?.position;
_position?.addListener(_positionListener);
_positionListener();
}
void _removeListener() {
_position?.removeListener(_positionListener);
}
void _positionListener() {
final FlexibleSpaceBarSettings settings =
context.inheritFromWidgetOfExactType(FlexibleSpaceBarSettings);
bool visible = settings == null || settings.currentExtent <= settings.minExtent;
if (_visible != visible) {
setState(() {
_visible = visible;
});
}
}
@override
Widget build(BuildContext context) {
return Visibility(
visible: _visible,
child: widget.child,
);
}
}

To integrate the SABT with our example, we simple add any title widget wrapped in the SABT widget like so:

          SliverAppBar(
primary: true,
pinned: true,
expandedHeight: 500,
title: SABT(child: Text("The title")),
flexibleSpace: FlexibleSpaceBar(
background: Container(
color: Colors.purple,
),
),
),

If you like, feel free to add some kind of animation to the appearance and/or disappearance of the title by replacing the Visibility widget with for example an AnimatedOpacity widget.

Blending titles using AnimatedOpacity widgets

--

--

Stefan Matthias Aust
ICNH
Editor for

App Developer · Co-Founder of I.C.N.H GmbH · Pen & paper role player