Flutter Functions

 

Building a Recipe App with Flutter: Data Management and Search

Flutter has rapidly gained popularity as a versatile framework for building cross-platform apps with a native feel. In this tutorial, we’ll delve into creating a recipe app using Flutter, focusing on two crucial aspects: data management and implementing a robust search functionality. By the end of this guide, you’ll have a solid understanding of how to handle data, set up a local database, and integrate a search feature into your app.

Building a Recipe App with Flutter: Data Management and Search

1. Prerequisites

Before diving into the implementation, ensure you have Flutter and Dart installed on your development environment. If not, follow the official installation guide on the Flutter website.

2. Setting Up the Project

Let’s start by setting up a new Flutter project. Open your terminal and execute the following commands:

bash
flutter create recipe_app
cd recipe_app

3. Designing the UI

An appealing user interface plays a vital role in any app’s success. For our recipe app, we’ll create a simple yet intuitive design. We’ll list the available recipes on the home screen, and users will be able to search for specific recipes using the search bar in the app bar.

3.1. Creating the Home Screen

Navigate to the lib directory of your project and replace the content of the main.dart file with the following code:

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

void main() {
  runApp(RecipeApp());
}

class RecipeApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Recipe App',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: HomeScreen(),
    );
  }
}

class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Recipes'),
      ),
      body: RecipeList(), // We'll implement RecipeList later
    );
  }
}

4. Managing Recipe Data

Now that we have our basic UI in place, it’s time to focus on managing the recipe data. We’ll use a SQLite database to store and retrieve recipes. Flutter provides the sqflite package to work with SQLite databases.

4.1. Adding Dependencies

Open your pubspec.yaml file and add the following dependency:

yaml
dependencies:
  flutter:
    sdk: flutter
  sqflite: ^2.0.0
  path: ^2.0.0

Then, run flutter pub get in your terminal to install the new dependency.

4.2. Creating the Recipe Model

In the lib directory, create a new file named recipe.dart to define the Recipe model:

dart
class Recipe {
  final int id;
  final String title;
  final String description;

  Recipe({required this.id, required this.title, required this.description});
}

4.3. Implementing the Database

Create a new file named database.dart in the lib directory to handle database operations:

dart
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';

import 'recipe.dart';

class RecipeDatabase {
  static final RecipeDatabase instance = RecipeDatabase._init();
  static Database? _database;

  RecipeDatabase._init();

  Future<Database> get database async {
    if (_database != null) return _database!;

    _database = await _initDB('recipe.db');
    return _database!;
  }

  Future<Database> _initDB(String filePath) async {
    final dbPath = await getDatabasesPath();
    final path = join(dbPath, filePath);

    return await openDatabase(path, version: 1, onCreate: _createDB);
  }

  Future<void> _createDB(Database db, int version) async {
    final idType = 'INTEGER PRIMARY KEY AUTOINCREMENT';
    final textType = 'TEXT NOT NULL';

    await db.execute('''
      CREATE TABLE $tableRecipes (
        ${RecipeFields.id} $idType,
        ${RecipeFields.title} $textType,
        ${RecipeFields.description} $textType
      )
    ''');
  }

  Future<Recipe> create(Recipe recipe) async {
    final db = await instance.database;

    final id = await db.insert(tableRecipes, recipe.toJson());
    return recipe.copy(id: id);
  }

  Future<Recipe> readRecipe(int id) async {
    final db = await instance.database;

    final maps = await db.query(
      tableRecipes,
      columns: RecipeFields.values,
      where: '${RecipeFields.id} = ?',
      whereArgs: [id],
    );

    if (maps.isNotEmpty) {
      return Recipe.fromJson(maps.first);
    } else {
      throw Exception('ID $id not found');
    }
  }
  // Additional database operations (update, delete, query all) can be added here.
}

class RecipeFields {
  static final List<String> values = [id, title, description];

  static final String id = '_id';
  static final String title = 'title';
  static final String description = 'description';
}

const tableRecipes = 'recipes';

In this code, we define the RecipeDatabase class responsible for managing the SQLite database. We also define the fields for the Recipe model and the operations to create and read recipes.

5. Displaying Recipes

Let’s update our RecipeList widget to fetch and display the recipes from the database:

dart
class RecipeList extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return FutureBuilder<List<Recipe>>(
      future: RecipeDatabase.instance.getAllRecipes(),
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.waiting) {
          return CircularProgressIndicator();
        } else if (snapshot.hasError) {
          return Text('Error: ${snapshot.error}');
        } else if (!snapshot.hasData || snapshot.data!.isEmpty) {
          return Text('No recipes found.');
        } else {
          return ListView.builder(
            itemCount: snapshot.data!.length,
            itemBuilder: (context, index) {
              final recipe = snapshot.data![index];
              return ListTile(
                title: Text(recipe.title),
                subtitle: Text(recipe.description),
              );
            },
          );
        }
      },
    );
  }
}

6. Implementing Search Functionality

A recipe app isn’t complete without a search feature. Let’s integrate a search bar into our app and make it functional.

6.1. Adding the Search Bar

Update the HomeScreen widget to include a search bar in the app bar:

dart
class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Recipes'),
        actions: [
          IconButton(
            onPressed: () {
              showSearch(context: context, delegate: RecipeSearchDelegate());
            },
            icon: Icon(Icons.search),
          ),
        ],
      ),
      body: RecipeList(),
    );
  }
}

6.2. Creating the Search Delegate

Create a new file named search.dart in the lib directory for the search delegate:

dart
class RecipeSearchDelegate extends SearchDelegate<Recipe> {
  @override
  List<Widget> buildActions(BuildContext context) {
    return [
      IconButton(
        onPressed: () {
          query = '';
        },
        icon: Icon(Icons.clear),
      ),
    ];
  }

  @override
  Widget buildLeading(BuildContext context) {
    return IconButton(
      onPressed: () {
        close(context, null);
      },
      icon: Icon(Icons.arrow_back),
    );
  }

  @override
  Widget buildResults(BuildContext context) {
    // Implement search results here
    return Container();
  }

  @override
  Widget buildSuggestions(BuildContext context) {
    // Implement search suggestions here
    return Container();
  }
}

6.3. Handling Search

We’ll implement the actual search functionality inside the buildResults and buildSuggestions methods of the RecipeSearchDelegate class. For brevity, let’s focus on the buildSuggestions method:

dart
@override
Widget buildSuggestions(BuildContext context) {
  final suggestionList = query.isEmpty
      ? []
      : RecipeDatabase.instance.searchRecipes(query);

  return ListView.builder(
    itemCount: suggestionList.length,
    itemBuilder: (context, index) {
      final recipe = suggestionList[index];
      return ListTile(
        title: Text(recipe.title),
        subtitle: Text(recipe.description),
      );
    },
  );
}

Conclusion

Congratulations! You’ve successfully built a recipe app with Flutter that handles data management and includes an efficient search feature. This tutorial covered setting up the project, designing the UI, managing recipe data with SQLite, and implementing search functionality. Now you have a strong foundation to further enhance and customize your app according to your needs. Happy coding!

In this tutorial, we explored the process of creating a recipe app using Flutter, with a specific focus on data management and search functionality. By following the step-by-step guide and incorporating the provided code samples, you can build a powerful and user-friendly recipe app that allows users to explore and discover various recipes seamlessly. Whether you’re a beginner or an experienced Flutter developer, this tutorial equips you with the knowledge and skills to create an engaging app that showcases your culinary creations.

Previously at
Flag Argentina
Brazil
time icon
GMT-3
Full Stack Systems Analyst with a strong focus on Flutter development. Over 5 years of expertise in Flutter, creating mobile applications with a user-centric approach.