Day 26: A Beginner’s Guide to Displaying Hive Data in Flutter ListView: Best Practices

Hemant Kumar Prajapati
6 min read2 days ago

--

Hey, Flutter enthusiasts! 👋 In our last blog, we talked about how to create tasks and store them using Hive. Now that we’ve got our tasks safely tucked away in Hive boxes, it’s time to figure out how to show that data in a beautiful ListView in your Flutter app.

Today, I’ll walk you through how to retrieve your stored data and display it in a ListView using ValueListenableBuilder. We’ll also explore some extra tips and tricks to make your UI more user-friendly and efficient. 🚀

So, let’s dive in!

| 🛠 Step 1: Using ValueListenableBuilder to Listen for Data Changes

When you’re dealing with data from Hive, you want your UI to react to changes automatically. This is where ValueListenableBuilder comes into play. It listens to changes in the data stored in your Hive box and updates the UI whenever something changes. It’s like magic but for your code!

Let’s first see a simple example of using ValueListenableBuilder to show data from Hive.

Example: Simple Task List Using ValueListenableBuilder

import 'package:flutter/material.dart';
import 'package:hive/hive.dart';

class TaskList extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Task List')),
body: ValueListenableBuilder(
valueListenable: Hive.box('tasksBox').listenable(),
builder: (context, Box box, widget) {
if (box.values.isEmpty) {
return Center(child: Text("No tasks available!"));
}

return ListView.builder(
itemCount: box.length,
itemBuilder: (context, index) {
var task = box.getAt(index);
return ListTile(
leading: Icon(Icons.task),
title: Text(task['title']),
subtitle: Text(task['description']),
);
},
);
},
),
);
}
}

🔍 What’s Happening Here?

  • ValueListenableBuilder is used to listen for changes in the Hive box (tasksBox). It automatically updates the UI when data changes.
  • We use ListView.builder to dynamically generate a list of tasks.
  • The leading icon makes the task visually appealing, and the title and description make it informative.

Why Use ValueListenableBuilder?

  • Automatic UI Updates: You don’t have to manually call setState()—your UI refreshes itself when data changes. 💡
  • Optimized Performance: It’s efficient because it listens only to the necessary data in the Hive box, reducing unnecessary rebuilds and boosting app performance.

| 🛠 Step 2: Integrating ValueListenableBuilder in Your Project

Now that we understand the basics, let’s get into the real-world scenario where you need to display a task list from your MonthlyScreen. This part is all about taking what you’ve already built and enhancing it to fetch and display tasks from Hive.

In your monthly_screen.dart, you already have a structure in place for the calendar grid, but we’re going to focus on the task list. Here’s how you can use ValueListenableBuilder and ListView.builder to show the tasks for the current month.

Here’s the section of your code where we’ll add the ListView for tasks:

  @override
Widget build(BuildContext context) {
return SingleChildScrollView(
physics: AlwaysScrollableScrollPhysics(),
child: Column(
children: [
ValueListenableBuilder(
valueListenable:
Hive.box<TodoModel>(HiveConstants.todoReportBox)
.listenable(),
builder: (context, box, child) {
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconButton(
icon: const Icon(Icons.arrow_back_ios),
onPressed: () => _changeMonth(-1),
),
Text(
'${_monthName(currentMonth.month)} ${currentMonth.year}',
style: const TextStyle(
fontSize: 24, fontWeight: FontWeight.w500),
),
IconButton(
icon: const Icon(Icons.arrow_forward_ios),
onPressed: () => _changeMonth(1),
),
],
),
const Gap(12),
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: List.generate(
7,
(index) => Text(
[
'Sun',
'Mon',
'Tue',
'Wed',
'Thu',
'Fri',
'Sat'
][index],
style: const TextStyle(
fontWeight: FontWeight.w500,
fontSize: 18,
color: Colors.blueGrey),
)),
),
),
const Gap(12),
GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 7),
itemCount: datesGrid.length,
itemBuilder: (context, index) {
DateTime date = datesGrid[index];
bool isCurrentMonth =
date.month == currentMonth.month;
bool isCompleted =
areAllTasksCompletedForDate(box, date);
return Padding(
padding: const EdgeInsets.all(4.0),
child: CircleAvatar(
backgroundColor: isCompleted
? TColors.appPrimaryColor
: Colors.transparent,
child: Text(
date.day.toString(),
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 16,
color: isCompleted
? Colors.white
: isCurrentMonth
? Colors.black
: Colors.grey,
),
),
),
);
},
),
],
);
}),
const Divider(),
ValueListenableBuilder(
valueListenable:
Hive.box<TodoModel>(HiveConstants.todoBox).listenable(),
builder: (context, value, child) {
var data = value.values;
var box =
Hive.box<TodoModel>(HiveConstants.todoReportBox).values;
return ListView.builder(
shrinkWrap: true,
itemCount: data.length,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
final todo = data.elementAt(index);
return Container(
margin: const EdgeInsets.all(6),
padding: const EdgeInsets.symmetric(
horizontal: 12, vertical: 16),
decoration: BoxDecoration(
border: Border.all(),
color: box.any((element) =>
element.title == todo.title &&
element.category == todo.category &&
element.reminderTime == todo.reminderTime)
? TColors.appPrimaryColor
: Colors.white,
borderRadius: BorderRadius.circular(8),
boxShadow: const [
BoxShadow(
color: Colors.black,
offset: Offset(1.5, 2),
spreadRadius: 2,
blurStyle: BlurStyle.solid)
]),
child: Column(
children: [
Row(
children: [
const CircleAvatar(
backgroundColor: Colors.redAccent,
radius: 8),
const Gap(8),
Text(todo.category),
Spacer(),
PopupMenuHelper.buildPopupMenu(context,
onSelected: (value) async {
switch (value) {
case "complete":
await Provider.of<TodoHandlerProvider>(
context,
listen: false)
.completeTask(
todoModel: TodoModel(
title: todo.title,
category: todo.category,
isCompleted: true,
reminderTime:
todo.reminderTime,
createdAt: DateTime.now()));
break;
case "delete":
await Provider.of<TodoHandlerProvider>(
context,
listen: false)
.deleteTask(index);
break;
default:
break;
}
}, optionsList: [
if (box.contains(todo))
{"complete": "Complete"},
{"delete": "Delete"}
])
],
),
const Gap(8),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
todo.title,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500),
),
const Icon(Icons.flag, color: Colors.redAccent)
],
),
const Divider(),
Row(
children: [
const Icon(
Icons.calendar_month_rounded,
color: Colors.black54,
),
const Gap(8),
Text(formatDateTimeToString(todo.createdAt))
],
),
const Gap(4),
Row(
children: [
const Icon(
Icons.timelapse_rounded,
color: Colors.black54,
),
const Gap(8),
Text(formatTimeFromDateTime(todo.reminderTime))
],
),
],
),
);
});
},
)
],
));
}

Key Highlights:

  1. ValueListenableBuilder listens for any updates in the todoBox Hive box.
  2. ListView.builder creates a dynamic list based on the tasks available for the selected month.
  3. Each task has:
  • A CircleAvatar that shows the task category.
  • The task title and a flag icon to highlight its importance.
  • Date and time of creation and reminder, displayed with calendar and time icons.

| 🏆 Bonus Tips for Enhanced UI

1. Use Custom Icons for Categories

You can improve the UI by displaying unique icons for each task category. Here’s an example:

Icon _getCategoryIcon(String category) {
switch (category) {
case 'Work':
return Icon(Icons.work, color: Colors.blueAccent);
case 'Personal':
return Icon(Icons.person, color: Colors.green);
case 'Shopping':
return Icon(Icons.shopping_cart, color: Colors.orange);
default:
return Icon(Icons.task, color: Colors.grey);
}
}

2. Add Swipe-to-Delete Functionality

To improve user interaction, you can allow users to delete tasks by swiping. Here’s an example:

ListView.builder(
itemCount: data.length,
itemBuilder: (context, index) {
final todo = data.elementAt(index);
return Dismissible(
key: Key(todo.title),
onDismissed: (direction) {
Hive.box<TodoModel>(HiveConstants.todoBox).deleteAt(index);
Fluttertoast.showToast(msg: "Task Deleted");
},
child: ListTile(
title: Text(todo.title),
subtitle: Text(formatDateTimeToString(todo.createdAt)),
leading: Icon(Icons.task),
),
);
},
);

| Conclusion

  • ValueListenableBuilder keeps your UI reactive by listening to changes in the Hive database, making it easy to display real-time data.
  • ListView.builder is perfect for rendering long, dynamic lists efficiently.
  • You can make your UI more intuitive by adding icons, swipe-to-delete, and custom layouts for each task.

Now you’ve learned how to show your Hive data in a ListView using Flutter’s ValueListenableBuilder and ListView.builder. You’ve also got a few bonus tips on enhancing the UI for a polished, user-friendly experience. 👏

Keep building those awesome Flutter apps, and stay tuned for more cool tips and tricks. Happy coding! 🚀

| 🚀 Join the Journey!

We’ve built an amazing todo app, “Noted App,” together, but the adventure doesn’t stop here. You can checkout the repository on GitHub and follow along day by day to complete the project with us in this open-source endeavor. 🌟💻

👉 Explore the GitHub Repository:

Dive into the code, contribute, and collaborate with other developers. Let’s build something great together.

| 🌟 Enjoyed this tutorial?

For more tips, tutorials, and insights into Flutter, be sure to follow my Medium blog! Stay connected, and let’s continue building amazing things together.

This is just Part 26: Ping me on other platforms… too….

👉 Follow me:

Happy coding! 🎉 !! Happy Flutter !!

💬 Comment Section is Open!

Share your thoughts or ask questions below.

👏 Applaud if you enjoyed this!

Your claps help others find this content.

➕ Follow for more!

Stay updated with more tips and tutorials.

🌿 Thank you for reading! If you liked this post and want to support me, you can buy me a coffee here 👇

--

--

Hemant Kumar Prajapati

▁▂▃▅▆▇ Professional Flutter Freelancer 🚀 | SDE1 at TechSaga Corporation | MVP Development for Startups 🧳 | Cross-platform App Development Expert 📱|>