DEV Community

Cover image for Making the Escapists Crafting Guide App  2 — SliverList and Sticky Headers
Aldrin Navarro
Aldrin Navarro

Posted on • Originally published at aldnav.com

2 1

Making the Escapists Crafting Guide App 2 — SliverList and Sticky Headers

On Part 1, I made the working version of the Escapists Crafting Guide app using Flutter. I used ListView widget to show the different tools and items available for crafting.

Today I am going to improve the app slightly by showing on the list the category where these tools belong. I am also going to replace the ListView with SliverList — I describe it as a fancier list view. I am also exploring flutter_sticky_header[repository] [pub.dev] to achieve the behavior when the user scrolls and the category sticks to the top until the next category is in the current view. I will also be polishing the design along the way.

alt Categories of tools

Categories of tools source: Gamepedia The Escapists Crafting Guide

SliverList

To use the SliverList, it needs to be wrapped on a CustomScrollView which comes with the familiar API that we already learned from using ListView with slight changes. This widget requires a list of children widgets called slivers.

A sliver is a portion of a scrollable area. A sliver could be a SliverAppBar, SliverList, or a SliverGrid.

CustomScrollView(
slivers: [
SliverAppBar(
flexibleSpace: FlexibleSpaceBar(
title: Text("Escapists Craft"),
),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
/// For dynamic content
}
),
),
]
)

Data structure

Previously, I was just working with a linear list of all items. Right now I need to refactor it to group the tools by categories.

My input data contains a list of items, and for each item it has a category.

[
  {
    "name": "Flimsy Pickaxe",
    "category": "Tools"
  },
  {
    "name": "Lightweight Pickaxe",
    "category": "Tools",
  },
  {
    "name": "Cup of Molten Chocolate",
    "category": "Weapon"
  }
]
Enter fullscreen mode Exit fullscreen mode

A function is required to create a map data structure of the tools. The map will then be used to build the slivers. The slivers will contain the category as the header — the one that sticks to the top when scrolled, and then followed by the rest of the items belonging to the category.

  Map categorizeItems(items) {
    var categories = Map();
    items.forEach((element) {
      if (!categories.containsKey(element.category)) {
        categories[element.category] = List();
      }
      categories[element.category].add(element);
    });
    return categories;
  }
Enter fullscreen mode Exit fullscreen mode

Sticky header

I am using flutter_sticky_header to achieve the sticky header effect. First, update pubspec.yaml.

dependencies:
  flutter_sticky_header: ^0.5.0
Enter fullscreen mode Exit fullscreen mode

Then run flutter pub get.

The basic structure looks like below. Each one of this is a child of the CustomScrollView.slivers

SliverStickyHeader(
    header: Header(title: category),
    sliver: SliverList(
        delegate: SliverChildBuilderDelegate(
            (BuildContext context, int index) {
                return InkWell(
                    child: ItemCard(data: items[index]),
                    onTap: () {...}
                );
            },
            childCount: items.length,
        ), // SliverChildBuilderDelegate
    ), // SliverList
)
Enter fullscreen mode Exit fullscreen mode

The resulting code looks like the following.

class _MyHomePageState extends State<MyHomePage> {
Map categorizeItems(items) {
var categories = Map();
items.forEach((element) {
if (!categories.containsKey(element.category)) {
categories[element.category] = List();
}
categories[element.category].add(element);
});
return categories;
}
List<Widget> buildSlivers(itemsMap) {
List<Widget> slivers = [];
itemsMap.forEach((category, items) {
slivers.add(SliverStickyHeader(
header: Header(title: category),
sliver: SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return InkWell(
child: ItemCard(data: items[index]),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailPage(
item: items[index],
),
),
);
},
);
},
childCount: items.length,
),
),
));
});
return slivers;
}
// Widget build(BuildContext context) ...
// Build the widget with data.
Map categorizedItems = categorizeItems(_filteredItems);
var categorizedSlivers = buildSlivers(categorizedItems);
return Scaffold(
body: Container(
color: Colors.white,
child: CustomScrollView(
slivers: [
SliverAppBar(
pinned: true,
flexibleSpace: FlexibleSpaceBar(
title: Text("Escapists Craft"),
),
backgroundColor: Colors.black,
textTheme: TextTheme(
headline6: TextStyle(
color: Colors.black,
fontSize: 36.0,
),
),
),
SliverAppBar(
floating: true,
snap: true,
flexibleSpace: FlexibleSpaceBar(
title: SearchBar(
searchTextController: searchTextController),
),
backgroundColor: Colors.white,
toolbarHeight: 10.0,
),
...categorizedSlivers, // list of tools with categories
],
),
),
);
}
view raw sliverlist.dart hosted with ❤ by GitHub

Conclusion

Now, I have finally ticked off an improvement. Will continue to do more.

  • A separator between categories on the list view

Next, I will be using another sliver to achieve a feature that I always wanted to have — a complete spread or a tree, not just the immediate requirements, to show all components required to craft a tool.

Until next time!

References

Image of Datadog

Create and maintain end-to-end frontend tests

Learn best practices on creating frontend tests, testing on-premise apps, integrating tests into your CI/CD pipeline, and using Datadog’s testing tunnel.

Download The Guide

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay