Flutter List Iterate: CooKingKong App – Step 6

As the relation between the pages becomes complex, we need to be careful. Why? Because we have been handling List and Map. When we iterate Flutter List, we must assure that we send correct data.

Otherwise, our app might crash.

So far, we have learned a few key concepts while building a recipe app.

We have seen how we can send data from one page to the other page.

In addition, we have also learned the named route to send data in a better way.

Let us take a look at the previous steps.

Firstly, we have built the categories home page. Here we can see every category of food recipe.

Flutter navigation send data_ categories page
Flutter navigation send data_ categories page

Next, we see how every category houses different type of food items.

Model view controller architecture first Example
Model view controller architecture first Example

After that, we can click any food item and see the associated title.

Model view controller architecture second Example
Model view controller architecture second Example

But, we want to see the full content of a food item. Why? Because, we know that dummy food class has many properties.

The question is, how we can get every content of the food item?

It is only possible if we pass the unique food id through the controller food item.

 void selectMeal(BuildContext context) {
    Navigator.of(context).pushNamed(
      IndiividualFoodPage.routeName,
      arguments: id,
    );
  }

// code is incomplete for brevity
// please clone this branch of GitHub repository

In our previous section, we have sent the title. As a result, whenever we had tapped any food item, we saw the title.

However, that is not our intention. We want to see all the recipe on the individual food page. Right?

Therefore, this time we have replaced the title argument to id.

As a result, we can now use a Dart list method “firstWhere”.

What does this function do?

This function takes one parameter element and searches that element in the list.

In our case, we have searched the same way.

final foodId = ModalRoute.of(context)!.settings.arguments as String;
    final selectedMeal =
        dummyLorenIpsumLorenIpsumFood.firstWhere((food) => food.id == foodId);

// code is incomplete for brevity
// please clone this branch of GitHub repository

Why Flutter List Iterate is important?

We will answer the above question. But, before that, we want to answer the following query.

If we take a look at any list in Dart or Flutter, what do we see?

We see a list of items. Right?

Our dummy food list is also like that.

import 'food.dart';

const dummyLorenIpsumLorenIpsumFood = [
  LorenIpsumFood(
    id: 'f1',
    categoryID: ['c1', 'c2', 'c8'],
    title: 'Lorem ipsum dolor sit amet',
    complexity: Complexity.simple,
    imageUrl:
        'https://cdn.pixabay.com/photo/2016/08/11/08/04/vegetables-1584999_960_720.jpg',
    duration: 20,
    ingredients: [
      '1 DolorSan sit amet, consectetur adipiscing elit',
      '1 Lorem ipsum dolor sit amet',
      '1 Lorem ipsum dol',
      '250g Lorem ipsum dolor',
      'Lorem ipsum',
      'Lorem ipsum dolor'
    ],
    steps: [
      'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
      'Lorem ipsum dolor sit amet, consectetur.',
      'Lorem ipsum dolo, consectetur adipiscing elit.',
      'Lorem ipsum dolor sit amet, consectetur.',
      'Lorem ipsum dolor sit amet, adipiscing elit.',
      'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
      'Lorem ipsum dolor sit amet.'
    ],
    isVegan: true,
    isVegetarian: true,
  ),
  LorenIpsumFood(
    id: 'f2',
    categoryID: ['c2', 'c5', 'c4'],
    title: 'Lorem ipsum',
    complexity: Complexity.simple,
...
// code is incomplete for brevity
// please clone this branch of GitHub repository

The above data is coming from the model folder.

We have created objects. And each food object has unique id. Now, based on that id we can search the list.

But we need to be careful that as the argument we should pass the id. Not any other property.

Suppose we have mistakenly sent the title instead of id.

What will happen?

void selectMeal(BuildContext context) {
    Navigator.of(context).pushNamed(
      IndiividualFoodPage.routeName,
      arguments: title,
    );
  }
...
final foodId = ModalRoute.of(context)!.settings.arguments as String;
    final selectedMeal =
        dummyLorenIpsumLorenIpsumFood.firstWhere((food) => food.id == foodId);
...

As we see these properties don’t match at all.

As a result, this will crash the app and throw an error.

Flutter List iterate example one
Flutter List iterate example one

However, if send the id, and check that against the list items, it will work.

Now we can click any food item on the category page. And it takes us to the individual food page.

Flutter List iterate example two
Flutter List iterate example two

Everybody loves Chinese food. So we scroll down and tap the last item.

It will display the detail.

Flutter List iterate example three
Flutter List iterate example three

Now the individual food page has many features. We can scroll the page to see every property.

Not only that we can also scroll the individual properties like “steps” as we see in the above image.

Let us see the code of individual food page, so that we will realize how it works.

import 'package:flutter/material.dart';

import '../model/dummy_foods.dart';

///Displaying the individual food page
///
class IndiividualFoodPage extends StatelessWidget {
  const IndiividualFoodPage({Key? key}) : super(key: key);
  static const routeName = '/food-detail';

  Widget displayTitle(BuildContext context, String text) {
    return Container(
      margin: const EdgeInsets.symmetric(vertical: 10),
      child: Text(
        text,
        style: Theme.of(context).textTheme.headline2,
      ),
    );
  }

  Widget displayContent(Widget child) {
    return Container(
      decoration: BoxDecoration(
        color: Colors.white,
        border: Border.all(color: Colors.grey),
        borderRadius: BorderRadius.circular(10),
      ),
      margin: const EdgeInsets.all(10),
      padding: const EdgeInsets.all(10),
      height: 150,
      width: 300,
      child: child,
    );
  }

  @override
  Widget build(BuildContext context) {
    final foodId = ModalRoute.of(context)!.settings.arguments as String;
    final selectedMeal =
        dummyLorenIpsumLorenIpsumFood.firstWhere((food) => food.id == foodId);
    return Scaffold(
      appBar: AppBar(
        title: Text(selectedMeal.title),
      ),
      body: SingleChildScrollView(
        child: Column(
          children: <Widget>[
            Container(
              padding: const EdgeInsets.all(2.0),
              height: 300,
              width: double.infinity,
              child: Image.network(
                selectedMeal.imageUrl,
                fit: BoxFit.cover,
              ),
            ),
            displayTitle(context, 'Ingredients'),
            displayContent(
              ListView.builder(
                itemBuilder: (ctx, index) => Card(
                  color: Theme.of(context).colorScheme.secondary,
                  child: Padding(
                      padding: const EdgeInsets.symmetric(
                        vertical: 5,
                        horizontal: 10,
                      ),
                      child: Text(selectedMeal.ingredients[index])),
                ),
                itemCount: selectedMeal.ingredients.length,
              ),
            ),
            displayTitle(context, 'Steps'),
            displayContent(
              ListView.builder(
                itemBuilder: (ctx, index) => Column(
                  children: [
                    ListTile(
                      leading: CircleAvatar(
                        child: Text('# ${(index + 1)}'),
                      ),
                      title: Text(
                        selectedMeal.steps[index],
                      ),
                    ),
                    const Divider()
                  ],
                ),
                itemCount: selectedMeal.steps.length,
              ),
            ),
          ],
        ),
      ),
    );
  }
}

Certainly we can move forward and make this app bigger by adding the sellers list. And as a result we can convert this recipe app to a shopping app.

Clone the repository and run locally

In the next sections, we will learn some more core features of Flutter. However, if you want to test this step in your local machine, please clone this GitHub repository.

What Next?

Books at Leanpub

Books in Apress

My books at Amazon

GitHub repository

Technical blog

Twitter


Posted

in

, , ,

by

Comments

3 responses to “Flutter List Iterate: CooKingKong App – Step 6”

  1. […] data type list in flutter and dart plays a key role in our expense checker app. Although we have learned a few important concepts […]

  2. […] How we can build a Food Recipe App with List and Map […]

Leave a Reply