<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[dtengeri's blog]]></title><description><![CDATA[dtengeri's blog]]></description><link>https://blog.dtengeri.com</link><generator>RSS for Node</generator><lastBuildDate>Sat, 18 Apr 2026 07:12:31 GMT</lastBuildDate><atom:link href="https://blog.dtengeri.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Call the API endpoints - the Chopper HTTP client]]></title><description><![CDATA[Intro
This is the second part of the blog post series that aims to implement the February Challenge of flutterchallenge.dev the Tinder for Cats and Dogs.
This post shows an implementation of the communication layer of our application by using the cho...]]></description><link>https://blog.dtengeri.com/call-the-api-endpoints-the-chopper-http-client</link><guid isPermaLink="true">https://blog.dtengeri.com/call-the-api-endpoints-the-chopper-http-client</guid><category><![CDATA[Flutter]]></category><dc:creator><![CDATA[Dávid Tengeri]]></dc:creator><pubDate>Fri, 10 Jun 2022 06:47:38 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/8bghKxNU1j0/upload/v1654838710943/2SyteilW3.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-intro">Intro</h1>
<p>This is the second part of <a target="_blank" href="https://blog.dtengeri.com/series/tinder-for-cats-dogs">the blog post series</a> that aims to implement the February Challenge of <a target="_blank" href="https://flutterchallenge.dev">flutterchallenge.dev</a> the <a target="_blank" href="https://flutterchallenge.dev/tinder-for-cats-and-dogs">Tinder for Cats and Dogs</a>.</p>
<p>This post shows an implementation of the communication layer of our application by using the <a target="_blank" href="https://pub.dev/packages/chopper">chopper</a> package.</p>
<p>You can find the source code at https://github.com/dtengeri/tinder_cat_dog_app</p>
<h1 id="heading-what-is-chopperhttpspubdevpackageschopper">What is <a target="_blank" href="https://pub.dev/packages/chopper">chopper</a>?</h1>
<p>What does the <a target="_blank" href="https://pub.dev/packages/chopper">chopper</a> package provide us and why it is useful?</p>
<p><a target="_blank" href="https://pub.dev/packages/chopper">chopper</a> is an HTTP client generator. It generates source code for you, to manage HTTP calls in your app. It generates the boring stuff, you just need to define the methods and annotate them with some meta information, chopper will translate it to HTTP calls and handles JSON serialization, HTTP headers and every HTTP related configuration for you.</p>
<h1 id="heading-add-chopper-to-your-app">Add chopper to your app</h1>
<p>Let's add <a target="_blank" href="https://pub.dev/packages/chopper">chopper</a> to the Tinder for Cats and Dogs application.</p>
<p>Just for a remainder, the Tinder for Cats and Dogs was the first challenge of <a target="_blank" href="https://flutterchallenge.dev">flutterchallenge.dev</a>. It provides a <a target="_blank" href="https://tinder-cat-dog-api.herokuapp.com/swagger.html">REST API</a> to get the list of available cats and dogs and to store the user's votes.
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1648968833615/C5iiJPDfc.png" alt="Screenshot 2022-04-03 at 8.53.14.png" /></p>
<p>Let's start with adding the required dependencies to our application.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">dependencies:</span>
  <span class="hljs-attr">chopper:</span> <span class="hljs-string">^4.0.5</span>

<span class="hljs-attr">dev_dependencies:</span>
  <span class="hljs-attr">build_runner:</span> <span class="hljs-string">^2.1.11</span>
  <span class="hljs-attr">chopper_generator:</span> <span class="hljs-string">^4.0.5</span>
</code></pre>
<p>Since <a target="_blank" href="https://pub.dev/packages/chopper">chopper</a> is a code generator, we are going to use the <a target="_blank" href="https://pub.dev/packages/build_runner">build_runner</a> to generate the code the same way as for <a target="_blank" href="https://pub.dev/packages/freezed">freezed</a> in the <a target="_blank" href="https://blog.dtengeri.com/represent-the-data-coming-from-a-rest-api-with-freezed">first part</a> of the <a target="_blank" href="https://blog.dtengeri.com/series/tinder-for-cats-dogs">series</a>.</p>
<h2 id="heading-chopper-basics">Chopper basics</h2>
<p><a target="_blank" href="https://pub.dev/packages/chopper">chopper</a> main class is the <code>ChopperClient</code> which does the communication with our backend service. It relies on services. A service is a <code>ChopperService</code>, which is the generated code for the endpoints. Since we are using <a target="_blank" href="https://pub.dev/packages/freezed">freezed</a> it provides us JSON serialization as well. We can create a custom converter for <a target="_blank" href="https://pub.dev/packages/chopper">chopper</a> so it can automatically convert the request and response body from JSON to our models.</p>
<h2 id="heading-create-the-first-service">Create the first service</h2>
<p>Let's start with creating our first service, for the <code>/cats</code> endpoint.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:chopper/chopper.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:tinder_cat_dog_app/features/animals/animals.dart'</span>;

<span class="hljs-comment">// The place of the generated code.</span>
<span class="hljs-keyword">part</span> <span class="hljs-string">'cat_list_service.chopper.dart'</span>;

<span class="hljs-comment">// This annotation tells chopper that this service</span>
<span class="hljs-comment">// should send its requests to the /cats path.</span>
<span class="hljs-meta">@ChopperApi</span>(baseUrl: <span class="hljs-string">'/cats'</span>)
<span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CatListService</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ChopperService</span> </span>{
  <span class="hljs-comment">// This method creates the service based on the generated code.</span>
  <span class="hljs-keyword">static</span> CatListService create([ChopperClient? client]) =&gt;
      _$CatListService(client);

  <span class="hljs-comment">// This tells chopper to generate a method that wil send a GET request</span>
  <span class="hljs-comment">// to the /cats endpoint and returns a list of Cat models.</span>
  <span class="hljs-meta">@Get</span>()
  Future&lt;Response&lt;<span class="hljs-built_in">List</span>&lt;Cat&gt;&gt;&gt; getCats();
}
</code></pre>
<p><a target="_blank" href="https://pub.dev/packages/chopper">chopper</a> provides annotations that can be used to aid the code generation. In the <code>CatListService</code> we define a <code>getCats()</code> method that will send a <code>GET</code> request to our REST API's <code>/cats</code> endpoint. The endpoint's response is in JSON format</p>
<pre><code class="lang-json">[
  {
    <span class="hljs-attr">"id"</span>: <span class="hljs-string">"9eef0518d8b5a333ee1fcb1a06a1474a23192c6a"</span>,
    <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Luna"</span>,
    <span class="hljs-attr">"path"</span>: <span class="hljs-string">"/cats/cat001.jpeg"</span>
  }
]
</code></pre>
<p>The generated code processes this JSON string and converts its content to our <code>Cat</code> model and adds all cats to a list. And all of this happens by just adding the <code>@Get()</code> annotation.</p>
<p>Run </p>
<pre><code class="lang-bash">flutter pub run build_runner build  --delete-conflicting-outputs
</code></pre>
<p>to generate the service or watch your file changes and generate the <a target="_blank" href="https://pub.dev/packages/chopper">chopper</a> services automatically by</p>
<pre><code class="lang-bash">flutter pub run build_runner watch  --delete-conflicting-outputs
</code></pre>
<h2 id="heading-processing-json-strings">Processing JSON strings</h2>
<p>We can provide converters to <code>ChopperClient</code> that could process JSON strings and convert them to our own models.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:chopper/chopper.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:tinder_cat_dog_app/features/core/core.dart'</span>;

<span class="hljs-comment">/// <span class="markdown">This function gets a JSON map and returns a model of type [T].</span></span>
<span class="hljs-keyword">typedef</span> JsonFactory&lt;T&gt; = T <span class="hljs-built_in">Function</span>(<span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt; json);

<span class="hljs-comment">/// <span class="markdown">A [JsonConverter] for chopper that uses converts our models to and </span></span>
<span class="hljs-comment">/// <span class="markdown">from JSON.</span></span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">JsonSerializableConverter</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">JsonConverter</span> </span>{
  <span class="hljs-comment">/// <span class="markdown">Creates a new [JsonSerializableConverter] with the given [factories].</span></span>
  <span class="hljs-keyword">const</span> JsonSerializableConverter(<span class="hljs-keyword">this</span>.factories);
  <span class="hljs-comment">/// <span class="markdown">Collection of the factories of our model [Type]s. </span></span>
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">Type</span>, JsonFactory&gt; factories;

  T _decodeMap&lt;T&gt;(<span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt; values) {
    <span class="hljs-comment">/// <span class="markdown">Get jsonFactory using Type parameters</span></span>
    <span class="hljs-comment">/// <span class="markdown">if not found or invalid, throw error or return null</span></span>
    <span class="hljs-keyword">final</span> jsonFactory = factories[T];
    <span class="hljs-keyword">if</span> (jsonFactory == <span class="hljs-keyword">null</span> || jsonFactory <span class="hljs-keyword">is</span>! JsonFactory&lt;T&gt;) {
      <span class="hljs-comment">/// <span class="markdown">throw serializer not found error;</span></span>
      <span class="hljs-keyword">throw</span> ArgumentError(<span class="hljs-string">'Serializer not found'</span>);
    }

    <span class="hljs-keyword">return</span> jsonFactory(values);
  }

  <span class="hljs-built_in">List</span>&lt;T&gt; _decodeList&lt;T&gt;(<span class="hljs-built_in">List</span> values) =&gt;
      values.where((v) =&gt; v != <span class="hljs-keyword">null</span>).map&lt;T&gt;((v) =&gt; _decode&lt;T&gt;(v)).toList();

  <span class="hljs-built_in">dynamic</span> _decode&lt;T&gt;(entity) {
    <span class="hljs-keyword">if</span> (entity <span class="hljs-keyword">is</span> <span class="hljs-built_in">Iterable</span>) <span class="hljs-keyword">return</span> _decodeList&lt;T&gt;(entity.toList());

    <span class="hljs-keyword">if</span> (entity <span class="hljs-keyword">is</span> <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt;) <span class="hljs-keyword">return</span> _decodeMap&lt;T&gt;(entity);

    <span class="hljs-keyword">return</span> entity;
  }

  <span class="hljs-comment">/// <span class="markdown">Converts a reponse coming from the server to [ResultType].</span></span>
  <span class="hljs-meta">@override</span>
  Response&lt;ResultType&gt; convertResponse&lt;ResultType, Item&gt;(Response response) {
    <span class="hljs-keyword">final</span> jsonRes = <span class="hljs-keyword">super</span>.convertResponse(response);

    <span class="hljs-keyword">return</span> jsonRes.copyWith&lt;ResultType&gt;(body: _decode&lt;Item&gt;(jsonRes.body));
  }

  <span class="hljs-comment">/// <span class="markdown">Converts the request body if it implements [ToJsonConvertable].</span></span>
  <span class="hljs-comment">/// 
  <span class="markdown">/// It calls [ToJsonConvertable.toJson] method on the body to convert</span></span>
  <span class="hljs-comment">/// <span class="markdown">the body to JSON string.</span></span>
  <span class="hljs-meta">@override</span>
  Request convertRequest(Request request) {
    <span class="hljs-keyword">final</span> convertedRequest = <span class="hljs-keyword">super</span>.convertRequest(request);
    <span class="hljs-keyword">if</span> (request.body <span class="hljs-keyword">is</span> ToJsonConvertable) {
      <span class="hljs-keyword">return</span> convertedRequest.copyWith(
        body: (request.body <span class="hljs-keyword">as</span> ToJsonConvertable).toJson(),
      );
    }
    <span class="hljs-keyword">return</span> convertedRequest;
  }

  <span class="hljs-meta">@override</span>
  Response convertError&lt;ResultType, Item&gt;(Response response) {
    <span class="hljs-keyword">final</span> jsonRes = <span class="hljs-keyword">super</span>.convertError(response);

    <span class="hljs-keyword">return</span> jsonRes;
  }
}
</code></pre>
<p>There are 2 key parts in the converter.</p>
<ol>
<li>We have to provide a map for all the types that our chopper client can handle through the REST APIs. This map contains the factory functions that could convert a JSON map to the given type. Such a function is <code>Cat.fromJson</code>, which is generated by <a target="_blank" href="https://pub.dev/packages/freezed">freezed</a>.</li>
<li>In the <code>convertRequest</code> method we are checking the type of the request body. If it implements <code>ToJsonConvertable</code> and has a <code>toJson()</code> method, it will use it to convert the data class to JSON. This is true for our models generated by <a target="_blank" href="https://pub.dev/packages/freezed">freezed</a>. We only have to extend or implement the <code>ToJsonConvertable</code> class.</li>
</ol>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:freezed_annotation/freezed_annotation.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:tinder_cat_dog_app/features/core/core.dart'</span>;

<span class="hljs-keyword">part</span> <span class="hljs-string">'vote_param.freezed.dart'</span>;
<span class="hljs-keyword">part</span> <span class="hljs-string">'vote_param.g.dart'</span>;

<span class="hljs-comment">/// <span class="markdown">Extend [ToJsonConvertable] so [VoteParam] can be used</span></span>
<span class="hljs-comment">/// <span class="markdown">as a request body and it will be converted automatically to JSON.</span></span>
<span class="hljs-meta">@freezed</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">VoteParam</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ToJsonConvertable</span> <span class="hljs-title">with</span> <span class="hljs-title">_</span>$<span class="hljs-title">VoteParam</span> </span>{
  <span class="hljs-keyword">const</span> <span class="hljs-keyword">factory</span> VoteParam({
    <span class="hljs-meta">@JsonKey</span>(name: <span class="hljs-string">'vote_type'</span>) <span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> voteType,
    <span class="hljs-meta">@JsonKey</span>(name: <span class="hljs-string">'animal_id'</span>) <span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> animalId,
    <span class="hljs-keyword">required</span> <span class="hljs-built_in">bool</span> liked,
  }) = _VoteParam;

  <span class="hljs-keyword">factory</span> VoteParam.fromJson(<span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt; json) =&gt;
      _$VoteParamFromJson(json);
}
</code></pre>
<p>The <code>ToJsonConvertable</code> is an abstract class with the definition of <code>toJson()</code>.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ToJsonConvertable</span> </span>{
  <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt; toJson();
}
</code></pre>
<h2 id="heading-configure-the-requests">Configure the requests</h2>
<p>Let's see a more complex service that uses variables in the URLs, sends data as the request body and uses Authorization headers.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:chopper/chopper.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:tinder_cat_dog_app/features/animals/animals.dart'</span>;

<span class="hljs-keyword">part</span> <span class="hljs-string">'vote_service.chopper.dart'</span>;

<span class="hljs-meta">@ChopperApi</span>(baseUrl: <span class="hljs-string">'/votes'</span>)
<span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">VoteService</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ChopperService</span> </span>{
  <span class="hljs-keyword">static</span> VoteService create([ChopperClient? client]) =&gt; _$VoteService(client);

  <span class="hljs-comment">// We can provide values for HTTP headers like authorization token</span>
  <span class="hljs-comment">// with the @Header annotation.</span>
  <span class="hljs-meta">@Get</span>()
  Future&lt;Response&lt;<span class="hljs-built_in">List</span>&lt;Vote&gt;&gt;&gt; getVotes({
    <span class="hljs-meta">@Header</span>(<span class="hljs-string">'Authorization'</span>) <span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> token,
  });

  <span class="hljs-comment">// We can use variables in the URL by using {variable-name} in the</span>
  <span class="hljs-comment">// @Get() annotation and marking a method parameter with @Path().</span>
  <span class="hljs-meta">@Get</span>(path: <span class="hljs-string">'/{id}'</span>)
  Future&lt;Response&lt;Vote&gt;&gt; getVote({
    <span class="hljs-meta">@Path</span>() <span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> id,
    <span class="hljs-meta">@Header</span>(<span class="hljs-string">'Authorization'</span>) <span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> token,
  });

  <span class="hljs-comment">// We can provide the body of a POST request by annotate a method parameter</span>
  <span class="hljs-comment">// with @Body().</span>
  <span class="hljs-meta">@Post</span>()
  Future&lt;Response&lt;Vote&gt;&gt; createVote({
    <span class="hljs-meta">@Body</span>() <span class="hljs-keyword">required</span> VoteParam voteParam,
    <span class="hljs-meta">@Header</span>(<span class="hljs-string">'Authorization'</span>) <span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> token,
  });

  <span class="hljs-meta">@Put</span>(path: <span class="hljs-string">'/{id}'</span>)
  Future&lt;Response&lt;Vote&gt;&gt; updateVote({
    <span class="hljs-meta">@Path</span>() <span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> id,
    <span class="hljs-meta">@Body</span>() <span class="hljs-keyword">required</span> VoteParam voteParam,
    <span class="hljs-meta">@Header</span>(<span class="hljs-string">'Authorization'</span>) <span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> token,
  });

  <span class="hljs-meta">@Delete</span>(path: <span class="hljs-string">'/{id}'</span>)
  Future&lt;Response&gt; deleteVote({
    <span class="hljs-meta">@Path</span>() <span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> id,
    <span class="hljs-meta">@Header</span>(<span class="hljs-string">'Authorization'</span>) <span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> token,
  });
}
</code></pre>
<p>We can use variables in the URL, which will be replaced by one of the method parameters.</p>
<pre><code class="lang-dart"><span class="hljs-meta">@Get</span>(path: <span class="hljs-string">'/{id}'</span>)
  Future&lt;Response&lt;Vote&gt;&gt; getVote({
    <span class="hljs-meta">@Path</span>() <span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> id,
    <span class="hljs-meta">@Header</span>(<span class="hljs-string">'Authorization'</span>) <span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> token,
  });
</code></pre>
<p>The <code>{id}</code> in the path creates a placeholder in the URL and the <code>@Path()</code> annotation before the <code>id</code> method parameter connects it to this placeholder.</p>
<p>We can provide HTTP headers as well by using <code>@Header()</code>. We can use it for example to set a JWT token in the <code>Authorization</code> header.</p>
<h2 id="heading-create-the-chopperclient">Create the <code>ChopperClient</code></h2>
<p>Let's configure the <code>ChopperClient</code> with our generated services and converter, so we can use it in our app. We're going to use a factory called <code>ChopperClientFactory</code>.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:chopper/chopper.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:http/http.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:tinder_cat_dog_app/features/animals/animals.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:tinder_cat_dog_app/features/core/core.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ChopperClientFactory</span> </span>{
  <span class="hljs-comment">/// <span class="markdown">Creates a new [ChopperClient] with our services and converter.</span></span>
  <span class="hljs-keyword">static</span> ChopperClient create([Client? client]) =&gt; ChopperClient(
        client: client,
        baseUrl: <span class="hljs-string">'https://tinder-cat-dog-api.herokuapp.com'</span>,
        services: [
          <span class="hljs-comment">// These are the services that we made and chopper</span>
          <span class="hljs-comment">// generated their logic.</span>
          AuthService.create(),
          CatListService.create(),
          DogListService.create(),
          VoteService.create(),
        ],
        <span class="hljs-comment">// This is our custom converter.</span>
        converter: <span class="hljs-keyword">const</span> JsonSerializableConverter({
          Cat: Cat.fromJson,
          Dog: Dog.fromJson,
          Vote: Vote.fromJson,
        }),
      );
}
</code></pre>
<h2 id="heading-use-our-client">Use our client</h2>
<p>It's time to use our client to make HTTP requests.
Let's create a new instance of it</p>
<pre><code class="lang-dart"><span class="hljs-keyword">final</span> chopper = ChopperClientFactory.create();
</code></pre>
<p>Get the list of cats</p>
<pre><code class="lang-dart"><span class="hljs-keyword">final</span> catListService = chopper.getService&lt;CatListService&gt;();
<span class="hljs-keyword">final</span> response = <span class="hljs-keyword">await</span> catListService.getCats();
<span class="hljs-keyword">if</span> (response.statusCode == <span class="hljs-number">200</span> &amp;&amp; response.body != <span class="hljs-keyword">null</span>) {
  <span class="hljs-keyword">for</span> (Cat cat <span class="hljs-keyword">in</span> response.body!) {
    <span class="hljs-comment">// Do something with the cat.</span>
  }
}
</code></pre>
<p>Create a vote</p>
<pre><code class="lang-dart"><span class="hljs-keyword">final</span> voteService = chopper.getService&lt;VoteService&gt;();
<span class="hljs-keyword">final</span> response = <span class="hljs-keyword">await</span> voteService.createVote(
  token: <span class="hljs-string">'Bearer xxxx'</span>,
  voteParam: <span class="hljs-keyword">const</span> VoteParam(voteType: <span class="hljs-string">'cat'</span>, animalId: <span class="hljs-string">'1'</span>, liked: <span class="hljs-keyword">true</span>),
);
</code></pre>
<h1 id="heading-summary">Summary</h1>
<p>In this post, we created the communication layer of our application that uses the REST API and our data classes. The <a target="_blank" href="https://pub.dev/packages/chopper">chopper</a> package provides code generation features that help us quickly create services that communicate with our API. It generates the code for making HTTP requests. Our task is to annotate our methods and parameters and the rest is done by <a target="_blank" href="https://pub.dev/packages/chopper">chopper</a>.</p>
<h1 id="heading-resources">Resources</h1>
<p>You can find the source code of the application at https://github.com/dtengeri/tinder_cat_dog_app</p>
]]></content:encoded></item><item><title><![CDATA[Represent the data coming from a REST API with freezed]]></title><description><![CDATA[This year in the February challenge at flutterchallenge.dev the goal was to build a Tinder-like application for cats and dogs by using a REST API to fetch data. This is the first part of a series that shows an implementation for this challenge and no...]]></description><link>https://blog.dtengeri.com/represent-the-data-coming-from-a-rest-api-with-freezed</link><guid isPermaLink="true">https://blog.dtengeri.com/represent-the-data-coming-from-a-rest-api-with-freezed</guid><category><![CDATA[Flutter]]></category><dc:creator><![CDATA[Dávid Tengeri]]></dc:creator><pubDate>Sun, 03 Apr 2022 07:56:38 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/3-RBvvU7zog/upload/v1648883339737/7JkhOTvEi.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This year in the February challenge at <a target="_blank" href="https://flutterchallenge.dev/tinder-for-cats-and-dogs">flutterchallenge.dev</a> the goal was to build a Tinder-like application for cats and dogs by using a REST API to fetch data. This is the first part of a series that shows an implementation for this challenge and now we are going to create our data classes to represent the data coming from the REST endpoints with the help of the package <a target="_blank" href="https://pub.dev/packages/freezed">freezed</a>.</p>
<p><code>freezed</code> is a code generator tool that helps you build your data classes with tons of helpful features, like the <code>copyWith()</code> or <code>fromJson()</code> methods.</p>
<h2 id="heading-add-the-dependencies">Add the dependencies</h2>
<p>So let's start with a fresh new Flutter project and add <code>freezed</code> to the dependencies:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># pubspec.yaml</span>
<span class="hljs-attr">dependencies:</span>
  <span class="hljs-attr">freezed_annotation:</span> <span class="hljs-string">^1.1.0</span>
  <span class="hljs-attr">json_annotation:</span> <span class="hljs-string">^4.4.0</span>

<span class="hljs-attr">dev_dependencies:</span>
  <span class="hljs-attr">build_runner:</span> <span class="hljs-string">^2.1.8</span>
  <span class="hljs-attr">freezed:</span> <span class="hljs-string">^1.1.1</span>
  <span class="hljs-attr">json_serializable:</span> <span class="hljs-string">^6.1.5</span>
</code></pre>
<p>The <code>freezed_annotations</code> will help us mark our classes so the <code>freezed</code> package will know which classes need to be generated when we run <code>build_runner</code>. If we add <code>json_annotation</code> and <code>json_serializable</code> to the dependencies as well, <code>freezed</code> can generate the <code>toJson()</code> and <code>fromJson()</code> methods as well.</p>
<h2 id="heading-create-our-data-representation">Create our data representation</h2>
<p>Let's see what we the REST API provides us. The challenge provides a <a target="_blank" href="https://tinder-cat-dog-api.herokuapp.com/swagger.html">swagger documentation</a> about the available endpoints. Our goal is to have a class for each endpoint to represent its JSON response in our app.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1648968833615/C5iiJPDfc.png" alt="Screenshot 2022-04-03 at 8.53.14.png" /></p>
<h3 id="heading-model-the-api-with-freezed">Model the API with <code>freezed</code></h3>
<p>Let's start with the <code>/cats</code> endpoint which will return an array of cats in the following format</p>
<pre><code class="lang-json">[
  {
    <span class="hljs-attr">"id"</span>: <span class="hljs-string">"9eef0518d8b5a333ee1fcb1a06a1474a23192c6a"</span>,
    <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Luna"</span>,
    <span class="hljs-attr">"path"</span>: <span class="hljs-string">"/cats/cat001.jpeg"</span>
  }
]
</code></pre>
<p>So our <code>Cat</code> class should have 3 fields. This is how it could be created with the help of <code>freezed</code>. Let's create a <code>cat.dart</code> file under the folder <code>lib/features/animals/models</code>. </p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:freezed_annotation/freezed_annotation.dart'</span>;

<span class="hljs-comment">// This file contains the generated code by freezed.</span>
<span class="hljs-keyword">part</span> <span class="hljs-string">'cat.freezed.dart'</span>;
<span class="hljs-comment">// This file contains the generated code by json_serializable.</span>
<span class="hljs-keyword">part</span> <span class="hljs-string">'cat.g.dart'</span>;

<span class="hljs-comment">// We mark the class with @freezed annotation so freezed knows it has </span>
<span class="hljs-comment">// some to work to do.</span>
<span class="hljs-comment">// _$Cat is a generated class which contains all the constructors, </span>
<span class="hljs-comment">// methods that makes our class immutable.</span>
<span class="hljs-meta">@freezed</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Cat</span> <span class="hljs-title">with</span> <span class="hljs-title">_</span>$<span class="hljs-title">Cat</span> </span>{
  <span class="hljs-comment">// With the help of this factory constructor we define the fields </span>
  <span class="hljs-comment">// of our class.</span>
  <span class="hljs-comment">// _Cat is a class that will hold the implementation details. </span>
  <span class="hljs-keyword">const</span> <span class="hljs-keyword">factory</span> Cat({
    <span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> id,
    <span class="hljs-keyword">required</span> <span class="hljs-built_in">String?</span> name,
    <span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> path,
  }) = _Cat;
  <span class="hljs-comment">// Adding this factory will instruct freezed and json_serializable to </span>
  <span class="hljs-comment">// generate the fromJson() and toJson() methods for us.</span>
  <span class="hljs-keyword">factory</span> Cat.fromJson(<span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt; json) =&gt; _$CatFromJson(json);
}
</code></pre>
<p>Now we have to run <code>build_runner</code> to generate the code so the <code>cat.freezed.dart</code> and <code>cat.g.dart</code> files are created and all the errors are eliminated from our IDE.</p>
<p>You can generate the code once by running </p>
<pre><code class="lang-bash">flutter pub run build_runner build --delete-conflicting-outputs
</code></pre>
<p>But if you are developing an app you can watch for changes and always generate the code without running manually the <code>build_runner</code>.</p>
<pre><code class="lang-bash">flutter pub run build_runner watch --delete-conflicting-outputs
</code></pre>
<p><code>build_runner</code> will scan your codebase and if there is a code generator that finds something to work on, it will execute it.</p>
<p>Let's move on to the next model, to the <code>Vote</code>. The <code>/votes</code>, and <code>/votes/{id}</code> endpoints. Whenever we get, create or modify a vote, the API responses with a structure like this.</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"id"</span>: <span class="hljs-number">1</span>,
  <span class="hljs-attr">"user_id"</span>: <span class="hljs-number">1</span>,
  <span class="hljs-attr">"vote_type"</span>: <span class="hljs-string">"cat"</span>,
  <span class="hljs-attr">"animal_id"</span>: <span class="hljs-string">"9eef0518d8b5a333ee1fcb1a06a1474a23192c6a"</span>,
  <span class="hljs-attr">"liked"</span>: <span class="hljs-literal">true</span>,
  <span class="hljs-attr">"created_at"</span>: <span class="hljs-string">"2022-01-07T11:39:40.992Z"</span>,
  <span class="hljs-attr">"updated_at"</span>: <span class="hljs-string">"2022-01-07T11:39:40.992Z"</span>
}
</code></pre>
<p>We can do the same as we did with the cats.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:freezed_annotation/freezed_annotation.dart'</span>;

<span class="hljs-keyword">part</span> <span class="hljs-string">'vote.freezed.dart'</span>;
<span class="hljs-keyword">part</span> <span class="hljs-string">'vote.g.dart'</span>;

<span class="hljs-meta">@freezed</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Vote</span> <span class="hljs-title">with</span> <span class="hljs-title">_</span>$<span class="hljs-title">Vote</span> </span>{
  <span class="hljs-keyword">const</span> <span class="hljs-keyword">factory</span> Vote({
    <span class="hljs-keyword">required</span> <span class="hljs-built_in">int</span> id,
    <span class="hljs-keyword">required</span> <span class="hljs-built_in">int</span> user_id,
    <span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> vote_type,
    <span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> animal_id,
    <span class="hljs-keyword">required</span> <span class="hljs-built_in">bool</span> liked,
    <span class="hljs-keyword">required</span> <span class="hljs-built_in">DateTime</span> created_at,
    <span class="hljs-keyword">required</span> <span class="hljs-built_in">DateTime</span> updated_at,
  }) = _Vote;

  <span class="hljs-keyword">factory</span> Vote.fromJson(<span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt; json) =&gt; _$VoteFromJson(json);
}
</code></pre>
<p>In addition to the basic <code>int</code> and <code>String</code> types, the <code>json_serialiable</code> can handle <code>DateTime</code> as well.
We have the <code>part</code> definitions, the class with the <code>mixin</code> and the <code>factory</code> method with the fields we want in our class along with the <code>fromJson</code>.</p>
<h3 id="heading-represent-the-http-request-body">Represent the HTTP request body</h3>
<p>When we want to create or modify a vote, the <code>POST /votes</code> and <code>PUT /votes/{id}</code> endpoints have a request body, which is basically a JSON with specific fields. We can represent this JSON the same way as we did with the data we are getting back from the API. So this</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"vote_type"</span>: <span class="hljs-string">"cat"</span>,
  <span class="hljs-attr">"animal_id"</span>: <span class="hljs-string">"9eef0518d8b5a333ee1fcb1a06a1474a23192c6a"</span>,
  <span class="hljs-attr">"liked"</span>: <span class="hljs-literal">true</span>
}
</code></pre>
<p>can be managed by</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:freezed_annotation/freezed_annotation.dart'</span>;

<span class="hljs-keyword">part</span> <span class="hljs-string">'vote_param.freezed.dart'</span>;
<span class="hljs-keyword">part</span> <span class="hljs-string">'vote_param.g.dart'</span>;

<span class="hljs-meta">@freezed</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">VoteParam</span> <span class="hljs-title">with</span> <span class="hljs-title">_</span>$<span class="hljs-title">VoteParam</span> </span>{
  <span class="hljs-keyword">const</span> <span class="hljs-keyword">factory</span> VoteParam({
    <span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> vote_type,
    <span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> animal_id,
    <span class="hljs-keyword">required</span> <span class="hljs-built_in">bool</span> liked,
  }) = _VoteParam;

  <span class="hljs-keyword">factory</span> VoteParam.fromJson(<span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt; json) =&gt;
      _$VoteParamFromJson(json);
}
</code></pre>
<p>Following the above, we can represent our data in our app easily. If you don't want to do this manually and you are using VSCode, I can recommend the <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=hirantha.json-to-dart">Json to Dart Model</a> extension, which can generate your data classes from a JSON string and it can work with <code>freezed</code>. </p>
<h3 id="heading-fix-the-linting-errors">Fix the linting errors</h3>
<p>You'll get lint errors for the <code>Vote</code> class because of the variable naming</p>
<pre><code><span class="hljs-type">Name</span> non-<span class="hljs-keyword">constant</span> identifiers <span class="hljs-keyword">using</span> lowerCamelCase.
</code></pre><p>You can fix this by using the <code>JsonKey</code> annotation like this</p>
<pre><code class="lang-dart"><span class="hljs-comment">// ignore_for_file: invalid_annotation_target</span>

<span class="hljs-keyword">import</span> <span class="hljs-string">'package:freezed_annotation/freezed_annotation.dart'</span>;

<span class="hljs-keyword">part</span> <span class="hljs-string">'vote_param.freezed.dart'</span>;
<span class="hljs-keyword">part</span> <span class="hljs-string">'vote_param.g.dart'</span>;

<span class="hljs-meta">@freezed</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">VoteParam</span> <span class="hljs-title">with</span> <span class="hljs-title">_</span>$<span class="hljs-title">VoteParam</span> </span>{
  <span class="hljs-keyword">const</span> <span class="hljs-keyword">factory</span> VoteParam({
    <span class="hljs-meta">@JsonKey</span>(name: <span class="hljs-string">'vote_type'</span>) <span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> voteType,
    <span class="hljs-meta">@JsonKey</span>(name: <span class="hljs-string">'animal_id'</span>) <span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> animalId,
    <span class="hljs-keyword">required</span> <span class="hljs-built_in">bool</span> liked,
  }) = _VoteParam;

  <span class="hljs-keyword">factory</span> VoteParam.fromJson(<span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt; json) =&gt;
      _$VoteParamFromJson(json);
}
</code></pre>
<p>By default, you'll get another lin error due to this <a target="_blank" href="https://github.com/rrousselGit/freezed/issues/488">issue</a>, you can safely ignore it by putting the following at the top of your file.</p>
<pre><code class="lang-dart"><span class="hljs-comment">// ignore_for_file: invalid_annotation_target</span>
</code></pre>
<h2 id="heading-summary">Summary</h2>
<p>By using code generation, you can quickly add the data classes to your project to model a REST API. The good thing is that you'll get lots of features by default that can improve the quality of your projects in the long run and it can speed up your development time since you don't have to write the code by yourself.</p>
<p>You can find the relevant source code <a target="_blank" href="https://github.com/dtengeri/tinder_cat_dog_app">here</a>.</p>
<p>In the next part of the series, I'll introduce the communication with the REST API with the help of the <a target="_blank" href="https://pub.dev/packages/chopper">chopper</a> package.</p>
]]></content:encoded></item><item><title><![CDATA[Document your dart and Flutter packages]]></title><description><![CDATA[Document your code
When you are working on a package that you want to share with others, either on pub.dev or internally with your team, you should provide documentation for your public API. 
Luckily, we have a great example, the Flutter framework ha...]]></description><link>https://blog.dtengeri.com/document-your-dart-and-flutter-packages</link><guid isPermaLink="true">https://blog.dtengeri.com/document-your-dart-and-flutter-packages</guid><category><![CDATA[Flutter]]></category><dc:creator><![CDATA[Dávid Tengeri]]></dc:creator><pubDate>Wed, 16 Feb 2022 15:37:53 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/djDgVMWd04E/upload/v1644781760356/jHrCgTC0l.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-document-your-code">Document your code</h2>
<p>When you are working on a package that you want to share with others, either on <a target="_blank" href="https://pub.dev">pub.dev</a> or internally with your team, you should provide documentation for your public API. </p>
<p>Luckily, we have a great example, the Flutter framework has really great documentation with lots of examples, code snippets. </p>
<p>We can make our package just as nice as the Flutter framework by writing our documentation as doc comments. </p>
<p>In order to write such a comment, we should start the comment with <code>///</code>.</p>
<pre><code class="lang-dart"><span class="hljs-comment">/// <span class="markdown">This class is an example of what we could achieve with </span></span>
<span class="hljs-comment">/// <span class="markdown">dart doc comments</span></span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyAwesomeFeature</span> </span>{
  <span class="hljs-comment">/// <span class="markdown">Creates a new instance of [MyAwesomeFeature] that can do</span></span>
  <span class="hljs-comment">/// <span class="markdown">some cool stuff on [title] and [subTitle].</span></span>
  <span class="hljs-comment">/// <span class="markdown">Usage: </span></span>
  <span class="hljs-comment">/// <span class="markdown"><span class="hljs-code">```dart</span></span></span>
  <span class="hljs-comment">/// <span class="markdown"><span class="hljs-code">MyAwesomeFeautre(</span></span></span>
  <span class="hljs-comment">///   <span class="markdown"><span class="hljs-code">title: 'Hello',</span></span></span>
  <span class="hljs-comment">///   <span class="markdown"><span class="hljs-code">subTitle: 'Flutter!',</span></span></span>
  <span class="hljs-comment">/// <span class="markdown"><span class="hljs-code">).doSomeCoolThings();</span></span></span>
  <span class="hljs-comment">/// <span class="markdown"><span class="hljs-code">```</span></span></span>
  MyAwesomeFeature({
    <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.title,
    <span class="hljs-keyword">this</span>.subTitle,
  });

  <span class="hljs-comment">/// <span class="markdown">This will be the title of our feature.</span></span>
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> title;

  <span class="hljs-comment">/// <span class="markdown">Optional subtitle, to better express ourselves.</span></span>
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String?</span> subTitle;

  <span class="hljs-comment">/// <span class="markdown">This method is so cool, it works with the [title] and makes it super cool. </span></span>
  <span class="hljs-keyword">void</span> doSomeCoolThings() {}
}
</code></pre>
<p>We can reference attributes, methods, other classes inside our doc comments, by wrapping them inside <code>[</code> and <code>]</code> as you can see in the example above. These references will be converted to links inside the generated documentation. You can reference any class inside your project or even a method of a class. </p>
<h2 id="heading-generate-your-documentation">Generate your documentation</h2>
<p>Once we added the comments to our public API, we can generate the documentation. It is an automatic process when you publish it on <a target="_blank" href="https://pub.dev">pub.dev</a>. If we are not working on a public package, we can generate the documentation manually. Before Flutter 2.10 and Dart 2.16, we could use the <code>dartdoc</code> command-line tool. From Dart 2.16, this tool has been moved under the main <code>dart</code> command, so we can invoke it by running <code>dart doc</code>. Unfortunately, in Dart 2.16, and 2.16.1, it has a <a target="_blank" href="https://github.com/dart-lang/dartdoc/issues/2934">bug</a>, which may be fixed in the upcoming 2.16.2 release. Until then we can still use <code>dartdoc</code> if we have an earlier version.</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> your_package
dartdoc .
</code></pre>
<p>Or when we are building a Flutter package and the tool does not find the Flutter SDK:</p>
<pre><code class="lang-bash">FLUTTER_ROOT=/path/to/flutter/sdk dartdoc .
</code></pre>
<p>The generated documentation will be in the <code>docs/api</code> directory and it will look something like this.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1645025081094/ICux1D82W.png" alt="Screenshot 2022-02-16 at 16.24.14.png" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1645025161546/DSmKwUJo4.png" alt="Screenshot 2022-02-16 at 16.25.29.png" /></p>
<h3 id="heading-additional-options">Additional options</h3>
<p>You have plenty of options to customize the generation. You have to create a <code>dartdoc_options.yaml</code> in the root of your package.  Let me highlight some of its features. </p>
<h4 id="heading-macros-templates">Macros, templates</h4>
<p>You can define templates and insert them in other places inside your doc comments.
For example you can create a template for the author information:</p>
<pre><code class="lang-dart"><span class="hljs-comment">/// <span class="markdown">{@template author<span class="hljs-emphasis">_information}</span></span></span>
<span class="hljs-comment">/// <span class="markdown"><span class="hljs-emphasis">Made by: David Tengeri <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">@dtengeri</span>&gt;</span></span></span></span></span>
<span class="hljs-comment">/// <span class="markdown"><span class="hljs-emphasis">{@endtemplate}</span></span></span>
</code></pre>
<p>And you can add this to your comments by inserting it as a macro:</p>
<pre><code class="lang-dart"><span class="hljs-comment">/// <span class="markdown">My Awesome Library</span></span>
<span class="hljs-comment">/// <span class="markdown">{@macro author<span class="hljs-emphasis">_information}</span></span></span>
<span class="hljs-comment">/// <span class="markdown"><span class="hljs-emphasis">Details come here</span></span></span>
</code></pre>
<h4 id="heading-categories">Categories</h4>
<p>You can define categories in the <code>dartdoc_options.yaml</code>. These categories can have their own page and they get a link in the left sidebar. </p>
<pre><code class="lang-yaml"><span class="hljs-attr">dartdoc:</span>
  <span class="hljs-attr">categories:</span>
    <span class="hljs-attr">"My Cool Category":</span>
      <span class="hljs-attr">markdown:</span> <span class="hljs-string">doc/my_cool_category.md</span>
</code></pre>
<p>Here, the <code>md</code> file contains the details of your category. You can mark a library, class or field with your category, so they will be listed on the summary page of the category.</p>
<pre><code class="lang-dart"><span class="hljs-comment">/// <span class="markdown">{@category My Cool Category}</span></span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyAwesomeFeature</span> </span>{}
</code></pre>
<p>You can assign multiple categories to the same item.</p>
<h4 id="heading-exclude">Exclude</h4>
<p>You can exclude any commented part from the documentation with the help of <code>@nodoc</code>.</p>
<pre><code class="lang-dart"><span class="hljs-comment">/// <span class="markdown">This feature must not appear in the documentation.</span></span>
<span class="hljs-comment">///
<span class="markdown">/// {@nodoc}</span></span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">HiddenFeature</span> </span>{}
</code></pre>
<h4 id="heading-image-animation-etc">Image, animation, etc.</h4>
<p>You have plenty of other options to improve your documentation, by adding images, video files, examples and other configurations.
You can find all available options on the <a target="_blank" href="https://pub.dev/packages/dartdoc">dartdoc</a> package site. </p>
<h2 id="heading-code-organization">Code organization</h2>
<p>While I was working on some packages, I noticed a pattern in Flutter widgets about how they structure the fields and methods inside a class. After that I tried to stick to this approach as well, so the structure of the code in my packages will be familiar to Flutter devs. </p>
<p>In the widgets in the Flutter SDK, there is a doc comment for each class, which describe the intention of the widgets.</p>
<p>As I saw, the first item in the widget classes is the constructors. Each of them has doc comments, usually with usage examples.</p>
<p>After all constructors and factories come the list of attributes, nicely commented. </p>
<p>Then comes the <code>build()</code> method.</p>
<p>I think you can see the pattern. Since you usually create the widgets and don't call their methods, it is more valuable to have the constructors at the beginning of the class, so you can find how to use them easier. </p>
<h2 id="heading-resources">Resources</h2>
<p>You can find the source code of the sample package at https://github.com/dtengeri/dart_doc_example</p>
]]></content:encoded></item><item><title><![CDATA[Synchronize the scrolling of your ListViews in Flutter]]></title><description><![CDATA[In my current project, I have a requirement where I display multiple ListViews below each other and these lists must scroll together. It does not matter which list is scrolled by the user, the other one should follow it. 
You can use ScrollController...]]></description><link>https://blog.dtengeri.com/synchronize-the-scrolling-of-your-listviews-in-flutter</link><guid isPermaLink="true">https://blog.dtengeri.com/synchronize-the-scrolling-of-your-listviews-in-flutter</guid><category><![CDATA[Flutter]]></category><dc:creator><![CDATA[Dávid Tengeri]]></dc:creator><pubDate>Fri, 04 Feb 2022 18:30:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/8IhVh0xU2Hc/upload/v1643877499248/GlxmITqSx.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In my current project, I have a requirement where I display multiple <code>ListView</code>s below each other and these lists must scroll together. It does not matter which list is scrolled by the user, the other one should follow it. </p>
<p>You can use <code>ScrollController</code> when you want to manage the scrolling of a <code>ListView</code>. 
My first idea was to use the same controller for each list, but that won't work, it will not update the list that is not scrolled.</p>
<p>Another option would be to implement the synchronization by listening to changes on the controllers. But there is a great package that could help us and it is made by Google developers, the <a target="_blank" href="https://pub.dev/packages/linked_scroll_controller">linked_scroll_controller</a>.</p>
<p>Let's see how we can use it.</p>
<p>Let's say we have 2 horizontal list views below each other.</p>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyHomePage</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatefulWidget</span> </span>{
  <span class="hljs-keyword">const</span> MyHomePage({Key? key}) : <span class="hljs-keyword">super</span>(key: key);

  <span class="hljs-meta">@override</span>
  _MyHomePageState createState() =&gt; _MyHomePageState();
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_MyHomePageState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span>&lt;<span class="hljs-title">MyHomePage</span>&gt; </span>{
  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Scaffold(
      appBar: AppBar(),
      body: SafeArea(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            <span class="hljs-keyword">const</span> Text(<span class="hljs-string">'Awesome list 1'</span>),
            SizedBox(
              height: <span class="hljs-number">150</span>,
              <span class="hljs-comment">// First list</span>
              child: ListView.builder(
                scrollDirection: Axis.horizontal,
                itemBuilder: (context, index) =&gt; _ListItem(
                  index: index,
                ),
              ),
            ),
            <span class="hljs-keyword">const</span> Divider(),
            <span class="hljs-keyword">const</span> Text(<span class="hljs-string">'Awesome list 2'</span>),
            SizedBox(
              height: <span class="hljs-number">150</span>,
              <span class="hljs-comment">// Second list</span>
              child: ListView.builder(
                scrollDirection: Axis.horizontal,
                itemBuilder: (context, index) =&gt; _ListItem(
                  index: index,
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_ListItem</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
  <span class="hljs-keyword">const</span> _ListItem({
    Key? key,
    <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.index,
  }) : <span class="hljs-keyword">super</span>(key: key);
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">int</span> index;

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Card(
      child: Center(
        child: Padding(
          padding: <span class="hljs-keyword">const</span> EdgeInsets.all(<span class="hljs-number">8.0</span>),
          child: Text(<span class="hljs-string">'List item <span class="hljs-subst">$index</span>'</span>),
        ),
      ),
    );
  }
}
</code></pre>
<p>This is how it looks like</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643998252814/dPj_02REs.gif?height=500" alt="scroll.gif" /></p>
<p>Let's synchronize our lists.</p>
<p>First we need to add <a target="_blank" href="https://pub.dev/packages/linked_scroll_controller">linked_scroll_controller</a> to our dependencies in <code>pubspec.yaml</code>:</p>
<pre><code class="lang-yml"><span class="hljs-attr">dependencies:</span>
  <span class="hljs-attr">linked_scroll_controller:</span> <span class="hljs-string">^0.2.0</span>
</code></pre>
<p>Then we need to create the scroll controller group, which will manage the synchronization for us.</p>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_MyHomePageState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span>&lt;<span class="hljs-title">MyHomePage</span>&gt; </span>{
  <span class="hljs-comment">// Create a new scoll controller group</span>
  <span class="hljs-keyword">late</span> <span class="hljs-keyword">final</span> LinkedScrollControllerGroup _controllerGroup =
      LinkedScrollControllerGroup();
  <span class="hljs-comment">// Create 2 new scroll controllers in the group, so they are connected.</span>
  <span class="hljs-keyword">late</span> <span class="hljs-keyword">final</span> ScrollController _controller1 = _controllerGroup.addAndGet();
  <span class="hljs-keyword">late</span> <span class="hljs-keyword">final</span> ScrollController _controller2 = _controllerGroup.addAndGet();

  <span class="hljs-comment">// ...</span>
}
</code></pre>
<p>Then we just need to add the 2 controllers to our <code>ListView</code>s.</p>
<pre><code class="lang-dart"><span class="hljs-comment">// ...</span>
Column(
  mainAxisAlignment: MainAxisAlignment.center,
  children: [
    <span class="hljs-keyword">const</span> Text(<span class="hljs-string">'Awesome list 1'</span>),
    SizedBox(
      height: <span class="hljs-number">150</span>,
      child: ListView.builder(
        <span class="hljs-comment">// Use the controller</span>
        controller: _controller1,
        scrollDirection: Axis.horizontal,
        itemBuilder: (context, index) =&gt; _ListItem(
          index: index,
        ),
      ),
    ),
    <span class="hljs-keyword">const</span> Divider(),
    <span class="hljs-keyword">const</span> Text(<span class="hljs-string">'Awesome list 2'</span>),
    SizedBox(
      height: <span class="hljs-number">150</span>,
      child: ListView.builder(
        <span class="hljs-comment">// Use the controller</span>
        controller: _controller2,
        scrollDirection: Axis.horizontal,
        itemBuilder: (context, index) =&gt; _ListItem(
          index: index,
        ),
      ),
    ),
  ],
),
<span class="hljs-comment">// ...</span>
</code></pre>
<p>This is how it works:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643999082533/wFQ142HQE.gif?height=500" alt="scroll_sync.gif" /></p>
<p>That's it!</p>
<p>You can find the source code at https://github.com/dtengeri/linked_list_views_example</p>
]]></content:encoded></item><item><title><![CDATA[Localize your Flutter packages]]></title><description><![CDATA[Flutter's basic blocks are reusable widgets. You create a widget and you can use it everywhere in your application.
But what happens when you have multiple applications and you want to use the same widgets? You can create a Flutter package, which can...]]></description><link>https://blog.dtengeri.com/localize-your-flutter-packages</link><guid isPermaLink="true">https://blog.dtengeri.com/localize-your-flutter-packages</guid><category><![CDATA[Flutter]]></category><dc:creator><![CDATA[Dávid Tengeri]]></dc:creator><pubDate>Fri, 21 Jan 2022 10:32:30 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/jBSTNenQxok/upload/v1642761003798/RJe--UQmj.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Flutter's basic blocks are reusable widgets. You create a widget and you can use it everywhere in your application.</p>
<p>But what happens when you have multiple applications and you want to use the same widgets? You can create a Flutter package, which can be shared amongst your project and with everyone else by publishing it on <a target="_blank" href="https://pub.dev">pub.dev</a>. </p>
<p>When you develop your app to support multiple locales, your Flutter package should support that as well. Let's see how it works with the built-in solutions using the flutter_localizations and <a target="_blank" href="https://pub.dev/packages/intl">intl</a> packages.</p>
<p>Basically, you can follow the <a target="_blank" href="https://docs.flutter.dev/development/accessibility-and-localization/internationalization">great tutorial</a> on <a target="_blank" href="https://flutter.dev/">flutter.dev</a> about the localization of an application. We need to do the same with minor differences.</p>
<h2 id="heading-prepare-the-package">Prepare the package</h2>
<p>First, let's create our Flutter package with</p>
<pre><code class="lang-bash">flutter create --template=package my_awesome_widgets
</code></pre>
<p>Let's add the required dependencies to <code>pubspec.yaml</code></p>
<pre><code class="lang-yaml"><span class="hljs-attr">dependencies:</span>
  <span class="hljs-attr">flutter:</span>
    <span class="hljs-attr">sdk:</span> <span class="hljs-string">flutter</span>
  <span class="hljs-attr">flutter_localizations:</span>
    <span class="hljs-attr">sdk:</span> <span class="hljs-string">flutter</span>
  <span class="hljs-attr">intl:</span> <span class="hljs-string">^0.17.0</span>
</code></pre>
<p>With the help of <code>flutter_localizations</code> and <code>intl</code> packages, Flutter can generate the localization classes based on your strings and translations. But we need to enable the code generation by extending the Flutter specific part in the <code>pubspec.yaml</code> file:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">flutter:</span>
  <span class="hljs-attr">generate:</span> <span class="hljs-literal">true</span>
</code></pre>
<h2 id="heading-configure-the-code-generation">Configure the code generation</h2>
<p>The next step is to create the configuration file for the localization. It's name is <code>l10n.yaml</code> and it should be at the root of your Flutter project.</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># This is the home of your localization files.</span>
<span class="hljs-attr">arb-dir:</span> <span class="hljs-string">lib/l10n</span>
<span class="hljs-comment"># The name of the file where you define your translatable strings</span>
<span class="hljs-attr">template-arb-file:</span> <span class="hljs-string">my_awesome_widgets_en.arb</span>
<span class="hljs-comment"># Your generated code will be placed to this directory</span>
<span class="hljs-attr">output-dir:</span> <span class="hljs-string">lib/l10n/generated</span>
<span class="hljs-comment"># The name of the generated class </span>
<span class="hljs-attr">output-class:</span> <span class="hljs-string">MyAwesomeWidgetsLocalizations</span>
<span class="hljs-comment"># The file name for the generated code</span>
<span class="hljs-attr">output-localization-file:</span> <span class="hljs-string">my_awesome_widgets_localizations.dart</span>
<span class="hljs-comment"># This tells Flutter where it should generate the code.</span>
<span class="hljs-attr">synthetic-package:</span> <span class="hljs-literal">false</span>
<span class="hljs-comment"># This tells the code generator to create nullable getters for our strings or not.</span>
<span class="hljs-attr">nullable-getter:</span> <span class="hljs-literal">false</span>
</code></pre>
<p>The option that we will need for our package is the <code>synthetic-package</code>, which controls whether the generated code should go under <code>.dart_tool</code> or to the output directory defined in <code>output-dir</code>. Since we want to share our package, we will need to export it, so it should be in a place that can be added as an <code>export</code> statement.</p>
<h2 id="heading-translatable-strings">Translatable strings</h2>
<p>Let's define our translatable strings. Based on the configuration above, the base translation template is <code>lib/l10n/my_awesome_widgets_en.arb</code>. It is a JSON like file using the <a target="_blank" href="https://icu.unicode.org/home">ICU format</a></p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"appTitle"</span>: <span class="hljs-string">"My Awesome Widgets"</span>,
  <span class="hljs-attr">"@appTitle"</span>: {
    <span class="hljs-attr">"description"</span>: <span class="hljs-string">"This is the title of the application"</span>
  }
}
</code></pre>
<p>All translatable string has key and optional meta information, which helps the translator and provide additional input for the code generator.
We have lots of options like parameters, pluralization. </p>
<h3 id="heading-using-a-placeholder">Using a placeholder</h3>
<pre><code class="lang-json">{
  <span class="hljs-attr">"welcome"</span>: <span class="hljs-string">"Welcome {name}"</span>,
  <span class="hljs-attr">"@welcome"</span>: {
    <span class="hljs-attr">"description"</span>: <span class="hljs-string">"The welcome messge on the home page"</span>,
    <span class="hljs-attr">"placeholders"</span>: {
      <span class="hljs-attr">"name"</span>: {
        <span class="hljs-attr">"type"</span>: <span class="hljs-string">"String"</span>
      }
    }
  }
}
</code></pre>
<h3 id="heading-pluralization">Pluralization</h3>
<pre><code class="lang-json">{
  <span class="hljs-attr">"friendRequests"</span>: <span class="hljs-string">"{count, plural, =0{You have no new friend request} =1{You have one reuquest} other{You have {count} requests}}"</span>,
  <span class="hljs-attr">"@friendRequests"</span>: {
    <span class="hljs-attr">"description"</span>: <span class="hljs-string">"The number of pending friends requests"</span>,
    <span class="hljs-attr">"placeholders"</span>: {
      <span class="hljs-attr">"count"</span>: {
        <span class="hljs-attr">"type"</span>: <span class="hljs-string">"int"</span>
      }
    }
  }
}
</code></pre>
<h3 id="heading-selection">Selection</h3>
<p>You can define different strings based on the value of the input parameter. This could be useful for example for texts that are different for the genders in your language.</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"sendMessage"</span>: <span class="hljs-string">"{sex, select, male{Send him a message} female{Send her a message} other{Send a message}}"</span>,
  <span class="hljs-attr">"@sendMessage"</span>: {
      <span class="hljs-attr">"description"</span>: <span class="hljs-string">"Message on the send button"</span>,
      <span class="hljs-attr">"placeholders"</span>: {
          <span class="hljs-attr">"sex"</span>: {
            <span class="hljs-attr">"type"</span>: <span class="hljs-string">"String"</span>
          }
      }
  }
}
</code></pre>
<p>But the <code>select</code> is not restricted to only gender-based strings, you can use it for any input that requires a different string representation based on its content.</p>
<h3 id="heading-number-and-date-formatting">Number and date formatting</h3>
<p>You can specify the format for numbers or dates in the <code>arb</code> file, so it will use the locale-specific version when it is displayed.</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"discountPrice"</span>: <span class="hljs-string">"Discount: {price}"</span>,
  <span class="hljs-attr">"@discountPrice"</span>: {
    <span class="hljs-attr">"placeholders"</span>: {
      <span class="hljs-attr">"price"</span>: {
        <span class="hljs-attr">"type"</span>: <span class="hljs-string">"double"</span>,
        <span class="hljs-attr">"format"</span>: <span class="hljs-string">"currency"</span>
      }
    }
  },
  <span class="hljs-attr">"shippingDate"</span>: <span class="hljs-string">"Estimated shipping at {date}"</span>,
  <span class="hljs-attr">"@shippingDate"</span>: {
    <span class="hljs-attr">"placeholders"</span>: {
      <span class="hljs-attr">"date"</span>: {
        <span class="hljs-attr">"type"</span>: <span class="hljs-string">"DateTime"</span>,
        <span class="hljs-attr">"format"</span>: <span class="hljs-string">"yMMMd"</span>
      }
    }
  }
}
</code></pre>
<h2 id="heading-usage">Usage</h2>
<p>So far we have the translation strings, we can generate the classes by running:</p>
<pre><code class="lang-bash">flutter gen-l10n
</code></pre>
<p>Which will output the following</p>
<pre><code class="lang-text">Because l10n.yaml exists, the options defined there will be used instead.
To use the command line arguments, delete the l10n.yaml file in the Flutter project.
</code></pre>
<p>This will generate the <code>MyAwesomeWidgetsLocalizations</code> class under <code>lib/l10n/generated</code>.
Let's export the class by adding the following line to <code>lib/my_awesome_widgets.dart</code></p>
<pre><code class="lang-dart"><span class="hljs-keyword">export</span> <span class="hljs-string">'l10n/generated/my_awesome_widgets_localizations.dart'</span>;
</code></pre>
<p>Now we can use it in one of our cool widget</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:my_awesome_widgets/my_awesome_widgets.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CoolTexts</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
  <span class="hljs-keyword">const</span> CoolTexts({Key? key}) : <span class="hljs-keyword">super</span>(key: key);

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Column(
      children: [
        Text(MyAwesomeWidgetsLocalizations.of(context).appTitle),
        Text(MyAwesomeWidgetsLocalizations.of(context).welcome(<span class="hljs-string">'John'</span>)),
        Text(MyAwesomeWidgetsLocalizations.of(context).friendRequests(<span class="hljs-number">0</span>)),
        Text(MyAwesomeWidgetsLocalizations.of(context).friendRequests(<span class="hljs-number">1</span>)),
        Text(MyAwesomeWidgetsLocalizations.of(context).friendRequests(<span class="hljs-number">2</span>)),
        Text(MyAwesomeWidgetsLocalizations.of(context).sendMessage(<span class="hljs-string">'male'</span>)),
        Text(MyAwesomeWidgetsLocalizations.of(context).sendMessage(<span class="hljs-string">'female'</span>)),
        Text(
          MyAwesomeWidgetsLocalizations.of(context).sendMessage(<span class="hljs-string">'unspecfied'</span>),
        ),
        Text(
          MyAwesomeWidgetsLocalizations.of(context).discountPrice(<span class="hljs-number">20</span>),
        ),
        Text(
          MyAwesomeWidgetsLocalizations.of(context)
              .shippingDate(<span class="hljs-built_in">DateTime</span>.now()),
        ),
      ],
    );
  }
}
</code></pre>
<h2 id="heading-add-another-locale">Add another locale</h2>
<p>If you want to add another locale to your package, you have to create a new <code>arb</code> file next to the <code>my_awesome_widgets_en.arb</code>.  For example a Hungarian translation will look like</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"appTitle"</span>: <span class="hljs-string">"My Awesome Widgets"</span>,
  <span class="hljs-attr">"welcome"</span>: <span class="hljs-string">"Üdvözöllek {name}"</span>,
  <span class="hljs-attr">"friendRequests"</span>: <span class="hljs-string">"{count, plural, =0{Nincs új követési kérésed} =1{Egy új követési kérésed van} other{{count} követési kérésed van}}"</span>,
  <span class="hljs-attr">"sendMessage"</span>: <span class="hljs-string">"{sex, select, male{Küldj neki egy üzenetet} female{Küldj neki egy üzenetet} other{Küldj neki egy üzenetet}}"</span>,
  <span class="hljs-attr">"discountPrice"</span>: <span class="hljs-string">"Akciós ár: {price}"</span>,
  <span class="hljs-attr">"shippingDate"</span>: <span class="hljs-string">"Tervezett szállítási dátum {date}"</span>
}
</code></pre>
<p>It is fine to have only the strings in your other locales, you don't have to repeat the meta-information.</p>
<p>Running <code>flutter gen-l10n</code> will create a class for the Hungarian translation next to the English version.</p>
<h2 id="heading-use-the-package-in-a-flutter-application">Use the package in a Flutter application</h2>
<p>Once we have our package with the shareable widgets, we can use it as a dependency in our Flutter applications. The only thing we have to do is to add the localization delegate to our application, so the localization SDK will know how to create the `` for each locales.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter_localizations/flutter_localizations.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:my_awesome_widgets/my_awesome_widgets.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyAwesomeApp</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
  <span class="hljs-keyword">const</span> MyAwesomeApp({Key? key}) : <span class="hljs-keyword">super</span>(key: key);

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> MaterialApp(
      localizationsDelegates: <span class="hljs-keyword">const</span> [
        <span class="hljs-comment">// Add the localization delegate of your package</span>
        MyAwesomeWidgetsLocalizations.delegate,
        GlobalMaterialLocalizations.delegate,
        GlobalCupertinoLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
      ],
      supportedLocales: <span class="hljs-keyword">const</span> [
        Locale(<span class="hljs-string">'en'</span>),
        Locale(<span class="hljs-string">'hu'</span>),
      ],
      home: Scaffold(
        appBar: AppBar(),
        body: <span class="hljs-keyword">const</span> Center(
          child: CoolTexts(),
        ),
      ),
    );
  }
}
</code></pre>
<div class="hn-table">
<table>
<thead>
<tr>
<td>English</td><td>Hungarian</td></tr>
</thead>
<tbody>
<tr>
<td><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642758922006/3ZRyMISKw.png?height=500" alt="Simulator Screen Shot - iPhone 12 Pro - 2022-01-21 at 10.50.20.png" /></td><td><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642758934935/ZpA5JgC0u.png?height=500" alt="Simulator Screen Shot - iPhone 12 Pro - 2022-01-21 at 10.54.12.png" /></td></tr>
</tbody>
</table>
</div><h2 id="heading-testing-the-package">Testing the package</h2>
<p>We want to share a reliable package that is not sensitive to changes we make by adding new features. So we add tests to our package. In order to test the widgets that use locales, you have to provide the same environment for them as they would be used in a real app. This means you have to provide a <code>MaterialApp</code> and the delegates as well.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter_test/flutter_test.dart'</span>;

<span class="hljs-keyword">import</span> <span class="hljs-string">'package:my_awesome_widgets/my_awesome_widgets.dart'</span>;

<span class="hljs-keyword">void</span> main() {
  group(<span class="hljs-string">'CoolTexts'</span>, () {
    testWidgets(<span class="hljs-string">'displays the texts'</span>, (tester) <span class="hljs-keyword">async</span> {
      <span class="hljs-keyword">await</span> tester.pumpWidget(
        <span class="hljs-keyword">const</span> MaterialApp(
          <span class="hljs-comment">// Add the delegates defined by our package</span>
          localizationsDelegates:
              MyAwesomeWidgetsLocalizations.localizationsDelegates,
          home: Scaffold(
            <span class="hljs-comment">// Add our widget that we want to test</span>
            body: CoolTexts(),
          ),
        ),
      );
      <span class="hljs-comment">// Check it works correctly</span>
      expect(find.text(<span class="hljs-string">'Welcome John'</span>), findsOneWidget);
    });
  });
}
</code></pre>
<h2 id="heading-summary">Summary</h2>
<p>This article became longer than I expected. My goal was to show how to add locales to your Flutter package and how can you use it in your applications. Feel free to leave your thoughts in the comment section.</p>
<h2 id="heading-resources">Resources</h2>
<ul>
<li><a target="_blank" href="https://github.com/dtengeri/flutter_package_localization_sample">Github repository of the sample package and application</a></li>
<li><a target="_blank" href="https://docs.google.com/document/d/10e0saTfAv32OZLRmONy866vnaw0I2jwL8zukykpgWBc/edit#heading=h.4jrxhcva98zs">i18n user guide</a></li>
<li><a target="_blank" href="https://phrase.com/blog/posts/flutter-localization">A Guide to Flutter Localization</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Migrating to Hashnode with the focus on Flutter]]></title><description><![CDATA[I decided to move my blogging to Hashnode, and start from scratch focusing mostly on Flutter.]]></description><link>https://blog.dtengeri.com/migrating-to-hashnode-with-the-focus-on-flutter</link><guid isPermaLink="true">https://blog.dtengeri.com/migrating-to-hashnode-with-the-focus-on-flutter</guid><dc:creator><![CDATA[Dávid Tengeri]]></dc:creator><pubDate>Thu, 20 Jan 2022 09:42:00 GMT</pubDate><content:encoded><![CDATA[<p>I decided to move my blogging to Hashnode, and start from scratch focusing mostly on Flutter. </p>
]]></content:encoded></item></channel></rss>