<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Yayo Code</title>
        <link>https://yayocode.com</link>
        <description>Coding and tutorials</description>
        <lastBuildDate>Thu, 16 Apr 2026 16:12:51 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <image>
            <title>Yayo Code</title>
            <url>https://yayocode.com/favicon.png</url>
            <link>https://yayocode.com</link>
        </image>
        <copyright>All rights reserved 2026, Yayo Code</copyright>
        <category>Science and Technology</category>
        <item>
            <title><![CDATA[Dart: Multiply without using the multiplication '*' operator]]></title>
            <link>https://yayocode.com/post/d7ITN4MaHM3eAg9TRhHg</link>
            <guid>d7ITN4MaHM3eAg9TRhHg</guid>
            <pubDate>Thu, 23 Nov 2023 23:03:44 GMT</pubDate>
            <description><![CDATA[In this challenge we will learn two ways to multiply two numbers without using the multiplication '*' operator]]></description>
            <content:encoded><![CDATA[<img src="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2Fd7ITN4MaHM3eAg9TRhHg%2Fe610a770-f425-485d-8189-851542f39700.png?alt=media&token=d25fe262-896a-4594-8dd7-683e3fae9440" alt="Cover image"> 
 In this challenge, the task is multiplying two numbers without using the multiplication '*' operator. There are many ways to solve this challenge, and we will learn two of them in this article.

## Solution 1: Using a loop

This is the most common solution, and we must understand how multiplication works: multiplying a quantity by a number consists of adding that quantity as many times as the number indicates. So, 4×3 is equal to adding the number 4 three times. In code, it can be expressed as:

```dart
final result = 4 + 4 + 4;
```

If we analyze for a moment, we can see that to solve this problem, we need to use a loop. In this case, the best option is a `for` loop. So, we can create a function called `multiply` and have something like this:

```dart
num multiply(num a, num b) {
  num result = 0;
  for (int i = 0; i < b; i++) {
    result += a;
  }
  return result;
}
```

However, there is an issue with the above code; it only works if both arguments are positive numbers. We can check if `b` is negative if we also need to support negative numbers. In that case, instead of adding, we will subtract. The code would look like this:

```dart
num multiply(num a, num b) {
  num result = 0;
  bool isNegative = b < 0;

  for (int i = 0; i < b.abs(); i++) {
    result = isNegative ? result - a : result + a;
  }
  return result;
}
```

We use the `b.abs()` function in the condition because if `b` were negative, the condition would always be false. Another thing to note is that we do not check if the variable `a` is negative because the result is negative when adding two negative numbers.

You can run the code of the first solution in DartPad. Play with the variables to see different results.

<DartPad
   id="b8d5112ee0c8c6a3c57cea3e12eb6630" 
   width="100%" 
   height="500px" 
   split="80" 
/>

## Solution 2: Using rules of fractions

For this solution, one must be familiar with the rules of fractions, specifically the following rule:

<MyImage
    src="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2Fd7ITN4MaHM3eAg9TRhHg%2Ff590de47-df9c-4626-b34a-e041920a1a78.png?alt=media&token=7a9cb38f-96e5-4bb7-8c2d-dc7b04f83f00"
    width="30%"
    priority="false"
    smallScreenWidth="80%"
    aspectRatio="409/279"
    description="Rule: a/b/c = ac/b"
/>

What is interesting about this rule is when we substitute the value of **b** with the number **1**. Let's do it:

<MyImage
    src="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2Fd7ITN4MaHM3eAg9TRhHg%2F222c07c8-34ff-46ad-86f9-60597e7738da.png?alt=media&token=3fa08ef5-4677-455c-9844-5caa648b35b7"
    width="70%"
    priority="false"
    smallScreenWidth="100%"
    aspectRatio="1300/279"
    description="Substitute b with 1"
/>


As we can see, the multiplication of `a * c` is equivalent to `a/(1/c)`. Now, with all this information, our multiplication function would look like this:

```dart
num multiply(num a, num c) {
    return a / (1 / c);
}
```

However, there is an issue with the above code because any number divided by zero will cause an exception in the program. Therefore, if `c` is zero, we will return `0` as the result. After updating, the code would be as follows:

```dart
num multiply(num a, num c) {
    return c == 0 ? 0 : a / (1 / c);
}
```

You can run the code of the second solution in DartPad. Play with the variables to see different results.

<DartPad
   id="1bfaf8616d1cd930edf08468bea2ce47" 
   width="100%" 
   height="400px" 
   split="80" 
/>]]></content:encoded>
            <category>Dart</category>
            <category>Coding Interview</category>
            <enclosure url="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2Fd7ITN4MaHM3eAg9TRhHg%2Fe610a770-f425-485d-8189-851542f39700.png?alt=media&amp;token=d25fe262-896a-4594-8dd7-683e3fae9440" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Dart: Reverse an integer number]]></title>
            <link>https://yayocode.com/post/usEUQgEfJUbktbQKibcZ</link>
            <guid>usEUQgEfJUbktbQKibcZ</guid>
            <pubDate>Mon, 13 Nov 2023 15:06:05 GMT</pubDate>
            <description><![CDATA[In this challenge, we are going to reverse an integer number. For example, if you got the digits 321, reversing them would give you 123]]></description>
            <content:encoded><![CDATA[<img src="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2FusEUQgEfJUbktbQKibcZ%2Fa14b776d-6fb6-4f69-b8db-774dff3c311a.png?alt=media&token=8f47a440-ebb2-4770-81c6-c185d48fcc0b" alt="Cover image"> 
 In this challenge, we are going to reverse an integer number. For example, if you got the digits **321**, reversing them would give you **123**

This solution will use a while loop, modulo, and integer division.

First, we need to obtain the last digit of the given number, which is always the remainder when dividing it by **10**. For example, to get the **1** from the number **321**:

<MyImage
    src="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2FusEUQgEfJUbktbQKibcZ%2Ff567a380-562f-4081-b898-51c98e3ed39f.png?alt=media&token=26da4280-93a7-4e65-bd4f-07f20a5991d5"
    width="50%"
    priority="true"
    smallScreenWidth="70%"
    aspectRatio="454/392"
description="When we divide by 10, the remainder is always the last digit"
/>

Now that we know how to get the last digit of the given number, we need to store that digit in a variable and remove it from the given number.

```dart 
int number = 321;
int lastDigit = number % 10;

// TODO: remove the digit from the given number
number = ???????????;
```

Removing the last digit from the given number is straightforward. We divide by **10**, and the quotient will not contain the last digit:

<MyImage
    src="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2FusEUQgEfJUbktbQKibcZ%2F70fba392-e93d-404f-83b4-30652f552de1.png?alt=media&token=46b42039-f2cd-4bc4-ab09-42e7b0a65740"
    width="50%"
    priority="false"
    smallScreenWidth="70%"
    aspectRatio="380/324"
description="The quotient or final answer does not include the last digit"
/>

So our code would look like this:

```dart 
int number = 321;
int lastDigit = number % 10;

number ~/= 10;
```

Now, we need to repeat this code as long as the number is not equal to **0**, so we'll add a loop:

```dart
int number = 321;

// loop while the number is not 0
while (number != 0) {
    int lastDigit = number % 10;
    number ~/= 10;
}
```

Finally, we must create a variable `int reversed` to add the last digit. Since we need to add the digit to the right side of the previous digit each iteration, and we can't use concatenation, we'll multiply the reversed number by **10** to add the digit. The final code will look like this:

<DartPad
   id="40d69ace29104e0ef1b7ee23a340da33" 
   width="100%" 
   height="650px" 
   split="80" 
/>

<Note
   title="Note"
   text="
I have noticed that some people say this code does not work if the first digit of the given number is **0**. For example, the number **0123** should be reversed to **3210**. Well, the answer is simple. When storing **0123** in an integer variable, it automatically becomes **123**, as the leading zero has no value. Therefore, when reversed, it becomes **321**."/>]]></content:encoded>
            <category>Dart</category>
            <category>Coding Interview</category>
            <enclosure url="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2FusEUQgEfJUbktbQKibcZ%2Fa14b776d-6fb6-4f69-b8db-774dff3c311a.png?alt=media&amp;token=8f47a440-ebb2-4770-81c6-c185d48fcc0b" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Dart: Convert integer to hours and minutes]]></title>
            <link>https://yayocode.com/post/QXZfaJ9uvjwVqRRMD30h</link>
            <guid>QXZfaJ9uvjwVqRRMD30h</guid>
            <pubDate>Mon, 13 Nov 2023 08:52:03 GMT</pubDate>
            <description><![CDATA[In this challenge, the task is to transform an integer into hours and minutes format, separated by colons. For instance, if the given number is 68, the corresponding output would be 1:8]]></description>
            <content:encoded><![CDATA[<img src="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2FQXZfaJ9uvjwVqRRMD30h%2F22ea5a8e-7c14-42b0-a8c0-db9530342cbe.png?alt=media&token=fb739e14-b45c-4449-abea-6299c8c5f6fa" alt="Cover image"> 
 In this challenge, the task is to transform an integer into hours and minutes format, separated by colons. For instance, if the given number is **68**, the corresponding output would be **1:8**

## Examples

```dart
Input: 128
Output: 2:8
```

```dart 
Input: 35
Output: 0:35
```

## Solution
This problem is straightforward. If we divide by **60**, we get the number of hours. We can use the integer division `~/`:

For example, the integer division `128 ~/ 60` result is **2** hours. And to get the minutes, we can use the modulo `%`. The result of the modulo is the remainder of the division, so `128 % 60` is **8** minutes.

It can be seen more clearly in the following image:

<MyImage
    src="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2FQXZfaJ9uvjwVqRRMD30h%2F4ff60136-0871-44fb-9014-d6950b83ee8a.png?alt=media&token=f32b6a25-2945-437b-9444-37c524e65c25"
    width="65%"
    priority="true"
    smallScreenWidth="100%"
    aspectRatio="633/350"
    description="The result is 2:8"
/>

So, the final solution would look like this:

```dart
void main() {
  
  int num = 128;
  print('${(num ~/ 60)}:${num % 60}');
  
}
```

You can run the live example on DartPad:

<DartPad
   id="de5c523d1d8aa819ecbd64bcea6c5128" 
   width="100%" 
   height="355px" 
   split="65" 
/>]]></content:encoded>
            <category>Dart</category>
            <category>Coding Interview</category>
            <enclosure url="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2FQXZfaJ9uvjwVqRRMD30h%2F22ea5a8e-7c14-42b0-a8c0-db9530342cbe.png?alt=media&amp;token=fb739e14-b45c-4449-abea-6299c8c5f6fa" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Cubit in Action: Creating a Responsive News Application with Multi-Language Support. Part 3]]></title>
            <link>https://yayocode.com/post/r1ym2otazt5mc0z8e91n</link>
            <guid>r1ym2otazt5mc0z8e91n</guid>
            <pubDate>Sun, 12 Nov 2023 13:18:17 GMT</pubDate>
            <description><![CDATA[Learn how to build a responsive and multilingual news app with Cubit, using a scalable architecture and unit and widget testing]]></description>
            <content:encoded><![CDATA[<img src="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2Fr1ym2otazt5mc0z8e91n%2Feea9b3e0-2c7a-4452-aae2-a71de3bd714c.png?alt=media&token=464ebfa2-fb00-4fd8-a426-83e900579394" alt="Cover image"> 
 This is the third part of this article series, in which we will create a responsive news application supporting three languages: Spanish, English, and Chinese. Additionally, we will implement a scalable architecture and add unit tests and widget tests. The application will display the latest headlines and provide the option to search for news of interest and select your preferred language.

Remember that you can find the source code on [GitHub][sourceCode]. And don't forget to check out the other articles in this tutorial series:

- [Part 1: Introduction, architecture, and data sources.][parte1]

- [Part 2: Dependency injection, repositories, and business logic.][parte2]

- [Part 3: Presentation layer and support for multiple languages.][parte3](this article)

### Presentation Layer: User Interface

As mentioned, the presentation layer handles interactions and what the user can see: buttons, images, text, etc.

We need to create an application with support for three different screen sizes:

<MyImage
    src="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2Fr1ym2otazt5mc0z8e91n%2Ff385dfbb-ce34-4daa-a9b2-fed383fcc738.png?alt=media&token=170d0d2d-bcf4-4269-b952-8849043ecb84
"   
    width="80%"
    priority="false"
    smallScreenWidth="100%"
    aspectRatio="1280/496"
    description="Responsive app with support for three sizes."
/>

#### User Interface: Computer

Let's begin with the largest screen size designed for a desktop computer. We'll break down the user interface into different parts:

<MyImage
    src="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2Fr1ym2otazt5mc0z8e91n%2Ff33de4ce-2914-489b-86d9-6ca47be5bbdf.png?alt=media&token=203ce5b6-4bf5-4c63-94dd-efc76768eb46
"
    width="65%"
    priority="false"
    smallScreenWidth="100%"
    aspectRatio="817/648"
/>

We can identify three main components:

1. **Search Bar:** Users can enter text to search for news of interest.

2. **News List:** Comprising several news articles; clicking on any reveals the news details.

3. **News Details:** Users can view news details, and clicking "Read More" opens the browser to the news page.

While each of these parts can be further broken down, let's focus on these three. For the search bar, we'll create a widget called `SearchWidget`. For news details, we'll create a widget named `DetailsWidget`, and for each article in the news list, the widget will be called `ListItem`.

Let's start by creating the `SearchWidget`:

```dart
// Using Flutter Hooks instead of a StatefulWidget.
class SearchWidget extends HookWidget {
  const SearchWidget({
    Key? key,
    required this.onChanged,
    required this.onClearPress,
  });

  // Callback is executed when the clear button is pressed in the search bar.
  final VoidCallback onClearPress;

  // Callback executed each time the search term changes.
  final ValueChanged<String> onChanged;

  @override
  Widget build(BuildContext context) {
    // Create a TextEditingController with Flutter Hooks.
    final controller = useTextEditingController();

    // State that helps us show or hide the clear button
    // based on whether there is text in the search bar or not.
    final searchTermEmpty = useState(controller.text.isEmpty);

    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: TextFormField(
        controller: controller,
        onChanged: (text) {
          // Delay the call to "onChanged" to avoid multiple API calls.
          EasyDebounce.debounce(
            'search-news',
            const Duration(milliseconds: 300),
            () => onChanged.call(text),
          );
          searchTermEmpty.value = controller.text.isEmpty;
        },
        decoration: InputDecoration(
          prefixIcon: const Icon(Icons.search),
          labelText: 'Search News',
          // Hide or show the clear button based on whether there is text.
          suffixIcon: searchTermEmpty.value
              ? null
              : IconButton(
                  onPressed: () {
                    controller.clear();
                    onClearPress.call();
                  },
                  icon: const Icon(Icons.clear),
                ),
          border: const OutlineInputBorder(),
        ),
      ),
    );
  }
}
```

<Note
   title="Learn More"
   text="
In the `SearchWidget` widget, we have utilized the following packages:

- **[Flutter Hooks](https://pub.dev/packages/flutter_hooks)**: It helps eliminate repetitive code and makes it more readable. Learn more about it in this [videotutorial.](https://youtu.be/WYXLNNSqjqs)

- **[EasyDebounce](https://pub.dev/packages/easy_debounce)**: This package prevents excessive API calls when users type in the search bar.

"/>

Now, let's proceed to create the `DetailsWidget`:

```dart
class DetailsWidget extends StatelessWidget {
  const DetailsWidget({
    Key? key,
    required this.article,
  });

  final Article article;

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: double.infinity,
      child: SingleChildScrollView(
        child: Column(
          children: [
            // If the news lacks an image, display a red container;
            // otherwise, use `CachedNetworkImage` to show it.
            article.urlToImage == null
                ? Container(color: Colors.red, height: 250)
                : CachedNetworkImage(
                    width: 450,
                    imageUrl: article.urlToImage!,
                    placeholder: (context, url) => const Center(
                      child: CircularProgressIndicator(),
                    ),
                    errorWidget: (context, url, error) =>
                        const Icon(Icons.error),
                  ),
            Text(
              article.title,
              style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 18),
            ),
            const SizedBox(height: 8),
            Text('${article.content}'),
            const SizedBox(height: 8),

            // Clicking the button opens the news in the browser.
            ElevatedButton(
              onPressed: () => launchUrlString(article.url),
              child: const Text('Read More'),
            ),
            const SizedBox(height: 16),
          ],
        ),
      ),
    );
  }
}
```

Now, let's create the `ListItem` widget:

```dart
// Constant to define the width and height of the image.
const _imageSize = 120.0;

class ListItem extends StatelessWidget {
  const ListItem({
    Key? key,
    required this.article,
    required this.onTap,
  });

  final Article article;
  final GestureTapCallback onTap;

  @override
  Widget build(BuildContext context) {
    // GestureDetector helps detect if this widget has been pressed.
    return GestureDetector(
      onTap: onTap,
      child: Card(
        margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
        child: Row(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            // `_Image` is a private widget that helps show the image or
            // display an empty container if the news lacks an image.
            _Image(url: article.urlToImage),
            Expanded(
              child: Padding(
                padding: const EdgeInsets.all(8.0),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    // Display the news title.
                    Text(
                      article.title,
                      maxLines: 2,
                      overflow: TextOverflow.ellipsis,
                      style: const TextStyle(
                        fontWeight: FontWeight.bold,
                        fontSize: 16,
                      ),
                    ),
                    // If the news has a description, display it.
                    if (article.description != null) ...[
                      const SizedBox(height: 16),
                      Text(
                        article.description!,
                        maxLines: 3,
                        overflow: TextOverflow.ellipsis,
                      )
                    ]
                  ],
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

// `_Image` is a private widget that helps show the news image.
class _Image extends StatelessWidget {
  const _Image({
    required this.url,
  });

  final String? url;

  @override
 

 Widget build(BuildContext context) {
    // If the URL is null, display a red container.
    return url == null
        ? Container(
            width: _imageSize,
            height: _imageSize,
            color: Colors.red,
          )
        // If the URL is not null, use `CachedNetworkImage` to show the image.
        : CachedNetworkImage(
            width: _imageSize,
            height: _imageSize,
            imageUrl: url!,
            fit: BoxFit.cover,
            // While the image is loading, show a CircularProgressIndicator.
            placeholder: (context, url) => const SizedBox(
              width: _imageSize,
              height: _imageSize,
              child: Center(
                child: CircularProgressIndicator(),
              ),
            ),
            // In case of an error, display the error icon.
            errorWidget: (context, url, error) => const Icon(Icons.error),
          );
  }
}
```

We have now built the three main components: the search bar `SearchWidget`, the news details `DetailsWidget`, and the widget representing each news item in the news list `ListItem`. The next step is to integrate `NewsCubit` with the user interface.

### Integrating NewsCubit with the User Interface

Let's create the home screen, which will be displayed when users open the application. We'll name it `NewsScreen`.

```dart
class NewsScreen extends StatelessWidget {
  const NewsScreen({Key? key});

  @override
  Widget build(BuildContext context) {
    // Get the current locale of the application.
    final locale = Localizations.localeOf(context);

    // Use BlocProvider to create a NewsCubit and add it to the widget tree.
    return BlocProvider<NewsCubit>(
      // Create a NewsCubit with the current locale and call the searchNews function
      // to initiate the search as soon as the NewsScreen is visible.
      create: (context) => NewsCubit(locale)..searchNews(),
      child: Scaffold(
        appBar: AppBar(
          title: Text('News App'),
        ),
        // Use BlocBuilder to act according to the states of NewsCubit.
        body: BlocBuilder<NewsCubit, NewsState>(
          builder: (context, state) {
            // Depending on the state, return different widgets.
            return ?????;
          },
        ),
      ),
    );
  }
}
```

Now, let's display the `NewsScreen` when the application is launched:

```dart
void main() async {
  // Inject dependencies when starting the application.
  injectDependencies();

  runApp(
    // Create a MaterialApp that displays the NewsScreen.
    const MaterialApp(
      title: 'Flutter Demo',
      home: NewsScreen(),
    ),
  );
}
```

Depending on the current state of the cubit, we should return a different widget in the `builder` function of BlocBuilder:

```dart
    BlocBuilder<NewsCubit, NewsState>(
      builder: (context, state) {
        // Use a column to align the widgets.
        return Column(
          children: [
            // The first widget is the search bar.
            SearchWidget(
              // When the text changes, if it is longer than three characters,
              // call searchNews to perform a new search.
              onChanged: (text) {
                context
                    .read<NewsCubit>()
                    .searchNews(search: text.length > 3 ? text : null);
              },
              // When the clear button is pressed, call clearSearch to perform a new search without a search term.
              onClearPress: context.read<NewsCubit>().clearSearch,
            ),
            // Then we have an "Expanded" because the content after the search bar will take up the remaining space.
            Expanded(
              // The "Builder" is unnecessary, but it helps to use "if else" more organized.
              child: Builder(
                builder: (context) {
                  if (state is NewsSuccessState) {
                    // If the state is successful, display the _BigSize widget.
                    return _BigSize(news: state.news);
                  } else if (state is NewsErrorState) {
                    // If the state is an error, display text with the error.
                    return Text(state.message);
                  } else {
                    // If the state is loading, display a loading indicator.
                    return const Center(
                      child: CircularProgressIndicator(),
                    );
                  }
                },
              ),
            ),
          ],
        );
      },
    )
```

We've implemented the content of the BlocBuilder, but there's a widget we haven't created yet. `_BigSize` is the widget containing the large screens' user interface structure. Let's see how to implement it:

```dart
class _BigSize extends HookWidget {
  // Initialize the list of news in the constructor.
  const _BigSize({required this.news});

  final List<Article> news;

  @override
  Widget build(BuildContext context) {
    // Use hooks to maintain the state of the selected news.
    final currentNew = useState(news.firstOrNull);

    // Use Row to separate the screen into two. The news list is on the left, and the details are on the right.
    return Row(
      children: [
        // The news list has a fixed width of 450.
        SizedBox(
          width: 450,
          child: ListView.builder(
            itemCount: news.length,
            // When pressing an item in the list, update the local state with the selected news.
            itemBuilder: (_, int index) => ListItem(
              article: news[index],
              onTap: () => currentNew.value = news[index],
            ),
          ),
        ),
        // Display the details of the selected news.
        if (currentNew.value != null)
          Expanded(
            child: DetailsWidget(
              article: currentNew.value!,
            ),
          ),
      ],
    );
  }
}
```

Now, we can run the application and see the result.

<MyImage
    src="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2Fr1ym2otazt5mc0z8e91n%2F14d088a6-8f21-4823-a7cc-c310fdf797e7.webp?alt=media&token=876a8d7d-64e5-4e8f-9ec3-120f952f7591
"
    width="65%"
    priority="false"
    smallScreenWidth="100%"
    aspectRatio="640/480"
    description="Application without phones and tablets support"
/>

The application looks good; we can select different news, but the problem is that the application does not adapt to screen changes and shows overflow errors. Let's continue by adding support for other screen sizes.

#### User Interface: Tablet or iPad

It's time to add support for tablets and iPads. In this case, the news list and details will be on separate screens, as shown in the following image:

<MyImage
    src="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2Fr1ym2otazt5mc0z8e91n%2F2de3fce5-731e-42d3-b8b0-5c8bebd8f364.png?alt=media&token=97032d55-ac1a-44a3-b681-7a8405ae2246"
    width="75%"
    priority="false"
    smallScreenWidth="100%"
    aspectRatio="1219/575"
    description="1. When pressing a news item in the list, we navigate to the news details."
/>
 
Let's create a new screen called `NewsDetailScreen`, which will be used to view the details of the news:

```Dart
// Define a type that we will use to pass the news
// as an argument during navigation.
typedef NewsDetailScreenArgs = ({Article article});

class NewsDetailScreen extends StatelessWidget {
  const NewsDetailScreen({Key? key});

  @override
  Widget build(BuildContext context) {
    // Get the news.
    final article = context.args<NewsDetailScreenArgs>().article;

    // Create a Scaffold with an AppBar and use the DetailsWidget
    // widget to display the news details.
    return Scaffold(
      appBar: AppBar(
        title: const Text('News Details'),
      ),
      body: DetailsWidget(article: article),
    );
  }
}
```

The above code shows that the `NewsDetailScreen` class is straightforward since we are reusing the `DetailsWidget`.

We will use routes for navigation, so let's create a new class called `Routes` that will manage the routes.

```Dart
class Routes {
  // Root route
  static const newsScreen = '/';

  // News details route
  static const detailsScreen = '/details';

  static Route routes(RouteSettings settings) {

    // Helper function to create MaterialPageRoute and avoid
    // having too much repetitive code.
    MaterialPageRoute buildRoute(Widget widget) {
      return MaterialPageRoute(builder: (_) => widget, settings: settings);
    }

    // Use a switch to navigate to the desired route; if the route
    // does not exist; throw an exception.
    return switch (settings.name) {
      newsScreen => buildRoute(const NewsScreen()),
      detailsScreen => buildRoute(const NewsDetailScreen()),
      _ => throw Exception('Route does not exist'),
    };
  }
}
```

We must also modify `MaterialApp` and use `onGenerateRoute` instead of `home`.

```Dart
    return MaterialApp(
      title: 'Flutter Demo',
      onGenerateRoute: Routes.routes,
    );
```

Great, we now have navigation ready, but we must also add support for tablets and iPads. Let's create a new private widget that will display the list on medium-sized screens:

```Dart
class _MediumSize extends StatelessWidget {
  // Initialize the list of news in the constructor.
  const _MediumSize({required this.news});

  final List<Article> news;

  @override
  Widget build(BuildContext context) {
    // Create the news list.
    return ListView.builder(
      itemCount: news.length,
      itemBuilder: (_, int index) => ListItem(
        article: news[index],
        onTap: () {
          // When pressing an item in the list, navigate to the details screen.
          final args = (article: news[index]);
          Navigator.pushNamed(context, Routes.detailsScreen, arguments: args);
        },
      ),
    );
  }
}
``` 

Finally, to decide whether to show the `_BigSize` or `_MediumSize` widget, we will use a `LayoutBuilder` as follows:

```Dart
    return LayoutBuilder(
      builder: (context, constraints) {
        // If the available maximum width is less than 900 units, 
        // show _MediumSize; otherwise, show _BigSize.
        return switch (constraints.maxWidth) {
          < 900 => _MediumSize(news: state.news),
          _ => _BigSize(news: state.news),
        };
      },
    );
```

If we run the application and resize the screen, we can see that the user interface adapts correctly.

#### User Interface: Mobile

It's time to add support for mobile phones. But this part is a task for the reader, so let's see how the user interface looks:

<MyImage
    src="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2Fr1ym2otazt5mc0z8e91n%2F7cdde2a8-3b83-4fd1-9175-44551ee0d706.png?alt=media&token=7f2b37a8-b9fc-42ac-bb9d-202ec0211530"
    width="65%"
    priority="false"
    smallScreenWidth="100%"
    aspectRatio="661/575"
    description="1. When pressing a news item in the list, we navigate to the news details."
/>

The user interface is very similar to the tablet interface, so it won't be complicated to create. But here are some tips:

- Create a new widget to represent each news item in the list.

- Create a new widget to display the news list. For consistency in the code, I would call it `_SmallSize`.

- Modify the logic of the `LayoutBuilder` to display the `_SmallSize` widget.

- **NO** need to create any new screens or routes.

If you encounter any issues, remember that you can download the source code from [GitHub][sourceCode].

### Multi-language Support

Now, let's add support for multiple languages to the application. We will use the [EasyLocalization][easyLocalization] package, simplifying adding multiple languages. Learn more about this package in this [videotutorial][easyLocalizationYoutube].

First, we need to decide which languages we want to support. We can refer to the documentation of the two APIs we are going to support:

1. [/v2/everything:][newsApiEverything] Supports the `language` parameter with values like **es**, **en**, **zh**, and many more.

2. [/v2/top-headlines:][newsApiTop] Supports the `country` parameter with values like **mx**, **ve**, **us**, **tw**, and many more.

This application will only add support for a few languages and countries. Let's start by creating a list of supported locales:

```Dart
const spanishMx = Locale('es', 'MX'); // Spanish from Mexico
const spanishVe = Locale('es', 'VE'); // Spanish from Venezuela
const englishUs = Locale('en', 'US'); // English from the USA
const chineseTw = Locale('zh', 'TW'); // Chinese from Taiwan

// Map with the names of the supported languages
final supportedLocales = <Locale, String>{
  englishUs: 'English - US',
  spanishMx: 'Español - MX',
  spanishVe: 'Español - VE',
  chineseTw: '中文 - TW',
};
```

We also need three JSON files with translations for the supported languages.

For English, we have `en.json`:

```JSON
{
  "top_headlines": "Top Headlines",
  "search_news": "Search News",
  "view_more": "View more"
}
```

For Spanish, we have `es.json`:

```JSON
{
  "top_headlines": "Titulares Principales",
  "search_news": "Buscar Noticias",
  "view_more": "Ver más"
}
```

And for Chinese, we have `zh.json`:

```JSON
{
  "top_headlines": "最新的新聞",
  "search_news": "找新聞",
  "view_more": "看更多"
}
```

[EasyLocalization][easyLocalization] supports code generation that creates each key in a map, making it easier to access in the code. For example:

```Dart
// Without code generation, we might make mistakes like the following:
Text('top_headLines'.tr());

// Did you find the error? Instead of writing 'top_headlines', we wrote 'top_headLines'.

// But with code generation, we can't make any mistakes:
Text(LocaleKeys.top_headlines.tr())
```

Let's run the following command to generate all the necessary code:
```sh
flutter pub run easy_localization:generate -f keys -S resources/translations -O lib/src/localization -o locale_keys.g.dart
flutter pub run easy_localization:generate -S resources/translations -O lib/src/localization
```

<Note
   title="Note"
   text="
Don't worry if you don't understand the above command. In the [source code](https://tinyurl.com/rytwmvvu), you can find the **generate_code.sh** file and execute it from the terminal or console to generate all the necessary code.
"/>

After running the above command, the `codegen_loader.g.dart` and `locale_keys.g.dart` files will be generated inside the `lib/src/localization` folder.

After generating the necessary code, let's initialize the [EasyLocalization][easyLocalization] package. We'll modify the `main()` function as follows:

```Dart
void main() async {

  // Ensure the initialization of WidgetsFlutterBinding and EasyLocalization
  WidgetsFlutterBinding.ensureInitialized();
  await EasyLocalization.ensureInitialized();

  injectDependencies();
  runApp(const MyApp());
}
```

We also need to configure [EasyLocalization][easyLocalization] and enclose our `MaterialApp` with the `EasyLocalization` widget:

```Dart
class MyApp extends StatelessWidget {
  const MyApp({Key? key});

  @override
  Widget build(BuildContext context) {
    return EasyLocalization(
      // The fallback language is English from the USA
      fallbackLocale: englishUs,
      useFallbackTranslations: true,

      // Pass the list of supported languages
      supportedLocales: supportedLocales.keys.toList(),

      // path is not necessary because we are using generated code
      path: 'xxx',

      // Pass the generated class
      assetLoader: const CodegenLoader(),

      // Enclose MaterialApp with the EasyLocalization widget
      child: const _MaterialApp(),
    );
  }
}

class _MaterialApp extends StatelessWidget {
  const _MaterialApp();

  @override
  Widget build(BuildContext context) {
    // Configure MaterialApp to support different languages
    return MaterialApp(
      title: 'Flutter Demo',
      locale: context.locale,
      onGenerateRoute: Routes.routes,
      debugShowCheckedModeBanner: false,
      supportedLocales: context.supportedLocales,
      localizationsDelegates: context.localizationDelegates,
    );
  }
}
```

Let's return to the `NewsScreen` class and make two modifications. The first modification is to use the [EasyLocalization][easyLocalization] extension to get the current `locale`. So, replace the following code:

```Dart
// Replace this line of code
final locale = Localizations.localeOf(context);

// with
final locale = context.locale;
```

The difference in the above code is that using the [EasyLocalization][easyLocalization] extension is much shorter.

We also need to add a `PopupMenuButton` that allows us to select between the languages supported by our application:

<MyImage
    src="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2Fr1ym2otazt5mc0z8e91n%2F61ae6572-3eb2-40cd-898c-ddcfd62bcb9b.png?alt=media&token=1cfd5aa0-5a58-4a52-8199-66bedbfc2576"
    width="40%"
    priority="false"
    smallScreenWidth="60%"
    aspectRatio="427/432"
    description="PopupMenuButton to select the language."
/>

Let's create a widget called `_LanguagePopUpMenu` that will allow us to display a menu to select from the supported languages:

```Dart
class _LanguagePopUpMenu extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // Create a PopupMenuButton with the translate icon
    return PopupMenuButton<Locale>(
      icon: const Icon(Icons.translate),
      itemBuilder: (context) {
        // Iterate through each of the supported languages to create
        // a list of PopupMenuItem
        return supportedLocales.entries.map((it) {
          return PopupMenuItem(
            value: it.key,
            child: Text(it.value),
          );
        }).toList();
      },
      // Each time we select a language from the menu, update
      // the language of the application and the cubit.
      onSelected: (selected

Locale) {
        context.setLocale(selectedLocale);
        context.read<NewsCubit>().setLocale(selectedLocale);
      },
    );
  }
}
```

Then, add it to the **AppBar** in the `actions:` property:

```Dart
 // Add the _LanguagePopUpMenu widget to the AppBar
 appBar: AppBar(
   title: Text(LocaleKeys.top_headlines.tr()),
   actions: [
      _LanguagePopUpMenu(),
   ],
 ),
```

Now, we can run the application, and we will have the option to select the language and search in any of the supported languages.

This concludes this series of articles, so remember that you can find the source code on [GitHub][sourceCode]. And don't forget to visit the other articles in this tutorial series:

- [Part 1: Introduction, Architecture, and Data Sources.][parte1]

- [Part 2: Dependency Injection, Repositories, and Business Logic.][parte2]

- [Part 3: Presentation Layer and Multi-language Support.][parte3](this article)

[parte1]: /post/j66drgpxoelhslflwkvw
[parte2]: /post/6xxh4e60lq4x8fwwfkhu
[parte3]: /post/r1ym2otazt5mc0z8e91n
[sourceCode]: https://tinyurl.com/rytwmvvu
[easyLocalizationYoutube]: https://youtu.be/XAptuhecO6I
[easyLocalization]: https://pub.dev/packages/easy_localization
[newsApiEverything]: https://newsapi.org/docs/endpoints/everything
[newsApiTop]: https://newsapi.org/docs/endpoints/top-headlines]]></content:encoded>
            <category>Cubit</category>
            <category>Dart</category>
            <category>Flutter</category>
            <enclosure url="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2Fr1ym2otazt5mc0z8e91n%2Feea9b3e0-2c7a-4452-aae2-a71de3bd714c.png?alt=media&amp;token=464ebfa2-fb00-4fd8-a426-83e900579394" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Cubit in Action: Creating a Responsive News Application with Multi-Language Support. Part 2]]></title>
            <link>https://yayocode.com/post/6xxh4e60lq4x8fwwfkhu</link>
            <guid>6xxh4e60lq4x8fwwfkhu</guid>
            <pubDate>Sat, 11 Nov 2023 14:07:55 GMT</pubDate>
            <description><![CDATA[Learn how to build a responsive and multilingual news app with Cubit, using a scalable architecture and unit and widget testing]]></description>
            <content:encoded><![CDATA[<img src="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2F6xxh4e60lq4x8fwwfkhu%2Fbbadfb22-5ad5-4e57-a303-a59fd5a3fb86.png?alt=media&token=604bd90c-84ad-4db0-b011-7a2c2b8064db" alt="Cover image"> 
 This is the second part of this article series, in which we will create a responsive news application supporting three languages: Spanish, English, and Chinese. Additionally, we will implement a scalable architecture and add unit tests and widget tests. The application will display the latest headlines and provide the option to search for news of interest and select your preferred language.

Remember that you can find the source code on [GitHub][sourceCode]. And don't forget to check out the other articles in this tutorial series:

- [Part 1: Introduction, architecture, and data sources.][parte1]

- [Part 2: Dependency injection, repositories, and business logic.][parte2] (this article)

- [Part 3: Presentation layer and support for multiple languages.][parte3]

## Dependency Injection with [GetIt][getIt]

[GetIt][getIt] is a service locator package that helps us adhere to the [Dependency Inversion Principle][wikiDI], which aims to reduce coupling between software components and promote flexibility and ease of maintenance.

Let's create a new file called `dependency_injection.dart`, where we will initialize [GetIt][getIt] and create a helper function to inject `NewsDataSource`:

```Dart
final getIt = GetIt.instance;

Future<void> injectDependencies() async {
  getIt.registerLazySingleton(() => NewsDataSource());
}
```

Then, in the `main()` function, we call `injectDependencies()` to register the dependencies:

```Dart
void main() async {
 
  // Inject dependencies when starting the application
  injectDependencies();

  runApp(const MyApp());
}
```

Why should we use dependency injection in our application? Later, when we write unit tests, we can create a "Mock" class, for example, `MockNewsDataSource`, and inject it into the tests, allowing us to simulate different scenarios.

## Data Layer

### Repository: NewsRepository

In this small application, the repository is merely an intermediary between the data sources and the cubits.

<Note
   title="Note"
   text="
Remember that, in larger applications, the repository has several uses, such as managing data coming from the data source, transforming it, and saving it to the cache or local database, etc.
"/>

We create the `NewsRepository` class with the `fetchEverything` and `fetchTopHeadlines` functions that call the data source to retrieve the data:

```Dart
class NewsRepository {
  // Get the data source we injected earlier
  final NewsDataSource _dataSource = getIt();

  // Call the data source to get all news
  Future<List<Article>> fetchEverything({
    required Locale locale,
    String? search,
  }) =>
      _dataSource.fetchEverything(
        language: locale.languageCode,
        search: search,
      );

  // Call the data source to get the latest headlines
  Future<List<Article>> fetchTopHeadlines({
    required Locale locale,
  }) =>
      _dataSource.fetchTopHeadlines(
        country: locale.countryCode!,
      );
}
```

Finally, we have finished coding the data layer, where we created the `NewsDataSource` class acting as the gateway to our external data sources and established a robust set of tests to ensure its correct operation in various situations. We also learned to use dependency injection with [GetIt][getIt], providing flexibility and ease of testing.

## The Application Layer

This layer is responsible for handling business logic and direct interaction with users. It interacts directly with the data layer to obtain and store information, making it crucial for writing unit tests since most of the business logic resides here.

### Business Logic: NewsCubit

The `NewsCubit` class is responsible for maintaining the user interface's state and calling the repository to retrieve news to be displayed to the user. Before coding the cubit, let's examine the possible states of the application:

<MyImage
    src="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2F6xxh4e60lq4x8fwwfkhu%2F6588fd67-3624-4797-80c7-6462ba7119d3.png?alt=media&token=6db2513d-8989-4fec-8234-660a0c6c5337"
    width="75%"
    priority="false"
    smallScreenWidth="100%"
    aspectRatio="1030/615"
    description="Application States: Loading, Success, Error"
/>

- **Loading State**: We show a loading animation on the user interface during this state. A request to the API is being made during this state. The `NewsLoadingState` class represents this state.

- **Success State**: When the API response is successful, we enter the success state and display a news list on the user interface. The `NewsSuccessState` class represents this state, containing the news list.

- **Error State**: If the API response fails, we enter the error state and display the error message on the user interface. The `NewsErrorState` class represents this state, containing the error message.

When creating a [cubit][cubit], it's necessary to indicate the type of the state. In our case, there are three states: `NewsLoadingState`, `NewsSuccessState`, and `NewsErrorState`. We achieve this by using inheritance:

```Dart
// All states will extend from NewsState
sealed class NewsState extends Equatable {
  @override
  List<Object> get props => [];
}

// Loading state
class NewsLoadingState extends NewsState {}

// Success state
class NewsSuccessState extends NewsState {

  // The list of news
  final List<Article> news;

  NewsSuccessState(this.news);

  @override
  List<Object> get props => [news];
}

// Error state
class NewsErrorState extends NewsState {

  // The error message
  final String message;

  NewsErrorState(this.message);

  @override
  List<Object> get props => [message];
}
```

Now that we have defined the states, we can create the `NewsCubit` class:

```Dart
class NewsCubit extends Cubit<NewsState> {

  // The cubit has a dependency in the repository
  final NewsRepository _repository = getIt();

  NewsCubit() : super(NewsLoadingState());
}
```

To complete the implementation of the `NewsCubit`, we need to understand the types of interactions the user can have with the application. The following image illustrates these interactions:

<MyImage
    src="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2F6xxh4e60lq4x8fwwfkhu%2F601333e0-74ee-4f29-91ce-e3c9fcb310c0.png?alt=media&token=dc2a5dbe-7a56-42c8-8d91-dd9ded549246"
    width="65%"
    priority="false"
    smallScreenWidth="100%"
    aspectRatio="625/615"
    description="Places where the user can interact with the application"
/>

There are three places where the user can interact with the application:

1. **Search Bar**: The user can enter text to search for news of interest.
2. **Language Selection**: Let the user change the application's language.
3. **Clear Button**: Clears the content of the search bar and displays the latest news in the application.

Now we can create three functions in our cubit to handle the search, language change, and clear button events:

```Dart
class NewsCubit extends Cubit<NewsState> {
  final NewsRepository _repository = getIt();

  // Helper variable containing the text we are searching for
  String? _currentSearch;

  // Helper variable containing the language and country of the news we are searching for
  Locale _currentLocale;

  // Initialize "locale" in the constructor
  NewsCubit(Locale locale)
      : _currentLocale = locale,
        super(NewsLoadingState());

  // When the user enters a search term, we call this function
  Future<void> searchNews({
    String? search,
  }) async {
    _currentSearch = search;
    return _search();
  }

  // When the user presses the clear button, we call this function
  Future<void> clearSearch() async {
    _currentSearch = null;
    return _search();
  }

  // When the user changes the language of the application, we call this function
  Future<void> setLocale(Locale newLocale) async {
    if (_currentLocale == newLocale) return;
    _currentLocale = newLocale;
    return _search();
  }

  // This function abstracts the search logic to avoid code repetition in the [searchNews], [clearSearch], and [setLocale] functions
  Future<void> _search() async {
    
  }
}
```

We have already created the three necessary functions for the presentation layer to send user-generated events to the `NewsCubit`. We can also see that the three functions share the search logic, which we are going to implement in the private function `_search()`:

```Dart
Future<void> _search() async {
  // The code is within a try-catch block to catch
  // exceptions thrown from the data layer
  try {

    // Emit the loading state to have the presentation layer display the loading interface
    emit(NewsLoadingState());

    // Using a switch, if [_currentSearch] is null, call the API for the latest headlines
    // but if it is not null, call the API to search for all news
    final news = await switch (_currentSearch) {
      null => _repository.fetchTopHeadlines(
          locale: _currentLocale,
        ),
      _ => _repository.fetchEverything(
          locale: _currentLocale,
          search: _currentSearch,
        )
    };

    // Emit the success state with the list of news for the presentation layer to display the news
    emit(NewsSuccessState(news));

  } on Exception catch (e) {
    
    // In case of any exception, catch it and emit an error state for the presentation
    // layer to display the error
    if (e is MissingApiKeyException) {
      emit(NewsErrorState('Please check the API key'));
    } else if (e is ApiKeyInvalidException) {
      emit(NewsErrorState('The API key is not valid'));
    } else {
      emit(NewsErrorState('Unknown error'));
    }
  }
}
```

We have completed all the functions, variables, etc., that comprise the `NewsCubit` class. If we put everything together, the code will be:

```Dart
class NewsCubit extends Cubit<NewsState> {
  final NewsRepository _repository = getIt();
  String? _currentSearch;
  Locale _currentLocale;

  NewsCubit(Locale locale)
      : _currentLocale = locale,
        super(NewsLoadingState());

  Future<void> searchNews({
    String? search,
  }) async {
    _currentSearch = search;
    return _search();
  }

  Future<void> clearSearch() async {
    _currentSearch = null;
    return _search();
  }

  Future<void> setLocale(Locale newLocale) async {
    if (_currentLocale == newLocale) return;
    _currentLocale = newLocale;
    return _search();
  }

  Future<void> _search() async {
    try {
      emit(NewsLoadingState());

      final news = await switch (_currentSearch) {
        null => _repository.fetchTopHeadlines(
            locale: _currentLocale,
          ),
        _ => _repository.fetchEverything(
            locale: _currentLocale,
            search: _currentSearch,
          )
      };

      emit(NewsSuccessState(news));

    } on Exception catch (e) {

      if (e is MissingApiKeyException) {
        emit(NewsErrorState('Please check the API key'));
      } else if (e is ApiKeyInvalidException) {
        emit(NewsErrorState('The api key is not valid'));
      } else {
        emit(NewsErrorState('Unknown error'));
      }
    }
  }
}
```

#### Testing the NewsCubit Class

It's time to test that the `NewsCubit` class works as expected. To test a cubit, we can use the [bloc_test][blocTest] package, which makes testing cubits and blocs easier. Let's create a file called `news_cubit_test.dart` inside the test folder and set up the basic structure:

```Dart
// Mock will allow us to return mock data to the cubit from the repository
class MockNewsRepository extends Mock implements NewsRepository {}

// Mock Locale that we will use to pass to the cubit in the constructor and the setLocale function
const mockLocale = Locale('en', 'US');

// Calling the fetchTopHeadlines function will return this article
const mockTopArticle = Article(title: "TopArticle", url: "someUrl");

// Calling the fetchEverything function will return this article
const mockEverythingArticle = Article(title: "Everything", url: "someUrl");

void main() {
  late MockNewsRepository mockRepo;
  
  // setUp is called before each test.
  setUp(() async {
    mockRepo = MockNewsRepository();

    // Injecting MockNewsRepository
    getIt.registerSingleton<NewsRepository>(mockRepo);
  });

  // tearDown is called after each test.
  tearDown(() async {
    // Resetting getIt to its initial state
    await getIt.reset();
  });
}
```

Now, we need to configure `MockNewsRepository` so that the `fetchTopHeadlines` and `fetchEverything` functions return an article when called:

```Dart
setUp(() async {
  mockRepo = MockNewsRepository();
  getIt.registerSingleton<NewsRepository>(mockRepo);

  // When the fetchEverything function is called with mockLocale and any
  // search term, it returns a list containing the mockEverythingArticle
  when(() => mockRepo.fetchEverything(
        locale: mockLocale,
        search: any(named: 'search'),
      )).thenAnswer((_) async => [mockEverythingArticle]);

  // When the fetchTopHeadlines function is called with mockLocale
  // it returns a list containing the mockTopArticle
  when(() => mockRepo.fetchTopHeadlines(locale: mockLocale))
      .thenAnswer((_) async => [mockTopArticle]);
});

```

The first test we're going to perform is to verify that calling the `searchNews` function emits the correct states and calls the `fetchTopHeadlines` function correctly:

```Dart
blocTest<NewsCubit, NewsState>(
  'When the search term is null '
  'fetchTopHeadlines will be called '
  'and the state will contain the mockTopArticle',

  // Create the cubit with mockLocale
  build: () => NewsCubit(mockLocale),

  // Call the searchNews function
  act: (cubit) async => cubit.searchNews(),

  // States should be emitted in the correct order
  expect: () => [
    NewsLoadingState(),
    NewsSuccessState(const [mockTopArticle]),
  ],

  // Verify that the fetchTopHeadlines function was called 1 time with the
  // argument mockLocale
  verify: (cubit) {
    verify(() => mockRepo.fetchTopHeadlines(locale: mockLocale)).called(1);
  }
);
```

In the second test, we'll pass a search text to the `searchNews` function. Therefore, the `fetchEverything` function should be called, and the states should be emitted correctly:

```Dart
blocTest<NewsCubit, NewsState>(
  'When the search term is not null '
  'fetchEverything will be called '
  'and the state will contain the mockEverythingArticle',

  // Create the Cubit with mockLocale
  build: () => NewsCubit(mockLocale),

  // Call the searchNews function with a search text
  act: (cubit) async => cubit.searchNews(search: 'Hello world'),

  // States should be emitted in the correct order
  expect: () => [
    NewsLoadingState(),
    NewsSuccessState(const [mockEverythingArticle]),
  ],

  // Verify that the fetchEverything function was called 1 time with the
  // arguments mockLocale and 'Hello world'
  verify: (cubit) {
    verify(
      () => mockRepo.fetchEverything(
        locale: mockLocale,
        search: 'Hello world',
      ),
    ).called(1);
  }
);

```

For the third test, we'll call the `searchNews` function with a search text and then call the `clearSearch` function. We need to verify that the `fetchEverything` and `fetchTopHeadlines` functions are called correctly and that the `NewsCubit` states are emitted correctly:

```Dart
blocTest<NewsCubit, NewsState>(
  'When the search term is not null '
  'fetchEverything will be called '
  'and then clearing the search will trigger a new search '
  'then fetchTopHeadlines will be called '
  'and states will be emitted correctly',

  // Create the Cubit with mockLocale
  build: () => NewsCubit(mockLocale),

  // Call the searchNews function with a search text
  // and then call the clearSearch function
  act: (cubit) async {
    await cubit.searchNews(search: 'Hello world');
    await cubit.clearSearch();
  },

  // States should be emitted in the correct order
  expect: () => [
    NewsLoadingState(),
    NewsSuccessState(const [mockEverythingArticle]),
    NewsLoadingState(),
    NewsSuccessState(const [mockTopArticle]),
  ],

  // Verify that the fetchEverything and fetchTopHeadlines functions
  // were called 1 time with the correct arguments
  verify: (cubit) {
    verify(
      () => mockRepo.fetchEverything(
        locale: mockLocale,
        search: 'Hello world',
      ),
    ).called(1);
    verify(() => mockRepo.fetchTopHeadlines(locale: mockLocale)).called(1);
  }
);

```

Finally, let's add one more test. In this test, we want to verify that the error state is emitted correctly if the repository throws an exception:

```Dart
blocTest<NewsCubit, NewsState>(
  'When the Api key is not valid exception is handled correctly',

  build: () {
    // Configure mockRepo to throw an ApiKeyInvalidException
    // when the fetchTopHeadlines function is called
    when(() => mockRepo.fetchTopHeadlines(locale: mockLocale))
        .thenAnswer((_) async => throw ApiKeyInvalidException());
    
    // Create the cubit with mockLocale
    return NewsCubit(mockLocale);
  },
  // Call the searchNews function
  act: (cubit) async => cubit.searchNews(),
  
  // States should be emitted in the correct order and 
  // the last state is the error state
  expect: () => [
    NewsLoadingState(),
    NewsErrorState('The API key is not valid'),
  ],
);

```

Great! We have completed the `NewsCubit` class, which will be responsible for maintaining the application's state. We have also added tests to ensure it works as expected.

In the next article, we will work with the presentation layer to learn how to create a responsive application supporting multiple languages.

Remember that you can find the source code on [GitHub][sourceCode]. And don't forget to check out the other articles in this tutorial series:

- [Part 1: Introduction, architecture, and data sources.][parte1]

- [Part 2: Dependency injection, repositories, and business logic.][parte2] (this article)

- [Part 3: Presentation layer and support for multiple languages.][parte3]


[parte1]: /post/j66drgpxoelhslflwkvw
[parte2]: /post/6xxh4e60lq4x8fwwfkhu
[parte3]: /post/r1ym2otazt5mc0z8e91n
[sourceCode]: https://tinyurl.com/rytwmvvu 
[blocTest]: https://pub.dev/packages/bloc_test
[getIt]: https://pub.dev/packages/get_it
[cubit]: https://bloclibrary.dev/#/coreconcepts?id=cubit
[wikiDI]: https://en.wikipedia.org/wiki/Dependency_inversion_principle]]></content:encoded>
            <category>Cubit</category>
            <category>Dart</category>
            <category>Flutter</category>
            <enclosure url="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2F6xxh4e60lq4x8fwwfkhu%2Fbbadfb22-5ad5-4e57-a303-a59fd5a3fb86.png?alt=media&amp;token=604bd90c-84ad-4db0-b011-7a2c2b8064db" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Cubit in Action: Creating a Responsive News Application with Multi-Language Support. Part 1]]></title>
            <link>https://yayocode.com/post/j66drgpxoelhslflwkvw</link>
            <guid>j66drgpxoelhslflwkvw</guid>
            <pubDate>Tue, 07 Nov 2023 08:49:15 GMT</pubDate>
            <description><![CDATA[Learn how to build a responsive and multilingual news app with Cubit, using a scalable architecture and unit and widget testing]]></description>
            <content:encoded><![CDATA[<img src="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2Fj66drgpxoelhslflwkvw%2F0852855e-02d3-4700-9a99-b63f4948f390.png?alt=media&token=e84bcef4-9e8b-43e8-b30d-c659fcb8fbee" alt="Cover image"> 
 In this series of articles, we will create a responsive news application supporting three languages: Spanish, English, and Chinese. Additionally, we will implement a scalable architecture and add unit tests and widget tests. The application will display the latest headlines and provide the option to search for news of interest and select your preferred language.

<MyImage
    src="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2Fj66drgpxoelhslflwkvw%2Feae43144-f99a-4ae6-ba38-ca45d911d1ad.webp?alt=media&token=0f3d9fe4-f580-405a-b3d6-7d01ed9d9119"
    width="75%"
    priority="false"
    smallScreenWidth="100%"
    aspectRatio="640/480"
    description="Responsive news app supporting three different sizes."
/>

You can find the source code on [GitHub][sourceCode]. Also, don't forget to check out the other articles in this tutorial series:

- [Part 1: Introduction, architecture, and data sources.][parte1] (this article)

- [Part 2: Dependency injection, repositories, and business logic.][parte2]

- [Part 3: Presentation layer and support for multiple languages.][parte3]

## Purpose of this Tutorial:

- Create a responsive app supporting small, medium, and large devices.

- Learn to use [cubit][cubit] as a state manager.

- Establish a simple, scalable architecture allowing for testing.

- Learn how to add unit tests and widget tests to our project.

- Make REST API requests using [http](https://pub.dev/packages/http).

- Apply dependency injection with [GetIt][getIt] (not to be confused with GetX).

## Prerequisites (Optional):

- Obtain an API key from [https://newsapi.org/](https://newsapi.org/).

## Application Architecture

Defining an architecture is crucial when creating an application as it offers numerous benefits, such as:

- **Maintainability:** A solid architecture facilitates error detection and correction, as well as the addition of new updates and features.

- **Team Collaboration:** A well-defined architecture guides developers, reducing conflicts as everyone understands the project's structure.

- **Code Quality:** With architecture, we can define standards and best practices that the entire team must follow, ensuring consistency and quality in the project.

- **Code Reusability:** A good architecture helps abstract code sections that can be reused in different project parts.

- **Testable Code:** A good architecture allows us to separate different application parts into sections with specific responsibilities, making it easier to test each section in isolation without depending on others.

There are many other important reasons for having an architecture, but that can be an article of its own. In a few words, defining an architecture helps us create maintainable, scalable, testable code that promotes team collaboration.

The architecture of this project is very similar to what can be found in the [Flutter Bloc documentation](https://bloclibrary.dev/#/architecture).

<MyImage
    src="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2Fj66drgpxoelhslflwkvw%2F7d4adecb-b4b5-4ebe-9101-f96a80ed4c45.png?alt=media&token=ffbd37c5-eb92-416f-a453-ad155d6008e2"
    width="85%"
    priority="false"
    smallScreenWidth="100%"
    aspectRatio="862/330"
    description="Architecture: data sources, repositories, business logic, and presentation layer."
/>

As seen in the image above, the architecture is divided into three parts: external data sources, the data layer, and the application layer. What is the responsibility of each?

- **External Data Sources:** Refer to the different sources from which an application can obtain information, such as a database, web APIs, local files, device sensors, etc.

- **Data Layer:** Manages interactions with data sources and allows other parts of the application to access data in an organized and secure manner. This layer is further divided into:

  - **Data Sources:** Connect directly with external data sources.

  - **Repositories:** Act as intermediaries between the data and application layers. They manage data coming from the data source, transform it, and save it in the cache or local database, among other functions.

- **Application Layer:** Handles business logic and direct user interaction. It is divided into two parts:

  - **Business Logic:** Our blocs and cubits are located here. It is where we write about business logic and manage our widgets' state.

  - **Presentation Layer:** This is where all our widgets are located and is responsible for interactions and what the user can see: buttons, images, text, etc.

<Note
   title="Note"
   text="
In this article, we will **not** address the dotted part, which involves connecting to a local database to store news and accessing it when offline.
"/>

## External Data Source: [News API][newsApi]

In our application, the external data source is [News API][newsApi], and two APIs are of interest:

1. [/v2/everything:][newsApiEverything] This API searches through millions of articles and will be used when the user enters text in the search bar.

2. [/v2/top-headlines:][newsApiTop] This API provides top news and headlines for a country and will be used on the homepage.

In the [documentation](https://newsapi.org/docs/endpoints/everything), we can see that when calling either of these two APIs, a successful response returns a JSON like this:


```JSON
{
  "status": "ok",
  "totalResults": 1,
  "articles": [
    {
      "source": {
        "id": "cbs-news",
        "name": "CBS News"
      },
      "author": "Sophie Lewis",
      "title": "\"Bachelor\" host Chris Harrison is temporarily \"stepping aside\" after \"excusing historical racism\" - CBS News",
      "description": "\"By excusing historical racism, I defended it,\" Harrison wrote, announcing he will not host the \"After the Final Rose\" live special.",
      "url": "https://www.cbsnews.com/news/chris-harrison-the-bachelor-stepping-aside-comments-rachel-lindsay-backlash/",
      "urlToImage": "https://cbsnews1.cbsistatic.com/hub/i/r/2021/02/12/0cf2794b-2b04-4ced-84ce-1d72e26ed2cb/thumbnail/1200x630/8d464cc0fdf23566bbea2e01c41401ab/gettyimages-1204035255.jpg",
      "publishedAt": "2021-02-14T12:18:00Z",
      "content": "Chris Harrison, the longtime host of \"The Bachelor\" and \"The Bachelorette,\" announced Saturday that he is \"stepping aside\" from the franchise for an undisclosed \"period of time.\" The bombshell announ… [+7754 chars]"
    }
  ]
}
```

However, when the request is not successful, the response contains an error code (see [here][newsApiError]), and the JSON looks like this:

```JSON
{
  "status": "error",
  "code": "apiKeyMissing",
  "message": "Your API key is missing. Append this to the URL with the apiKey param, or use the x-api-key HTTP header."
}
```

Analyzing both responses, we will need two classes to model the response. The first class, let's call it `Article`, represents everything inside the `articles` list:

```Dart
@JsonSerializable()
class Article extends Equatable {
  final String title;
  final String? author;
  final String? description;
  final String? urlToImage;
  final String? content;
  final String url;

  const Article({
    required this.title,
    required this.url,
    this.author,
    this.content,
    this.urlToImage,
    this.description,
  });

  factory Article.fromJson(Map<String, dynamic> json) =>
      _$ArticleFromJson(json);

  Map<String, dynamic> toJson() => _$ArticleToJson(this);

  @override
  List<Object?> get props =>
      [title, author, description, urlToImage, content, url];
}
```

<Note
   title="Note"
   text="
We're using the following packages in the `Article` class:

- **Equatable**: It simplifies comparing two objects of the same class without overriding `==` and `hashCode`. You can learn more [here](/post/VK2XfvNw9Ac9Eqxo9MHF).

- **JsonSerializable**: It generates code to convert JSON to objects of the `Article` class.
"/>


The second class represents the values of the response, either successful or unsuccessful. We're also using the [Equatable][equatable] and [JsonSerializable][jsonSerializable] packages:

```Dart
@JsonSerializable()
class ApiResponse extends Equatable {
  final String status;
  final String? code;
  final List<Article>? articles;

  const ApiResponse(
    this.status,
    this.code,
    this.articles,
  );

  factory ApiResponse.fromJson(Map<String, dynamic> json) =>
      _$ApiResponseFromJson(json);

  Map<String, dynamic> toJson() => _$ApiResponseToJson(this);

  @override
  List<Object?> get props => [status, code, articles];
}
```

Now that we've defined the classes to help represent JSON as Dart objects, we can proceed with the class responsible for making API requests.

<Note
   title="Reminder"
   text="Execute the command to generate the necessary code for the [JsonSerializable](https://pub.dev/packages/json_serializable) package: `flutter pub run build_runner watch --delete-conflicting-outputs`
"/>


## Data Layer

As mentioned, this layer interacts with data sources, whether an external API or a local database. It also allows other parts of the application to access data in an organized and secure manner. It is divided into data sources and repositories.

Sure, here is the translation of the Markdown article with improved grammar:

### Data Source: NewsDataSource

The `NewsDataSource` class will be responsible for making requests to the [News APIs][newsApi]. We will use the [http][http] package, which allows us to make HTTP requests. Let's create the `NewsDataSource` class and initialize some variables:

```Dart
class NewsDataSource {
  
  // Visit https://newsapi.org/ to obtain your API key
  static const String apiKey = 'xxxxxxxxxxxxxxxxxxxxxxxxx';

  static const String baseUrl = 'newsapi.org';
  static const String everything = '/v2/everything';
  static const String topHeadlines = '/v2/top-headlines';

  final Client _httpClient;

  NewsDataSource({Client? httpClient}) : _httpClient = httpClient ?? Client();
}
```

We have added the base URL of [News API][newsApi] and the paths for the two APIs we will use. We also have the **API KEY**, which we can obtain on the [News API][newsApi] website. Finally, we have a `Client` object that will handle making requests to the APIs.

You might wonder why we are initializing the client as follows:

```Dart
  final Client _httpClient;

  NewsDataSource({Client? httpClient}) : _httpClient = httpClient ?? Client();
```

Instead of doing it like this:

```Dart
  final Client _httpClient = Client();

  NewsDataSource();
```

The answer is simple. Passing `Client` in the constructor allows us to pass a **Mock** to perform unit tests on the `NewsDataSource` class.

Now, let's create a helper function responsible for making requests to the APIs and returning an object of type `ApiResponse` with the response:

```Dart
  Future<ApiResponse> _callGetApi({
    required String endpoint,
    required Map<String, String> params,
  }) async {
    
    // Create the URI for making a request to the API
    final uri = Uri.https(baseUrl, endpoint, params);
    final response = await _httpClient.get(uri);

    // Use `json.decode` to convert the response body to a Json object
    final result = ApiResponse.fromJson(json.decode(response.body));

    // If the response contains an error, throw an exception
    if (result.status == 'error') {
      if (result.code == 'apiKeyMissing') throw MissingApiKeyException();
      if (result.code == 'apiKeyInvalid') throw ApiKeyInvalidException();
      throw Exception();
    }

    // If there is no error, return the API result
    return result;
  }

// Define some custom exceptions that we can handle in the application layer
class MissingApiKeyException implements Exception {}
class ApiKeyInvalidException implements Exception {}
```

The `_callGetApi` function will be called whenever we make a request to either of the APIs. The error codes are in the [News API documentation][newsApiError]. For this example, we only create exceptions for `apiKeyMissing` and `apiKeyInvalid`.

Now, let's create two functions that will help us call the APIs we are interested in:

- [/v2/everything][newsApiEverything]
- [/v2/top-headlines][newsApiTop]

```Dart
  // Make a request to /v2/top-headline. Receive the country
  Future<List<Article>> fetchTopHeadlines({
    required String country,
  }) async {
    final params = {
      'apiKey': apiKey,
      'country': country,
    };

    final result = await _callGetApi(
      endpoint: topHeadlines,
      params: params,
    );
    return result.articles!;
  }

  // Make a request to /v2/everything. Receive the language and a search text
  Future<List<Article>> fetchEverything({
    required String language,
    String? search,
  }) async {
    final params = {
      'apiKey': apiKey,
      'language': language,
    };

    if (search != null) params['q'] = search;

    final result = await _callGetApi(
      endpoint: everything,
      params: params,
    );
    return result.articles!;
  }
```

We have created two functions, `fetchTopHeadlines` and `fetchEverything`, each with its respective parameters. Each function constructs the `params` parameters according to each API's requirements.

We have completed all the functions, variables, etc., that comprise the `NewsDataSource` class. Putting it all together, the code is:

```Dart
class MissingApiKeyException implements Exception {}
class ApiKeyInvalidException implements Exception {}

class NewsDataSource {
  // Visit https://newsapi.org/ to obtain your API key
  static const String apiKey = 'xxxxxxxxxxxxxxxxxxxxxxxxx';

  static const String baseUrl = 'newsapi.org';
  static const String everything = '/v2/everything';
  static const String topHeadlines = '/v2/top-headlines';

  final Client _httpClient;

  NewsDataSource({Client? httpClient}) : _httpClient = httpClient ?? Client();

  Future<List<Article>> fetchTopHeadlines({
    required String country,
  }) async {
    final params = {
      'apiKey': apiKey,
      'country': country,
    };

    final result = await _callGetApi(
      endpoint: topHeadlines,
      params: params,
    );
    return result.articles!;
  }

  Future<List<Article>> fetchEverything({
    required String language,
    String? search,
  }) async {
    final params = {
      'apiKey': apiKey,
      'language': language,
    };

    if (search != null) params['q'] = search;

    final result = await _callGetApi(
      endpoint: everything,
      params: params,
    );
    return result.articles!;
  }

  Future<ApiResponse> _callGetApi({
    required String endpoint,
    required Map<String, String> params,
  }) async {
    final uri = Uri.https(baseUrl, endpoint, params);

    final response = await _httpClient.get(uri);
    final result = ApiResponse.fromJson(json.decode(response.body));

    if (result.status == 'error') {
      if (result.code == 'apiKeyMissing') throw MissingApiKeyException();
      if (result.code == 'apiKeyInvalid') throw ApiKeyInvalidException();
      throw Exception();
    }

    return result;
  }
}
```

#### Testing the NewsDataSource Class

Now let's perform tests on the `NewsDataSource` class. To do this, we will use the [Mockito][mockito] package, which allows us to create "mocks" and simulate successful and failed requests to the APIs.

We will create three **.json** files that will contain simulated responses from the API. The first file contains a failed response with the error `apiKeyInvalid`. With this file, we will test if the `ApiKeyInvalidException` exception is thrown correctly:

```Json
{
  "status": "error",
  "code": "apiKeyInvalid",
  "message": "Your API key is invalid or incorrect. Check your key, or go to https://newsapi.org to create a free API key."
}
```

The second file contains the error `apiKeyMissing`, and with it, we will test if the `MissingApiKeyException` exception is thrown correctly:

```Json
{
  "status": "error",
  "code": "apiKeyMissing",
  "message": "Your API key is missing. Append this to the URL with the apiKey param, or use the x-api-key HTTP header."
}
```

The third file simulates the successful response from the API and contains two news:

```Json
{
  "status": "ok",
  "totalResults": 2,
  "articles": [
    {
      "source": {
        "id": "cbs-news",
        "name": "CBS News"
      },
      "author": "Sophie Lewis",
      "title": "\"Bachelor\" host Chris Harrison is temporarily \"stepping aside\" after \"excusing historical racism\" - CBS News",
      "description": "\"By excusing historical racism, I defended it,\" Harrison wrote, announcing he will not host the \"After the Final Rose\" live special.",
      "url": "https://www.cbsnews.com/news/chris-harrison-the-bachelor-stepping-aside-comments-rachel-lindsay-backlash/",
      "urlToImage": "https://cbsnews1.cbsistatic.com/hub/i/r/2021/02/12/0cf2794b-2b04-4ced-84ce-1d72e26ed2cb/thumbnail/1200x630/8d464cc0fdf23566bbea2e01c41401ab/gettyimages-1204035255.jpg",
      "publishedAt": "2021-02-14T12:18:00Z",
      "content": "Chris Harrison, the longtime host of \"The Bachelor\" and \"The Bachelorette,\" announced Saturday that he is \"stepping aside\" from the franchise for an undisclosed \"period of time.\" The bombshell announ… [+7754 chars]"
    },
    {
      "source": {
        "id": null,
        "name": "KOCO Oklahoma City"
      },
      "author": "KOCO Staff",
      "title": "Winter storm brings heavy snow, causing hazardous driving conditions across Oklahoma - KOCO Oklahoma City",
      "description": "A winter storm moving across Oklahoma Sunday morning is dumping heavy snow, causing hazardous driving conditions.",
      "url": "https://www.koco.com/article/winter-storm-brings-heavy-snow-causing-hazardous-driving-conditions-across-oklahoma/35500508",
      "urlToImage": "https://kubrick.htvapps.com/htv-prod-media.s3.amazonaws.com/images/poster-image-2021-02-14t060232-115-1613304161.jpg?crop=1.00xw:1.00xh;0,0&resize=1200:*",
      "publishedAt": "2021-02-14T12:17:00Z",
      "content": "OKLAHOMA CITY —A winter storm moving across Oklahoma Sunday morning is dumping heavy snow, causing hazardous driving conditions. \r\n[Check latest weather alerts in your area | Check live traffic condi… [+2381 chars]"
    }
  ]
}
``` 

Let's create a file called **data_source_test.dart** inside the **test** folder and set up the basic structure:

```Dart
// Mocks that allow us to simulate successful and failed requests
class MockClient extends Mock implements Client {}
class FakeUri extends Fake implements Uri {}

// Path where the .json files are located
const mockPath = 'test/data_source_test';

void main() {
  late MockClient mockClient;
  late NewsDataSource dataSource;

  setUpAll(() {
    registerFallbackValue(FakeUri());
  });

  // setUp is called before each test.
  setUp(() {
    mockClient = MockClient();

    // Create a NewsDataSource object and pass the mockClient
    dataSource = NewsDataSource(httpClient: mockClient);
  });

  // Group the tests for the fetchEverything function
  group('When calling fetchEverything', () {
     // Here we will write the tests for this group
  });

  // Group the tests for the fetchTopHeadlines function
  group('When calling fetchTopHeadlines', () {
     // Here we will write the tests for this group
  });
}

// Helper function to read .json files as Strings. 
Future<String> getMockBody(String filePath) => File(filePath).readAsString();
```

<Note
   title="Important"
   text="
In this article, we will only test the `fetchEverything` function. Your homework is to add tests to the `fetchTopHeadlines` function.
"/>

The first test we will do is to verify that calling the API is successful. Since the JSON file we created for successful tests contains two news, we can verify that calling the `fetchEverything` function returns two news:

```Dart
    test('The response contains two news', () async {
      // Get the JSON string with the successful response
      final response = await getMockBody('$mockPath/success_response.json');

      // Tell the mockClient that when it receives a GET request,
      // return the JSON with the successful response and status 200. 
      when(() => mockClient.get(any())).thenAnswer((_) async {
        return Response(response, 200, headers: headers);
      });

      // Call the fetchEverything function
      final articles = await dataSource.fetchEverything(language: 'es');

      // The expected result is two news, each with a different author
      expect(articles.length, 2);
      expect(articles[0].author, 'Sophie Lewis');
      expect(articles[1].author, 'KOCO Staff');
    });
``` 

In the above test, we verify that calling the `fetchEverything` function returns the expected result. In the next test, we will verify that when calling the `fetchEverything` function with the arguments `language:'es'` and `search:'Hello world'`, the `Client` receives the arguments successfully:

```Dart
    test('The request contains the expected parameters', () async {
      // Get the JSON content of the successful response
      final response = await getMockBody('$mockPath/success_response.json');

      // Configure the mockClient so that, when receiving a GET request,
      // it returns the JSON of the successful response and a status 200. 
      when(() => mockClient.get(any())).thenAnswer((_) async {
        return Response(response, 200, headers: headers);
      });

      // Arguments that the Client should receive
      const language = 'es';
      const searchTerm = 'Hello world';

      // Call the fetchEverything function
      await dataSource.fetchEverything(
        language: language,
        search: searchTerm,
      );

      // Create the expected Uri with the provided arguments
      final uri = Uri.https(
        NewsDataSource.baseUrl,
        NewsDataSource.everything,
        {
          'apiKey': NewsDataSource.apiKey,
          'language': language,
          'q': searchTerm,
        },
      );

      // Verify that the Client was called with the expected Uri
      verify(() => mockClient.get(uri)).called(1);
    });
```

Lets create the test where the response is unsuccessful, and the function throws the `MissingApiKeyException` exception:

```Dart
    test('Correctly throws Missing API Key Exception for non-successful response', () async {
      // Obtain the JSON content of the failed response
      final response = await getMockBody('$mockPath/api_key_missing.json');

      // Configure the mockClient so that, upon receiving a GET request,
      // it returns the JSON of the failed response and a status code of 200.
      when(() => mockClient.get(any())).thenAnswer((_) async {
        return Response(response, 200);
      });

      // When calling the fetchEverything function, we expect the
      // MissingApiKeyException exception to be thrown
      expect(
          () => dataSource.fetchEverything(language: 'es'),
          throwsA(
              predicate((exception) => exception is MissingApiKeyException)));
    });
```

And finally, the test where the response is unsuccessful, and the function throws the `ApiKeyInvalidException` exception:

```Dart
    test('Correctly throws Invalid API Key Exception for non-successful response', () async {
      // Obtain the JSON content of the failed response
      final response = await getMockBody('$mockPath/api_key_invalid.json');

      // Configure the mockClient so that, upon receiving a GET request,
      // it returns the JSON of the failed response and a status code of 200.
      when(() => mockClient.get(any())).thenAnswer((_) async {
        return Response(response, 200);
      });

      // When calling the fetchEverything function, we expect the
      // ApiKeyInvalidException exception to be thrown
      expect(
          () => dataSource.fetchEverything(language: 'es'),
          throwsA(
              predicate((exception) => exception is ApiKeyInvalidException)));
    });
```

Fantastic, we have completed the `NewsDataSource` class and added tests to ensure it works as expected.

The following article will explore how to use dependency injection to initialize the `NewsDataSource` class so the repository can use it.

Remember that you can find the source code on [GitHub][sourceCode]. And don't forget to check out the other articles in this tutorial series:

- [Part 1: Introduction, architecture, and data sources.][parte1] (this article)

- [Part 2: Dependency injection, repositories, and business logic.][parte2]

- [Part 3: Presentation layer and support for multiple languages.][parte3]


[parte1]: /post/j66drgpxoelhslflwkvw
[parte2]: /post/6xxh4e60lq4x8fwwfkhu
[parte3]: /post/r1ym2otazt5mc0z8e91n
[sourceCode]: https://tinyurl.com/rytwmvvu
[equatable]: https://pub.dev/packages/equatable
[jsonSerializable]: https://pub.dev/packages/json_serializable
[http]: https://pub.dev/packages/http
[mockito]: https://pub.dev/packages/mockito
[getIt]: https://pub.dev/packages/get_it
[cubit]: https://bloclibrary.dev/#/coreconcepts?id=cubit
[newsApi]: https://newsapi.org
[newsApiError]: https://newsapi.org/docs/errors
[newsApiEverything]: https://newsapi.org/docs/endpoints/everything
[newsApiTop]: https://newsapi.org/docs/endpoints/top-headlines]]></content:encoded>
            <category>Cubit</category>
            <category>Dart</category>
            <category>Flutter</category>
            <enclosure url="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2Fj66drgpxoelhslflwkvw%2F0852855e-02d3-4700-9a99-b63f4948f390.png?alt=media&amp;token=e84bcef4-9e8b-43e8-b30d-c659fcb8fbee" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Flutter: Decision making: switch expression]]></title>
            <link>https://yayocode.com/post/hdiustpxfcmwautndxda</link>
            <guid>hdiustpxfcmwautndxda</guid>
            <pubDate>Sat, 03 Jun 2023 09:53:31 GMT</pubDate>
            <description><![CDATA[Learn what the switch expression is and how to use it in Dart and Flutter]]></description>
            <content:encoded><![CDATA[<img src="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2Fhdiustpxfcmwautndxda%2F56b179a4-e199-4d0a-b982-ddaf67d83346.png?alt=media&token=d0eee5d0-f894-4ad3-aebc-8e6f17d0bccf" alt="Cover image"> 
 Previously, we learned about the [switch case][2] structure, which helps us assign or execute specific logic based on the evaluated comparison result.

Dart 3 introduced the [switch expression][1], which is an improvement over the classic [switch case][2]. This enhanced version allows using the switch as an expression, which means it can produce a value that can be assigned to a variable or used wherever an expression is expected.

Let's see a simple example. Assuming that Monday is the first day of the week and represented by the number one, Tuesday is the second day, and so on until Sunday. Given an integer, we want to print the corresponding day. Using the classic [switch case][2], the code would look like this:

```Dart
// Example using the classic switch case
void main() {
  final currentDay = getDay(5);
  print('Today is $currentDay'); // Today is Friday
}

String getDay(int day) {
  switch (day) {
    case 1:
      return 'Monday';
    case 2:
      return 'Tuesday';
    case 3:
      return 'Wednesday';
    case 4:
      return 'Thursday';
    case 5:
      return 'Friday';
    case 6:
      return 'Saturday';
    case 7:
      return 'Sunday';
    default:
      return 'The day does not exist';
  }
}
```

In the above code, we defined multiple cases with all possible values for the days of the week, and we included a default case for values that do not match any of the days of the week.

Using the switch expression, we can make the code more concise and easier to read:

```Dart
// Example using the new switch expression
void main() {
  final currentDay = getDay(5);
  print('Today is $currentDay'); // Today is Friday
}

String getDay(int day) {
  return switch (day) {
    1 => 'Monday',
    2 => 'Tuesday',
    3 => 'Wednesday',
    4 => 'Thursday',
    5 => 'Friday',
    6 => 'Saturday',
    7 => 'Sunday',
    _ => 'The day does not exist',
  };
}

```

In this example, the `day` expression is evaluated and compared to different cases using the `=>` syntax. If the value of `day` is 1, the result is `'Monday'`. The underscore `_` is used as a default case for any other value that does not match the previous cases.

## Pattern Matching

Pattern matching is a technique that allows us to check if a value matches a specific pattern and perform actions based on it. It is used to make more complex and flexible comparisons than simple equalities or inequalities.

If we want to determine whether a day is a "weekday" or a "weekend," we can write the following code:

```Dart
void main() {
  final currentDay = getDay(4);
  print('Today is $currentDay'); // Weekday
}

String getDay(int day) {
  return switch (day) {
    >= 1 && <= 5 => 'Weekday',
    6 || 7 => 'Weekend',
    _ => 'The day does not exist',
  };
}
```

We can also assign the value produced by the switch expression to a variable. The modified code would look like this:

```Dart
void main() {
  final day = 5;
  final currentDay = switch (day) {
    >= 1 && <= 5 => 'Weekday',
    6 || 7 => 'Weekend',
    _ => 'The day does not exist',
  };

  print('Today is $currentDay'); // Weekday
}

```

## Sealed Classes

**Sealed** classes in Dart are often used in combination with switch expressions. The switch structure can exhaustively handle the subclasses of a sealed class, ensuring that all possible cases are handled.

```Dart
void main() {
  print('The area of the circle is: ${calculateArea(Circle(5))}');
  print('The area of the square is: ${calculateArea(Square(10))}');
}

double calculateArea(Shape shape) {
  return switch (shape) {
    Square(side: final side) => side * side,
    Circle(radius: final radius) => 3.14 * radius * radius,
  };
}

sealed class Shape {}

class Square extends Shape {
  Square(this.side);
  final double side;
}

class Circle extends Shape {
  Circle(this.radius);
  final double radius;
}
```

The above example works because the `Shape` class is **sealed**. If it were not, we would encounter a [non_exhaustive_switch_expression](https://dart.dev/tools/diagnostic-messages#non_exhaustive_switch_expression) error.

## Guard Clauses

We can use the **when** keyword in conjunction with switch to specify a guard clause. For example, if we want to deserialize JSON:

```Dart
void main() {
  var animal1Json = {
    'name': 'Max',
    'type': 'Cat',
  };

  var animal2Json = {
    'name': 'Coco',
    'type': 'Dog',
  };

  var anotherJson = {
    'name': 'Yayo',
    'age': '23',
  };

  print(parseJson(animal1Json)); // The cat is: Max
  print(parseJson(animal2Json)); // The dog is: Coco
  print(parseJson(anotherJson)); // Exception: Error while deserializing JSON
}

Animal parseJson(Map<String, dynamic> map) {
  return switch (map) {
    {
      'name': String name,
      'type': String type,
    }
        when type == 'Cat' => Cat(name),
    {
      'name': String name,
      'type': String type,
    }
        when type == 'Dog' => Dog(name),
    _ => throw Exception('Error while deserializing JSON')
  };
}

class Animal {
  Animal(this.name);
  final String name;
}

class Cat extends Animal {
  Cat(String name) : super(name);

  @override
  String toString() => 'The cat is: $name';
}

class Dog extends Animal {
  Dog(String name) : super(name);

  @override
  String toString() => 'The dog is: $name';
}
```

In the above code snippet, we have three JSON objects: one representing a dog, another representing a cat, and the last one having no type. In the switch, we use the `when type == 'Dog'` or `when type == 'Cat'` to evaluate if the JSON represents a dog or cat and create an instance of the respective `Dog` or `Cat` class. If the JSON has an unsupported format, we throw an exception.

## Conclusion

In conclusion, switch expressions in Dart 3 significantly improve over traditional switch case structures. This new version allows using the switch as an expression, making the code more concise and readable.

It also introduces the ability to perform pattern matching, providing greater comparison flexibility. Additionally, we can use sealed classes in combination with switch, ensuring that all possible cases are handled.

Finally, guard clauses, using the **when** keyword, allow us to perform additional checks for JSON deserialization or handle special cases.

Overall, these enhancements to switch expressions in Dart 3 make it easier to develop more concise and robust applications.

[1]: https://dart.dev/language/branches#switch-expressions 'switch expression'
[2]: /post/6oGfE6g8vcJLJo5Fp1nc 'switch case']]></content:encoded>
            <enclosure url="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2Fhdiustpxfcmwautndxda%2F56b179a4-e199-4d0a-b982-ddaf67d83346.png?alt=media&amp;token=d0eee5d0-f894-4ad3-aebc-8e6f17d0bccf" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[What is new in Dart 3? Exploring the new features: Switch Expressions, Patterns, Records, Class Modifiers, and More]]></title>
            <link>https://yayocode.com/post/xjnmnrp0opiwuclcfnyd</link>
            <guid>xjnmnrp0opiwuclcfnyd</guid>
            <pubDate>Thu, 25 May 2023 00:35:36 GMT</pubDate>
            <description><![CDATA[This article is an introduction to the new features of Dart 3, such as switch expressions, patterns, records, class modifiers, etc.]]></description>
            <content:encoded><![CDATA[<img src="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2Fxjnmnrp0opiwuclcfnyd%2F0c94ba6a-df77-411d-a6f3-a465b2294734.png?alt=media&token=05daeb45-f330-41ed-a894-ea409bcf666e" alt="Cover image"> 
 Dart has experienced significant improvements and innovations with the release of its version 3. This update brings features and functionalities that make Dart a more efficient, modern, and user-friendly language for web and mobile application development.

Dart 3 was introduced by Google during Google I/O 2023 and was announced as the largest release to date.

## 100% Sound Null Safety

Starting from Dart 2.12, a new feature called null safety was introduced. This feature aims to improve code safety and stability by providing stronger null checks. If you attempt to assign a null value to a non-nullable variable, the compiler will generate a compile-time error. This helps reduce the possibility of null-related errors and enhances code robustness.

In Dart 3, the language is 100% sound null safety.

## Records

A new feature in Dart 3 is Records, which allows a single object to contain multiple objects. One use case is when we want to return two or more values from a function.

Previously, when we wanted to return more than one object from a function, we had to create an extra class or use a package like [Tuple][1].

The following code snippet shows that to return the `age` and `name` from a function, we had to create a class called `User`:

```Dart
// Example without using records
void main() {
  final user = getUserInfo();

  print('Age: ${user.age}');
  print('Name: ${user.name}');
}

User getUserInfo() {
  return User(18, 'Yayo');
}

class User {
  final int age;
  final String name;

  User(this.age, this.name);
}
```

Now, let's see the difference using records:

```Dart
// Example using records
void main() {
  final user = getUserInfo();

  print('Age: ${user.$1}');
  print('Name: ${user.$2}');
}

(int, String) getUserInfo() {
  return (18, 'Yayo');
}
```

We can see that we have saved a few lines of code using records, and there is no need to create the `User` class anymore.

## Patterns

Let's go back and take a look at the previous example. A problem with using records is that without creating the `User` class, we would need to write `user.$1` instead of `user.name` to access the name, which makes the code harder to understand. However, we can write code that is easy to comprehend using patterns.

Using patterns, we can improve the code readability in the previous example:

```Dart
void main() {
  final user = getUserInfo();

  print('Age: ${user.age}');
  print('Name: ${user.name}');
}

({int age, String name}) getUserInfo() {
  return (age: 12, name: 'Yayo');
}
```

We can see that by using records and patterns, we can have more readable code without creating a `User` class and use `user.name` and `user.age`.

### Patterns: Destructuring

We can even use destructuring, which is a technique in patterns to extract and assign values from a data structure to individual variables concisely and efficiently. The code would look like this:

```Dart
void main() {
  final (:age, :name) = getUserInfo();

  print('Age: $age');
  print('Name: $name');
}

({int age, String name}) getUserInfo() {
  return (age: 12, name: 'Yayo');
}
```

### Patterns: Pattern Matching

Pattern matching is a technique that allows you to check if a value matches a specific pattern and perform actions based on that. It performs more complex and flexible comparisons than simple equalities or inequalities.

In the following code snippet, we can see that now we can use conditions within a `switch`:

```Dart
void main() {
  int number = 15;

  switch (number) {
    case 1:
      print('The number is 1');

    case > 1 && < 20:
      print('The number is greater than 10 and less than 20');

    case > 20:
      print('The number is greater than 20');
  }
}
```

We can also use patterns to check if `case` statements in a `switch` match a collection:

```Dart
void main() {
  final users = ['Yayo', 'Carlos'];

  switch (users) {
    case ['Yayo', 'Carlos']:
      print('The list contains Yayo and Carlos');

    case ['Diego']:
      print('The list contains Diego');

    case ['Diana']:
      print('The list contains Diana');
  }
}

```

#### Validating JSON with Patterns

We can use patterns to [validate a JSON][6] and extract its values. Let's suppose we have the following JSON:

```Dart
var json = {
  'user': ['Lily', 13]
};

var {'user': [name, age]} = json;
```

To avoid runtime errors, we need to perform several validations, such as checking if the data type is correct, if the JSON is not empty, etc. The code without using patterns would look like this:

```Dart
// Example without using patterns
if (json is Map<String, Object?> &&
    json.length == 1 &&
    json.containsKey('user')) {
  var user = json['user'];
  if (user is List<Object> &&
      user.length == 2 &&
      user[0] is String &&
      user[1] is int) {
    var name = user[0] as String;
    var age = user[1] as int;
    print('User $name is $age years old.');
  }
}
```

But if we use patterns, we can validate the JSON in just three lines of code:

```Dart
if (json case {'user': [String name, int age]}) {
  print('User $name is $age years old.');
}
```

All the necessary validations are performed within the `if-case` statement:

- json is a map, because it must first match the outer map pattern to proceed.
  - And, since it’s a map, it also confirms json is not null.
- json contains a key user.
- The key user pairs with a list of two values.
- The types of the list values are String and int.
- The new local variables to hold the values are String and int.

## Switch Expression

We are familiar with the `switch case` structure that helps us assign or execute specific logic based on the evaluated comparison result.

Let's see a simple example. Assuming that Monday is the first day of the week and is represented by the number one, Tuesday is the second day, represented by the number two, and so on until Sunday. Given an integer, we want to print the corresponding day. Using the classic `switch case`, the code would look like this:

```Dart
// Example using the classic switch case
void main() {
  final currentDay = getDay(5);
  print('Today is $currentDay'); // Today is Friday
}

String getDay(int day) {
  switch (day) {
    case 1:
      return 'Monday';
    case 2:
      return 'Tuesday';
    case 3:
      return 'Wednesday';
    case 4:
      return 'Thursday';
    case 5:
      return 'Friday';
    case 6:
      return 'Saturday';
    case 7:
      return 'Sunday';
    default:
      return 'Invalid day';
  }
}
```

We can see that we had to write `case` and `return` multiple times. But with Dart 3 and [switch expressions][2], we can make the code more concise and easier to read:

```Dart
// Example using the new switch expression
void main() {
  final currentDay = getDay(5);
  print('Today is $currentDay'); // Today is Friday
}

String getDay(int day) {
  return switch (day) {
    1 => 'Monday',
    2 => 'Tuesday',
    3 => 'Wednesday',
    4 => 'Thursday',
    5 => 'Friday',
    6 => 'Saturday',
    7 => 'Sunday',
    _ => 'Invalid day',
  };
}

```

In this example, the expression `day` is evaluated and compared with different cases using the `=>` syntax. If the value of `day` is 1, the result is `'Monday'`. The underscore `_` is used as a default case for any other value that doesn't match the previous cases.

And if we want to determine whether it's a "weekday" or a "weekend" day, we can write the following code:

```Dart
void main() {
  final currentDay = getDay(4);
  print('Today is $currentDay'); // Weekday
}

String getDay(int day) {
  return switch (day) {
    >= 1 && <= 5 => 'Weekday',
    6 || 7 => 'Weekend',
    _ => 'Invalid day',
  };
}
```

## Sealed Classes

In Dart 3, the keyword used to define a sealed class is `sealed`. Like in other programming languages, a sealed class in Dart is a class that cannot be inherited by other classes outside its declaration. This means that subclasses of the sealed class must be declared in the same file.

Sealed classes in Dart are often used in combination with switch expressions. The switch structure can exhaustively use the subclasses of a sealed class, ensuring that all possible cases are handled.

Here's an example of using a sealed class in a switch expression:

```Dart
void main() {
  final result = evaluate(Success());
  print(result); // Success: Request successful
}

String evaluate(Result result) {
  return switch (result) {
    Success(message: final message) => 'Success: $message',
    Error(message: final message) => 'Error: $message',
  };
}

sealed class Result {
  final String message;

  Result(this.message);
}

class Success extends Result {
  Success() : super('Request successful');
}

class Error extends Result {
  Error() : super('Request unsuccessful');
}
```

<Note
   title="Remember that"
   text="
The above example works because the `Result` class is **sealed**. If it weren't sealed, we would get a [non_exhaustive_switch_expression](https://dart.dev/tools/diagnostic-messages#non_exhaustive_switch_expression) error.
"/>

## Class Modifiers

Class modifiers are reserved words used to control the visibility and behavior of a class.

In Dart 3, the following class modifiers have been added:

- `base`
- `final`
- `interface`
- `sealed`

We won't go into detail about each of them in this article, but you can visit the [documentation][5] to learn more about how they work.

## Conclusion

Dart 3 has introduced several features that make the lives of programmers much easier by making Dart a more expressive language.

Personally, the best additions are **patterns** and **records** because now I can do more with much less code.

I hope you enjoyed this article, and if you have any questions, don't forget to follow me on my social media.

[1]: https://pub.dev/packages/tuple 'tuple package'
[2]: https://dart.dev/language/branches#switch-expressions 'switch expression'
[3]: /post/6oGfE6g8vcJLJo5Fp1nc 'switch case'
[4]: https://dart.dev/language/class-modifiers#sealed 'sealed class'
[5]: https://dart.dev/language/class-modifiers 'class modifiers'
[6]: https://dart.dev/language/patterns#validating-incoming-json 'validate json']]></content:encoded>
            <category>Dart</category>
            <category>Flutter</category>
            <enclosure url="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2Fxjnmnrp0opiwuclcfnyd%2F0c94ba6a-df77-411d-a6f3-a465b2294734.png?alt=media&amp;token=05daeb45-f330-41ed-a894-ea409bcf666e" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Flutter: Loops: break & continue]]></title>
            <link>https://yayocode.com/post/z7FkK8HSr0RgVJFwUTxw</link>
            <guid>z7FkK8HSr0RgVJFwUTxw</guid>
            <pubDate>Fri, 27 Jan 2023 13:10:10 GMT</pubDate>
            <description><![CDATA[Learn what break & continue keywords are and how they work in Dart & Flutter]]></description>
            <content:encoded><![CDATA[<img src="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2Fz7FkK8HSr0RgVJFwUTxw%2Ff4c62e4c-09f2-4bde-b77a-46a0343c4775.png?alt=media&token=6cc76e42-7f03-42c5-9f5b-76cffc694a72" alt="Cover image"> 
 Dart supports two loop control statements: 

- break
- continue

They can be used inside the **for**, **while**, or **do while** loops to terminate the loop or jump to the next iteration.


## The break statement

The break statement terminates the loop and continues with the program execution. 

In the following example, we use the **break** statement to terminate the **for** loop when the value of the variable **i** equals 5: `i == 5`.  

<DartPad 
   id="ebd8b2936ba46c176bbe39679034dc5d" 
   width="100%" 
   height="450px" 
   split="60" 
/>

As we can see, the **for** loop did not print the numbers 5, 6, 7, 8, 9, and 10 because it was terminated early by the **break** statement. 

## The continue statement

The continue statement terminates the current loop and continues with the next iteration.

In the following example, we use the **continue** statement to terminate the **for** loop and continue with the next iteration when the value of the variable **i** equals 5: `i == 5`.  

<DartPad 
   id="ecc110f20f6e1a1886566acd2147cea2" 
   width="100%" 
   height="520px" 
   split="50" 
/>

As we can see, the **for** loop did not print the number 5 because the iteration was terminated by the **continue** statement, and unlike the **break** statement, the numbers 6, 7, 8, 9, and 10 were printed. 
]]></content:encoded>
            <category>Dart Course</category>
            <category>Dart</category>
            <category>Flutter</category>
            <enclosure url="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2Fz7FkK8HSr0RgVJFwUTxw%2Ff4c62e4c-09f2-4bde-b77a-46a0343c4775.png?alt=media&amp;token=6cc76e42-7f03-42c5-9f5b-76cffc694a72" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Flutter: Loops: for]]></title>
            <link>https://yayocode.com/post/1vl2rjjzWdpvtRti84Rm</link>
            <guid>1vl2rjjzWdpvtRti84Rm</guid>
            <pubDate>Fri, 27 Jan 2023 13:06:43 GMT</pubDate>
            <description><![CDATA[Learn what the for loop is and how it works in Dart & Flutter. And learn the different types of for loops: for & for-in]]></description>
            <content:encoded><![CDATA[<img src="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2F1vl2rjjzWdpvtRti84Rm%2Fae5de18f-2d71-4911-9aaf-fa4a9084d6bf.png?alt=media&token=5b5ddb14-e376-4a43-a3a8-354b5d55f61c" alt="Cover image"> 
 The **for loop** is one of the most common loops in Dart and probably in other programming languages. It allows us to execute a code block repeatedly. In Dart, we have different kinds of **for loops**:

- for loop
- for-in loop

## The for loop

The classic **for loop** allows us to execute a code block as long as the given condition is true. The syntax is:

```dart
for (initialization; condition; statement) {
  // code that will be executed
}
```

- **initialization:** Will be executed only one time. Usually, we will initialize a helper variable that we will use to iterate inside the **for loop**.
- **condition:** The code block inside the **for loop** will be executed as long as this condition is true.
- **statement:** Will be executed every time after the code block. Usually, it is here where we will increase the value of the variable created on the initialization.

Let's write a program to print the numbers from one to ten using a **for loop**. The flowchart using a **for loop** is:

<MyImage
    src="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2F1vl2rjjzWdpvtRti84Rm%2F0a1e3453-055c-468b-8ad5-c0419fc7ebc7.png?alt=media&token=cfae452f-a356-4586-8851-eebcca482173"
    width="50%"
    priority="true"
    smallScreenWidth="75%"
    aspectRatio="1343/1814"
    description="The for loop"
/>

1. **Initialization:** we create a variable **i** with an initial value of **1**: `var i = 1`
2. **Condition:** We will loop as long as **i** is less or equals to **10**: `i <= 10`
3. We will print the value of **i**: `print( i++ )`
4. **Statement:** We will increase the value of **i** by one using **++**: `i++`

Let's run the code in DartPad:

<DartPad 
   id="53ef4f60d42678a574dc98b761f18be2" 
   width="100%" 
   height="490px" 
   split="43" 
/>

### Use a for loop to iterate a list

We can also use a **for loop** to iterate each element in a list. Let's write a program to learn how to do it. 

Given the next list of cars:

```Dart
var cars = ['Toyota', 'Mazda', 'Nissan'];
```

We want to print each of them using a **for loop**. What do we have to do?

1. On the **initialization**, we create a variable with an initial value of **0** because the first position in a list is at the index **0**: `var i = 0`
2. On the **condition**, we will check if **i** is less than the list lenght: `i < cars.length`, we will loop as long as this condition is true.
3. On the **statement**, we will increase the value of **i** by one: `i++`.

Let's see the code in DartPad:

<DartPad 
id="f883089e7dd7d2b4312bbeb430809aca" 
   width="100%" 
   height="425px" 
   split="61" 
/>

## The for-in loop

The **for-in loop** is used to iterate through elements of collections like the list. It is very common to use the **for-in loop** when we want to iterate a list and do not want to know the element index.

The syntax is:

```Dart
for(var item in list) {
  // code that will be executed
}
```
- **list:** Is the list that we want to iterate
- **item:** Each item of  the list is assigned to this variable

Let's write a program in DartPad to iterate the list of cars from the previous example:

<DartPad  id="440f6ffa969f0fc4ee054e61849d679a" 
   width="100%" 
   height="415px" 
   split="61" 
/>]]></content:encoded>
            <category>Dart</category>
            <category>Dart Course</category>
            <category>Flutter</category>
            <enclosure url="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2F1vl2rjjzWdpvtRti84Rm%2Fae5de18f-2d71-4911-9aaf-fa4a9084d6bf.png?alt=media&amp;token=5b5ddb14-e376-4a43-a3a8-354b5d55f61c" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Flutter: Loops: do-while]]></title>
            <link>https://yayocode.com/post/IgFPSTuDUgTrTa2wVkki</link>
            <guid>IgFPSTuDUgTrTa2wVkki</guid>
            <pubDate>Fri, 27 Jan 2023 12:18:13 GMT</pubDate>
            <description><![CDATA[Learn what the do-while loop is and how it works in Dart & Flutter]]></description>
            <content:encoded><![CDATA[<img src="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2FIgFPSTuDUgTrTa2wVkki%2Fb73b2f7f-3bcf-49b5-be08-5880936dd56b.png?alt=media&token=09a159a9-7242-4891-b1c9-b06318a8d2ff" alt="Cover image"> 
 The **do-while loop** executes a code block once and then will execute it again as long as the given condition is true. The syntax is the following:

```Dart
do{
  // code that will be executed at least once
} while ( condition );
```

Let's write a program to print the numbers from one to ten using a **do-while loop**. The flowchart using a **do-while loop** is:

<MyImage
    src="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2FIgFPSTuDUgTrTa2wVkki%2F913cc10d-cbf4-4939-a920-cfd32bd1d54b.png?alt=media&token=8ebc8ff1-b0d8-4006-ad7a-3093891a504e"
    width="50%"
    priority="true"
    smallScreenWidth="80%"
    aspectRatio="1100/1100"
    description="The do-while loop"
/>

1. We start with a variable **i** with initial value of **1**: `var i = 1`
2. We will print the value of **i** then we will increase it by one using **++**: `print( i++ )`
3. We will loop as long as as **i** is less or equals to **10**: `i <= 10`

Let's run the code in DartPad:

<DartPad 
   id="fb2e27dc08443789a07d27214cb4db0b" 
   width="100%" 
   height="550px" 
   split="50" 
/>

## Use a do-while loop to iterate a list

We can also use a **do-while loop** to iterate each element in a list. Let's write a program to learn how to do it. 

Given the next list of cars:

```Dart
var cars = ['Toyota', 'Mazda', 'Nissan'];
```

We want to print each of them using a **do-while loop**. What do we have to do?

1. We need a variable with an initial value of **0** because the first position in a list is at the index 0: `var i = 0`
2. We will access the item in the list, print it, then increase the value of **i** by one: `print(cars[i++])`
3. We will loop as long as **i** is less than the list length: `i < cars.length`

Let's see the code in DartPad:

<DartPad 
   id="ddfc31a5c80d087374b2451e916faae1" 
   width="100%" 
   height="450px" 
   split="65" 
/>]]></content:encoded>
            <category>Dart</category>
            <category>Dart Course</category>
            <category>Flutter</category>
            <enclosure url="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2FIgFPSTuDUgTrTa2wVkki%2Fb73b2f7f-3bcf-49b5-be08-5880936dd56b.png?alt=media&amp;token=09a159a9-7242-4891-b1c9-b06318a8d2ff" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Flutter: Loops: while]]></title>
            <link>https://yayocode.com/post/aT1E5IkIMn21tdqCNR0N</link>
            <guid>aT1E5IkIMn21tdqCNR0N</guid>
            <pubDate>Fri, 27 Jan 2023 12:02:57 GMT</pubDate>
            <description><![CDATA[Learn what the while loop is and how it works in Dart & Flutter]]></description>
            <content:encoded><![CDATA[<img src="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2FaT1E5IkIMn21tdqCNR0N%2Fb2ebc545-8597-48f6-a485-2e12c4657ee2.png?alt=media&token=036ef71d-c5d6-47b5-88fd-dc6d22851aca" alt="Cover image"> 
 The **while loop** executes a code block as long as the given condition is true. The syntax is the following:

```Dart
while( condition ){
  // code that will be executed
}
```

Let's write a program to print the numbers from one to ten using a **while loop**. The flowchart using a **while loop** is:

<MyImage
    src="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2FaT1E5IkIMn21tdqCNR0N%2F7335e557-bccd-44a9-aeac-772fdfa8423d.png?alt=media&token=f55018f2-3de5-4f80-aeaa-eb0a7f545666"
    width="50%"
    priority="true"
    smallScreenWidth="80%"
    aspectRatio="1100/1142"
    description="The while loop"
/>

1. We start with a variable **i** with initial value of **1**: `var i = 1`
2. We will loop as long as as **i** is less or equals to **10**: `i <= 10`
3. We will print the value of **i** then we will increase it by one using **++**: `print( i++ )`

Let's run the code in DartPad:

<DartPad 
   id="48f11e7201865976c7fd9a689c7a5e26" 
   width="100%" 
   height="550px" 
   split="50" 
/>

## Use a while loop to iterate a list

We can also use a **while loop** to iterate each element in a list. Let's write a program to learn how to do it. 

Given the next list of cars:

```Dart
var cars = ['Toyota', 'Mazda', 'Nissan'];
```

We want to print each of them using a **while loop**. What do we have to do?

1. We need a variable with an initial value of **0** because the first position in a list is at the index 0: `var i = 0`
2. We will loop as long as **i** is less than the list length: `i < cars.length`
3. We will access the item in the list, print it, then increase the value of **i** by one: `print(cars[i++])`

Let's see the code in DartPad:

<DartPad 
   id="3e9d66c554cf5f21d591001464eb3a64" 
   width="100%" 
   height="450px" 
   split="65" 
/>]]></content:encoded>
            <category>Dart</category>
            <category>Dart Course</category>
            <category>Flutter</category>
            <enclosure url="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2FaT1E5IkIMn21tdqCNR0N%2Fb2ebc545-8597-48f6-a485-2e12c4657ee2.png?alt=media&amp;token=036ef71d-c5d6-47b5-88fd-dc6d22851aca" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Flutter: Collections: Map]]></title>
            <link>https://yayocode.com/post/EoUe4YoRaXQGiP11ANvH</link>
            <guid>EoUe4YoRaXQGiP11ANvH</guid>
            <pubDate>Sat, 21 Jan 2023 23:38:58 GMT</pubDate>
            <description><![CDATA[Learn what the Map is and how it works in Dart & Flutter and how to create them]]></description>
            <content:encoded><![CDATA[<img src="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2FEoUe4YoRaXQGiP11ANvH%2F2e1f0ff7-c897-437f-b87c-41bfeee69d11.png?alt=media&token=f05b361d-2142-43cc-91c0-0394a58db5fa" alt="Cover image"> 
 The **Map** is a type of collection that stores items in **"key-value"** pairs, and you can access the values by their **key**.

There are three types of Maps:

- [HashMap][1] is unordered (no order is guaranteed),
- [LinkedHashMap][2] iterates in key insertion order,
- [SplayTreeMap][3] iterates the keys in sorted order.

<Note
   title="Note"
   text="
In this article, we will learn about **Map** in general and not dive into details of each type of **Map**.
"/>

In the following code, we have the color name as **key** and their value in hexadecimal as **value**:


```dart  
final colorHex = {
  // key: value
  'white': '#FFFFFF',
  'blue': '#0000FF',
  'red': '#FF0000',
};

print(colorHex['white']); // prints #FFFFFF
print(colorHex['blue']); // prints #0000FF
print(colorHex['red']); // prints #FF0000
```

Dart is smart enough to infer the type of the **key** is `String` and the type of the **value** is `String`.  So Dart infers the map type is `<String, String>`.

To access the **value**, we use the **key**. For example, to print the value of `#FFFFFF`, we must access it by the key `'white'` like this: `colorHex['white']`.

We can also create maps with different types of **key-values** for example, `<int, String>` or `<String, boolean>`:


```dart  
// Dart infer the type is <int, string>
final daysOfTheWeek = {
  1: 'Monday',
  2: 'Tuesday',
  3: 'Wednesday',
  4: 'Thursday',
  5: 'Friday',
  6: 'Saturday',
  7: 'Sunday',
};
```

## Create a Map
There are a few ways to create maps in Dart. If you want an empty map:

```Dart
// May cause a type warning. What type of **key-value** will we add?
final emptyMap = {};

// Specifying the type
final emptyMap = <int, String>{};

// Specifying the type 
Map<int, String> emptyMap = {};
```

## Useful properties and functions

- To get the number of elements in the map:
   ```dart
   colorHex.length;
   ```
- To check if the map is empty, we can check if the size equals zero, `colorHex.length == 0`, or we can use `isEmpty`
   ```dart
   colorHex.isEmpty;
   ```
- To check if the list contains a **key**:
   ```dart
   colorHex.containsKey('white');
   ```
- To add more elements to the map:
   ```dart
   colorHex['green'] = '00FF00';
   ```
- To change the value of a given key:
   ```dart
   colorHex['green']= 'Some random string';
   ```
- To delete all elements of the map:
   ```dart
   colorHex.clear()
   ```

Let's run the following example in DartPad:

<DartPad 
   id="46dc2d7d40a8ae7c8edd95cc0584b8d5" 
   width="100%" 
   height="850px" 
   split="70" 
/>

[1]: https://api.dart.dev/stable/2.19.0/dart-collection/HashMap-class.html
[2]: https://api.dart.dev/stable/2.19.0/dart-collection/LinkedHashMap-class.html
[3]: https://api.dart.dev/stable/2.19.0/dart-collection/SplayTreeMap-class.html]]></content:encoded>
            <category>Dart</category>
            <category>Dart Course</category>
            <category>Flutter</category>
            <enclosure url="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2FEoUe4YoRaXQGiP11ANvH%2F2e1f0ff7-c897-437f-b87c-41bfeee69d11.png?alt=media&amp;token=f05b361d-2142-43cc-91c0-0394a58db5fa" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Flutter: Collections: List]]></title>
            <link>https://yayocode.com/post/pJ4jYtKU3JmbJIUARytG</link>
            <guid>pJ4jYtKU3JmbJIUARytG</guid>
            <pubDate>Sat, 21 Jan 2023 23:38:12 GMT</pubDate>
            <description><![CDATA[Learn what the List is and how they work in Dart & Flutter. Arrays in Dart & Flutter how to create them.]]></description>
            <content:encoded><![CDATA[<img src="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2FpJ4jYtKU3JmbJIUARytG%2F02ff2241-145b-4961-8311-60c931399565.png?alt=media&token=1455a36e-8519-4f3d-a006-56cda01e7010" alt="Cover image"> 
 The most common collection in almost any programming language is the **array**. In Dart, arrays are objects of type `List`. Lists allow us to store multiple values in a single variable. For example the following three variables:

```dart
final car1 = 'Toyota';
final car2 = 'Mazda';
final car3 = 'Nissan';

print(car1); // prints Toyota
print(car2); // prints Mazda
print(car3); // prints Nissan
```

If we use a **List**, it become:

```dart
final cars = ['Toyota', 'Mazda', 'Nissan'];

print(cars[0]); // prints Toyota
print(cars[1]); // prints Mazda
print(cars[2]); // prints Nissan

// Or we can print the list:
print(cars); // prints [Toyota, Mazda, Nissan]
```

In the previous code, we have a single variable, `cars`, that stores the three strings `'Toyota', 'Mazda', and 'Nissan'`. To access the stored values, we must indicate their position (also known as index). For example, **Toyota** is stored in the index zero `cars[0]`.

The first element in the list is always at the index **0**, and the last is at the index **n-1**, where **n** is the total number of elements in the list.

In Dart, the `List` is an ordered collection that maintains the insertion order of the items. If we add the elements in the following order **Toyota, Mazda, Nissan** the first element will be **Toyota**, the second one **Mazda**, and the third one **Nissan**

## Create a List

There are a few ways to create lists in Dart. If you want an empty list:

```Dart
// May cause a type warning. What type of element will we add?
final emptyList = [];

// Specifying the type. 
final emptyList = <String>[]; 

// Using the List.empty() constructor
final emptyList = List<String>.empty(growable: true);

// if growable is false, we will not be able to add new elements
final emptyList = List<String>.empty(growable: false);
```

<Note
   title="Note"
   text="
In the past, we could use `List()` to initialize an empty list, but this is now deprecated. Check more details [here](https://dart.dev/null-safety/understanding-null-safety#no-unnamed-list-constructor)
"/>

## Useful properties and functions

- To get the number of elements in the list:
   ```dart
   cars.length;
   ```
- To check if the list is empty, we can check if the size equals zero, `cars.length == 0`, or we can use `isEmpty`
   ```dart
   cars.isEmpty;
   ```
- To check if the list contains an element:
   ```dart
   cars.contains('Toyota');
   ```
- To add more elements to the list:
   ```dart
   cars.add('Ford');
   ```
- To change the value of a given index:
   ```dart
   cars[0]="BMW";
   ```
- To delete all elements of the list:
   ```dart
   cars.clear()
   ```

Let's run the following example in DartPad:

<DartPad 
   id="6c7f9792266d18b4b7f38a9ef9d25656" 
   width="100%" 
   height="900px" 
   split="65" 
/>]]></content:encoded>
            <category>Dart</category>
            <category>Dart Course</category>
            <category>Flutter</category>
            <enclosure url="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2FpJ4jYtKU3JmbJIUARytG%2F02ff2241-145b-4961-8311-60c931399565.png?alt=media&amp;token=1455a36e-8519-4f3d-a006-56cda01e7010" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Flutter: Decision making: switch case]]></title>
            <link>https://yayocode.com/post/6oGfE6g8vcJLJo5Fp1nc</link>
            <guid>6oGfE6g8vcJLJo5Fp1nc</guid>
            <pubDate>Sat, 21 Jan 2023 23:37:20 GMT</pubDate>
            <description><![CDATA[Learn what is the switch case statement and how to use in Dart and Flutter]]></description>
            <content:encoded><![CDATA[<img src="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2F6oGfE6g8vcJLJo5Fp1nc%2Fcae1f279-f2cd-4fec-9fba-c0f73b754ed9.png?alt=media&token=f881d76d-b261-4ac7-819b-02c2fd5109fb" alt="Cover image"> 
 The **switch case** statement is similar to the [if, else-if, else][1] but only allows us to compare equality **(==)**. The **switch case** syntax is as follows:

```Dart
switch(expression) {
    case value1:
        // statements
        break;
    case value2:
        // statements
        break;
    case value3:
        // statements
        break;
    default:
       // default statements
}
```

If the **expression** result is equal to a **value**, then the code inside that case is executed. When the Dart code reaches the **break** keyword, it will break out of the switch and continue the execution of the program. 

Let's see the following example in DartPad: Let's say we want to print the day of the week given a number between one and seven. Monday is equal to one, Tuesday is equal to two, and so on:

<DartPad 
   id="f2f2a5b14a7666cfb62e67f95b938350" 
   width="100%" 
   height="820px" 
   split="85" 
/>

What happens if we do not add the **break** keyword? Well, then the next **case** will be executed. Try removing some of the **break** keywords from the previous example. What is the result? 

## Grouping cases

Sometimes we want to group more than one case to execute the same code. Let's see the following example in DartPad: Given a number between one and seven, we want to print if it's a weekday or weekend: 

<DartPad 
   id="cff028819c96ff9ef2072b2f5da6dbff" 
   width="100%" 
   height="620px" 
   split="90" 
/>

In the previous example, we grouped cases 1, 2, 3, 4, and 5 to print `Is weekday`, and we grouped cases 6 and 7 to print `Is weekend`. Also, we have a **default** statement that will print `Wrong day` in case the given number does not match any day of the week.

[1]: /post/wARNG43YtuxWoUlPjhBO]]></content:encoded>
            <category>Dart</category>
            <category>Dart Course</category>
            <category>Flutter</category>
            <enclosure url="https://firebasestorage.googleapis.com/v0/b/yayo-code-blog-prod.appspot.com/o/locales%2Fen%2Fposts%2F6oGfE6g8vcJLJo5Fp1nc%2Fcae1f279-f2cd-4fec-9fba-c0f73b754ed9.png?alt=media&amp;token=f881d76d-b261-4ac7-819b-02c2fd5109fb" length="0" type="image/png"/>
        </item>
    </channel>
</rss>