From CRUD to Search: Upgrading Your SQLite Items App with Bloc

Jitesh YadavJitesh Yadav
4 min read

Hello everyone,

In this follow-up to our blog about SQLite CRUD operations, we'll be adding search functionality to our items app. There's no need to update the pubspec.yaml file, as all necessary dependencies are already included. We'll be making changes to our SQLite file, Bloc file, and UI file to implement this feature.

Let's dive right in!

SQLite File

First, we need to update our SQLite helper file to include a method for searching items. Add the following method to your DatabaseHelper class. What this function is doing is that, it is taking a variable named keyword of datatype String and checking if that variable is present in the table of items or not.

my_sqlite.dart

Future<List<Map<String, dynamic>>> searchItems(String keyword) async {
    final db = await initDB();
    return await db.query(
      'items',
      where: 'name LIKE ?',
      whereArgs: ['%$keyword%'],
    );
  }

Bloc File

Next, update your Bloc file to handle the search functionality. Modify your ItemsBloc class to include a new event for searching items. We are adding another event called SearchItem where the keyword of the above function will be passed.

items_event.dart

class SearchItem extends ItemsEvent {
  final String keyword;
  const SearchItem(this.keyword);
}

items_bloc.dart

//Dependencies
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:equatable/equatable.dart';

//Paths
import '../my_sqlite.dart';

part 'items_event.dart';
part 'items_state.dart';

class ItemsBloc extends Bloc<ItemsEvent, ItemsState> {
  final DatabaseHelper databaseHelper;
  ItemsBloc(this.databaseHelper) : super(ItemsInitial()) {
    on<LoadItems>(_onLoadItems);
    on<AddItem>(_onAddItem);
    on<UpdateItem>(_onUpdateItem);
    on<DeleteItem>(_onDeleteItem);
    on<SearchItem>(_onSearchItem);
  }

  void _onLoadItems(LoadItems event, Emitter<ItemsState> emit) async {
    emit(ItemLoadInProgress());
    try {
      final items = await databaseHelper.fetchItems();
      emit(ItemLoadSuccess(items));
    } catch (_) {
      emit(const ItemOperationFailure(
          "Failed to fetch the items in the database"));
    }
  }

  void _onAddItem(AddItem event, Emitter<ItemsState> emit) async {
    await databaseHelper.insertItem(event.name);
    add(LoadItems());
  }

  void _onUpdateItem(UpdateItem event, Emitter<ItemsState> emit) async {
    await databaseHelper.updateItem(event.id, event.name);
    add(LoadItems());
  }

  void _onDeleteItem(DeleteItem event, Emitter<ItemsState> emit) async {
    await databaseHelper.deleteItem(event.id);
    add(LoadItems());
  }

  void _onSearchItem(SearchItem event, Emitter<ItemsState> emit) async {
    emit(ItemLoadInProgress());
    try {
      final items = await databaseHelper.searchItems(event.keyword);
      emit(ItemLoadSuccess(items));
    } catch (_) {
      emit(const ItemOperationFailure(
          "Failed to search the items in the database"));
    }
  }
}

UI File

Finally, update your UI file to include a search bar that triggers the search functionality. Modify your HomePage class as follows:

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:items/bloc/items_bloc.dart';

class HomePage extends StatelessWidget {
  HomePage({super.key});

  void _showItemDialog(BuildContext context, int? id, String existingName) {
    final TextEditingController controller =
        TextEditingController(text: existingName);
    showDialog(
      context: context,
      builder: (context) {
        return AlertDialog(
          title: Text(id == null ? 'Add Item' : 'Update Item'),
          content: TextField(
            controller: controller,
            decoration: const InputDecoration(hintText: 'Enter item name'),
          ),
          actions: [
            TextButton(
              onPressed: () {
                Navigator.of(context).pop();
              },
              child: const Text('Cancel'),
            ),
            TextButton(
              onPressed: () {
                final itemName = controller.text;
                if (id == null) {
                  context.read<ItemsBloc>().add(AddItem(itemName));
                } else {
                  context.read<ItemsBloc>().add(UpdateItem(id, itemName));
                }
                Navigator.of(context).pop();
              },
              child: Text(id == null ? 'Add' : 'Update'),
            ),
          ],
        );
      },
    );
  }

  final TextEditingController keywordController = TextEditingController();

  void _searchItems(BuildContext context) {
    final searchTerm = keywordController.text;
    context.read<ItemsBloc>().add(SearchItem(searchTerm));
  }

  @override
  Widget build(BuildContext context) {
    context.read<ItemsBloc>().add(LoadItems());
    return Scaffold(
      appBar: AppBar(
        title: const Text('Flutter CRUD with Bloc'),
      ),
      body: BlocBuilder<ItemsBloc, ItemsState>(
        builder: (context, state) {
          if (state is ItemLoadInProgress) {
            return const Center(child: CircularProgressIndicator());
          } else if (state is ItemLoadSuccess) {
            return Column(
              children: [
                Padding(
                  padding:
                      const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
                  child: TextField(
                    controller: keywordController,
                    decoration: InputDecoration(
                      border: OutlineInputBorder(
                        borderRadius: BorderRadius.circular(18),
                      ),
                      hintText: 'Search items',
                      suffixIcon: const Icon(Icons.search),
                    ),
                    onChanged: (_) {
                      _searchItems(context);
                    },
                  ),
                ),
                ListView.builder(
                  shrinkWrap: true,
                  itemCount: state.items.length,
                  itemBuilder: (context, index) {
                    final item = state.items[index];
                    return Card(
                      color: Colors.white,
                      child: Padding(
                        padding: const EdgeInsets.symmetric(horizontal: 12),
                        child: Row(
                          mainAxisAlignment: MainAxisAlignment.spaceBetween,
                          children: [
                            Text(item['name']),
                            Row(
                              mainAxisSize: MainAxisSize.min,
                              children: [
                                IconButton(
                                  icon: const Icon(Icons.edit),
                                  onPressed: () {
                                    _showItemDialog(
                                        context, item['id'], item['name']);
                                  },
                                ),
                                IconButton(
                                  icon: const Icon(Icons.delete),
                                  onPressed: () {
                                    context
                                        .read<ItemsBloc>()
                                        .add(DeleteItem(item['id']));
                                  },
                                ),
                              ],
                            ),
                          ],
                        ),
                      ),
                    );
                  },
                ),
              ],
            );
          } else if (state is ItemOperationFailure) {
            return const Center(child: Text('Failed to load items'));
          }
          return Container();
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          _showItemDialog(context, null, '');
        },
        child: const Icon(Icons.add),
      ),
    );
  }
}

By integrating search functionality into our SQLite items app using Bloc, we've significantly enhanced the app's usability and efficiency. This upgrade allows users to easily find specific items, improving their overall experience. Keep experimenting and refining your app to meet your users' needs better. You can find the updated code in the github link.

Happy coding!

0
Subscribe to my newsletter

Read articles from Jitesh Yadav directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Jitesh Yadav
Jitesh Yadav