Slivers in Flutter: Getting Started
In this article you’ll learn about Slivers in Flutter, how they work, and use them to make a beautifully designed app for recipes. By Michael Malak.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Slivers in Flutter: Getting Started
25 mins
- Getting Started
- Understanding Slivers
- Displaying Recipe List
- SliverFixedExtentList and SliverList
- Building a Scrollable App Bar
- Reusing SliverAppBar
- Inserting a Footer to CustomScrollView
- Adjusting the Layout
- Controlling the SliverGrid Layout
- Adding the Ingredients Grid
- Adding the Numbers Grid
- Implementing SliverPersistentHeaderDelegate
- Customizing a Subheader With SliverPersistentHeader
- Where to Go From Here?
SliverFixedExtentList and SliverList
As you may have guessed from their names, SliverList
is more generic and flexible compared to SliverFixedExtentList
. The latter assumes a fixed extent for its children, while the former doesn’t specify an extent and renders the children widgets with their constraints.
If you wanted to rewrite the recipe list using SliverList
instead of SliverFixedExtentList
you could do so by wrapping RecipeItem
with a height constraint. No need to make this change in your code, but it’s here for your reference:
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) => SizedBox(
height: 110,
child: Padding(
padding: const EdgeInsets.only(bottom: 20),
child: RecipeItem(recipes[index])),
),
childCount: recipes.length,
),
),
Which of the two, SliverList
and SliverFixedExtentList
, do you think is more efficient?
[spoiler title=”SliverList vs SliverFixedExtentList”]
SliverFixedExtentList
is highly more efficient than SliverList
as it doesn’t need to calculate the extent of its children dynamically. Try to use SliverListExtentList
as possible, and only resort to SliverList
if you need to support children of varying or dynamic extents.
[/spoiler]
Time for you to extend Reciperlich with a nice scrollable App Bar.
Building a Scrollable App Bar
Now that you have a list of recipes, you need to add a cool scrollable app bar. Developers commonly use slivers to customize app bars with behavior different from Flutter’s default AppBar
widget.
Since you’ll reuse the same app bar in the app’s two pages, create a new AppBarWidget
in the shared folder.
In lib/shared_widgets, add a new file named app_bar_widget.dart and add the following code to it:
import 'package:flutter/material.dart';
import '../constants/colors.dart';
import 'image_with_top_shadow_widget.dart';
class AppBarWidget extends StatelessWidget {
// 1
final String text;
final String imagePath;
final bool centerTitle;
const AppBarWidget({
Key key,
@required this.text,
@required this.imagePath,
this.centerTitle = false,
}) : assert(text != null),
assert(imagePath != null),
super(key: key);
@override
Widget build(BuildContext context) {
// 2
return SliverAppBar(
title: Text(
text,
style: const TextStyle(
fontSize: 30,
fontWeight: FontWeight.bold,
),
),
backgroundColor: AppColors.navy,
centerTitle: centerTitle,
// 3
expandedHeight: 200.0,
// 4
pinned: true,
elevation: 0,
// 5
flexibleSpace: FlexibleSpaceBar(
background: ImageWithTopShadowWidget(imagePath),
),
);
}
}
Here’s a step-by-step code breakdown:
-
AppBarWidget
takes two mandatory properties: the title text and image path with. It also takes a Boolean property which decides if it should center the text title, which default tofalse
. -
SliverAppBar
is a scrollable app bar. You give it the same properties asAppBar
as well as properties that define the scrollability behavior. - You provide the
expandedHeight
to set the app bar’s size when it’s fully expanded. By setting the value here, you make the app bar scrollable. - When you set
pinned
totrue
, you let the app bar remain visible at the start of the scroll view. - You add an image with a shadow widget stacked behind the toolbar and the tab bar. By adding it to
FlexibleSpaceBar
, you set its height to the same as the app bar’s overall height.
SliverAppBar
has three Booleans in its configuration. When you change them, you alter the behavior of the app bar when scrolling:
-
pinned
: When set totrue
, the app bar will remain visible as the user scrolls. -
floating
: By setting it totrue
, you make the app bar visible as soon as the user scrolls towards the app bar. Otherwise, they’ll need to scroll near the top of the scroll view to reveal the app bar. -
snap
: Whentrue
, the app bar will fully expand as you scroll, which is helpful when you have a text field in the app bar.
Check out this great interactive demo from Flutter documentation for these three Booleans.
Now that you added the shared AppBarWidget
, you’ll add it to the recipe list page.
Reusing SliverAppBar
Back in lib/pages/recipe_list/recipe_list_page.dart, add an import to AppBarWidget
at the top of the file:
import '../../constants/app_image_paths.dart';
import '../../shared_widgets/app_bar_widget.dart';
Now, replace // TODO: Add a cool AppBar
with:
const AppBarWidget(
text: 'Reciperlich',
imagePath: AppImagePaths.mainImage,
centerTitle: true,
),
Here’s what you just did:
- Instead of adding
AppBarWidget
to theappBar
inScaffold
, you inserted it directly as a child inCustomListView
above the recipe list, since it is a sliver. - Then, you used
AppImagePaths
to provide theAppBarWidget
with the main image path.
Build and run. You’ll see your recipe list page in action!
Now, you’ll add AppBarWidget
to the other page, RecipePage
.
Go to lib/pages/recipe/recipe_page.dart and, at the top of the file, add:
import '../../shared_widgets/app_bar_widget.dart';
Next, replace // TODO: add an AppBarWidget
with:
AppBarWidget(
text: recipe.title,
imagePath: recipe.mainImagePath,
),
Build and run to ensure the AppBarWidget
works. Then, click any recipe item. When RecipePage
opens, you’ll see the app bar behaves the same on both pages.
Inserting a Footer to CustomScrollView
Now you have two slivers inside CustomScrollView
in RecipeListPage
. They animate together when you scroll.
But what if you want to add a widget that produces a RenderBox
, like a SizedBox
or a Container
? And what if you want to add it to CustomScrollView
and scroll it with the other slivers even though it’s not a sliver?
No worries! Flutter’s got your back. This is the perfect situation to use SliverToBoxAdapter
to convert boxes to slivers.
You’ll now add a footer to the CustomScrollView
in RecipeListPage
. Go over to lib/pages/recipe_list/widgets/footer_widget.dart, where you’ll find FooterWidget
. It returns a Column
widget.
You’ll use it as a footer for RecipeListPage
.
Go back to lib/pages/recipe_list/recipe_list_page.dart. Import FooterWidget
at the top of the file:
import 'widgets/footer_widget.dart';
Then, add the following at the end of the slivers
argument, right under SliverFixedExtentList
inside CustomScrollView
:
const SliverToBoxAdapter(
child: FooterWidget(),
),
Build and run. See the footer when you scroll at the end. Isn’t it lovely?
Adjusting the Layout
The recipe list needs some padding. However, Flutter’s Padding
widget doesn’t take slivers. You’ll need widgets like Padding
, SafeArea
, Opacity
and Visibility
but for slivers.
Once again, Flutter comes to the rescue! You’ll use classes that give the same functionality but take slivers: SliverPadding
, SliverOpacity
, SliverOpacity
and SliverVisibility
.
RenderSliver
always start with Sliver. Hence, you have SliverAppBar
, SliverGrid
, SliverList
and SliverPadding
. This usually comes in handy when you use autocomplete in your IDE.Wrap SliverFixedExtentList
like this:
SliverPadding(
padding: const EdgeInsets.all(20),
sliver: SliverFixedExtentList(...),
),
Build and run. See that you’ve adjusted the padding.