Simple Recipe List App using Flutter and API 2021

Oflutter - Simple Recipe List App using flutter and API

In this article, we will learn how to create a simple recipe list app using Flutter and API. This tutorial is for absolute beginners who want to learn how to make the right API structure in the project and use it in the app.

This article is more of a guide for the video tutorial that I created. Please, check the conclusion for the video link

Note: This tutorial is for integrating API to the flutter app, and not how to create custom widgets and/or layout.

1. Install dependancies and project setup

Before we begin to integrate our new app from scratch, we need to configure and install the necessary dependencies in our newly created project.

Go to pubspec.yaml and turn off null safety feature.

environment:
  sdk: ">=2.7.0 <3.0.0"

We are not going to use null safety because for new developers it might be really hard to understand and work with it. So, by setting sdk: “>=2.7.0 <3.0.0” we are disabling it.

Next, we are going to set up dependencies that we are going to use in our project.

  • http: ^0.13.1

Note: At the time you are reading this article, a newer version of this dependence may be released. Please, check the pub.dev website for updates.

This is how your dependencies should look so far.

dependencies:
  flutter:
    sdk: flutter
  http: ^0.13.1

2. Project Structure

In this step, let’s create the right project structure so we have our code organized and reusable.

Go to lib folder, and create: models and views folders. Then, inside models create two files recipe.dart and recipe.api.dart. In the views folder, create home.dart and widgets folder. Finally, inside widgets folder, create recipe_card.dart file.

I know it might be confusing for the beginner, so here are the dropdown details of the lib folder.

  • models – this folder will contain our Recipe class and API class.
    • recipe.dart – this file will contain our Recipe object
    • recipe.api.dart – this file will contain our function to communicate between an app and the web server using API.
  • views – this folder will contain our pages for the app
    • home.dart – this file is our main page of the app
    • widgets – this folder will contain our custom widgets
      • recipe_card.dart – this file is our custom recipe card widget

The final project setup should look like this:

экрана 2021 04 27 в 2.38.23 PM
project setup flutter

Note: Since we are going to use only one model – recipe, we will not create a separate file for it. However, if you have more than one model, then it’s better to create a separate file e.g models -> recipe -> [recipe.api.dart, recipe.dart]

3. Create Custom Recipe Widget (recipe_card.dart)

In this step, we will create a custom recipe widget card that will be displayed on the home screen. Since I was mentioned at the beginning of the video that this tutorial is focused on the API. You can just copy and paste the code below and put it in the recipe_card.dart file

import 'package:flutter/material.dart';

class RecipeCard extends StatelessWidget {
  final String title;
  final String rating;
  final String cookTime;
  final String thumbnailUrl;
  RecipeCard({
    @required this.title,
    @required this.cookTime,
    @required this.rating,
    @required this.thumbnailUrl,
  });
  @override
  Widget build(BuildContext context) {
    return Container(
      margin: EdgeInsets.symmetric(horizontal: 22, vertical: 10),
      width: MediaQuery.of(context).size.width,
      height: 180,
      decoration: BoxDecoration(
        color: Colors.black,
        borderRadius: BorderRadius.circular(15),
        boxShadow: [
          BoxShadow(
            color: Colors.black.withOpacity(0.6),
            offset: Offset(
              0.0,
              10.0,
            ),
            blurRadius: 10.0,
            spreadRadius: -6.0,
          ),
        ],
        image: DecorationImage(
          colorFilter: ColorFilter.mode(
            Colors.black.withOpacity(0.35),
            BlendMode.multiply,
          ),
          image: NetworkImage(thumbnailUrl),
          fit: BoxFit.cover,
        ),
      ),
      child: Stack(
        children: [
          Align(
            child: Padding(
              padding: EdgeInsets.symmetric(horizontal: 5.0),
              child: Text(
                title,
                style: TextStyle(
                  fontSize: 19,
                ),
                overflow: TextOverflow.ellipsis,
                maxLines: 2,
                textAlign: TextAlign.center,
              ),
            ),
            alignment: Alignment.center,
          ),
          Align(
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Container(
                  padding: EdgeInsets.all(5),
                  margin: EdgeInsets.all(10),
                  decoration: BoxDecoration(
                    color: Colors.black.withOpacity(0.4),
                    borderRadius: BorderRadius.circular(15),
                  ),
                  child: Row(
                    children: [
                      Icon(
                        Icons.star,
                        color: Colors.yellow,
                        size: 18,
                      ),
                      SizedBox(width: 7),
                      Text(rating),
                    ],
                  ),
                ),
                Container(
                  padding: EdgeInsets.all(5),
                  margin: EdgeInsets.all(10),
                  decoration: BoxDecoration(
                    color: Colors.black.withOpacity(0.4),
                    borderRadius: BorderRadius.circular(15),
                  ),
                  child: Row(
                    children: [
                      Icon(
                        Icons.schedule,
                        color: Colors.yellow,
                        size: 18,
                      ),
                      SizedBox(width: 7),
                      Text(cookTime),
                    ],
                  ),
                )
              ],
            ),
            alignment: Alignment.bottomLeft,
          ),
        ],
      ),
    );
  }
}

4. Create Home Page View (home.dart)

In this step, we will create an initial home page for our app. In the home.dart file, create a stateful widget and return a scaffold instead of a container. Inside scaffold add AppBar widget, and create a title. Import RecipeCard widget and return in body widget.

Then, populate a RecipeCard with fake data for now. We will use it within API a bit later.

This is how your code should looks like:

import 'package:flutter/material.dart';
import 'package:food_recipe/views/widgets/recipe_card.dart';

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Icon(Icons.restaurant_menu),
              SizedBox(width: 10),
              Text('Food Recipes'),
            ],
          ),
        ),
        body: RecipeCard(
          title: 'My recipe',
          rating: '4.9',
          cookTime: '30 min',
          thumbnailUrl: 'https://lh3.googleusercontent.com/ei5eF1LRFkkcekhjdR_8XgOqgdjpomf-rda_vvh7jIauCgLlEWORINSKMRR6I6iTcxxZL9riJwFqKMvK0ixS0xwnRHGMY4I5Zw=s360',
        ));
  }
}

5. Refactor main.dart file

Finally, before we begin to work with API, let’s go to our main.dart file and refactor everything.

Your code should look like this:

import 'package:flutter/material.dart';
import 'package:food_recipe/views/home.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Food recipe',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
        primaryColor: Colors.white,
        textTheme: TextTheme(
          bodyText2: TextStyle(color: Colors.white),
        ),
      ),
      home: HomePage(),
    );
  }
}

Now, if you refresh and reload, your app should look like this.

recipe flutter app
recipe app

6. Integrating API with an app

Now, we can finally begin to integrate API with an app. We are going to use a Yummly API to receive recipes. So, go to the Yummly API and register there, the price is free for 500 requests per month. So, you will be able to make only 500 requests for free within your account.

Now, what we are going to use from this API is GET feeds/list. A quick note, I will blur the x-rapidapi-key because of the limitation of 500 requests. So, use your own key.

oflutter yummly

Now, leave the tab open, and come back to the Flutter project

7. Create Recipe class (recipe.dart)

Now, go to recipe.dart file in models folder and create a Recipe class.

You code should look like this:

class Recipe {
  final String name;
  final String images;
  final double rating;
  final String totalTime;

  Recipe({this.name, this.images, this.rating, this.totalTime});

  factory Recipe.fromJson(dynamic json) {
    return Recipe(
        name: json['name'] as String,
        images: json['images'][0]['hostedLargeUrl'] as String,
        rating: json['rating'] as double,
        totalTime: json['totalTime'] as String);
  }

  static List<Recipe> recipesFromSnapshot(List snapshot) {
    return snapshot.map((data) {
      return Recipe.fromJson(data);
    }).toList();
  }

  @override
  String toString(){
    return 'Recipe {name: $name, image: $images, rating: $rating, totalTime: $totalTime}';
  }
}

8. Create Recipe API

In this step, we will create a Recipe API class to communicate between the web server and our app.

Go to recipe.api.dart file and create our RecipeAPI class

Your code should look like this:

import 'dart:convert';
import 'package:food_recipe/models/recipe.dart';
import 'package:http/http.dart' as http;

class RecipeApi {
  static Future<List<Recipe>> getRecipe() async {
    var uri = Uri.https('yummly2.p.rapidapi.com', '/feeds/list',
        {"limit": "18", "start": "0", "tag": "list.recipe.popular"});

    final response = await http.get(uri, headers: {
      "x-rapidapi-key": "YOUR API KEY FROM YUMMLY API",
      "x-rapidapi-host": "yummly2.p.rapidapi.com",
      "useQueryString": "true"
    });

    Map data = jsonDecode(response.body);
    List _temp = [];

    for (var i in data['feed']) {
      _temp.add(i['content']['details']);
    }

    return Recipe.recipesFromSnapshot(_temp);
  }
}

Note: Instead of “x-rapidapi-key”: “YOUR API KEY FROM YUMMLY API“, put your API KEY from Step #6

9. Make and API Call Inside Home Page

Finally, call the API getRecipes() inside home.dart page.

Your code should looks like this

import 'package:flutter/material.dart';
import 'package:food_recipe/models/recipe.api.dart';
import 'package:food_recipe/models/recipe.dart';
import 'package:food_recipe/views/widgets/recipe_card.dart';

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  List<Recipe> _recipes;
  bool _isLoading = true;

  @override
  void initState() {
    super.initState();
    getRecipes();
  }

  Future<void> getRecipes() async {
    _recipes = await RecipeApi.getRecipe();
    setState(() {
      _isLoading = false;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Icon(Icons.restaurant_menu),
              SizedBox(width: 10),
              Text('Food Recipe')
            ],
          ),
        ),
        body: _isLoading
            ? Center(child: CircularProgressIndicator())
            : ListView.builder(
                itemCount: _recipes.length,
                itemBuilder: (context, index) {
                  return RecipeCard(
                      title: _recipes[index].name,
                      cookTime: _recipes[index].totalTime,
                      rating: _recipes[index].rating.toString(),
                      thumbnailUrl: _recipes[index].images);
                },
              ));
  }
}

This is final result of the app:

oflutter final recipe app with api

Conclusion

This was a guide on how to integrate an API with the Flutter app, go and check out my video for a more detailed explanation. YouTube Video Link

You May Also Like

Leave a Reply

Your email address will not be published. Required fields are marked *

two × 5 =