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.

4.9 (20) · 2 Reviews

Download materials
Save for later
Share
You are currently viewing page 2 of 3 of this article. Click here to view the first page.

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:

  1. 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 to false.
  2. SliverAppBar is a scrollable app bar. You give it the same properties as AppBar as well as properties that define the scrollability behavior.
  3. 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.
  4. When you set pinned to true, you let the app bar remain visible at the start of the scroll view.
  5. 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 to true, the app bar will remain visible as the user scrolls.
  • floating: By setting it to true, 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: When true, 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 the appBar in Scaffold, you inserted it directly as a child in CustomListView above the recipe list, since it is a sliver.
  • Then, you used AppImagePaths to provide the AppBarWidget with the main image path.

Build and run. You’ll see your recipe list page in action!

Recipe list without padding

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.

Recipe page with custom App Bar

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?

Recipe List with Footer

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.

Note: The names of widgets that produce slivers with 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.

Recipe Page Final