<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mathieu Kerjouan</title>
    <description>The latest articles on DEV Community by Mathieu Kerjouan (@niamtokik).</description>
    <link>https://dev.to/niamtokik</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1302415%2Ff02d4ee9-14d9-45ec-966a-6bb53e0e3240.jpeg</url>
      <title>DEV Community: Mathieu Kerjouan</title>
      <link>https://dev.to/niamtokik</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/niamtokik"/>
    <language>en</language>
    <item>
      <title>Protocol Buffers in Elixir</title>
      <dc:creator>Mathieu Kerjouan</dc:creator>
      <pubDate>Fri, 26 Jun 2026 10:00:00 +0000</pubDate>
      <link>https://dev.to/niamtokik/protocol-buffers-in-elixir-4daa</link>
      <guid>https://dev.to/niamtokik/protocol-buffers-in-elixir-4daa</guid>
      <description>&lt;p&gt;Serializing data is an important topic. The main goal is to convert some kind of data in another format compatible for some tasks, usually, to send them over the network. JSON is probably one of the most used serializer, but because of its simplicity, can be slow and can lead to other issues. Indeed, JSON is using a text-like format, and disallow usage of binary in any form - except if they are converted in base64 string for example. Furthermore, JSON does not optimize its payload and does not care by default of the data containing in it.&lt;/p&gt;

&lt;p&gt;Today, we will talk a bit of Protocol Buffer or protobuf, a binary serializer format created by Google for their internal use. Instead of JSON, protobuf payload is using a binary format and its data format is specified. One can't add a new field in the payload without having created the specification for it.&lt;/p&gt;

&lt;h1&gt;
  
  
  Requirements
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://manpages.debian.org/trixie/protobuf-compiler/protoc.1.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;protoc&lt;/code&gt;&lt;/a&gt; is required. One can install it from &lt;a href="https://github.com/protocolbuffers/protobuf/releases/tag/v35.1" rel="noopener noreferrer"&gt;the latest release on Github&lt;/a&gt;, or use one's package manager distribution. &lt;code&gt;protoc&lt;/code&gt; is used to compile protocol buffer data structure definition in a specific language. In our case, we will need to install an external plugins to help &lt;code&gt;protoc&lt;/code&gt; to generate Elixir modules.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;mix escript.install hex protobuf 0.16.0
&lt;span class="c"&gt;...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Bootstrapping
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;mix new pb
&lt;span class="go"&gt;* creating README.md
* creating .formatter.exs
* creating .gitignore
* creating mix.exs
* creating lib
* creating lib/pb.ex
* creating test
* creating test/test_helper.exs
* creating test/pb_test.exs

Your Mix project was created successfully.
You can use "mix" to compile it, test it, and more:

    cd pb
    mix test

Run "mix help" for more commands

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;pb
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; mix.exs &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
&lt;/span&gt;&lt;span class="go"&gt;defmodule Pb.MixProject do
  use Mix.Project

  def project do
    [
      app: :pb,
      version: "0.1.0",
&lt;/span&gt;&lt;span class="gp"&gt;      elixir: "~&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;1.19&lt;span class="s2"&gt;",
&lt;/span&gt;&lt;span class="go"&gt;      start_permanent: Mix.env() == :prod,
      deps: deps()
    ]
  end

  def application, do: [extra_applications: [:logger]]

&lt;/span&gt;&lt;span class="gp"&gt;  defp deps, do: [{:protobuf, "~&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;0.17.0&lt;span class="s2"&gt;"}]
&lt;/span&gt;&lt;span class="go"&gt;end

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;mix deps.get
&lt;span class="go"&gt;Resolving Hex dependencies...
Resolution completed in 0.024s
New:
  protobuf 0.17.0
* Getting protobuf (Hex package)
EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;mix escript.install hex protobuf 0.16.0
&lt;span class="c"&gt;...
&lt;/span&gt;&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;:&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/.mix/escripts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Payload Specification
&lt;/h1&gt;

&lt;p&gt;Let reuse again the rock-paper-scissors idea to design a new protobuf data-structure. The file will be stored in &lt;code&gt;./proto/v1/rps.proto&lt;/code&gt;, but it could be perhaps better to store it in &lt;code&gt;./priv/proto/v1/rps.proto&lt;/code&gt;, not sure yet. Actually, we don't really care for now, because this protocol buffer file will be compiled into Elixir module. The first step is to define the syntax we are using, in our case, &lt;a href="https://protobuf.dev/programming-guides/proto3/" rel="noopener noreferrer"&gt;&lt;code&gt;proto3&lt;/code&gt;&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight protobuf"&gt;&lt;code&gt;&lt;span class="na"&gt;syntax&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"proto3"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, we use the &lt;a href="https://protobuf.dev/programming-guides/proto3/#packages" rel="noopener noreferrer"&gt;&lt;code&gt;package&lt;/code&gt;&lt;/a&gt; keyword to create a new namespace. In our case, it will create an Elixir module prefixed by &lt;code&gt;Rps&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight protobuf"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;rps&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have 3 shapes to create, an &lt;a href="https://protobuf.dev/programming-guides/proto3/#enum" rel="noopener noreferrer"&gt;&lt;code&gt;enum&lt;/code&gt;&lt;/a&gt; should do the job. By convention, the enumerated values should be &lt;a href="https://protobuf.dev/programming-guides/proto3/#prefixing-values" rel="noopener noreferrer"&gt;prefixed&lt;/a&gt; to avoid naming conflict. When we will create a new Shape enum, the default value used will be &lt;code&gt;SHAPE_UNKNOWN&lt;/code&gt;, because it's the &lt;a href="//protobuf.dev/programming-guides/proto3/#enum-default"&gt;first one defined&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight protobuf"&gt;&lt;code&gt;&lt;span class="kd"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;Shape&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;SHAPE_UNKNOWN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="na"&gt;SHAPE_ROCK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="na"&gt;SHAPE_PAPER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="na"&gt;SHAPE_SCISSORS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our second main object we will use is the result of the game. We have 3 values to define, &lt;code&gt;win&lt;/code&gt;, &lt;code&gt;draw&lt;/code&gt; and &lt;code&gt;loss&lt;/code&gt;; they can be defined as another &lt;code&gt;enum&lt;/code&gt; following the same previously shown conventions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight protobuf"&gt;&lt;code&gt;&lt;span class="kd"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;RESULT_UNKNOWN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="na"&gt;RESULT_WIN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="na"&gt;RESULT_DRAW&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="na"&gt;RESULT_LOSS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Raw &lt;code&gt;enum&lt;/code&gt;s are kinda useless without using them inside messages. The first one we can create is the data-structure sent by a player to a server, it will only contain - for now - a &lt;code&gt;Shape&lt;/code&gt;. If you are not familiar with the protobuf syntax, like me when I started this publication, the type of the data is on far-left followed by the name of the field. The assignment is not an integer value, but the field number used by the &lt;a href="https://protobuf.dev/programming-guides/encoding/" rel="noopener noreferrer"&gt;protocol buffer wire format&lt;/a&gt;. In this case, the &lt;code&gt;shape&lt;/code&gt; field will have the number &lt;code&gt;1&lt;/code&gt; assigned.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight protobuf"&gt;&lt;code&gt;&lt;span class="kd"&gt;message&lt;/span&gt; &lt;span class="nc"&gt;Play&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;Shape&lt;/span&gt; &lt;span class="na"&gt;shape&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let try to define another data-structure for a player. A player will have an &lt;code&gt;id&lt;/code&gt; as &lt;code&gt;integer&lt;/code&gt;, and an optional name as &lt;code&gt;string&lt;/code&gt;. Protobuf is offering a &lt;a href="https://protobuf.dev/programming-guides/encoding/" rel="noopener noreferrer"&gt;wide range of scalar types&lt;/a&gt;, the best one(s) will mostly depend of our needs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight protobuf"&gt;&lt;code&gt;&lt;span class="kd"&gt;message&lt;/span&gt; &lt;span class="nc"&gt;Player&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;int64&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;optional&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a client is asking for an opponent, the servers should return a list of available players. Protocol buffer does not define something like a list of an array, instead, it uses the &lt;a href="https://protobuf.dev/programming-guides/proto3/#other" rel="noopener noreferrer"&gt;&lt;code&gt;repeated&lt;/code&gt; keyword&lt;/a&gt; to create a pseudo-list of objects while encoding or decoding the payload.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight protobuf"&gt;&lt;code&gt;&lt;span class="kd"&gt;message&lt;/span&gt; &lt;span class="nc"&gt;Players&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;repeated&lt;/span&gt; &lt;span class="n"&gt;Player&lt;/span&gt; &lt;span class="na"&gt;players&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The client select one player from the previous list and start to play with an other player as opponent. The data-structure will then contain a &lt;code&gt;player&lt;/code&gt; object and a &lt;code&gt;shape&lt;/code&gt; object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight protobuf"&gt;&lt;code&gt;&lt;span class="kd"&gt;message&lt;/span&gt; &lt;span class="nc"&gt;Play&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;Player&lt;/span&gt; &lt;span class="na"&gt;player&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;Shape&lt;/span&gt; &lt;span class="na"&gt;shape&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, the server is returning the result, containing an unique &lt;code&gt;id&lt;/code&gt; as &lt;code&gt;string&lt;/code&gt;, player's informations and the final result for the player. I don't think this is the best data-structure to return because the &lt;code&gt;result&lt;/code&gt; can lead to some confusion (who's the winner or loser?) but it will do the job for this example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight protobuf"&gt;&lt;code&gt;&lt;span class="kd"&gt;message&lt;/span&gt; &lt;span class="nc"&gt;PlayResult&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;Player&lt;/span&gt; &lt;span class="na"&gt;player&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;Player&lt;/span&gt; &lt;span class="na"&gt;opponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;Result&lt;/span&gt; &lt;span class="na"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If everything looks good, we can compile this protocol buffer file into an Elixir module with &lt;code&gt;protoc&lt;/code&gt;. It will create the &lt;code&gt;./lib/proto/v1/rps.pb.ex&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;protoc &lt;span class="nt"&gt;--elixir_out&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;./lib proto/v1/rps.proto
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let invoke &lt;code&gt;iex&lt;/code&gt; to play with these structures. To encode a &lt;code&gt;struct&lt;/code&gt; in protobuf, we can use &lt;a href="https://protobuf.hexdocs.pm/Protobuf.html#encode/1" rel="noopener noreferrer"&gt;&lt;code&gt;Protobuf.encode/1&lt;/code&gt;&lt;/a&gt;, it will return a &lt;code&gt;binary&lt;/code&gt;. Here few examples from the message specifications defined above.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;iex &lt;span class="nt"&gt;-S&lt;/span&gt; mix
&lt;span class="gp"&gt;iex(1)&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;encoded_player &lt;span class="o"&gt;=&lt;/span&gt; Protobuf.encode&lt;span class="o"&gt;(&lt;/span&gt;
&lt;span class="go"&gt;  %Rps.Player{
    id: 1,
    name: "test" 
  })
&lt;/span&gt;&lt;span class="gp"&gt;&amp;lt;&amp;lt;8, 1, 18, 4, 116, 101, 115, 116&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;iex(2)&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Protobuf.encode&lt;span class="o"&gt;(&lt;/span&gt;
&lt;span class="go"&gt;  %Rps.Player{
    id: 2 
  })
&lt;/span&gt;&lt;span class="gp"&gt;&amp;lt;&amp;lt;8, 2&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;iex(3)&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Protobuf.encode&lt;span class="o"&gt;(&lt;/span&gt;
&lt;span class="go"&gt;  %Rps.Players{
    players: []
  })
""

&lt;/span&gt;&lt;span class="gp"&gt;iex(4)&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;encoded_players &lt;span class="o"&gt;=&lt;/span&gt; Protobuf.encode&lt;span class="o"&gt;(&lt;/span&gt;
&lt;span class="go"&gt;  %Rps.Players{
    players: [
      %Rps.Player{id: 1},
      %Rps.Player{id: 2, name: "test"}
    ]
  })
&lt;/span&gt;&lt;span class="gp"&gt;&amp;lt;&amp;lt;10, 2, 8, 1, 10, 8, 8, 2, 18, 4, 116, 101, 115, 116&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;iex(5)&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;encoded_play &lt;span class="o"&gt;=&lt;/span&gt; Protobuf.encode&lt;span class="o"&gt;(&lt;/span&gt;
&lt;span class="go"&gt;  %Rps.Play{
    player: %Rps.Player{ id: 1},
    shape: 1 
  })
&lt;/span&gt;&lt;span class="gp"&gt;&amp;lt;&amp;lt;10, 2, 8, 1, 16, 1&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;iex(6)&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;encoded_play &lt;span class="o"&gt;=&lt;/span&gt; Protobuf.encode&lt;span class="o"&gt;(&lt;/span&gt;
&lt;span class="go"&gt;  %Rps.Play{
    player: %Rps.Player{ id: 1},
    shape: :SHAPE_ROCK
  })
&lt;/span&gt;&lt;span class="gp"&gt;&amp;lt;&amp;lt;10, 2, 8, 1, 16, 1&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;iex(7)&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;encoded_playresult &lt;span class="o"&gt;=&lt;/span&gt; Protobuf.encode&lt;span class="o"&gt;(&lt;/span&gt;
&lt;span class="go"&gt;  %Rps.PlayResult{
    id: "random_string",
    result: :RESULT_WIN,
    player: %Rps.Player{id: 1},
    opponent: %Rps.Player{id: 2}
  })
&amp;lt;&amp;lt;10, 13, 114, 97, 110, 100, 111, 109,
  95, 115, 116, 114, 105, 110, 103, 18,
&lt;/span&gt;&lt;span class="gp"&gt;  2, 8, 1, 26, 2, 8, 2, 32, 1&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When it comes to decode, one can use &lt;a href="https://protobuf.hexdocs.pm/Protobuf.html#decode/3" rel="noopener noreferrer"&gt;&lt;code&gt;Protobuf.decode/2&lt;/code&gt;&lt;/a&gt; or &lt;a href="https://protobuf.hexdocs.pm/Protobuf.html#decode/3" rel="noopener noreferrer"&gt;&lt;code&gt;Protobuf.decode/3&lt;/code&gt;&lt;/a&gt;. The first argument is the encoded binary and the second is the Elixir module required to decode the payload.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;iex(8)&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Protobuf.decode&lt;span class="o"&gt;(&lt;/span&gt;encoded_player, Rps.Player&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="go"&gt;%Rps.Player{
  id: 1,
  name: "test",
  __unknown_fields__: [],
  __protobuf__: true
}

&lt;/span&gt;&lt;span class="gp"&gt;iex(9)&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Protobuf.decode&lt;span class="o"&gt;(&lt;/span&gt;encoded_players, Rps.Players&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="go"&gt;%Rps.Players{
  players: [
    %Rps.Player{id: 1, name: nil, __unknown_fields__: [], __protobuf__: true},
    %Rps.Player{id: 2, name: "test", __unknown_fields__: [], __protobuf__: true}
  ],
  __unknown_fields__: [],
  __protobuf__: true
}

&lt;/span&gt;&lt;span class="gp"&gt;iex(10)&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Protobuf.decode&lt;span class="o"&gt;(&lt;/span&gt;encoded_playresult, Rps.PlayResult&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="go"&gt;%Rps.PlayResult{
  id: "random_string",
  player: %Rps.Player{
    id: 1,
    name: nil,
    __unknown_fields__: [],
    __protobuf__: true
  },
  opponent: %Rps.Player{
    id: 2,
    name: nil,
    __unknown_fields__: [],
    __protobuf__: true
  },
  result: :RESULT_WIN,
  __unknown_fields__: [],
  __protobuf__: true
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, Here the generated Elixir module by &lt;code&gt;protoc&lt;/code&gt; you can find in &lt;code&gt;./lib/proto/v1/rps.pb.ex&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Rps&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Shape&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nv"&gt;@moduledoc&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;

  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Protobuf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;enum:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;full_name:&lt;/span&gt; &lt;span class="s2"&gt;"rps.Shape"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;protoc_gen_elixir_version:&lt;/span&gt; &lt;span class="s2"&gt;"0.16.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;syntax:&lt;/span&gt; &lt;span class="ss"&gt;:proto3&lt;/span&gt;

  &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:SHAPE_UNKONWN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:SHAPE_ROCK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:SHAPE_PAPER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:SHAPE_SCISSORS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Rps&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Result&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nv"&gt;@moduledoc&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;

  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Protobuf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;enum:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;full_name:&lt;/span&gt; &lt;span class="s2"&gt;"rps.Result"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;protoc_gen_elixir_version:&lt;/span&gt; &lt;span class="s2"&gt;"0.16.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;syntax:&lt;/span&gt; &lt;span class="ss"&gt;:proto3&lt;/span&gt;

  &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:RESULT_UNKONWN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:RESULT_WIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:RESULT_DRAW&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:RESULT_LOSS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Rps&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Player&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nv"&gt;@moduledoc&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;

  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Protobuf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;full_name:&lt;/span&gt; &lt;span class="s2"&gt;"rps.Player"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;protoc_gen_elixir_version:&lt;/span&gt; &lt;span class="s2"&gt;"0.16.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;syntax:&lt;/span&gt; &lt;span class="ss"&gt;:proto3&lt;/span&gt;

  &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type:&lt;/span&gt; &lt;span class="ss"&gt;:int64&lt;/span&gt;
  &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;proto3_optional:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type:&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Rps&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Play&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nv"&gt;@moduledoc&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;

  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Protobuf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;full_name:&lt;/span&gt; &lt;span class="s2"&gt;"rps.Play"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;protoc_gen_elixir_version:&lt;/span&gt; &lt;span class="s2"&gt;"0.16.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;syntax:&lt;/span&gt; &lt;span class="ss"&gt;:proto3&lt;/span&gt;

  &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:player&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type:&lt;/span&gt; &lt;span class="no"&gt;Rps&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Player&lt;/span&gt;
  &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:shape&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type:&lt;/span&gt; &lt;span class="no"&gt;Rps&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Shape&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;enum:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Rps&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;PlayResult&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nv"&gt;@moduledoc&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;

  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Protobuf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;full_name:&lt;/span&gt; &lt;span class="s2"&gt;"rps.PlayResult"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;protoc_gen_elixir_version:&lt;/span&gt; &lt;span class="s2"&gt;"0.16.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;syntax:&lt;/span&gt; &lt;span class="ss"&gt;:proto3&lt;/span&gt;

  &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type:&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;
  &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:player&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type:&lt;/span&gt; &lt;span class="no"&gt;Rps&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Player&lt;/span&gt;
  &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:opponent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type:&lt;/span&gt; &lt;span class="no"&gt;Rps&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Player&lt;/span&gt;
  &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type:&lt;/span&gt; &lt;span class="no"&gt;Rps&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;enum:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Rps&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Players&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nv"&gt;@moduledoc&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;

  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Protobuf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;full_name:&lt;/span&gt; &lt;span class="s2"&gt;"rps.Players"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;protoc_gen_elixir_version:&lt;/span&gt; &lt;span class="s2"&gt;"0.16.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;syntax:&lt;/span&gt; &lt;span class="ss"&gt;:proto3&lt;/span&gt;

  &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:players&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;repeated:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type:&lt;/span&gt; &lt;span class="no"&gt;Rps&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Player&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Layered Design
&lt;/h1&gt;

&lt;p&gt;While doing all those tests, I was thinking if it was a "good practices" or even possible to use protobuf with layers, a bit like a network packet. For example, a first payload is containing a protobuf header with the last part encoded as bytes. This header could then be used to help the decoder to know what kind of data is stored.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight protobuf"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;layer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;message&lt;/span&gt; &lt;span class="nc"&gt;header&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// checksum = "sha256-checksum"&lt;/span&gt;
  &lt;span class="k"&gt;required&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="na"&gt;checksum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// type = "application/protobuf/players"&lt;/span&gt;
  &lt;span class="k"&gt;required&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// payload = protobuf binary for players object&lt;/span&gt;
  &lt;span class="k"&gt;required&lt;/span&gt; &lt;span class="kt"&gt;bytes&lt;/span&gt; &lt;span class="na"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let compile it...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;protoc &lt;span class="nt"&gt;--elixir_out&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;./lib/ proto/v1/layer.proto
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;... And start a new shell to play with that.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;iex(1)&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;encoded_players &lt;span class="o"&gt;=&lt;/span&gt; Protobuf.encode&lt;span class="o"&gt;(&lt;/span&gt;%Rps.Players&lt;span class="o"&gt;{&lt;/span&gt; players: &lt;span class="o"&gt;[&lt;/span&gt;%Rps.Player&lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;: 1&lt;span class="o"&gt;}]&lt;/span&gt; &lt;span class="o"&gt;})&lt;/span&gt;
&lt;span class="gp"&gt;&amp;lt;&amp;lt;10, 2, 8, 1&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;iex(2)&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;encoded_headers &lt;span class="o"&gt;=&lt;/span&gt;  Protobuf.encode&lt;span class="o"&gt;(&lt;/span&gt;
&lt;span class="go"&gt;  %Layer.Header{
    checksum: Base.encode64(
      :crypto.hash(:sha256, encoded_players)
    ),
    type: "application/protobuf/players",
    payload: encoded_players
  })
&amp;lt;&amp;lt;10, 44, 87, 90, 83, 69, 79, 81, 90, 102, 75, 87, 71, 101, 57, 66, 75, 65, 121,
  55, 107, 121, 118, 108, 76, 70, 98, 90, 110, 70, 108, 109, 116, 108, 52, 66,
  69, 83, 79, 102, 67, 89, 117, 43, 56, 61, 18, 28, 97, 112, 112, 108, 105, 99,
  97, 116, 105, 111, 110, 47, 112, 114, 111, 116, 111, 98, 117, 102, 47, 112,
&lt;/span&gt;&lt;span class="gp"&gt;  108, 97, 121, 101, 114, 115, 26, 4, 10, 2, 8, 1&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this kind of structure, we can then encapsulate different kind of data without parsing them. In fact, we can also put more metadata in the headers to help the application to know what kind of format is stored in the payload. Even more, one could also add some encryption feature there. Unfortunately, I don't know if it's a good practice, because it can probably make things a bit confusing, but also more flexible. Here the code generated in &lt;code&gt;lib/proto/v1/layer.pb.ex&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Layer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Header&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nv"&gt;@moduledoc&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;

  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Protobuf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;full_name:&lt;/span&gt; &lt;span class="s2"&gt;"layer.header"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;protoc_gen_elixir_version:&lt;/span&gt; &lt;span class="s2"&gt;"0.16.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;syntax:&lt;/span&gt; &lt;span class="ss"&gt;:proto2&lt;/span&gt;

  &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:checksum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;required:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type:&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;
  &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;required:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type:&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;
  &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;required:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type:&lt;/span&gt; &lt;span class="ss"&gt;:bytes&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;This article was just a warmup, a kind of sandbox to see the capabilities of Protocol Buffer. At the same time, I was also looking to the &lt;a href="https://flatbuffers.dev/" rel="noopener noreferrer"&gt;FlatBuffers&lt;/a&gt; protocol trying to identify the difference between both format. I would prefer to use &lt;a href="https://cbor.io/" rel="noopener noreferrer"&gt;CBOR&lt;/a&gt; for my projects, but I would also like to avoid using too much dependencies from unknown repositories. Furthermore, &lt;code&gt;protobuf&lt;/code&gt; Dart package is already used by Dart and Flutter (for metrics IIRC), it can also be used for messaging as well.&lt;/p&gt;

&lt;p&gt;The previous code is containing a lot of design flaws, they can be "easily" fixed by redesigning a bit the data-structures and adding few of them. The procedure should look like that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;A connected client (with an active session) to the server list the available &lt;code&gt;Players&lt;/code&gt;;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The client ask the server to create a new &lt;code&gt;Arena&lt;/code&gt; for with one available &lt;code&gt;Player&lt;/code&gt;;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The server returns an &lt;code&gt;Arena&lt;/code&gt; containing an unique identifier, the name of the two players and arena's state (&lt;code&gt;waiting&lt;/code&gt;, &lt;code&gt;declined&lt;/code&gt;, &lt;code&gt;ready&lt;/code&gt;, &lt;code&gt;active&lt;/code&gt;, &lt;code&gt;done&lt;/code&gt;). A &lt;code&gt;waiting&lt;/code&gt; state is when the arena is waiting for the 2 players to become ready. A &lt;code&gt;ready&lt;/code&gt; state is when both player are &lt;code&gt;ready&lt;/code&gt; and can start playing together. An &lt;code&gt;active&lt;/code&gt; state is when both players are playing. A &lt;code&gt;done&lt;/code&gt; state is when the game is over, and one player won. A &lt;code&gt;declined&lt;/code&gt; state is when the opponent declined the invitation;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When the server is in &lt;code&gt;ready&lt;/code&gt; or &lt;code&gt;active&lt;/code&gt; state, both players can send their &lt;code&gt;Shape&lt;/code&gt;. The server returns a &lt;code&gt;Result&lt;/code&gt;. If one win, the Arena's state switch to &lt;code&gt;done&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Designing even a small game is not easy, but it can lead to really interesting result. We have a concrete example of distributed complexity there, with one user sending some data, waiting for result, and one actor (the server) keeping a private state to deal with both users.&lt;/p&gt;

&lt;p&gt;Anyway, it was the first article on Protobuf and probably not the last one. I know you would like to dig a bit more on this topic, as usual, a list of resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://hex.pm/packages/protobuf" rel="noopener noreferrer"&gt;&lt;code&gt;protobuf&lt;/code&gt; Elixir module&lt;/a&gt; on hex.pm;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/elixir-protobuf/protobuf" rel="noopener noreferrer"&gt;&lt;code&gt;protobuf&lt;/code&gt; Elixir source code&lt;/a&gt; on Github;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/elixir-protobuf/protobuf/tree/main/test" rel="noopener noreferrer"&gt;&lt;code&gt;protobuf&lt;/code&gt; Elixir module test source code&lt;/a&gt;;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://protobuf.dev/best-practices/dos-donts/" rel="noopener noreferrer"&gt;Official Protobuf Best Practices&lt;/a&gt;;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://groups.google.com/g/protobuf/" rel="noopener noreferrer"&gt;Official Protobuf Google Group&lt;/a&gt;;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/basho/erlang_protobuffs" rel="noopener noreferrer"&gt;&lt;code&gt;erlang_protobuffs&lt;/code&gt; Erlang module&lt;/a&gt; on github;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://jsontotable.org/blog/protobuf/protobuf-best-practices" rel="noopener noreferrer"&gt;Protocol Buffer Best Practices&lt;/a&gt; on JsonToTable.org;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://medium.com/@yusufss/roadmap-to-learning-protobuf-best-practices-5b5b60781997" rel="noopener noreferrer"&gt;Roadmap to Learning Protobuf &amp;amp; Best Practices&lt;/a&gt; by &lt;a href="https://medium.com/@yusufss" rel="noopener noreferrer"&gt;Yusuf Savaş&lt;/a&gt; at Medium;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://oneuptime.com/blog/post/2026-01-24-protocol-buffer-evolution/view" rel="noopener noreferrer"&gt;How to Handle Protocol Buffer Evolution&lt;/a&gt; from OneUptime.com;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://link.springer.com/content/pdf/10.1007/978-3-030-98467-0_9.pdf" rel="noopener noreferrer"&gt;Protocol Buffers&lt;/a&gt; by Chris Currier from &lt;a href="https://link.springer.com/book/10.1007/978-3-030-98467-0" rel="noopener noreferrer"&gt;Mobile Forensics – The File Format Handbook&lt;/a&gt;;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.hackingnote.com/en/cheatsheets/protobuf/" rel="noopener noreferrer"&gt;Cheatsheet - Protocol Buffers (Protobuf)&lt;/a&gt; from Hacking Notes;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/codethecoffee/proto-cheatsheet" rel="noopener noreferrer"&gt;Protocol Buffers for (Coding) Dummies&lt;/a&gt; on Github&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.packtpub.com/en-fr/product/protocol-buffers-handbook-9781805127215" rel="noopener noreferrer"&gt;Protocol Buffers Handbook&lt;/a&gt; by Clément Jean.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy Hack and Have Fun!&lt;/p&gt;




&lt;p&gt;Cover Image by &lt;a href="https://unsplash.com/@mszerszynski?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Mateusz Szerszyński&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/two-puffins-are-close-to-each-other-Da9wCD1-Rus?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>protobuf</category>
      <category>protocol</category>
      <category>serialization</category>
    </item>
    <item>
      <title>JSON Schema in Dart</title>
      <dc:creator>Mathieu Kerjouan</dc:creator>
      <pubDate>Thu, 25 Jun 2026 08:00:00 +0000</pubDate>
      <link>https://dev.to/niamtokik/json-schema-in-dart-3ik7</link>
      <guid>https://dev.to/niamtokik/json-schema-in-dart-3ik7</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Everything which distinguishes man from the animals depends upon this ability to volatilize perceptual metaphors in a schema, and thus to dissolve an image into a concept.&lt;br&gt;
-- Friedrich Niezsche&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When you know how to encode and decode JSON data, it can become annoying to deal with the structure every time. Worse, it can become a nightmare when some kind of data structure are being upgraded. That why some people created &lt;a href="https://json-schema.org/" rel="noopener noreferrer"&gt;JSON Schema&lt;/a&gt;. The idea is to define what kind of fields and values should be contained in JSON objects. A package called &lt;a href="https://pub.dev/packages/json_schema" rel="noopener noreferrer"&gt;&lt;code&gt;json_schema&lt;/code&gt;&lt;/a&gt; was created to implement this standard. &lt;/p&gt;

&lt;h1&gt;
  
  
  Bootstrapping
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;dart&lt;/span&gt; &lt;span class="n"&gt;create&lt;/span&gt; &lt;span class="n"&gt;validate&lt;/span&gt;
&lt;span class="n"&gt;Creating&lt;/span&gt; &lt;span class="n"&gt;validate&lt;/span&gt; &lt;span class="n"&gt;using&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt; &lt;span class="n"&gt;console&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;

  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;gitignore&lt;/span&gt;
  &lt;span class="n"&gt;analysis_options&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;yaml&lt;/span&gt;
  &lt;span class="n"&gt;CHANGELOG&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;md&lt;/span&gt;
  &lt;span class="n"&gt;pubspec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;yaml&lt;/span&gt;
  &lt;span class="n"&gt;README&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;md&lt;/span&gt;
  &lt;span class="n"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;validate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;dart&lt;/span&gt;
  &lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;validate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;dart&lt;/span&gt;
  &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;validate_test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;dart&lt;/span&gt;

&lt;span class="n"&gt;Running&lt;/span&gt; &lt;span class="n"&gt;pub&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;                     &lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
  &lt;span class="n"&gt;Resolving&lt;/span&gt; &lt;span class="n"&gt;dependencies&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="n"&gt;Downloading&lt;/span&gt; &lt;span class="n"&gt;packages&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="n"&gt;Changed&lt;/span&gt; &lt;span class="mi"&gt;48&lt;/span&gt; &lt;span class="n"&gt;dependencies&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
  &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="n"&gt;packages&lt;/span&gt; &lt;span class="n"&gt;have&lt;/span&gt; &lt;span class="n"&gt;newer&lt;/span&gt; &lt;span class="n"&gt;versions&lt;/span&gt; &lt;span class="n"&gt;incompatible&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;dependency&lt;/span&gt; &lt;span class="n"&gt;constraints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
  &lt;span class="n"&gt;Try&lt;/span&gt; &lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="n"&gt;dart&lt;/span&gt; &lt;span class="n"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;outdated&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;more&lt;/span&gt; &lt;span class="n"&gt;information&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="n"&gt;Created&lt;/span&gt; &lt;span class="n"&gt;project&lt;/span&gt; &lt;span class="n"&gt;validate&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;validate&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="n"&gt;started&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;following&lt;/span&gt; &lt;span class="nl"&gt;commands:&lt;/span&gt;

  &lt;span class="n"&gt;cd&lt;/span&gt; &lt;span class="n"&gt;validate&lt;/span&gt;
  &lt;span class="n"&gt;dart&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;

&lt;span class="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;cd&lt;/span&gt; &lt;span class="n"&gt;validate&lt;/span&gt;

&lt;span class="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;dart&lt;/span&gt; &lt;span class="n"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;add&lt;/span&gt; &lt;span class="n"&gt;json_schema&lt;/span&gt;
&lt;span class="n"&gt;Resolving&lt;/span&gt; &lt;span class="n"&gt;dependencies&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt; 
&lt;span class="n"&gt;Downloading&lt;/span&gt; &lt;span class="n"&gt;packages&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt; 
  &lt;span class="n"&gt;_fe_analyzer_shared&lt;/span&gt; &lt;span class="mf"&gt;103.0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;104.0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;available&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;analyzer&lt;/span&gt; &lt;span class="mf"&gt;13.3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;14.0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;available&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt; &lt;span class="mf"&gt;1.6&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;json_schema&lt;/span&gt; &lt;span class="mf"&gt;5.2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="n"&gt;package_config&lt;/span&gt; &lt;span class="mf"&gt;2.2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;3.0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;available&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;quiver&lt;/span&gt; &lt;span class="mf"&gt;3.2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;rfc_6901&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;uri&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;Changed&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="n"&gt;dependencies&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="n"&gt;packages&lt;/span&gt; &lt;span class="n"&gt;have&lt;/span&gt; &lt;span class="n"&gt;newer&lt;/span&gt; &lt;span class="n"&gt;versions&lt;/span&gt; &lt;span class="n"&gt;incompatible&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;dependency&lt;/span&gt; &lt;span class="n"&gt;constraints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;Try&lt;/span&gt; &lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="n"&gt;dart&lt;/span&gt; &lt;span class="n"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;outdated&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;more&lt;/span&gt; &lt;span class="n"&gt;information&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can edit &lt;code&gt;bin/validate.dart&lt;/code&gt; and import the &lt;code&gt;json_schema&lt;/code&gt; package.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:json_schema/json_schema.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Schema Conception
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;Don't trust, just verify.&lt;br&gt;
-- Steven Levitt&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For this example, we will use the examples present in the &lt;a href="https://json-schema.org/learn/getting-started-step-by-step" rel="noopener noreferrer"&gt;JSON Schema Getting Started tutorial&lt;/a&gt;. Their first JSON object looks like that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"productId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"productName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A green door"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;12.50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"home"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"green"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To help us with the tests, we can create a function helper instantiating a schema and validating data. Let call it &lt;code&gt;validate()&lt;/code&gt;. To instantiate a new &lt;a href="https://pub.dev/documentation/json_schema/latest/json_schema/JsonSchema-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;JsonSchema&lt;/code&gt;&lt;/a&gt; object, the &lt;a href="https://pub.dev/documentation/json_schema/latest/json_schema/JsonSchema/create.html" rel="noopener noreferrer"&gt;&lt;code&gt;JsonSchema.create()&lt;/code&gt;&lt;/a&gt; static function can be invoked. Then, the &lt;a href="https://pub.dev/documentation/json_schema/latest/json_schema/JsonSchema/validate.html" rel="noopener noreferrer"&gt;&lt;code&gt;JsonSchema.validate()&lt;/code&gt;&lt;/a&gt; method will be called to check if the &lt;code&gt;data&lt;/code&gt; passed is correct.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="si"&gt;${msg}&lt;/span&gt;&lt;span class="s"&gt;:"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;_schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JsonSchema&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;validate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_schema&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  &lt;/span&gt;&lt;span class="si"&gt;${validate}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;${data}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before doing the whole specification for this object, we can start to specify only one of those fields, like the &lt;code&gt;productId&lt;/code&gt;. &lt;code&gt;json_schema&lt;/code&gt; module can use both a &lt;code&gt;Map&amp;lt;String, dynamic&amp;gt;&lt;/code&gt; or a &lt;code&gt;JSON&amp;lt;String&amp;gt;&lt;/code&gt; to load this. To make our life easier, we will create functions wrapper returning the schema we want as a Map.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_productId&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;                                          
    &lt;span class="s"&gt;"description"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"The unique identifier for a product"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"type"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"integer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                              
  &lt;span class="p"&gt;};&lt;/span&gt;                                                
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A JSON schema describe an object and its properties. A &lt;code&gt;productId&lt;/code&gt; is an &lt;code&gt;integer&lt;/code&gt;. The &lt;a href="https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-00#rfc.section.9.1" rel="noopener noreferrer"&gt;&lt;code&gt;description&lt;/code&gt;&lt;/a&gt; field is not used by the validator, it's only a way to help developers and designer to understand the structure. An object can be defined by &lt;a href="https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-00#rfc.section.6.1.1" rel="noopener noreferrer"&gt;many types&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;null&lt;/code&gt; (e.g. &lt;code&gt;null&lt;/code&gt;);&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;boolean&lt;/code&gt; (e.g. &lt;code&gt;true&lt;/code&gt;&amp;nbsp;or &lt;code&gt;false&lt;/code&gt;);&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;number&lt;/code&gt; (e.g. &lt;code&gt;1&lt;/code&gt; or &lt;code&gt;-1.23&lt;/code&gt;);&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;integer&lt;/code&gt; (e.g &lt;code&gt;1&lt;/code&gt; or &lt;code&gt;1024&lt;/code&gt;);&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;string&lt;/code&gt; (e.g. &lt;code&gt;"test"&lt;/code&gt;);&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;object&lt;/code&gt; (e.g. &lt;code&gt;{}&lt;/code&gt;);&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;array&lt;/code&gt; (e.g. &lt;code&gt;[]&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of them are coming from the JSON specification itself.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_productName&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;               
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;                                          
    &lt;span class="s"&gt;"description"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Name of the product"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;           
    &lt;span class="s"&gt;"type"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"string"&lt;/span&gt;                                
  &lt;span class="p"&gt;};&lt;/span&gt;                                                
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;productName&lt;/code&gt; is a &lt;code&gt;string&lt;/code&gt;. Nothing complex here, the schema is similar to the &lt;code&gt;productId&lt;/code&gt; object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_price&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;                                          
    &lt;span class="s"&gt;"description"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"The price of the product"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"type"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"number"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                               
    &lt;span class="s"&gt;"exclusiveMinimum"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;                           
  &lt;span class="p"&gt;};&lt;/span&gt;                                                
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A &lt;code&gt;price&lt;/code&gt; is a &lt;code&gt;number&lt;/code&gt;. To avoid negative numbers, we are also enforcing the specification with the &lt;a href="https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-00#rfc.section.6.2.5" rel="noopener noreferrer"&gt;&lt;code&gt;exclusiveMinimum&lt;/code&gt;&lt;/a&gt; parameter. If a price is negative, the schema becomes invalid.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_tags&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;                                          
    &lt;span class="s"&gt;"description"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Tags for the product"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"type"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"array"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                                
    &lt;span class="s"&gt;"items"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;                                      
      &lt;span class="s"&gt;"type"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"string"&lt;/span&gt;                              
    &lt;span class="p"&gt;},&lt;/span&gt;                                              
    &lt;span class="s"&gt;"minitems"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                                  
    &lt;span class="s"&gt;"uniqueItems"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;                             
  &lt;span class="p"&gt;};&lt;/span&gt;                                                
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compound terms can also be created. The &lt;code&gt;tags&lt;/code&gt; object is defined as an &lt;code&gt;array&lt;/code&gt; or &lt;code&gt;string&lt;/code&gt;. It must have at least 1 items (see &lt;a href="https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-00#rfc.section.6.4.2" rel="noopener noreferrer"&gt;&lt;code&gt;minitems&lt;/code&gt;&lt;/a&gt; parameter) and must contain only unique items (see &lt;a href="https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-00#rfc.section.6.4.3" rel="noopener noreferrer"&gt;&lt;code&gt;uniqueItems&lt;/code&gt;&lt;/a&gt; parameter).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_dimensions&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;                                          
    &lt;span class="s"&gt;"type"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                               
    &lt;span class="s"&gt;"properties"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;                                 
      &lt;span class="s"&gt;"length"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;                                   
        &lt;span class="s"&gt;"type"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"number"&lt;/span&gt;                            
      &lt;span class="p"&gt;},&lt;/span&gt;                                            
      &lt;span class="s"&gt;"width"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;                                    
        &lt;span class="s"&gt;"type"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"number"&lt;/span&gt;                            
      &lt;span class="p"&gt;},&lt;/span&gt;                                            
      &lt;span class="s"&gt;"height"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;                                   
        &lt;span class="s"&gt;"type"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"number"&lt;/span&gt;                            
      &lt;span class="p"&gt;}&lt;/span&gt;                                             
    &lt;span class="p"&gt;},&lt;/span&gt;                                              
    &lt;span class="s"&gt;"required"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s"&gt;"length"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"width"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"height"&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;                                                
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another &lt;code&gt;object&lt;/code&gt; can be defined. It is also a compound term. In our case, the &lt;code&gt;dimensions&lt;/code&gt; of an item must have a &lt;code&gt;length&lt;/code&gt; as &lt;code&gt;number&lt;/code&gt;, a &lt;code&gt;width&lt;/code&gt; as &lt;code&gt;number&lt;/code&gt; and a &lt;code&gt;height&lt;/code&gt; as number too. All those fields are required (see &lt;a href="https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-00#rfc.section.6.5.3" rel="noopener noreferrer"&gt;&lt;code&gt;required&lt;/code&gt;&lt;/a&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dynamic&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;fullSchema&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"\$schema"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"https://json-schema.org/draft/2020-12/schema"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"\$id"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"https://example.com/product.schema.json"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Product"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;             
    &lt;span class="s"&gt;"description"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"A product in the catalog"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"type"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"object"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"required"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;  
      &lt;span class="s"&gt;"productId"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
      &lt;span class="s"&gt;"productName"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;    
      &lt;span class="s"&gt;"price"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="o"&gt;],&lt;/span&gt;          
    &lt;span class="s"&gt;"properties"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;     
      &lt;span class="s"&gt;"productId"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;_productId&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
      &lt;span class="s"&gt;"productName"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;_productName&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
      &lt;span class="s"&gt;"price"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;_price&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
      &lt;span class="s"&gt;"tags"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;_tags&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
      &lt;span class="s"&gt;"dimensions"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;_dimensions&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;                                          
  &lt;span class="o"&gt;};&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, the function &lt;code&gt;fullSchema()&lt;/code&gt; is containing the whole JSON schema. Lot of fields here a kinda mandatory and are directly related to the JSON Schema specification. Let test that in our main entry-point.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fullSchema&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="nl"&gt;msg:&lt;/span&gt; &lt;span class="s"&gt;"empty data"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="n"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fullSchema&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"productId"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nl"&gt;msg:&lt;/span&gt; &lt;span class="s"&gt;"productId only"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="n"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fullSchema&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"productId"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"productName"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"A green door"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"price"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;12.50&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nl"&gt;msg:&lt;/span&gt; &lt;span class="s"&gt;"minimal valid data"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="n"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fullSchema&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"productId"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"productName"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"A green door"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"price"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;12.50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"tags"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="s"&gt;"home"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s"&gt;"green"&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nl"&gt;msg:&lt;/span&gt; &lt;span class="s"&gt;"valid data with tags"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="n"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fullSchema&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"productId"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"productName"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"A green door"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"price"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;12.50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"tags"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="s"&gt;"home"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s"&gt;"green"&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="s"&gt;"dimensions"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="s"&gt;"length"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s"&gt;"width"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s"&gt;"height"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nl"&gt;msg:&lt;/span&gt; &lt;span class="s"&gt;"valid data with tags and dimensions"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can now run this application to see the result.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart run
&lt;span class="go"&gt;Resolving dependencies in `/tmp/dart/validate`... 
Downloading packages... 
Got dependencies in `/tmp/dart/validate`.
Building package executable... 
Built validate:validate.

empty data:
&lt;/span&gt;&lt;span class="gp"&gt;  INVALID, Errors: [#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;root&lt;span class="o"&gt;)&lt;/span&gt;: required prop missing: productId from &lt;span class="o"&gt;{}&lt;/span&gt;, /productId: required prop missing: productId from &lt;span class="o"&gt;{}&lt;/span&gt;, &lt;span class="c"&gt;# (root): required prop missing: productName from {}, /productName: required prop missing: productName from {}, # (root): required prop missing: price from {}, /price: required prop missing: price from {}]: {}&lt;/span&gt;
&lt;span class="go"&gt;
productId only:
&lt;/span&gt;&lt;span class="gp"&gt;  INVALID, Errors: [#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;root&lt;span class="o"&gt;)&lt;/span&gt;: required prop missing: productName from &lt;span class="o"&gt;{&lt;/span&gt;productId: 1&lt;span class="o"&gt;}&lt;/span&gt;, /productName: required prop missing: productName from &lt;span class="o"&gt;{&lt;/span&gt;productId: 1&lt;span class="o"&gt;}&lt;/span&gt;, &lt;span class="c"&gt;# (root): required prop missing: price from {productId: 1}, /price: required prop missing: price from {productId: 1}]: {productId: 1}&lt;/span&gt;
&lt;span class="go"&gt;
minimal valid data:
  VALID: {productId: 1, productName: A green door, price: 12.5}

valid data with tags:
  VALID: {productId: 1, productName: A green door, price: 12.5, tags: [home, green]}

valid data with tags and dimensions:
  VALID: {productId: 1, productName: A green door, price: 12.5, tags: [home, green], dimensions: {length: 1, width: 2, height: 3}}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, if the data received is missing some fields, the data is considered invalid. In other hands, if the requirements are there, the data is considered valid. Great, right? We now have a way to control the data received (or to control what we are sending).&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;Without approval and without scorn, but carefully studying the sentences word by word, one should trace them in the Discourses and verify them by the Discipline. If they are neither traceable in the Discourses nor verifiable by the Discipline, one must conclude thus: 'Certainly, this is not the Blessed One's utterance; this has been misunderstood by that bhikkhu - or by that community, or by those elders, or by that elder.' In that way, bhikkhus, you should reject it.&lt;br&gt;
-- Siddharta Gautama&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;JSON Schema is a good way to make your application resilient and stable. It can be used as documentation tool, it will help to create tests, and it will also ensure the data received are legit. If you are working with public APIs, it can also help you to create the OpenAPI specifications. Finally, it will help you to design your application by thinking on what it needs, it will lead to a lean application containing only the necessary values.&lt;/p&gt;

&lt;p&gt;JSON Schema can also be used to help us to reverse engineer private API interfaces by documenting what kind of data is working and which ones are not working. Creating specifications is great, Enjoy.&lt;/p&gt;

&lt;p&gt;Wants to know more about JSON Schema? Here few interesting resources for you.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://json-schema.org/" rel="noopener noreferrer"&gt;Official JSON Schema website&lt;/a&gt;;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://pub.dev/packages/json_schema" rel="noopener noreferrer"&gt;&lt;code&gt;json_schema&lt;/code&gt; package&lt;/a&gt; on pub.dev;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://pub.dev/documentation/json_schema/latest/" rel="noopener noreferrer"&gt;&lt;code&gt;json_schema&lt;/code&gt;&amp;nbsp;API documentation&lt;/a&gt; on pub.dev;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/workiva/json_schema" rel="noopener noreferrer"&gt;&lt;code&gt;json_schema&lt;/code&gt;&amp;nbsp;source code&lt;/a&gt; on Github;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/Workiva/json_schema/tree/master/example" rel="noopener noreferrer"&gt;&lt;code&gt;json_schema&lt;/code&gt;&amp;nbsp;examples&lt;/a&gt; on Github;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dev.to/_d7eb1c1703182e3ce1782/json-schema-explained-with-examples-the-complete-guide-2679"&gt;JSON Schema Explained with Examples: The Complete Guide&lt;/a&gt; by &lt;a href="https://dev.to/_d7eb1c1703182e3ce1782"&gt;楊東霖&lt;/a&gt; on dev.to;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dev.to/_d7eb1c1703182e3ce1782/how-to-validate-json-schema-a-complete-developer-guide-219e"&gt;How to Validate JSON Schema: A Complete Developer Guide&lt;/a&gt; by &lt;a href="https://dev.to/_d7eb1c1703182e3ce1782"&gt;楊東霖&lt;/a&gt; on dev.to;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dev.to/anh_qunnguyn_57549060f/json-schema-in-10-minutes-validation-types-real-examples-4pjg"&gt;JSON Schema in 10 Minutes — Validation, Types &amp;amp; Real Examples&lt;/a&gt; by &lt;a href="https://dev.to/anh_qunnguyn_57549060f"&gt;Anh Quân Nguyễn&lt;/a&gt; on dev.to;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://jsonwebtools.com/json-schema-examples" rel="noopener noreferrer"&gt;JSON Schema Examples - Real-World Schema Patterns&lt;/a&gt; by &lt;a href="https://jsonwebtools.com/about" rel="noopener noreferrer"&gt;Saurabh Goyal&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy Hack and Have fun!&lt;/p&gt;




&lt;p&gt;Cover Image by &lt;a href="https://unsplash.com/@killerfvith?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Alex wong&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/worms-eye-view-of-buildings-l5Tzv1alcps?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>json</category>
      <category>schema</category>
      <category>dart</category>
      <category>serialization</category>
    </item>
    <item>
      <title>OpenVPN Daemon Double Free Bug</title>
      <dc:creator>Mathieu Kerjouan</dc:creator>
      <pubDate>Wed, 24 Jun 2026 10:00:00 +0000</pubDate>
      <link>https://dev.to/niamtokik/openvpn-daemon-double-free-bug-9oa</link>
      <guid>https://dev.to/niamtokik/openvpn-daemon-double-free-bug-9oa</guid>
      <description>&lt;p&gt;Since 2016, I started manage my own VPN infrastructure for my own usage and also for testing purpose. My VPN is not always started, in fact, I use it rarely. After the latest upgrade of &lt;a href="https://www.openbsd.org/" rel="noopener noreferrer"&gt;OpenBSD&lt;/a&gt; to 7.9, I started to see the &lt;a href="https://manpages.debian.org/trixie/openvpn/openvpn.8.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;openvpn&lt;/code&gt;&lt;/a&gt; vanishing after few minutes of usage. It was not really important, but recently, I needed someone else to use the VPN for debugging purpose and... Every 3 or 4 minutes, the service crashed. Not really cool for a debugging session, fortunately, having &lt;a href="https://man.openbsd.org/tmux" rel="noopener noreferrer"&gt;&lt;code&gt;tmux&lt;/code&gt;&lt;/a&gt; (or &lt;a href="https://manpages.debian.org/trixie/tmate/tmate.1.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;tmate&lt;/code&gt;&lt;/a&gt;) running on the other side is life saving. Let stop the openvpn service and invoke it as a standalone service instead of a daemon.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;doas openvpn &lt;span class="nt"&gt;--config&lt;/span&gt; /etc/openvpn/server.conf
&lt;span class="go"&gt;2026-06-20 08:27:44 us=727112 TCP/UDP: Closing socket                                                                                                        
2026-06-20 08:27:46 us=638261 MULTI: multi_create_instance called                                                                                            
openvpn(90124) in free(): double free 0x3626945d540
Abort trap
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Indeed, something bad is happening with this application. It's not a good thing to see an &lt;code&gt;Abort trap&lt;/code&gt; or a &lt;code&gt;double free&lt;/code&gt; exception. Let check the &lt;a href="https://www.openbsd.org/79.html" rel="noopener noreferrer"&gt;release note from OpenBSD 7.9&lt;/a&gt;, but it did not mention anything regarding OpenVPN, is some case, OpenBSD team put some notes their if something really important about a package needs to be updated. Maybe this information can be found in the &lt;a href="//github.com/openbsd/ports/blob/master/net/openvpn/pkg/README"&gt;package's README&lt;/a&gt; but it was not the case. I already using most of the recommendations from this file.&lt;/p&gt;

&lt;p&gt;OpenVPN package on OpenBSD supports some flavors, like the mbedTLS one. Perhaps installing this version could fix the issue, it could be an easy workaround. (installing a flavor with &lt;code&gt;pkg_add&lt;/code&gt; can be done by postfix the package name with &lt;code&gt;--&lt;/code&gt; followed by a list of flavors separated by commas).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;doas pkg_delete openvpn
&lt;span class="c"&gt;...
&lt;/span&gt;&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;doas pkg_add openvpn--mbedtls
&lt;span class="c"&gt;...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let start it again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;openvpn &lt;span class="nt"&gt;--configuration&lt;/span&gt; myconfig.conf
&lt;span class="go"&gt;2026-06-20 09:09:43 us=709832 TCP/UDP: Closing socket                                                                                                       
openvpn(87978) in free(): double free 0x1957f910620                                                                                                           
Abort trap
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hmm, this is not a problem with this part of the application then. Let cleanup the openvpn configuration to see if we can find the culprit by removing all extra parameters and only keeping the mandatory ones.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;ca&lt;/span&gt; /...
&lt;span class="n"&gt;cert&lt;/span&gt; /...
&lt;span class="n"&gt;chroot&lt;/span&gt; /&lt;span class="n"&gt;var&lt;/span&gt;/&lt;span class="n"&gt;openvpn&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt;-&lt;span class="n"&gt;config&lt;/span&gt;-&lt;span class="n"&gt;dir&lt;/span&gt; &lt;span class="n"&gt;ccd&lt;/span&gt;
&lt;span class="n"&gt;dev&lt;/span&gt; &lt;span class="n"&gt;tun0&lt;/span&gt;
&lt;span class="n"&gt;dh&lt;/span&gt; /...
&lt;span class="n"&gt;dhcp&lt;/span&gt;-&lt;span class="n"&gt;option&lt;/span&gt; &lt;span class="n"&gt;DNS&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;.&lt;span class="m"&gt;1&lt;/span&gt;.&lt;span class="m"&gt;0&lt;/span&gt;.&lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;dhcp&lt;/span&gt;-&lt;span class="n"&gt;option&lt;/span&gt; &lt;span class="n"&gt;DOMAIN&lt;/span&gt; ...
&lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;openvpn&lt;/span&gt;
&lt;span class="n"&gt;key&lt;/span&gt; /...
&lt;span class="n"&gt;local&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;.&lt;span class="m"&gt;2&lt;/span&gt;.&lt;span class="m"&gt;3&lt;/span&gt;.&lt;span class="m"&gt;4&lt;/span&gt;
&lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="m"&gt;1194&lt;/span&gt;
&lt;span class="n"&gt;proto&lt;/span&gt; &lt;span class="n"&gt;udp&lt;/span&gt;
&lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;.&lt;span class="m"&gt;1&lt;/span&gt;.&lt;span class="m"&gt;0&lt;/span&gt;.&lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;.&lt;span class="m"&gt;255&lt;/span&gt;.&lt;span class="m"&gt;255&lt;/span&gt;.&lt;span class="m"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;tls&lt;/span&gt;-&lt;span class="n"&gt;auth&lt;/span&gt; /... &lt;span class="m"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;topology&lt;/span&gt; &lt;span class="n"&gt;subnet&lt;/span&gt;
&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;openvpn&lt;/span&gt;
&lt;span class="n"&gt;verb&lt;/span&gt; &lt;span class="m"&gt;11&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is basically the smallest configuration I can have for this service and it still crashes. Another great thing, we can install a debug version of openvpn on OpenBSD, so, why not?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;doas pkg_delete openvpn--mbedtls
&lt;span class="c"&gt;...
&lt;/span&gt;&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;doas pkg_add openvpn debug-openvpn
&lt;span class="c"&gt;...
&lt;/span&gt;&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;doas openvpn &lt;span class="nt"&gt;--config&lt;/span&gt; /etc/openvpn/server.conf
&lt;span class="go"&gt;2026-06-21 17:10:47 us=495655 Connection Attempt UDPv4 WRITE [54] to [AF_INET]1.2.3.4:32858: DATA ...
2026-06-21 17:10:47 us=496625 Connection Attempt UDPv4 write returned 54
2026-06-21 17:10:47 us=497180 Connection Attempt PO_CTL rwflags=0x0001 ev=4 arg=0xe6d6ae83e28
2026-06-21 17:10:47 us=497204 Connection Attempt PO_CTL rwflags=0x0001 ev=3 arg=0x00000002
2026-06-21 17:10:47 us=529222 Connection Attempt PO_WAIT[0,0] fd=4 rev=0x00000001 rwflags=0x0001 arg=0xe6d6ae83e28 [scalable]
openvpn(34334) in free(): double free 0xe6d6ae97000
2026-06-21 17:10:47 us=529957 Connection Attempt UDPv4 read returned 1222
&lt;/span&gt;&lt;span class="gp"&gt;2026-06-21 17:10:47 us=530561 Connection Attempt ACK read ID 1 (buf-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;len&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1168&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="go"&gt;2026-06-21 17:10:47 us=530609 Connection Attempt Valid packet (P_CONTROL_V1) with HMAC challenge from peer ([AF_INET]1.2.3.4:32858), accepting new connection.
2026-06-21 17:10:47 us=530628 Connection Attempt MULTI: multi_create_instance called
Abort trap
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After this last issue, I decided to test the configuration on another server running on OpenBSD as well. Same behavior. I currently don't have any servers on Linux to install OpenVPN on it and do the correct configuration, but I assume this is a bug from OpenVPN. Let have a look at the version.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;openvpn &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;span class="go"&gt;OpenVPN 2.7.2 x86_64-unknown-openbsd7.9 [SSL (mbed TLS)] [LZO] [LZ4] [MH/RECVDA] [AEAD]
library versions: mbed TLS 3.6.6, LZO 2.10
Originally developed by James Yonan
&lt;/span&gt;&lt;span class="gp"&gt;Copyright (C) 2002-2026 OpenVPN Inc &amp;lt;sales@openvpn.net&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;Compile time defines: enable_async_push=no enable_comp_stub=no enable_crypto_ofb_cfb=yes enable_dco=no enable_debug=yes enable_dlopen=unknown enable_dlopen_self=unknown enable_dlopen_self_static=unknown enable_dns_updown_by_default=yes enable_fast_install=needless enable_fragment=yes enable_gtk_doc=no enable_iproute2=no enable_libtool_lock=yes enable_lz4=yes enable_lzo=yes enable_management=yes enable_ntlm=yes enable_pam_dlopen=no enable_pedantic=no enable_pkcs11=no enable_plugin_auth_pam=no enable_plugin_down_root=yes enable_plugins=yes enable_port_share=yes enable_selinux=no enable_shared=yes enable_shared_with_static_runtimes=no enable_silent_rules=no enable_small=no enable_static=yes enable_strict=no enable_strict_options=no enable_systemd=no enable_werror=no enable_win32_dll=yes enable_wolfssl_options_h=yes with_aix_soname=aix with_crypto_library=mbedtls with_gnu_ld=yes with_mem_check=no with_openssl_engine=no with_sysroot=no
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we can create an issue on &lt;a href="https://github.com/OpenVPN/openvpn/issues/" rel="noopener noreferrer"&gt;OpenVPN bugtracker&lt;/a&gt; on Github. Thanks to &lt;a href="https://github.com/cron2" rel="noopener noreferrer"&gt;@cron2&lt;/a&gt;, he asked me to use &lt;a href="https://man.openbsd.org/gdb" rel="noopener noreferrer"&gt;&lt;code&gt;gdb&lt;/code&gt;&lt;/a&gt; to see what's happening.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;doas gdb openvpn
&lt;span class="go"&gt;GNU gdb 6.3                                                                                                                                                   
Copyright 2004 Free Software Foundation, Inc.                                                                                                                                                                                                                                                                               
GDB is free software, covered by the GNU General Public License, and you are                                                                                                                                                                                                                                                
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "amd64-unknown-openbsd7.9"...(no debugging symbols found)

(gdb) run /etc/openvpn/server.conf
Starting program: /usr/local/sbin/openvpn /etc/openvpn/server.conf
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
&lt;/span&gt;&lt;span class="c"&gt;...
&lt;/span&gt;&lt;span class="go"&gt;2026-06-21 18:08:29 us=538009 Connection Attempt MULTI: multi_create_instance called
openvpn(83681) in free(): double free 0xcd8b677f2a0

Program received signal SIGABRT, Aborted.

(gdb) where
2026-06-21 18:08:29 us=538009 Connection Attempt MULTI: multi_create_instance called
openvpn(83681) in free(): double free 0xcd8b677f2a0

Program received signal SIGABRT, Aborted.
thrkill () at /tmp/-:2
2       /tmp/-: No such file or directory.
        in /tmp/-
&lt;/span&gt;&lt;span class="gp"&gt;Current language:  auto;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;currently asm
&lt;span class="gp"&gt;#&lt;/span&gt;0  thrkill &lt;span class="o"&gt;()&lt;/span&gt; at /tmp/-:2
&lt;span class="gp"&gt;#&lt;/span&gt;1  0x87e238fe7c29df29 &lt;span class="k"&gt;in&lt;/span&gt; ?? &lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="gp"&gt;#&lt;/span&gt;2  0x00000cd91200f8bb &lt;span class="k"&gt;in &lt;/span&gt;_libc_abort &lt;span class="o"&gt;()&lt;/span&gt; at /usr/src/lib/libc/stdlib/abort.c:51
&lt;span class="gp"&gt;#&lt;/span&gt;3  0x00000cd912014714 &lt;span class="k"&gt;in &lt;/span&gt;wrterror &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;d&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Variable &lt;span class="s2"&gt;"d"&lt;/span&gt; is not available.
&lt;span class="go"&gt;) at /usr/src/lib/libc/stdlib/malloc.c:378
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;4  0x00000cd91201a681 &lt;span class="k"&gt;in &lt;/span&gt;find_chunknum &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;d&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Variable &lt;span class="s2"&gt;"d"&lt;/span&gt; is not available.
&lt;span class="go"&gt;) at include/machine/tcb.h:43
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;5  0x00000cd912015cb0 &lt;span class="k"&gt;in &lt;/span&gt;ofree &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;argpool&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0x7e622a409040, &lt;span class="nv"&gt;p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0xcd8b677f2a0, &lt;span class="nv"&gt;clear&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Variable &lt;span class="s2"&gt;"clear"&lt;/span&gt; is not available.
&lt;span class="go"&gt;) at /usr/src/lib/libc/stdlib/malloc.c:1659
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;6  0x00000cd912015933 &lt;span class="k"&gt;in &lt;/span&gt;_libc_free &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;ptr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0xcd8b677f2a0&lt;span class="o"&gt;)&lt;/span&gt; at /usr/src/lib/libc/stdlib/malloc.c:1729
&lt;span class="gp"&gt;#&lt;/span&gt;7  0x00000cd6a5471978 &lt;span class="k"&gt;in &lt;/span&gt;x_gc_free &lt;span class="o"&gt;()&lt;/span&gt; from /usr/local/sbin/openvpn
&lt;span class="gp"&gt;#&lt;/span&gt;8  0x00000cd6a54b1bb8 &lt;span class="k"&gt;in &lt;/span&gt;pre_connect_restore &lt;span class="o"&gt;()&lt;/span&gt; from /usr/local/sbin/openvpn
&lt;span class="gp"&gt;#&lt;/span&gt;9  0x00000cd6a548d7a9 &lt;span class="k"&gt;in &lt;/span&gt;init_instance &lt;span class="o"&gt;()&lt;/span&gt; from /usr/local/sbin/openvpn
&lt;span class="gp"&gt;#&lt;/span&gt;10 0x00000cd6a548fc21 &lt;span class="k"&gt;in &lt;/span&gt;inherit_context_child &lt;span class="o"&gt;()&lt;/span&gt; from /usr/local/sbin/openvpn
&lt;span class="gp"&gt;#&lt;/span&gt;11 0x00000cd6a54a1b94 &lt;span class="k"&gt;in &lt;/span&gt;multi_create_instance &lt;span class="o"&gt;()&lt;/span&gt; from /usr/local/sbin/openvpn
&lt;span class="gp"&gt;#&lt;/span&gt;12 0x00000cd6a54a0a06 &lt;span class="k"&gt;in &lt;/span&gt;multi_get_create_instance_udp &lt;span class="o"&gt;()&lt;/span&gt; from /usr/local/sbin/openvpn
&lt;span class="gp"&gt;#&lt;/span&gt;13 0x00000cd6a54a39e0 &lt;span class="k"&gt;in &lt;/span&gt;multi_process_incoming_link &lt;span class="o"&gt;()&lt;/span&gt; from /usr/local/sbin/openvpn
&lt;span class="gp"&gt;#&lt;/span&gt;14 0x00000cd6a54a0d12 &lt;span class="k"&gt;in &lt;/span&gt;multi_process_io_udp &lt;span class="o"&gt;()&lt;/span&gt; from /usr/local/sbin/openvpn
&lt;span class="gp"&gt;#&lt;/span&gt;15 0x00000cd6a54a9626 &lt;span class="k"&gt;in &lt;/span&gt;multi_io_process_io &lt;span class="o"&gt;()&lt;/span&gt; from /usr/local/sbin/openvpn
&lt;span class="gp"&gt;#&lt;/span&gt;16 0x00000cd6a54a7181 &lt;span class="k"&gt;in &lt;/span&gt;tunnel_server &lt;span class="o"&gt;()&lt;/span&gt; from /usr/local/sbin/openvpn
&lt;span class="gp"&gt;#&lt;/span&gt;17 0x00000cd6a54ad978 &lt;span class="k"&gt;in &lt;/span&gt;main &lt;span class="o"&gt;()&lt;/span&gt; from /usr/local/sbin/openvpn
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After an investigation by &lt;a href="https://github.com/OpenVPN/openvpn/issues/1060#issuecomment-4762931539" rel="noopener noreferrer"&gt;cron2&lt;/a&gt;, it seems the bug was from the DHCP/DNS options management.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;   &lt;span class="mi"&gt;3186&lt;/span&gt;
   &lt;span class="mi"&gt;3187&lt;/span&gt;         &lt;span class="cm"&gt;/* Free DNS options and reset them to pre-pull state */&lt;/span&gt;
   &lt;span class="mi"&gt;3188&lt;/span&gt;         &lt;span class="nf"&gt;gc_free&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;dns_options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="mi"&gt;3189&lt;/span&gt;         &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;gc_arena&lt;/span&gt; &lt;span class="n"&gt;dns_gc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gc_new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="mi"&gt;3190&lt;/span&gt;         &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;dns_options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;clone_dns_options&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;pp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;dns_options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;dns_gc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="mi"&gt;3191&lt;/span&gt;         &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;dns_options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dns_gc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great! We have found a bug, and the OpenVPN team already &lt;a href="http://gerrit.openvpn.net/c/openvpn/+/1715" rel="noopener noreferrer"&gt;fixed it&lt;/a&gt;. Regarding the workarounds, for now... I don't have a lot of solutions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;configure dhcpd instead of using the DHCP service from OpenVPN&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;configure a local DNS server on the client configuration side&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;configure transparent DNS connections forwarding via a firewall (e.g. pf in this case).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In fact, this is a great timing, I need to upgrade my VPN infrastructure, too many useless services are running on it and I would like to have something lean. I would also like to remove &lt;a href="https://manpages.debian.org/experimental/tinc/tinc.8.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;tinc&lt;/code&gt;&lt;/a&gt; (it was used to interconnect others servers) and use &lt;a href="https://man.openbsd.org/wg" rel="noopener noreferrer"&gt;&lt;code&gt;wireguard&lt;/code&gt;&lt;/a&gt; instead. The whole story of this bug can be seen on &lt;a href="https://github.com/OpenVPN/openvpn/issues/1060" rel="noopener noreferrer"&gt;Github issue @1060&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Happy Hack and have fun!&lt;/p&gt;




&lt;p&gt;Cover Image by &lt;a href="https://unsplash.com/@carlaquario31?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Carla Quario&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/green-and-pink-flower-bud-RbOT5aMbTsE?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>openbsd</category>
      <category>openvpn</category>
      <category>vpn</category>
      <category>network</category>
    </item>
    <item>
      <title>SignalApp UI Home Screen but with Flutter</title>
      <dc:creator>Mathieu Kerjouan</dc:creator>
      <pubDate>Tue, 23 Jun 2026 08:00:00 +0000</pubDate>
      <link>https://dev.to/niamtokik/signalapp-ui-home-screen-but-with-flutter-5fo6</link>
      <guid>https://dev.to/niamtokik/signalapp-ui-home-screen-but-with-flutter-5fo6</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;He who can copy can do.&lt;br&gt;
-- Leonardo Da Vinci&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;My UI skill is very low, I'm not really good with colors or themes and it's always a pain to design a full interface by myself... In short: I'm not an artist and it needs to be fixed. &lt;a href="https://signal.org/download/" rel="noopener noreferrer"&gt;SignalApp&lt;/a&gt; is one of my main messenger, the interface is simple and functional. The application source code is also available on &lt;a href="https://github.com/signalapp" rel="noopener noreferrer"&gt;Github&lt;/a&gt;, and the Android application is using Material3. I think it can be a good target.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F164ka5pkn9pbhnbkssgs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F164ka5pkn9pbhnbkssgs.png" alt="SignalApp Screenshot from appgefahren.de" width="800" height="612"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Trying to reproduce it completely will take a while, only a small subset of the interface will be re-implemented, the home and the user chat screens, both can be seen from the screenshot above.&lt;/p&gt;

&lt;h1&gt;
  
  
  Home Screen
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;Good artists copy, great artists steal.&lt;br&gt;
-- Pablo Picasso&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The Home screen will be the entry-point of the application, where the active conversations can be seen by the user. It is also the place where &lt;a href="https://signal.org/" rel="noopener noreferrer"&gt;SignalApp&lt;/a&gt; can be configured, when tapping on the User avatar on the top left of the application, a &lt;a href="https://api.flutter.dev/flutter/material/Drawer-class.html" rel="noopener noreferrer"&gt;Drawer&lt;/a&gt; menu should open. This menu is not displayed on the screenshot, then, only an empty one will be created.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqwexdjbot5qn2x8ntrjj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqwexdjbot5qn2x8ntrjj.png" alt="Top part of the Home Screen" width="605" height="340"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first easily identifiable element of the application is the top bar. I will assume this one can be created with the help of the &lt;a href="https://api.flutter.dev/flutter/material/AppBar-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;AppBar&lt;/code&gt;&lt;/a&gt; class, where the &lt;code&gt;leading&lt;/code&gt; parameter will contain the user picture (rounded). This element can be created with a &lt;a href="https://api.flutter.dev/flutter/material/CircleAvatar-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;CircleAvatar&lt;/code&gt;&lt;/a&gt; class. The &lt;code&gt;title&lt;/code&gt; parameter will display the text "Signal" in bold, a &lt;a href="https://api.flutter.dev/flutter/painting/TextStyle-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;TextStyle&lt;/code&gt;&lt;/a&gt; class can be set properly inside the &lt;a href="https://api.flutter.dev/flutter/widgets/Text-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;Text&lt;/code&gt;&lt;/a&gt; class &lt;a href="https://api.flutter.dev/flutter/widgets/Text/style.html" rel="noopener noreferrer"&gt;&lt;code&gt;style&lt;/code&gt;&lt;/a&gt; attribute. The parameters &lt;code&gt;actions&lt;/code&gt; will be set with 2 icons, one icon will represent a camera with &lt;a href="https://api.flutter.dev/flutter/cupertino/CupertinoIcons/camera-constant.html" rel="noopener noreferrer"&gt;&lt;code&gt;Icons.camera&lt;/code&gt;&lt;/a&gt; and the last one will represent a pencil using the &lt;a href="https://api.flutter.dev/flutter/cupertino/CupertinoIcons/pencil-constant.html" rel="noopener noreferrer"&gt;&lt;code&gt;pencil&lt;/code&gt;&lt;/a&gt; constant. Finally, the background color of this element is grey (&lt;code&gt;#fafafa&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;AppBar&lt;/span&gt; &lt;span class="nf"&gt;homeBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;AppBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nl"&gt;backgroundColor:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;grey&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="nl"&gt;titleTextStyle:&lt;/span&gt; &lt;span class="n"&gt;TextStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;color:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;black&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;fontWeight:&lt;/span&gt; &lt;span class="n"&gt;FontWeight&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;bold&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nl"&gt;leading:&lt;/span&gt; &lt;span class="n"&gt;Padding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;padding:&lt;/span&gt; &lt;span class="n"&gt;EdgeInsets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;8.0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;CircleAvatar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;backgroundColor:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;amber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'AH'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="n"&gt;Center&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'SignalCopyCat'&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="nl"&gt;actions:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="n"&gt;IconButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;onPressed:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; 
        &lt;span class="nl"&gt;icon:&lt;/span&gt; &lt;span class="n"&gt;Icon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Icons&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;camera_alt_outlined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="n"&gt;IconButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;onPressed:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
        &lt;span class="nl"&gt;icon:&lt;/span&gt; &lt;span class="n"&gt;Icon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Icons&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;edit_outlined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The top Bar using by Signal is not the most challenging part of the design, at least, when we don't have state to manage. The &lt;code&gt;homeBar()&lt;/code&gt; function created above is returning an &lt;a href="https://api.flutter.dev/flutter/material/AppBar-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;AppBar()&lt;/code&gt;&lt;/a&gt; object with some custom parameters.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://api.flutter.dev/flutter/material/AppBar/backgroundColor.html" rel="noopener noreferrer"&gt;&lt;code&gt;backgroundColor&lt;/code&gt;&lt;/a&gt; parameter is set to &lt;a href="https://api.flutter.dev/flutter/material/Colors/grey-constant.html" rel="noopener noreferrer"&gt;&lt;code&gt;grey&lt;/code&gt;&lt;/a&gt;. The &lt;a href="https://api.flutter.dev/flutter/material/AppBar/titleTextStyle.html" rel="noopener noreferrer"&gt;&lt;code&gt;titleTextStyle&lt;/code&gt;&lt;/a&gt; parameter has been set with a custom &lt;a href="https://api.flutter.dev/flutter/dart-ui/Color-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;Color&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://api.flutter.dev/flutter/dart-ui/FontWeight-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;FontWeight&lt;/code&gt;&lt;/a&gt;, this should be applied on the rest of the widgets instantiated below this bar.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://api.flutter.dev/flutter/material/AppBar/leading.html" rel="noopener noreferrer"&gt;&lt;code&gt;leading&lt;/code&gt;&lt;/a&gt; parameter (the right part of the bar) is using a &lt;a href="https://api.flutter.dev/flutter/widgets/Padding-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;Padding&lt;/code&gt;&lt;/a&gt; widget to reduce the side of its child element with the help of an &lt;a href="https://api.flutter.dev/flutter/painting/EdgeInsets-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;EdgeInsets&lt;/code&gt;&lt;/a&gt; object. The offset configured (&lt;code&gt;8.0&lt;/code&gt;) is applied on all sides, thanks to the &lt;a href="https://api.flutter.dev/flutter/painting/EdgeInsets/EdgeInsets.all.html" rel="noopener noreferrer"&gt;&lt;code&gt;EdgeInsets.all()&lt;/code&gt;&lt;/a&gt; constructor. Its only child is &lt;a href="https://api.flutter.dev/flutter/material/CircleAvatar-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;CircleAvatar&lt;/code&gt;&lt;/a&gt; widget containing only the initials of the users as a &lt;a href="https://api.flutter.dev/flutter/widgets/Text-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;Text()&lt;/code&gt;&lt;/a&gt; widget.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://api.flutter.dev/flutter/material/AppBar/title.html" rel="noopener noreferrer"&gt;&lt;code&gt;title&lt;/code&gt;&lt;/a&gt; parameter is only containing the title of the application as in a &lt;a href="https://api.flutter.dev/flutter/widgets/Text-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;Text()&lt;/code&gt;&lt;/a&gt; object, centered via a &lt;a href="https://api.flutter.dev/flutter/widgets/Center-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;Center()&lt;/code&gt;&lt;/a&gt; widget.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://api.flutter.dev/flutter/material/AppBar/actions.html" rel="noopener noreferrer"&gt;&lt;code&gt;actions&lt;/code&gt;&lt;/a&gt; parameter is using a list of &lt;a href="https://api.flutter.dev/flutter/material/IconButton-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;IconButton&lt;/code&gt;&lt;/a&gt; widget, which one set with an &lt;a href="https://api.flutter.dev/flutter/widgets/Icon-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;Icon()&lt;/code&gt;&lt;/a&gt;. The number of icons available with Material is astonishing... I took me a moment to find what I was looking for, &lt;a href="https://api.flutter.dev/flutter/material/Icons/camera_alt_outlined-constant.html" rel="noopener noreferrer"&gt;&lt;code&gt;Icons.camera_alt_outlined&lt;/code&gt;&lt;/a&gt; for the camera and &lt;a href="https://api.flutter.dev/flutter/material/Icons/edit_outlined-constant.html" rel="noopener noreferrer"&gt;&lt;code&gt;Icons.edit_outlined&lt;/code&gt;&lt;/a&gt; for the pencil. Other icons can also be used via the &lt;a href="https://api.flutter.dev/flutter/cupertino/CupertinoIcons-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;CubertinoIcons&lt;/code&gt;&lt;/a&gt; class (usually designed for iOS though).&lt;/p&gt;

&lt;p&gt;No callback functions have been set in the &lt;code&gt;onPressed&lt;/code&gt; parameters, this is not the goal of this article.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvodmhogim052xt2m5fwa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvodmhogim052xt2m5fwa.png" alt="Middle and bottom part of the Home screen" width="596" height="335"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next big part of the Home screen is taking the remaining place. This section can be scrolled. It is made of many similar elements probably derived from a &lt;a href="https://api.flutter.dev/flutter/material/ListTile-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;ListTile&lt;/code&gt;&lt;/a&gt; class. Those are split in 3 part, from the left to the right, the first part is an icon containing a picture of a remote user where we can reuse the &lt;a href="https://api.flutter.dev/flutter/material/CircleAvatar-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;CircleAvatar&lt;/code&gt;&lt;/a&gt; class. The second part contains the name of the user (in bold) above the last messages (if any). The last part contains the date of the last message received (with an attachment or an acknowledgement if any). When the user is tapping on one of these elements, it is redirected to a Chat Screen. The background color of this part of the application is white (&lt;code&gt;#ffffff&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;initials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;str&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;" "&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s"&gt;"?"&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;take&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Firstly, I think creating a function to convert a first name and last name (or any kind of string) to their initials can be helpful for the next part. This is a quick and dirty implementation, the string must be sanitized first and only characters (including UTF8 characters) should be used. It will do the job for know though, it could be nice to see what kind of algorithm SignalApp is using, I think the &lt;a href="https://github.com/signalapp/Signal-Android/blob/c8d2a06676fa3aaee16aa1ca250ffed352f0e5ba/app/src/main/java/org/thoughtcrime/securesms/util/NameUtil.kt#L18" rel="noopener noreferrer"&gt;&lt;code&gt;getAbbreviation()&lt;/code&gt;&lt;/a&gt; method  from &lt;a href="https://github.com/signalapp/Signal-Android/blob/c8d2a06676fa3aaee16aa1ca250ffed352f0e5ba/app/src/main/java/org/thoughtcrime/securesms/util/NameUtil.kt" rel="noopener noreferrer"&gt;&lt;code&gt;org.thoughtcrime.securesms.util.NameUtil&lt;/code&gt;&lt;/a&gt; is the one we are looking for.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;ListTile&lt;/span&gt; &lt;span class="nf"&gt;tile&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;subtitle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;trailing&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;MaterialColor&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;avatarColor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;notifications&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;received&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ListTile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nl"&gt;leading:&lt;/span&gt; &lt;span class="n"&gt;Badge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notifications&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="s"&gt;"0"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nl"&gt;isLabelVisible:&lt;/span&gt; &lt;span class="n"&gt;notifications&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;textColor:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;white&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;backgroundColor:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;CircleAvatar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;backgroundColor:&lt;/span&gt; &lt;span class="n"&gt;avatarColor&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;amber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Container&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;decoration:&lt;/span&gt; &lt;span class="n"&gt;BoxDecoration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;shape:&lt;/span&gt; &lt;span class="n"&gt;BoxShape&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;circle&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s"&gt;"??"&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;initials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="n"&gt;Flex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;direction:&lt;/span&gt; &lt;span class="n"&gt;Axis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;horizontal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;mainAxisAlignment:&lt;/span&gt; &lt;span class="n"&gt;MainAxisAlignment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;spaceBetween&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;children:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;Align&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;alignment:&lt;/span&gt; &lt;span class="n"&gt;Alignment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;topLeft&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="s"&gt;"John Smith"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nl"&gt;style:&lt;/span&gt; &lt;span class="n"&gt;TextStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;fontWeight:&lt;/span&gt; &lt;span class="n"&gt;FontWeight&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;bold&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;fontFamily:&lt;/span&gt; &lt;span class="s"&gt;"Roboto"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Align&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;alignment:&lt;/span&gt; &lt;span class="n"&gt;Alignment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;topRight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;trailing&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="s"&gt;"11:11 AM"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nl"&gt;style:&lt;/span&gt; &lt;span class="n"&gt;notifications&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; 
              &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;TextStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;fontWeight:&lt;/span&gt; &lt;span class="n"&gt;FontWeight&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;bold&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;fontSize:&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;fontFamily:&lt;/span&gt; &lt;span class="s"&gt;"Roboto"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
              &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TextStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;fontWeight:&lt;/span&gt; &lt;span class="n"&gt;FontWeight&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;normal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;fontSize:&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;fontFamily:&lt;/span&gt; &lt;span class="s"&gt;"Roboto"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nl"&gt;subtitle:&lt;/span&gt; &lt;span class="n"&gt;Flex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;direction:&lt;/span&gt; &lt;span class="n"&gt;Axis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;horizontal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;mainAxisAlignment:&lt;/span&gt; &lt;span class="n"&gt;MainAxisAlignment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;spaceBetween&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;children:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;Expanded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;flex:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;subtitle&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="s"&gt;"Last message..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nl"&gt;overflow:&lt;/span&gt; &lt;span class="n"&gt;TextOverflow&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ellipsis&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nl"&gt;style:&lt;/span&gt; &lt;span class="n"&gt;notifications&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; 
              &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;TextStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;fontWeight:&lt;/span&gt; &lt;span class="n"&gt;FontWeight&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;bold&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;fontSize:&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;fontFamily:&lt;/span&gt; &lt;span class="s"&gt;"Roboto"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
              &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TextStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;fontWeight:&lt;/span&gt; &lt;span class="n"&gt;FontWeight&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;normal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;fontSize:&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;fontFamily:&lt;/span&gt; &lt;span class="s"&gt;"Roboto"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;received&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
            &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;Icon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Icons&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;library_add_check_rounded&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;size:&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Container&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nl"&gt;onTap:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another function helper called &lt;code&gt;tile()&lt;/code&gt; will help us to generate each &lt;a href="https://api.flutter.dev/flutter/material/ListTile-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;ListTile()&lt;/code&gt;&lt;/a&gt; objects. This function can take 6 optional arguments, if those are not set, some default values are used instead.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://api.flutter.dev/flutter/material/ListTile/leading.html" rel="noopener noreferrer"&gt;&lt;code&gt;leading&lt;/code&gt;&lt;/a&gt; parameter is containing the user avatar, material gives us the &lt;a href="https://api.flutter.dev/flutter/material/Badge-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;Badge()&lt;/code&gt;&lt;/a&gt; object to deal with that. This one contains a &lt;a href="https://api.flutter.dev/flutter/material/Badge/label.html" rel="noopener noreferrer"&gt;&lt;code&gt;label&lt;/code&gt;&lt;/a&gt; (the blue circle containing the number of notifications). I tried to add a white border on this label, without success, perhaps my future me will fix that. If there is no notification, the &lt;a href="https://api.flutter.dev/flutter/material/Badge/isLabelVisible.html" rel="noopener noreferrer"&gt;&lt;code&gt;isLabelVisible&lt;/code&gt;&lt;/a&gt; parameter can be set to &lt;code&gt;false&lt;/code&gt;, in this case, the label will be removed. The only child is a &lt;a href="https://api.flutter.dev/flutter/material/CircleAvatar-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;CircleAvatar&lt;/code&gt;&lt;/a&gt; using a specific &lt;a href="https://api.flutter.dev/flutter/painting/BoxDecoration-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;BoxDecoration&lt;/code&gt;&lt;/a&gt; to produce a circle via &lt;a href="https://api.flutter.dev/flutter/painting/BoxShape.html#circle" rel="noopener noreferrer"&gt;&lt;code&gt;BoxShape.circle&lt;/code&gt;&lt;/a&gt;. By default, the initials of the &lt;code&gt;title&lt;/code&gt; variable will be used.&lt;/p&gt;

&lt;p&gt;Regarding badges, 2 modules can probably fix most of my issues here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://pub.dev/packages/info_label" rel="noopener noreferrer"&gt;&lt;code&gt;info_label&lt;/code&gt;&lt;/a&gt;: A high-performance Flutter label widget built on CustomPainter. Background, border, text, and overlay indicators are painted directly on canvas for minimal widget overhead;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://pub.dev/packages/badges" rel="noopener noreferrer"&gt;&lt;code&gt;badges&lt;/code&gt;&lt;/a&gt;: A package for creating badges. Badges can be used for an additional marker for any widget, e.g. show a number of items in a shopping cart.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;a href="https://api.flutter.dev/flutter/material/ListTile/title.html" rel="noopener noreferrer"&gt;&lt;code&gt;title&lt;/code&gt;&lt;/a&gt; parameter was configured to set the user name on the left and the date on the right. I thought at first I could use the &lt;a href="https://api.flutter.dev/flutter/material/ListTile/trailing.html" rel="noopener noreferrer"&gt;&lt;code&gt;trailing&lt;/code&gt;&lt;/a&gt; parameter to do that, but it was kinda messy, and way more complex. Anyway, a &lt;a href="https://api.flutter.dev/flutter/widgets/Flex-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;Flex&lt;/code&gt;&lt;/a&gt; widget is used to let us have a flexible positioning across the elements. Indeed, we want the widgets on an horizontal Axis (see &lt;a href="https://api.flutter.dev/flutter/painting/Axis.html#horizontal" rel="noopener noreferrer"&gt;&lt;code&gt;Axis.horizontal&lt;/code&gt;&lt;/a&gt;) with a space between them (see &lt;a href="https://api.flutter.dev/flutter/rendering/MainAxisAlignment.html#spaceBetween" rel="noopener noreferrer"&gt;&lt;code&gt;MainAxisAlignment.spaceBetween&lt;/code&gt;&lt;/a&gt;). Then the 2 widgets children can be aligned using the &lt;a href="https://api.flutter.dev/flutter/painting/Alignment/topLeft-constant.html" rel="noopener noreferrer"&gt;&lt;code&gt;Alignment.topLeft&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://api.flutter.dev/flutter/painting/Alignment/topRight-constant.html" rel="noopener noreferrer"&gt;&lt;code&gt;Alignment.topRight&lt;/code&gt;&lt;/a&gt; constants. Nothing smart here, both of them are &lt;a href="https://api.flutter.dev/flutter/widgets/Text-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;Text()&lt;/code&gt;&lt;/a&gt; widgets. &lt;/p&gt;

&lt;p&gt;The &lt;a href="https://api.flutter.dev/flutter/material/ListTile/subtitle.html" rel="noopener noreferrer"&gt;&lt;code&gt;subtitle&lt;/code&gt;&lt;/a&gt; is using the same hack than the &lt;code&gt;title&lt;/code&gt; parameter previously described. An element to left (last message from the remote user) and another one to the right (message ack).&lt;/p&gt;

&lt;p&gt;&lt;code&gt;onTap&lt;/code&gt; is not configured yet, but the idea when someone is tapping on one tile, he will be redirected to the chat screen.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Conversations&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatelessWidget&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Conversations&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ListView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;children:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;tile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;avatarColor:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;blueGrey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="s"&gt;"Tina Ukuku"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;subtitle:&lt;/span&gt; &lt;span class="s"&gt;"You set disappearing message time to 1..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;avatarText:&lt;/span&gt; &lt;span class="s"&gt;"TU"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;tile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;avatarColor:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;indigo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="s"&gt;"Chairman Meow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;subtitle:&lt;/span&gt; &lt;span class="s"&gt;"Missed call"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;avatarText:&lt;/span&gt; &lt;span class="s"&gt;"CM"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;notifications:&lt;/span&gt; &lt;span class="s"&gt;"2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;tile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;avatarColor:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;lightGreen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="s"&gt;"Myles Larson"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;subtitle:&lt;/span&gt; &lt;span class="s"&gt;"🎤 Voice Message"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;trailing:&lt;/span&gt; &lt;span class="s"&gt;"11:07 AM"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;notifications:&lt;/span&gt; &lt;span class="s"&gt;"7"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;tile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;avatarColor:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;lime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="s"&gt;"Cat Chat 🐈 🐱"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;subtitle:&lt;/span&gt; &lt;span class="s"&gt;"This is the instruction manual. 📎 Attachment"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;trailing:&lt;/span&gt; &lt;span class="s"&gt;"11:02 AM"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;tile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;avatarColor:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;blueGrey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="s"&gt;"Ali Smith"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;subtitle:&lt;/span&gt; &lt;span class="s"&gt;"📷 Attachment"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;trailing:&lt;/span&gt; &lt;span class="s"&gt;"10:38 AM"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;received:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;tile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;avatarColor:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;indigo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="s"&gt;"Kirk Family"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;subtitle:&lt;/span&gt; &lt;span class="s"&gt;"Happy birthday to you. Happy birthday to you!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;trailing:&lt;/span&gt; &lt;span class="s"&gt;"9:13 AM"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;notifications:&lt;/span&gt; &lt;span class="s"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;tile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;avatarColor:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;lightGreen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="s"&gt;"Jordan B."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;subtitle:&lt;/span&gt; &lt;span class="s"&gt;"Sticker Message"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;received:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;tile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;avatarColor:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;lime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="s"&gt;"Sunsets 🌅"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;subtitle:&lt;/span&gt; &lt;span class="s"&gt;"View-once media"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;trailing:&lt;/span&gt; &lt;span class="s"&gt;"TUE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;received:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;tile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;avatarColor:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;blueGrey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="s"&gt;"🧗‍♂️ Rock Climbers"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;subtitle:&lt;/span&gt; &lt;span class="s"&gt;"Which route should we take?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;trailing:&lt;/span&gt; &lt;span class="s"&gt;"TUE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;tile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;avatarColor:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;indigo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="s"&gt;"Nikki R."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;subtitle:&lt;/span&gt; &lt;span class="s"&gt;"Thanks! What a wonderful message to read in the morning!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;trailing:&lt;/span&gt; &lt;span class="s"&gt;"TUE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;tile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;avatarColor:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;lightGreen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="s"&gt;"Weather Forecasts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;subtitle:&lt;/span&gt; &lt;span class="s"&gt;"Raining all day 📷 Attachment"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;trailing:&lt;/span&gt; &lt;span class="s"&gt;"MON"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;tile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;avatarColor:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;lime&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;tile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;avatarColor:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;blueGrey&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;tile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;avatarColor:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;indigo&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;tile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;avatarColor:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;lightGreen&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;tile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;avatarColor:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;lime&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now our &lt;code&gt;tile()&lt;/code&gt; function is ready, we can create our &lt;code&gt;Conversations()&lt;/code&gt; object containing the list of user chats. This part creates a &lt;a href="https://api.flutter.dev/flutter/widgets/ListView-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;ListView&lt;/code&gt;&lt;/a&gt; object, containing a list of &lt;a href="https://api.flutter.dev/flutter/material/ListTile-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;ListTile&lt;/code&gt;&lt;/a&gt; created by our helper function. This part seems complex, but only one function is used many time to generated content and mimic the screenshot.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Home&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatelessWidget&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Scaffold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;appBar:&lt;/span&gt; &lt;span class="n"&gt;homeBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="n"&gt;Conversations&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything looks good, we then mix everything together inside an &lt;code&gt;Home&lt;/code&gt; class returning a &lt;a href="https://api.flutter.dev/flutter/material/Scaffold-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;Scaffold()&lt;/code&gt;&lt;/a&gt; object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyApp&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatelessWidget&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;MyApp&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;MaterialApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="s"&gt;'SignalCopyCat'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;home:&lt;/span&gt; &lt;span class="n"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="nl"&gt;theme:&lt;/span&gt; &lt;span class="n"&gt;ThemeData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;fontFamily:&lt;/span&gt; &lt;span class="s"&gt;"Roboto"&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nl"&gt;themeMode:&lt;/span&gt; &lt;span class="n"&gt;ThemeMode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;light&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;debugShowCheckedModeBanner:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;runApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;MyApp&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, the last step, we can create also our application using a &lt;a href="https://api.flutter.dev/flutter/material/MaterialApp-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;MaterialApp()&lt;/code&gt;&lt;/a&gt; object.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frpt2tv7c2xexzouyt7v0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frpt2tv7c2xexzouyt7v0.png" alt="SignalApp UI Home Screen but with Flutter" width="800" height="1778"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Above, a screenshot of this application from the Android emulator. The images/pictures are missing on purpose, I was not able to found the "correct" images. As you can see, the emojis are not correctly rendered as well. Still few bugs to fix, but I did that in few hours.&lt;/p&gt;

&lt;p&gt;I forgot something though, a big part of my code is using custom fonts (&lt;code&gt;Roboto&lt;/code&gt;). Fonts are kind of assets and must be downloaded then configured in the project. The &lt;a href="-%20https://docs.flutter.dev/cookbook/design/fonts"&gt;Use a custom font&lt;/a&gt; article on the official Flutter documentation is doing a great job to explain that.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;I’ve been imitated so well, I’ve heard people copy my mistakes.&lt;br&gt;
-- Jimi Hendrix&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is not the perfect 1:1 reproduction of the SignalApp Home screen... I still need more experience with Flutter. The terms used are also new to me, finding one icon similar to the one from the screenshot is taking time for example. The infinite amount of solution to display something is also time consuming. Here a list of few resources I checked during this experience.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://stackoverflow.com/a/58470417" rel="noopener noreferrer"&gt;Flutter - Wrapping text&lt;/a&gt; on StackOverflow;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.flutter.dev/ui/layout#lay-out-multiple-widgets-vertically-and-horizontally" rel="noopener noreferrer"&gt;Lay out multiple widgets vertically and horizontally&lt;/a&gt; from Flutter Documentation;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://stackoverflow.com/questions/61979970/how-to-show-a-notification-badge-on-the-app-icon-using-flutter" rel="noopener noreferrer"&gt;How to show a notification badge on the app icon using flutter?&lt;/a&gt; on StackOverflow;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://stackoverflow.com/questions/62227581/flutter-circle-avatar-with-border" rel="noopener noreferrer"&gt;Flutter CIrcle Avatar With Border&lt;/a&gt; on StackOverflow&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://stackoverflow.com/questions/57777737/flutter-give-container-rounded-border" rel="noopener noreferrer"&gt;Flutter give container rounded border&lt;/a&gt; on StackOverflow&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://stackoverflow.com/questions/71035632/how-to-make-a-widget-maximum-size-be-equal-to-the-flex-value-in-a-row" rel="noopener noreferrer"&gt;How to make a widget maximum size be equal to the flex value in a row&lt;/a&gt; on StackOverflow&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://fonts.google.com/selection?query=roboto" rel="noopener noreferrer"&gt;&lt;code&gt;Roboto&lt;/code&gt; fonts&lt;/a&gt; from Google fonts, an alternative font to the one used by &lt;a href="https://madegooddesigns.com/signal-font/" rel="noopener noreferrer"&gt;Signal&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Anyway, copying and reproducing is a great way to learn. It also train the mind to decompose every applications in small pieces. The same could have been done for &lt;a href="https://briarproject.org/" rel="noopener noreferrer"&gt;Briar&lt;/a&gt;, &lt;a href="https://simplex.chat/" rel="noopener noreferrer"&gt;SimpleX&lt;/a&gt; or &lt;a href="https://telegram.org/" rel="noopener noreferrer"&gt;Telegram&lt;/a&gt; applications, all of them are open-source and available on Github, Gitlab or somewhere else. Some of them are supporting mobile, desktop and web platform, in short, they are great targets. To summarize, if you want to learn how to design UI, try to recreate the design and the style of your favorite application.&lt;/p&gt;

&lt;p&gt;The next step? Why not create an open-source library in Dart/Flutter exposing these styles? Perhaps one day...&lt;/p&gt;

&lt;p&gt;Happy Hack and Have fun!&lt;/p&gt;




&lt;p&gt;Cover Image by &lt;a href="https://unsplash.com/@sigmund?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Compagnons&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/red-and-purple-light-digital-wallpaper-t-da_md1qMc?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt; &lt;/p&gt;

</description>
      <category>flutter</category>
      <category>signalapp</category>
      <category>design</category>
      <category>ui</category>
    </item>
    <item>
      <title>Substack/0: Authentication and Content Management</title>
      <dc:creator>Mathieu Kerjouan</dc:creator>
      <pubDate>Mon, 22 Jun 2026 08:00:00 +0000</pubDate>
      <link>https://dev.to/niamtokik/substack0-authentication-and-content-management-119n</link>
      <guid>https://dev.to/niamtokik/substack0-authentication-and-content-management-119n</guid>
      <description>&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Reverse_engineering" rel="noopener noreferrer"&gt;Reverse engineering&lt;/a&gt; network protocol is an interesting process, firstly because one can learn a lot of things you would probably never thought of, and secondly because it is a deep dive into developers logic. Reverse engineering is challenging, and is usually made of really small victories. Why doing that? Well, why not? In fact, most of those companies are selling our private data, our behaviors and our stats to other private companies, why should also be forced to use their own implementation of their client? That's probably one part of the idea... But it can also be to implement a library to communicate with a private API and give us the possibility to automatize recurrent tasks.&lt;/p&gt;

&lt;p&gt;Now, why &lt;a href="https://substack.com/" rel="noopener noreferrer"&gt;Substack&lt;/a&gt;? This is still a small social network, inspired by &lt;a href="http://x.com/" rel="noopener noreferrer"&gt;X/Twitter&lt;/a&gt; and mixed with a bit of &lt;a href="https://wordpress.com/" rel="noopener noreferrer"&gt;Wordpress&lt;/a&gt;. It's a kind of micro-blogging platform and even if they are offering a &lt;a href="https://substackapi.dev/quickstart" rel="noopener noreferrer"&gt;public API&lt;/a&gt;, using the private one is a good way to learn how their web and mobile applications are working. Furthermore, doing reversing engineering on a web stack can lead to interesting result. Indeed, a web application is made in JavaScript, most of the time using &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/AJAX" rel="noopener noreferrer"&gt;AJAX&lt;/a&gt; with stateless HTTP requests. It can be generalized and some tools can be created to help reverse engineering other private API.&lt;/p&gt;

&lt;p&gt;Anyway, Reverse Engineering is a goddamn great way to improve all your skills. Let's. Go.&lt;/p&gt;

&lt;h1&gt;
  
  
  Preparation
&lt;/h1&gt;

&lt;p&gt;The main tool we will use for this article will be &lt;a href="https://curl.se/" rel="noopener noreferrer"&gt;&lt;code&gt;curl&lt;/code&gt;&lt;/a&gt; and any kind of browser with a &lt;a href="https://www.chromium.org/chromium-os/developer-library/guides/device/developer-mode/" rel="noopener noreferrer"&gt;developer mode activated&lt;/a&gt;. In the developer window, got to the Network tab and find a way to preserve the logs (e.g. &lt;code&gt;Preserve log&lt;/code&gt; radio button on Chromium). A social network can produce a lot of noise, you should also filter the requests and display only the &lt;code&gt;Fetch/XHR&lt;/code&gt; ones.&lt;/p&gt;

&lt;p&gt;Having an easy way to document and test the API is also a good thing. If you are not familiar with &lt;a href="https://www.openapis.org/" rel="noopener noreferrer"&gt;OpenAPI&lt;/a&gt;, it's a good time to check that. If you are lazy, you can also use &lt;a href="https://insomnia.rest/" rel="noopener noreferrer"&gt;Insomnia&lt;/a&gt;, it's free and open-source. This tool is an alternative to &lt;a href="https://www.postman.com/downloads/" rel="noopener noreferrer"&gt;Postman&lt;/a&gt;. It will help us to create the specification and easily tests our finding. You can see this tool also as an extension of &lt;code&gt;curl&lt;/code&gt;, with a GUI.&lt;/p&gt;

&lt;p&gt;Automated tools can help you to do more, but the article here is not to show you how to use them, it's to show you how to reverse engineering an interface with cheap tools anyone can use. Yes, it's perhaps slower, but at least, you will learn a lot. You will also try and fail, the best way to learn discipline and tenacity.&lt;/p&gt;

&lt;h1&gt;
  
  
  Requirements
&lt;/h1&gt;

&lt;p&gt;If you have your own Substack account, I would recommend to create a new one dedicated for your reverse engineering goal. Perhaps more than one account, because if you want to publish/subscribe to someone else, you will need more than one account. Most of the companies are not allowing us to use our own clients to deal with their private API, and those accounts can be kicked, blocked, banned or simply removed because of that. You can use temporary email services like &lt;a href="http://yopmail.com" rel="noopener noreferrer"&gt;yopmail&lt;/a&gt;, &lt;a href="https://10minutemail.com/" rel="noopener noreferrer"&gt;10minutemail&lt;/a&gt; or any other alternative. You can also create a mail at &lt;a href="https://mail.tutanota.com/" rel="noopener noreferrer"&gt;Tutanota&lt;/a&gt; or &lt;a href="https://proton.me/mail" rel="noopener noreferrer"&gt;Protonmail&lt;/a&gt; if you want, probably better for long term reverse engineering sessions. For the rest of this post I will use a fresh account from 10minutemail, in case of credential leaks, it will not be a problem. Here more alternative, be careful, some of them will be blocked by the services you want to reverse engineering.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://10minutemail.com/" rel="noopener noreferrer"&gt;10 Minute Email&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.33mail.com/" rel="noopener noreferrer"&gt;33Mail&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.emailondeck.com/" rel="noopener noreferrer"&gt;Email on Deck&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://erine.email/" rel="noopener noreferrer"&gt;erine.email&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.fakemailgenerator.com/" rel="noopener noreferrer"&gt;fake Mail Generator&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.guerrillamail.com/" rel="noopener noreferrer"&gt;Guerilla Mail&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://inboxes.com/" rel="noopener noreferrer"&gt;Inboxes&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://maildrop.cc/" rel="noopener noreferrer"&gt;Maildrop&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.mailinator.com/" rel="noopener noreferrer"&gt;Mailinator&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://mailnesia.com/" rel="noopener noreferrer"&gt;Mailnesia&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.mintemail.com/" rel="noopener noreferrer"&gt;MintEmail&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.mohmal.com/en" rel="noopener noreferrer"&gt;Mohmal&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://reusable.email/" rel="noopener noreferrer"&gt;Reusable Email&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="//temp-mail.org"&gt;Temp Mail&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://tempr.email/" rel="noopener noreferrer"&gt;Tempr Email&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://tempail.com/en/" rel="noopener noreferrer"&gt;Tempail&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://temporarymail.com/" rel="noopener noreferrer"&gt;Temporary Mail&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.33mail.com/" rel="noopener noreferrer"&gt;33Mail&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Another thing is to use &lt;a href="https://manpages.debian.org/trixie/tor/tor.1.en.html" rel="noopener noreferrer"&gt;Tor&lt;/a&gt;, &lt;a href="https://manpages.debian.org/trixie/proxychains4/proxychains.1.en.html" rel="noopener noreferrer"&gt;Proxychains&lt;/a&gt; (via an &lt;a href="https://github.com/proxifly/free-proxy-list" rel="noopener noreferrer"&gt;open proxy list)&lt;/a&gt; or a VPN to avoid sharing your personal IP address. Some service providers can tag your IP address and share it with other companies. It is especially true for the big companies like Google, Facebook or X/Twitter. If you are trying to reverse engineer one of them, use something to hide your true IP address. If you have Tor configured on your dev machine, you can configure it for curl by setting &lt;code&gt;HTTP_PROXY&lt;/code&gt;, &lt;code&gt;HTTPS_PROXY&lt;/code&gt; or &lt;code&gt;ALL_PROXY&lt;/code&gt; &lt;a href="https://manpages.debian.org/trixie/curl/curl.1.en.html#ENVIRONMENT" rel="noopener noreferrer"&gt;environment variables&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ALL_PROXY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://127.0.0.1:9081
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you prefer control those proxy via the CLI, pass your proxy address to the &lt;a href="https://manpages.debian.org/trixie/curl/curl.1.en.html#ENVIRONMENT--proxy" rel="noopener noreferrer"&gt;&lt;code&gt;--proxy&lt;/code&gt;&lt;/a&gt; (or &lt;code&gt;-x&lt;/code&gt;) argument. I don't really have any preferences there though, you can use whatever methods you like.&lt;/p&gt;

&lt;p&gt;Last thing, if you are using someone else IP address, you will probably need to deal with captchas. Indeed, Tor nodes are public and some companies are blocking them (in fact, that's a good practice, a company who wants to offer better privacy would probably use onion hidden services). If you got captchas, solving them with &lt;code&gt;curl&lt;/code&gt; can be a real challenge, but you can easily found some crazy guys on the who who already worked on that, like &lt;a href="https://www.capsolver.com/blog/All/solve-captcha-with-curl" rel="noopener noreferrer"&gt;Solving CAPTCHA with cURL: A Step-by-Step Guide&lt;/a&gt; by Lucas Mitchell or the &lt;a href="https://github.com/DenimEvert/curl-scraping" rel="noopener noreferrer"&gt;&lt;code&gt;curl-scraping&lt;/code&gt;&lt;/a&gt; project.&lt;/p&gt;

&lt;h1&gt;
  
  
  HTTP Headers
&lt;/h1&gt;

&lt;p&gt;When using substack, you will probably notice in the developer window a lot of requests. Those requests contain HTTP headers. Most of them will be required to mimic the behavior of the Substack web application. Not all are required, it will depend of the context.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Accept" rel="noopener noreferrer"&gt;&lt;code&gt;Accept&lt;/code&gt;&lt;/a&gt;:  default to &lt;code&gt;*/*&lt;/code&gt;, to be set with the &lt;code&gt;--header&lt;/code&gt; curl flag;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Accept-Encoding" rel="noopener noreferrer"&gt;&lt;code&gt;Accept-Encoding&lt;/code&gt;&lt;/a&gt;: default to &lt;code&gt;gzip, deflate, br, zstd&lt;/code&gt;, to be set with the &lt;code&gt;--header&lt;/code&gt; curl flag;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Accept-Language" rel="noopener noreferrer"&gt;&lt;code&gt;Accept-Language&lt;/code&gt;&lt;/a&gt;: default to &lt;code&gt;en-US,en;q=0.5&lt;/code&gt;, to be set with the &lt;code&gt;--header&lt;/code&gt; curl flag;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cache-Control" rel="noopener noreferrer"&gt;&lt;code&gt;Cache-Control&lt;/code&gt;&lt;/a&gt;: default to &lt;code&gt;max-age=0&lt;/code&gt;, to be set with the &lt;code&gt;--header&lt;/code&gt; curl flag;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Type" rel="noopener noreferrer"&gt;&lt;code&gt;Content-Type&lt;/code&gt;&lt;/a&gt;: default to &lt;code&gt;application/json&lt;/code&gt;, this header should be set only for &lt;code&gt;POST&lt;/code&gt; and &lt;code&gt;DELETE&lt;/code&gt; requests for now;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cookie" rel="noopener noreferrer"&gt;&lt;code&gt;Cookie&lt;/code&gt;&lt;/a&gt;: this value is configured by curl with the help of the &lt;code&gt;-b&lt;/code&gt; and &lt;code&gt;-c&lt;/code&gt; flags;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Priority" rel="noopener noreferrer"&gt;&lt;code&gt;Priority&lt;/code&gt;&lt;/a&gt;: set by default to &lt;code&gt;u=1, i&lt;/code&gt;;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Referer" rel="noopener noreferrer"&gt;&lt;code&gt;Referer&lt;/code&gt;&lt;/a&gt;: set by default to &lt;code&gt;https://substack.com/&lt;/code&gt;;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Sec-CH-UA" rel="noopener noreferrer"&gt;&lt;code&gt;sec-ch-ua&lt;/code&gt;&lt;/a&gt;: set with the name and the version of the browser, for example, in my case: &lt;code&gt;"Brave";v="147", "Not.A/Brand";v="8", "Chromium";v="147"&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Sec-CH-UA-Mobile" rel="noopener noreferrer"&gt;&lt;code&gt;sec-ch-ua-mobile&lt;/code&gt;&lt;/a&gt;: set by default to &lt;code&gt;?0&lt;/code&gt;;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Sec-CH-UA-Platform" rel="noopener noreferrer"&gt;&lt;code&gt;sec-ch-ua-platform&lt;/code&gt;&lt;/a&gt;: set with your platform code name, for example on my case, on one of my Linux box: &lt;code&gt;"Linux"&lt;/code&gt;;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Sec-Fetch-Dest" rel="noopener noreferrer"&gt;&lt;code&gt;sec-fetch-dest&lt;/code&gt;&lt;/a&gt;: set by default to &lt;code&gt;empty&lt;/code&gt;;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Sec-Fetch-Mode" rel="noopener noreferrer"&gt;&lt;code&gt;sec-fetch-mode&lt;/code&gt;&lt;/a&gt;: set by default to &lt;code&gt;cors&lt;/code&gt;;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Sec-Fetch-Site" rel="noopener noreferrer"&gt;&lt;code&gt;sec-fetch-site&lt;/code&gt;&lt;/a&gt;: set by default to &lt;code&gt;same-origin&lt;/code&gt;;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Sec-GPC" rel="noopener noreferrer"&gt;&lt;code&gt;sec-gpc&lt;/code&gt;&lt;/a&gt;: set by default to &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can make our life a bit easier because most of those headers are statics and will probably never changes during across the HTTP requests. Creating environment variables can solve this issue, or even better, use &lt;a href="https://manpages.debian.org/trixie/curl/curl.1.en.html#H," rel="noopener noreferrer"&gt;header files&lt;/a&gt; with the &lt;code&gt;--headers&lt;/code&gt; argument from &lt;code&gt;curl&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; headers.txt &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
&lt;/span&gt;&lt;span class="go"&gt;Accept: */*
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;be sure curl is compatible with &lt;span class="nb"&gt;gzip&lt;/span&gt;,
&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;deflate, br or zstd. Those encodings are
&lt;span class="gp"&gt;#&lt;/span&gt;&amp;nbsp;the one &lt;span class="nb"&gt;set &lt;/span&gt;on Brave browser by default.
&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;If you receive a binary object from the
&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Substack end-points, it will probably
&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;because of that.
&lt;span class="go"&gt;Accept-Encoding: gzip, deflate, br, zstd
&lt;/span&gt;&lt;span class="gp"&gt;Accept-Language: en-US,en;&lt;/span&gt;&lt;span class="nv"&gt;q&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0.5
&lt;span class="go"&gt;Cache-Control: max-age=0
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Not all requests are sending data.
&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;Content-Type: application/json
&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;The cookies are managed with &lt;span class="nt"&gt;-j&lt;/span&gt; and &lt;span class="nt"&gt;-c&lt;/span&gt; arguments
&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;Cookie: 
&lt;span class="gp"&gt;Priority: u=1, i;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;Referer: https://substack.com/
&lt;/span&gt;&lt;span class="gp"&gt;sec-ch-ua: "Brave";&lt;/span&gt;&lt;span class="nv"&gt;v&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"147"&lt;/span&gt;, &lt;span class="s2"&gt;"Not.A/Brand"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nv"&gt;v&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"8"&lt;/span&gt;, &lt;span class="s2"&gt;"Chromium"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nv"&gt;v&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"147
&lt;/span&gt;&lt;span class="go"&gt;sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Linux"
sec-fetch-dest: empty
sec-fetch-mode: cors
sec-fetch-site: same-origin
sec-gpc: 1
&lt;/span&gt;&lt;span class="gp"&gt;User-Agent: Mozilla/5.0 (X11;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Linux x86_64&lt;span class="o"&gt;)&lt;/span&gt; AppleWebKit/537.36 &lt;span class="o"&gt;(&lt;/span&gt;KHTML, like Gecko&lt;span class="o"&gt;)&lt;/span&gt; Chrome/134.0.0.0 Safari/537.3
&lt;span class="go"&gt;EOF

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-H&lt;/span&gt;@headers.txt &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;target&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I invite you to read the headers specifications from the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers" rel="noopener noreferrer"&gt;Mozilla developer website&lt;/a&gt;. It contains a lot of useful information, and will probably be more accurate than me.&lt;/p&gt;

&lt;h1&gt;
  
  
  Authentication
&lt;/h1&gt;

&lt;p&gt;Substack is using MFA with OTP code via e-mail. If your account was correctly created previously, you can try to log to the web interface. A JSON is required though, it will contain your email, and few other fields:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;email&lt;/code&gt;: your email address as string&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;redirect&lt;/code&gt;: the redirected path as string&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;can_create_user&lt;/code&gt;: a boolean? I currently don't know this thing means.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The JSON object can now be sent to &lt;code&gt;https://substack.com/api/v1/email-login&lt;/code&gt; end-point. Note: you should probably store the cookies you received in &lt;code&gt;cookie.txt&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;touch &lt;/span&gt;cookie.txt
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;CURL_OPTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"-vLb cookie.txt -c cookie.txt -H@headers.txt"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The JSON object to authenticate via e-mail is kind simple, it has 3 fields, &lt;code&gt;email&lt;/code&gt;, &lt;code&gt;redirect&lt;/code&gt; and &lt;code&gt;can_create_user&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"x@vtmpj.net"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"redirect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"can_create_user"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let save that in &lt;code&gt;email_login.json&lt;/code&gt; and use it with &lt;code&gt;curl&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; email_login.json &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
&lt;/span&gt;&lt;span class="go"&gt;{
  "email": "x@vtmpj.net",
  "redirect": "/",
  "can_create_user": true
}
EOF

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CURL_OPTS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="go"&gt;  -H 'Content-Type: application/json' \
  -d @email_login.json \
  -X POST \
  https://substack.com/api/v1/email-login 
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It should return this kind of JSON object, containing &lt;code&gt;verification_code&lt;/code&gt; and &lt;code&gt;onboarding_redirect&lt;/code&gt; fields.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"verification_code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"optional"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"onboarding_redirect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After having executed this command, you should get an e-mail from substack  containing the 6 digits OTP code. If it's the case, you can send this code to the substack API to valid your authentication via OTP. The JSON object here is quite simple as well with 3 fields, where &lt;code&gt;code&lt;/code&gt; is containing the OTP code received by mail, &lt;code&gt;email&lt;/code&gt; containing the e-mail used for your &lt;code&gt;authentication&lt;/code&gt; and &lt;code&gt;redirection&lt;/code&gt; containing the URL to be redirected after a successful authentication.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"764945"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"x@vtmpj.net"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"redirect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://substack.com/"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let store this object in &lt;code&gt;email_otp_login_complete.json&lt;/code&gt; file for now.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; email_otp_login_complete.json &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
&lt;/span&gt;&lt;span class="go"&gt;{
  "code": "764945",
  "email": "x@vtmpj.net",
  "redirect": "https://substack.com/"
}
EOF 

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CURL_OPTS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="go"&gt;  -H 'Content-Type: application/json' \
  -d @email_otp_login_complete.json \
  -X POST \
  https://substack.com/api/v1/email-otp-login/complete
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After this request, you should now have a valid authenticated cookie containing your session. On my side, the session received was valid for 90 days (~3months).&lt;/p&gt;

&lt;p&gt;If your headers are not correctly configured (for example, you forgot to add the application type one), this kind of JSON object will be returned:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"errors"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"location"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"param"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"msg"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Invalid value"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"location"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"param"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"msg"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Invalid value"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"location"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"param"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"msg"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Invalid value"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While waiting for the OTP via e-mail, the application is sending a kind of ping.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CURL_OPTS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="go"&gt;  https://substack.com/api/v1/am_i_logged_in
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During the session, I also discover the link sent by Substack in the e-mail to validate the logins stay valid for a long period of time (still not sure of the delay though). In fact, after 2 successful login via e-mail, the requests started to return this message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Too many login emails"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"single"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If it's the case, it's probably mean the links from your e-mails are still active, and you can use them to fetch a session via &lt;code&gt;curl&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CURL_OPTS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="gp"&gt;  'https://email.mg-tx1.substack.com/c/$&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;payload&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;'
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;${payload}&lt;/code&gt; value is a base64uri string, probably encrypted (or random), used on the Substack side to validate the authentication. If this previous command is working, you should then be redirected to the website with a new session stored in the cookies.&lt;/p&gt;

&lt;p&gt;Note: it seems Substack is using HTTP/3.&lt;/p&gt;

&lt;h1&gt;
  
  
  Creating Content
&lt;/h1&gt;

&lt;p&gt;Posting messages on Substack look straightforward, but it can be challenging due to the JSON object asked by the end-point. Here an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"bodyJson"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"doc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"attrs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"schemaVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"v1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"paragraph"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"On Friday, it’s Erlang day."&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tabId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"for-you"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"surface"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"feed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"replyMinimumRole"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"everyone"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, Substack's post are following a specific schema. I'm not sure this is an open-source format, but you can find resources on the web for that, like the documentation from &lt;a href="https://substack-api.readthedocs.io/api-reference/" rel="noopener noreferrer"&gt;&lt;code&gt;substack-api&lt;/code&gt;&lt;/a&gt; on readthedocs. Anyway, let reuse that and past it in &lt;code&gt;message.json&lt;/code&gt; then invoke &lt;code&gt;curl&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; message.json &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
&lt;/span&gt;&lt;span class="go"&gt;{
  "bodyJson": {
    "type": "doc",
    "attrs": {
      "schemaVersion": "v1",
      "title": null
    },
    "content": [
      {
        "type": "paragraph",
        "content": [
          {
            "type": "text",
            "text": "On Friday, it’s Erlang day."
          }
        ]
      }
    ]
  },
  "tabId": "for-you",
  "surface": "feed",
  "replyMinimumRole": "everyone"
}
EOF

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CURL_OPTS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="go"&gt;  -H "Content-Type: application/json" \
  -X POST \
  -d @message.json\
  https://substack.com/api/v1/comment/feed
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In case of success, you will receive a JSON object containing a lot of information:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  "user_id": 12345678,
  "body": "My awesome message",
  "body_json": {
    "type": "doc",
    "attrs": {
      "schemaVersion": "v1",
      "title": null
    },
    "content": [
      {
        "type": "paragraph",
        "content": [
          {
            "type": "text",
            "text": "My awesome message"
          }
        ]
      }
    ]
  },
  "post_id": null,
  "publication_id": null,
  "media_clip_id": null,
  "ancestor_path": "",
  "type": "feed",
  "status": "published",
  "reply_minimum_role": "everyone",
  "id": 12345678,
  "deleted": false,
  "date": "2026-06-19T11:00:00.000Z",
  "name": "Aragog",
  "photo_url": "https://substack-post-media.s3.amazonaws.com/public/images/5d26902d-0000-abcd-1234-e82c0d0e9d55_144x144.png",
  "reactions": {
    "❤": 0
  },
  "children": [],
  "userStatus": {
    "bestsellerTier": null,
    "subscriberTier": null,
    "leaderboard": null,
    "vip": false,
    "badge": null,
    "subscriber": null
  },
  "user_bestseller_tier": null,
  "isFirstFeedCommentByUser": false,
  "reaction_count": 0,
  "restacks": 0,
  "restacked": false,
  "children_count": 0,
  "attachments": [],
  "language": null,
  "autotranslate_to": null
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great, we have published our first post with &lt;code&gt;curl&lt;/code&gt;. The interesting field for now from this payload:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;user_id&lt;/code&gt;: the identifier of the substack user, I think we can get it from another end-point, but it can be useful to have it;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;body&lt;/code&gt;: the body of the message published;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;body_json&lt;/code&gt;: the body using the Substack JSON format;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;status&lt;/code&gt;: the status of the post, if &lt;code&gt;published&lt;/code&gt;, then the post can be seen by everybody;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;id&lt;/code&gt;: probably the post identifier, we will check that later;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;date&lt;/code&gt;: the publication UTC date;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;name&lt;/code&gt;: the name of the Substack user;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The rest is mostly details we don't care for now.&lt;/p&gt;

&lt;h1&gt;
  
  
  Getting Content
&lt;/h1&gt;

&lt;p&gt;Many end-points can be used to retrieve posts information, in our case, we want the one to fetch only one comment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CURL_OPTS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="gp"&gt;  https://substack.com/api/v1/reader/comment/$&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;post_id&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The returned JSON object is huge. I will select only few of interesting fields.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"item"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"entity_key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"c-123456"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"comment"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"context"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"comment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;123456&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"body_json"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;521046804&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"feed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-06-19T12:04:29.130Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"reply_minimum_role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"everyone"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"media_clip_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Aragog"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"bio"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"handle"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"aragog"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"reaction_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"reactions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"❤"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"restacks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"restacked"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"children_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"attachments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"user_bestseller_tier"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"userStatus"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"language"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"autotranslate_to"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"parentComments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"isMuted"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"canReply"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"trackingParameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This object contains all information regarding the message we have posted on Substack, including lot of useful statistics.&lt;/p&gt;

&lt;h1&gt;
  
  
  Deleting Content
&lt;/h1&gt;

&lt;p&gt;Deleting content is easy, we can reuse the same end-point but using the DELETE HTTP method instead.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CURL_OPTS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="go"&gt;  -H "Content-Type: application/json" \
  -d '{}' \
  -X DELETE \
&lt;/span&gt;&lt;span class="gp"&gt;  https://substack.com/api/v1/comment/$&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;post_id&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In case of success, the end-point returns a 200 HTTP code with an empty JSON object.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;The full reverse engineering of the Substack private API is far from done and more is coming. I think we will have a lot of surprises in the future with this one. This is another reason why testing is so important, and if you can store the results of those tests, it's even better.&lt;/p&gt;

&lt;p&gt;In this article, you saw how to use &lt;code&gt;curl&lt;/code&gt; to call a private API, with the help of the browser to generate the right call. A draft of the &lt;a href="https://gist.github.com/niamtokik/4a52f58d4027cad5338d53b9c43c66c2" rel="noopener noreferrer"&gt;Insomnia scratch pad&lt;/a&gt; is available on Gist for now.&lt;/p&gt;

&lt;p&gt;I am not the only one working on this kind of projects, few of them are even sharing their code sources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/NHagar/substack_api" rel="noopener noreferrer"&gt;&lt;code&gt;substack_api&lt;/code&gt;&lt;/a&gt; by &lt;a href="https://github.com/NHagar" rel="noopener noreferrer"&gt;Nick Hagar&lt;/a&gt; on Github, an implementation in Python;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/ma2za/python-substack" rel="noopener noreferrer"&gt;&lt;code&gt;python-substack&lt;/code&gt;&lt;/a&gt; by &lt;a href="https://github.com/ma2za" rel="noopener noreferrer"&gt;Paolo Mazza&lt;/a&gt; on Github, another implementation in Python.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Anyway, this is the first publication of a long series about reverse engineering, and to be honest, I think starting our journey by reversing a public HTTP API is kinda easy. We don't have to deal with proprietary binary format or weird protocol designed in-house.&lt;/p&gt;

&lt;p&gt;As usual, Hack well and have fun!&lt;/p&gt;




&lt;p&gt;Cover Image by &lt;a href="https://unsplash.com/@alain_pham?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Alain Pham&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/construction-frame-P_qvsF7Yodw?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>reverse</category>
      <category>substack</category>
      <category>socialmedia</category>
    </item>
    <item>
      <title>Stateless Widgets in Flutters</title>
      <dc:creator>Mathieu Kerjouan</dc:creator>
      <pubDate>Fri, 19 Jun 2026 08:00:00 +0000</pubDate>
      <link>https://dev.to/niamtokik/stateless-widgets-in-flutters-eaf</link>
      <guid>https://dev.to/niamtokik/stateless-widgets-in-flutters-eaf</guid>
      <description>&lt;p&gt;Most of the blog posts and articles I've read about &lt;a href="https://api.flutter.dev/flutter/widgets/StatelessWidget-class.html" rel="noopener noreferrer"&gt;Stateless Widgets&lt;/a&gt; during the last weeks were mostly listing the differences with the &lt;a href="https://api.flutter.dev/flutter/widgets/StatefulWidget-class.html" rel="noopener noreferrer"&gt;Stateful Widgets&lt;/a&gt;. Sadly, I was hoping to have a deep description of stateless widgets and their usage. I mean, managing state is hard, everybody knows that, and if they can avoid having dealing with any kind of state, they will do it. What kind of application can be created without any state? Could a prototype or a demo app can work with any specific state? Let find out.&lt;/p&gt;

&lt;h1&gt;
  
  
  Definition
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;A widget that &lt;strong&gt;does not require mutable state&lt;/strong&gt; [...]&amp;nbsp;&lt;code&gt;Stateless&lt;/code&gt; widget are useful when the part of the user interface you are describing &lt;strong&gt;does not depend on anything other than the configuration information in the object itself&lt;/strong&gt; and the &lt;code&gt;BuildContext&lt;/code&gt; in which the widget is inflated.&lt;/p&gt;

&lt;p&gt;-- &lt;a href="https://api.flutter.dev/flutter/widgets/StatelessWidget-class.html" rel="noopener noreferrer"&gt;StatelessWidget class&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What does it mean? When an application is running, it needs to keep some state; for example, if your application is requiring credentials, it must be stored somewhere, this is a state. A &lt;a href="https://api.flutter.dev/flutter/widgets/StatelessWidget-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;StatelessWidget&lt;/code&gt;&lt;/a&gt; does not care about that and can be used to modify the Flutter application tree.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://api.flutter.dev/flutter/widgets/Widget-class.html" rel="noopener noreferrer"&gt;https://api.flutter.dev/flutter/widgets/Widget-class.html&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://api.flutter.dev/flutter/widgets/Element-class.html" rel="noopener noreferrer"&gt;https://api.flutter.dev/flutter/widgets/Element-class.html&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/flutter/flutter/blob/main/packages/flutter/lib/src/widgets/framework.dart#L523" rel="noopener noreferrer"&gt;https://github.com/flutter/flutter/blob/main/packages/flutter/lib/src/widgets/framework.dart#L523&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://api.flutter.dev/flutter/foundation/Key-class.html" rel="noopener noreferrer"&gt;https://api.flutter.dev/flutter/foundation/Key-class.html&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://api.flutter.dev/flutter/widgets/GlobalKey-class.html" rel="noopener noreferrer"&gt;https://api.flutter.dev/flutter/widgets/GlobalKey-class.html&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.flutter.dev/cookbook/navigation/navigation-basics" rel="noopener noreferrer"&gt;https://docs.flutter.dev/cookbook/navigation/navigation-basics&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Use Case
&lt;/h1&gt;

&lt;p&gt;I was looking for an use case, without any state... But it seems complicated to do that. At my level of knowledge, I would said &lt;a href="https://api.flutter.dev/flutter/widgets/StatelessWidget-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;StatelessWidget&lt;/code&gt;&lt;/a&gt; only application could be used to design the style of an application and the flow between the different screens. Then the data can be hardcoded in the code.&lt;/p&gt;

&lt;p&gt;But one problem arises: how to switch between &lt;code&gt;Screen&lt;/code&gt;s? We will need a &lt;code&gt;Navigator&lt;/code&gt; object, and this one is based on... &lt;a href="https://api.flutter.dev/flutter/widgets/StatefulWidget-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;StatefulWidget&lt;/code&gt;&lt;/a&gt;! A complete stateless application looks really hard to create right now. Let start to write the code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:flutter/material.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;main()&lt;/code&gt; entry-point will start the application using an &lt;code&gt;Init()&lt;/code&gt; object, based on the &lt;code&gt;Init&lt;/code&gt; class created just after. Why not direcly using the class &lt;code&gt;MyApp&lt;/code&gt; instead? We will see that on another post, but having a first Widget before the application can be quite helpful to deal with the application state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;runApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Init&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;Init&lt;/code&gt;&amp;nbsp;class extends a &lt;code&gt;StatelessWidget&lt;/code&gt; and returns &lt;code&gt;MyApp()&lt;/code&gt; object. Again, nothing complex there. This &lt;code&gt;Init&lt;/code&gt; class could have been replaced by the &lt;code&gt;main()&lt;/code&gt; entry-point.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Init&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatelessWidget&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Init&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;MyApp&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;MyApp&lt;/code&gt; class creates a &lt;code&gt;MaterialApp&lt;/code&gt; object when it is instantiated. This one will have a default route set to the &lt;code&gt;home&lt;/code&gt; parameter and a list of extra-routes defined in &lt;code&gt;routes&lt;/code&gt; attribute. The &lt;code&gt;Navigator&lt;/code&gt; part will have its own article very soon, but the idea is to easily switch from/to different screens.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyApp&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatelessWidget&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;MyApp&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;MaterialApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="s"&gt;'MyApp'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;home:&lt;/span&gt; &lt;span class="n"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="nl"&gt;routes:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"/blog"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;BlogPage&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="s"&gt;"/projects"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ProjectsPage&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="s"&gt;"/about"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;AboutPage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because all our pages/screens will have the same &lt;code&gt;AppBar&lt;/code&gt;, creating a &lt;code&gt;topBar&lt;/code&gt; helper function can be helpful. The trick here is to alter the &lt;code&gt;AppBar&lt;/code&gt; object returned based on the page displayed. If the current page is a reference to the active page, nothing happens, else, a new screen is opened. This is a really dirty way to deal with routes by the way, but it "works" for a cheap draft application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;AppBar&lt;/span&gt; &lt;span class="nf"&gt;topBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;AppBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"MyApp"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nl"&gt;leading:&lt;/span&gt; &lt;span class="n"&gt;IconButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;onPressed:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Navigator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;popUntil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ModalRoute&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'/'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="nl"&gt;icon:&lt;/span&gt; &lt;span class="n"&gt;Icon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;Icons&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;home&lt;/span&gt;
          &lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nl"&gt;actions:&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Widget&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;[&lt;/span&gt;
        &lt;span class="n"&gt;TextButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;onPressed:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;active&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;"blog"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="n"&gt;Navigator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;pushNamed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/blog"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"blog"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;TextButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;onPressed:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;active&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;"projects"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="n"&gt;Navigator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;pushNamed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/projects"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"projects"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;TextButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;onPressed:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;active&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;"about"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="n"&gt;Navigator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;pushNamed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/about"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"about"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;Home&lt;/code&gt; class displaying the home page. A list of &lt;a href="https://api.flutter.dev/flutter/material/ListTile-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;ListTile&lt;/code&gt;&lt;/a&gt; is created via the &lt;code&gt;listTiles&lt;/code&gt; function defined below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Home&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatelessWidget&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Scaffold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;appBar:&lt;/span&gt; &lt;span class="n"&gt;topBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"home"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="n"&gt;listTiles&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="n"&gt;ListTile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;leading:&lt;/span&gt; &lt;span class="n"&gt;Icon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;Icons&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;favorite&lt;/span&gt;
          &lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Title 0"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="nl"&gt;subtitle:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Subtitle 0"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="nl"&gt;trailing:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Trailing 0"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;ListTile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;leading:&lt;/span&gt; &lt;span class="n"&gt;Icon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;Icons&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;access_time_outlined&lt;/span&gt;
          &lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Lorem ipsum dolor sit amet"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="nl"&gt;subtitle:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="nl"&gt;trailing:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"himenaeos"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;listTiles()&lt;/code&gt;&amp;nbsp;function is creating a &lt;a href="https://api.flutter.dev/flutter/widgets/ColoredBox-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;ColoredBox()&lt;/code&gt;&lt;/a&gt; object, nothing more.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;ColoredBox&lt;/span&gt; &lt;span class="nf"&gt;listTiles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ListTile&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;tiles&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Color&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;white&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ColoredBox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nl"&gt;color:&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Material&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;children:&lt;/span&gt; &lt;span class="n"&gt;tiles&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;BlogPage&lt;/code&gt; class definition. Again, nothing really exciting there.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BlogPage&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatelessWidget&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;BlogPage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Scaffold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;appBar:&lt;/span&gt; &lt;span class="n"&gt;topBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"blog"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="n"&gt;Container&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Padding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;padding:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;16.0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;listTiles&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="n"&gt;ListTile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;leading:&lt;/span&gt; &lt;span class="n"&gt;Icon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;Icons&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;favorite&lt;/span&gt;
          &lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Title 0"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="nl"&gt;subtitle:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Subtitle 0"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="nl"&gt;trailing:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Trailing 0"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)])&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;ProjectsPage&lt;/code&gt; class definition, quite similar to the previous one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProjectsPage&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatelessWidget&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;ProjectsPage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Scaffold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;appBar:&lt;/span&gt; &lt;span class="n"&gt;topBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"projects"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="n"&gt;Container&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"projects"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, the &lt;code&gt;AboutPage&lt;/code&gt; class definition, the last one. No surprise, tt looks like the previous.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AboutPage&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatelessWidget&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;AboutPage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Scaffold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;appBar:&lt;/span&gt; &lt;span class="n"&gt;topBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"about"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="n"&gt;Container&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Container&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No theme, no color, nothing fancy, here. The code is working correctly on &lt;a href="https://dartpad.dev/" rel="noopener noreferrer"&gt;dartpad&lt;/a&gt;, so, if you want to modify it, you can.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqlbt9e6o2i5l1w1oey3k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqlbt9e6o2i5l1w1oey3k.png" alt="Screenshot of the final draft" width="662" height="902"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Talking only about &lt;a href="https://api.flutter.dev/flutter/widgets/StatelessWidget-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;StatelessWidget&lt;/code&gt;&lt;/a&gt; is perhaps not the best thing to do. In fact, using them can be quite limited without state. Maybe I still don't have enough experiences and can see more use cases though... I have even more questions than answers, for example, how to create a new &lt;a href="https://api.flutter.dev/flutter/dart-html/Screen-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;Screen&lt;/code&gt;&lt;/a&gt; without a &lt;code&gt;Navigator&lt;/code&gt;? After a quick research, it seems a &lt;code&gt;Screen&lt;/code&gt;&amp;nbsp;is a native interface, specified by the &lt;a href="https://www.w3.org/TR/cssom-view/#screen" rel="noopener noreferrer"&gt;w3c&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Anyway, If you want to know more about &lt;a href="https://api.flutter.dev/flutter/widgets/StatelessWidget-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;StatelessWidget&lt;/code&gt;&lt;/a&gt;, you can still check those links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://api.flutter.dev/flutter/widgets/StatelessWidget-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;StatelessWidget&lt;/code&gt; class&lt;/a&gt; from Flutter API documentation;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://blog.flutter.dev/how-to-create-stateless-widgets-6f33931d859" rel="noopener noreferrer"&gt;How to Create Stateless Widgets&lt;/a&gt; from Flutter blog;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dev.to/shahid6289/understanding-stateless-and-stateful-widgets-in-flutter-14cp"&gt;Understanding Stateless and Stateful Widgets in Flutter.&lt;/a&gt; by &lt;a class="mentioned-user" href="https://dev.to/shahid6289"&gt;@shahid6289&lt;/a&gt; &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dev.to/feministech/to-never-forget-again-stateless-vs-stateful-in-flutter-ho0"&gt;To never forget again: Stateless vs. Stateful in Flutter&lt;/a&gt; by @feministech &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dev.to/dailydevtips1/flutter-stateful-and-stateless-widgets-3bnh"&gt;Flutter Stateful and Stateless widgets&lt;/a&gt; by &lt;a class="mentioned-user" href="https://dev.to/dailydevtips1"&gt;@dailydevtips1&lt;/a&gt; &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dev.to/iqrafatimame/flutter-difference-between-stateful-and-stateless-widget-54le"&gt;Flutter: Difference Between Stateful and Stateless Widget&lt;/a&gt; by &lt;a class="mentioned-user" href="https://dev.to/iqrafatimame"&gt;@iqrafatimame&lt;/a&gt; &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://colinchflutter.github.io/2023-09-24/15-34-28-643008-best-practices-for-using-statelesswidget-in-flutter/" rel="noopener noreferrer"&gt;Best practices for using StatelessWidget in Flutter&lt;/a&gt; by colinchflutter on Github;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://gist.github.com/theailanguage/1e631bf0d4d3b2966a53bbbf094ad334" rel="noopener noreferrer"&gt;Stateless Widgets in Flutter&lt;/a&gt; on Gist;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://medium.com/@eggzotic/stateless-flutter-2e14b6ae30b5" rel="noopener noreferrer"&gt;Stateless Flutter&lt;/a&gt; by &lt;a href="https://medium.com/@eggzotic" rel="noopener noreferrer"&gt;eggzotic&lt;/a&gt; on Medium;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/eggzotic/flutter_create_stateless" rel="noopener noreferrer"&gt;&lt;code&gt;flutter_create_stateless&lt;/code&gt;&lt;/a&gt; repository on Github;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://bosctechlabs.com/flutter-stateless-widget-adding-interactivity-to-your-application/" rel="noopener noreferrer"&gt;Flutter Stateless Widget – Adding interactivity To Your Application&lt;/a&gt; by BOSC Lab;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://medium.com/@letmeflutter123/guide-to-flutter-stateless-widget-ee11d9d23375" rel="noopener noreferrer"&gt;Guide to Flutter Stateless Widget&lt;br&gt;
&lt;/a&gt; by &lt;a href="https://medium.com/@letmeflutter123" rel="noopener noreferrer"&gt;Zeeshan Ali&lt;/a&gt; on Medium;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://medium.com/flutter-tips/is-it-stateful-is-it-stateless-a-guide-for-choosing-widget-in-flutter-b92c2283c813" rel="noopener noreferrer"&gt;Is it Stateful? Is it Stateless? A guide for choosing widget in flutter&lt;/a&gt; by &lt;a href="https://medium.com/@mario-gunawan" rel="noopener noreferrer"&gt;Mario Gunawan&lt;/a&gt; on Medium;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/myvsparth/stateless_stateful" rel="noopener noreferrer"&gt;Stateful and Stateless Widget Flutter Example&lt;/a&gt; on Github;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://medium.com/@shresthabwes/using-widgets-wisely-stateless-vs-stateful-in-flutter-app-development-3191ab08e20d" rel="noopener noreferrer"&gt;Using widgets wisely (Stateless vs Stateful) in Flutter App Development&lt;/a&gt; by &lt;a href="https://medium.com/@shresthabwes" rel="noopener noreferrer"&gt;Shresthabwes&lt;/a&gt; on Medium&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://medium.com/@appdevinsights/stateless-widget-in-flutter-c3fa3c845859" rel="noopener noreferrer"&gt;Stateless Widget in flutter&lt;/a&gt; by &lt;a href="https://medium.com/@appdevinsights" rel="noopener noreferrer"&gt;App Dev Insights&lt;/a&gt; on Medium&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dev.to/kcl/designing-screens-in-flutter-4nn1"&gt;Designing Screens &amp;amp; Widgets In Flutter&lt;br&gt;
&lt;/a&gt; by &lt;a class="mentioned-user" href="https://dev.to/kcl"&gt;@kcl&lt;/a&gt; &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://gist.github.com/jamiecollinson/27a78ca05e168ffc483a587a98de3d0d" rel="noopener noreferrer"&gt;Stateless widget example&lt;/a&gt; on Gist&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://gist.github.com/chaadow/ffdd0af107fa2442d9da9a5e62beb5b7" rel="noopener noreferrer"&gt;Flutter demo card using proper stateless widget extraction&lt;/a&gt; on Gist.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nothing to add there, just... Hack well and Have fun!&lt;/p&gt;




&lt;p&gt;Cover Image by &lt;a href="https://unsplash.com/@enginakyurt?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;engin akyurt&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/a-tall-glass-sitting-on-top-of-a-table-fiN073Jt5L8?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>stateless</category>
      <category>widget</category>
      <category>mobile</category>
    </item>
    <item>
      <title>Packet Filtering with nftables on Linux</title>
      <dc:creator>Mathieu Kerjouan</dc:creator>
      <pubDate>Thu, 18 Jun 2026 08:00:00 +0000</pubDate>
      <link>https://dev.to/niamtokik/packet-filtering-with-nftables-on-linux-2l68</link>
      <guid>https://dev.to/niamtokik/packet-filtering-with-nftables-on-linux-2l68</guid>
      <description>&lt;p&gt;&lt;a href="https://manpages.debian.org/trixie/iptables/iptables.8.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;iptables&lt;/code&gt;&lt;/a&gt; is probably the most used firewall by all sysadmin in the Linux ecosystem. The most recent wave of Linux admins are using &lt;a href="https://manpages.debian.org/trixie/ufw/ufw.8.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;ufw&lt;/code&gt;&lt;/a&gt; as well, mostly due to Ubuntu. Coming from BSD world, I was using &lt;a href="https://man.freebsd.org/cgi/man.cgi?query=ipfw" rel="noopener noreferrer"&gt;&lt;code&gt;ipfw&lt;/code&gt;&lt;/a&gt; (on &lt;a href="https://man.freebsd.org/cgi/man.cgi?query=ipfw" rel="noopener noreferrer"&gt;FreeBSD&lt;/a&gt;), &lt;code&gt;packet-filter&lt;/code&gt; (on &lt;a href="https://man.freebsd.org/cgi/man.cgi?query=pf" rel="noopener noreferrer"&gt;FreeBSD&lt;/a&gt; and &lt;a href="https://man.openbsd.org/pf" rel="noopener noreferrer"&gt;OpenBSD&lt;/a&gt;), and hated &lt;a href="https://manpages.debian.org/trixie/iptables/iptables.8.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;iptables&lt;/code&gt;&lt;/a&gt; for its confusing interface. Time passed and &lt;a href="https://manpages.debian.org/trixie/nftables/nftables.8.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;nftables&lt;/code&gt;&lt;/a&gt; came along the road as a stable alternative to &lt;a href="https://manpages.debian.org/trixie/iptables/iptables.8.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;iptables&lt;/code&gt;&lt;/a&gt;. So, let have some fun with it.&lt;/p&gt;

&lt;p&gt;This post is not about NAT or packet forwarding. Those topics will require another publication due to their complexity. Furthermore, most of the sysadmins are configuring a firewall for servers (bare-metal) or cloud instance (virtual-machine). Sometimes, a firewall can also be used for containers, but most of the time, administrators are using the configuration automatically generated by Docker for example. In short: this post is more about filtering than translating packets.&lt;/p&gt;

&lt;h1&gt;
  
  
  Bootstrapping
&lt;/h1&gt;

&lt;p&gt;It's rare to see &lt;a href="https://manpages.debian.org/trixie/nftables/nftables.8.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;nftables&lt;/code&gt;&lt;/a&gt; installed by default on any Linux distribution. Usually, the kernel is already compiled with the requirements, but the userland tools are not there. On Debian-like distribution, the &lt;a href="https://packages.debian.org/trixie/nftables" rel="noopener noreferrer"&gt;&lt;code&gt;nftables&lt;/code&gt;&lt;/a&gt; package can easily be installed with the &lt;a href="https://manpages.debian.org/trixie/apt/apt-get.8.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;apt-get&lt;/code&gt;&lt;/a&gt; command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;nftables
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same can be done on your favorite Linux distribution with the help of the specific package manager like on &lt;a href="https://pkgs.alpinelinux.org/package/edge/main/x86_64/nftables" rel="noopener noreferrer"&gt;AlpineLinux&lt;/a&gt;, &lt;a href="https://packages.gentoo.org/packages/net-firewall/nftables" rel="noopener noreferrer"&gt;Gentoo&lt;/a&gt;, &lt;a href="https://archlinux.org/packages/extra/x86_64/nftables/" rel="noopener noreferrer"&gt;ArchLinux&lt;/a&gt; and so on.&lt;/p&gt;

&lt;p&gt;For the rest of this post, I will assume you are using &lt;a href="https://www.debian.org/releases/trixie/" rel="noopener noreferrer"&gt;Debian 13 (Trixie)&lt;/a&gt;, which is probably the most used Linux distribution one with Ubuntu, but even if you are using another distribution, the userland should be quite similar. All commands will also be executed as &lt;code&gt;root&lt;/code&gt; to avoid adding &lt;code&gt;sudo&lt;/code&gt; or &lt;code&gt;doas&lt;/code&gt; every time.&lt;/p&gt;

&lt;h1&gt;
  
  
  Command Line Interface
&lt;/h1&gt;

&lt;p&gt;The main userland tool to manage &lt;code&gt;nftables&lt;/code&gt; is called &lt;a href="https://manpages.debian.org/trixie/nftables/nftables.8.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;nft&lt;/code&gt;&lt;/a&gt;. This firewall is dynamic, and any kind of rules can be added or removed even when the firewall is enabled and active.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nft &lt;span class="nt"&gt;--help&lt;/span&gt;
&lt;span class="go"&gt;Usage: nft [ options ] [ cmds... ]

Options (general):
  -h, --help                      Show this help
  -v, --version                   Show version information
  -V                              Show extended version information

Options (ruleset input handling):
&lt;/span&gt;&lt;span class="gp"&gt;  -f, --file &amp;lt;filename&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;Read input from &amp;lt;filename&amp;gt;
&lt;span class="gp"&gt;  -D, --define &amp;lt;name=value&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;Define variable, e.g. &lt;span class="nt"&gt;--define&lt;/span&gt; &lt;span class="nv"&gt;foo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1.2.3.4
&lt;span class="go"&gt;  -i, --interactive               Read input from interactive CLI
&lt;/span&gt;&lt;span class="gp"&gt;  -I, --includepath &amp;lt;directory&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;Add &amp;lt;directory&amp;gt; to the paths searched &lt;span class="k"&gt;for &lt;/span&gt;include files. Default is: /etc
&lt;span class="go"&gt;  -c, --check                     Check commands validity without actually applying the changes.
  -o, --optimize                  Optimize ruleset

Options (ruleset list formatting):
  -a, --handle                    Output rule handle.
  -s, --stateless                 Omit stateful information of ruleset.
  -t, --terse                     Omit contents of sets.
  -S, --service                   Translate ports to service names as described in /etc/services.
  -N, --reversedns                Translate IP addresses to names.
  -u, --guid                      Print UID/GID as defined in /etc/passwd and /etc/group.
  -n, --numeric                   Print fully numerical output.
  -y, --numeric-priority          Print chain priority numerically.
  -p, --numeric-protocol          Print layer 4 protocols numerically.
  -T, --numeric-time              Print time values numerically.

Options (command output formatting):
  -e, --echo                      Echo what has been added, inserted or replaced.
  -j, --json                      Format output in JSON
&lt;/span&gt;&lt;span class="gp"&gt;  -d, --debug &amp;lt;level [,level...]&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Specify debugging level &lt;span class="o"&gt;(&lt;/span&gt;scanner, parser, &lt;span class="nb"&gt;eval&lt;/span&gt;, netlink, mnl, proto-ctx, segtree, all&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I personally think editing firewall rules like &lt;a href="https://manpages.debian.org/trixie/iptables/iptables.8.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;iptables&lt;/code&gt;&lt;/a&gt; or &lt;a href="https://manpages.debian.org/trixie/nftables/nftables.8.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;nftables&lt;/code&gt;&lt;/a&gt; directly from the command line can be dangerous. In case of mistake, one can easily lose access to the server, but at least, if the rule was not stored in the configuration file, a simple reboot of the machine should fix the issue. Unfortunately, it's also easier to do mistakes when using the CLI, especially if the rules are complex. One should be always careful and read more than once the command to be executed.&lt;/p&gt;

&lt;p&gt;It is also a good practice to use the &lt;code&gt;-c&lt;/code&gt; (check) flag to see what kind of changes will be applied before altering the loaded configuration. Before starting our journey with &lt;a href="https://manpages.debian.org/trixie/nftables/nftables.8.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;nftables&lt;/code&gt;&lt;/a&gt;, we will &lt;a href="https://manpages.debian.org/trixie/nftables/nft.8.en.html#RULESET" rel="noopener noreferrer"&gt;flush&lt;/a&gt; all the ruleset configured and recreate something from scratch. If you are using &lt;a href="https://manpages.debian.org/trixie/nftables/nftables.8.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;nftables&lt;/code&gt;&lt;/a&gt; right now, you should probably save your configuration, but I assume if you are here, it's probably the first time you are using it, so, the ruleset should already be empty.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nft flush ruleset
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More than one table can be created, but here, we will try to recreate from scratch the default permissive ruleset only from command line. A &lt;a href="https://manpages.debian.org/trixie/nftables/nftables.8.en.html#TABLES" rel="noopener noreferrer"&gt;table&lt;/a&gt; can be configured for an &lt;a href="https://manpages.debian.org/trixie/nftables/nftables.8.en.html#TABLES" rel="noopener noreferrer"&gt;address family&lt;/a&gt;, it means if a table is created with the family &lt;code&gt;ip&lt;/code&gt; it will work only for &lt;code&gt;ipv4&lt;/code&gt; address and not the rest. If you want to deal with &lt;code&gt;ipv4&lt;/code&gt; and &lt;code&gt;ipv6&lt;/code&gt; at the same time, the &lt;code&gt;inet&lt;/code&gt; family was made for you. At this time, 6 families exist:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;ip&lt;/code&gt;: catch only IPv4 packets;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;ip6&lt;/code&gt;: catch only IPv6 packets;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;inet&lt;/code&gt;: catch both IPv4 and IPv6 packets (dual stack);&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;arp&lt;/code&gt;: catch IPv4 ARP packets;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;bridge&lt;/code&gt;: catch packets from a bridge;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;netdev&lt;/code&gt;: catch packets on ingress and egress.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To create a new table, the command &lt;a href="https://manpages.debian.org/trixie/nftables/nftables.8.en.html#TABLES" rel="noopener noreferrer"&gt;&lt;code&gt;nft add table ${family} ${table_name}&lt;/code&gt;&lt;/a&gt; will be used. To list its content (ot check if the table already exist), the &lt;a href="https://manpages.debian.org/trixie/nftables/nftables.8.en.html#TABLES" rel="noopener noreferrer"&gt;&lt;code&gt;nft table list&lt;/code&gt;&lt;/a&gt; can be used.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nft add table inet filter 
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;bob nft list ruleset
&lt;span class="go"&gt;table inet filter {
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This new table is the only one on our system and will be used by default but a table without &lt;a href="https://manpages.debian.org/trixie/nftables/nftables.8.en.html#CHAINS" rel="noopener noreferrer"&gt;chains&lt;/a&gt; will do nothing. A &lt;a href="https://manpages.debian.org/trixie/nftables/nftables.8.en.html#CHAINS" rel="noopener noreferrer"&gt;chain&lt;/a&gt; contains the firewall rules to apply on packets, more than one chain can be created inside a table. A chain is also define by its &lt;code&gt;type&lt;/code&gt;, its &lt;code&gt;hook&lt;/code&gt; and its &lt;code&gt;priority&lt;/code&gt;, all these parameters are mandatory. The only type we will study today is the &lt;code&gt;filter&lt;/code&gt; type, mostly used to do filtering actions on packets. &lt;/p&gt;

&lt;p&gt;A &lt;code&gt;hook&lt;/code&gt; defines the flow of packets, for example, a chain set with the &lt;code&gt;input&lt;/code&gt; hook will catch only incoming packets, a chain set with the &lt;code&gt;output&lt;/code&gt; hook will catch only outgoing packets and finally a chain with the &lt;code&gt;forward&lt;/code&gt; hook will catch only the packets being forwarded.&lt;/p&gt;

&lt;p&gt;Finally, the &lt;code&gt;priority&lt;/code&gt; parameter defines the priority level if other chains exist on the system. Indeed, when an incoming packet is catched by &lt;code&gt;nftables&lt;/code&gt;, the firewall will take the first &lt;code&gt;table&lt;/code&gt; available with the corresponding address family and then, will select a chain based on its priority.&lt;/p&gt;

&lt;p&gt;The syntax here is a bit odd, be it is required. The parameters of the chain must be passed inside curly brackets, and then, can be interpreted by the shell. To avoid this situation, command should be passed as a string and not a list of argument. In short: you need single or double quotes around it. The command used to create a chain is &lt;a href="https://manpages.debian.org/trixie/nftables/nftables.8.en.html#CHAINS" rel="noopener noreferrer"&gt;&lt;code&gt;nft add chain ${family} ${table_name} ${chain_name} { $parameters; }&lt;/code&gt;&lt;/a&gt;. Let create our first input chain.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nft &lt;span class="s1"&gt;'add chain inet filter input {type filter hook input priority filter; policy accept;}'&lt;/span&gt;
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nft list chain inet filter input
&lt;span class="go"&gt;table inet filter {
        chain input {
&lt;/span&gt;&lt;span class="gp"&gt;                type filter hook input priority filter;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;policy accept&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="go"&gt;        }
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great, next, we can create our &lt;code&gt;output&lt;/code&gt; chain using the same process.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nft &lt;span class="s1"&gt;'add chain inet filter output {type filter hook output priority filter; policy accept;}'&lt;/span&gt;
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nft list chain inet filter output
&lt;span class="go"&gt;table inet filter {
        chain output {
&lt;/span&gt;&lt;span class="gp"&gt;                type filter hook output priority filter;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;policy accept&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="go"&gt;        }
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we can create our &lt;code&gt;forward&lt;/code&gt; chain. We will not use it in this article, but it can be required by some tools to work correctly, like Docker. Again, practically the same command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nft &lt;span class="s1"&gt;'add chain inet filter forward {type filter hook forward priority filter; policy accept;}'&lt;/span&gt;
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nft list chain inet filter forward
&lt;span class="go"&gt;table inet filter {
        chain forward {
&lt;/span&gt;&lt;span class="gp"&gt;                type filter hook forward priority filter;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;policy accept&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="go"&gt;        }
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have checked the commands before executing them, you probably saw the &lt;code&gt;policy&lt;/code&gt; parameter. This chain's parameter is used to define the default &lt;code&gt;policy&lt;/code&gt; of the chain. When a packet enters the firewall, it can then be accepted (&lt;code&gt;accept&lt;/code&gt;) or dropped (&lt;code&gt;drop&lt;/code&gt;). The default value is &lt;code&gt;accept&lt;/code&gt;, but a serious firewall configuration should use &lt;code&gt;drop&lt;/code&gt; instead.&lt;/p&gt;

&lt;p&gt;Anyway, we have now all our table and chains create on our system, we can list them with &lt;a href="https://manpages.debian.org/trixie/nftables/nftables.8.en.html#RULESET" rel="noopener noreferrer"&gt;&lt;code&gt;nft -a list ruleset&lt;/code&gt;&lt;/a&gt;. The &lt;code&gt;-a&lt;/code&gt; flag print also the handle id on each chains/rules and can be used to delete or modify it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nft &lt;span class="nt"&gt;-a&lt;/span&gt; list ruleset
&lt;span class="gp"&gt;table inet filter { #&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;handle 2
&lt;span class="gp"&gt;        chain input { #&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;handle 1
&lt;span class="gp"&gt;                type filter hook input priority filter;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;policy accept&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="go"&gt;        }

&lt;/span&gt;&lt;span class="gp"&gt;        chain output { #&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;handle 2
&lt;span class="gp"&gt;                type filter hook output priority filter;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;policy accept&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="go"&gt;        }

&lt;/span&gt;&lt;span class="gp"&gt;        chain forward { #&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;handle 3
&lt;span class="gp"&gt;                type filter hook forward priority filter;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;policy accept&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="go"&gt;        }
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lucky us, this output can also be used as configuration file! You can simply redirect it to a file, and voilà, you have your firewall configuration saved on your disk. Only the header will be missing, but it can easily be added with an &lt;code&gt;echo&lt;/code&gt; or &lt;code&gt;printf&lt;/code&gt; command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;touch &lt;/span&gt;nftables_default.conf
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x nftables_default.conf
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'#!/usr/sbin/nft -f'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; nftables_default.conf
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'flush ruleset'&lt;/span&gt; | &lt;span class="nb"&gt;tee &lt;/span&gt;nftables_default.conf
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nft &lt;span class="nt"&gt;-a&lt;/span&gt; list ruleset | &lt;span class="nb"&gt;tee &lt;/span&gt;nftables_default.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have a backup of our default permissive firewall configuration, we can play with the rules. In case of problem, like, if you are losing your connection, or you want to come back to this milestone, the only command you have to execute will be the one below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;./nftables_default.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you know, all our chains are permissive, accepting all packets by default. We will keep this policy during the design phase of the rules. Usually, if you have a server running, you are probably using &lt;a href="https://manpages.debian.org/trixie/openssh-server/sshd.8.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;sshd&lt;/code&gt;&lt;/a&gt; on it to grab a shell. It would be unfortunate to lose access to the server because of a firewall rule. Right? Yeah, so, let add our first rule to explicitly allow &lt;code&gt;ssh&lt;/code&gt; connections. The command to add a rule is &lt;a href="https://manpages.debian.org/trixie/nftables/nftables.8.en.html#RULES" rel="noopener noreferrer"&gt;&lt;code&gt;nft add rule ${family} ${table_name} ${chain_name} ${rule}&lt;/code&gt;&lt;/a&gt;, where &lt;code&gt;${rule}&lt;/code&gt; is a string made of keywords to match packets and defines the action to apply on them. In our case, we want to allow all packets from anywhere to the destination TCP/22 port.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nft add rule inet filter input tcp dport 22 accept
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nft &lt;span class="nt"&gt;-a&lt;/span&gt; list chain inet filter input
&lt;span class="go"&gt;table inet filter {
&lt;/span&gt;&lt;span class="gp"&gt;        chain input { #&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;handle 1
&lt;span class="gp"&gt;                type filter hook input priority filter;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;policy accept&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="gp"&gt;                tcp dport 22 accept #&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;handle 4
&lt;span class="go"&gt;        }
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you are on a server, you probably have other daemons running, like &lt;code&gt;nginx&lt;/code&gt; or &lt;code&gt;httpd&lt;/code&gt; (http servers). You would probably also allow TCP/80 and TCP/443 to be available to everyone, but, having a security there to restrict access to bots for example can be great. Furthermore, you are lazy, and you don't want to modify and reload your rules every time you restrict access to some wild IP addresses. This is where &lt;a href="https://manpages.debian.org/trixie/nftables/nftables.8.en.html#SETS" rel="noopener noreferrer"&gt;Sets&lt;/a&gt; will help us. A set can be created with the &lt;a href="https://manpages.debian.org/trixie/nftables/nftables.8.en.html#SETS" rel="noopener noreferrer"&gt;&lt;code&gt;nft add set ${family} ${table_name} ${set_name} { params* }&lt;/code&gt;&lt;/a&gt; command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nft &lt;span class="s1"&gt;'add set inet filter blackhole {
&lt;/span&gt;&lt;span class="gp"&gt;    type inet_proto;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;    flags timeout;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;    timeout 60s;&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;span class="go"&gt;  }'
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Parameters given to a set are useful. For example, the set &lt;code&gt;blackhole&lt;/code&gt; previously created will store &lt;code&gt;inet_proto&lt;/code&gt; (TCP or UDP port) and the &lt;code&gt;timeout&lt;/code&gt; flag configures the retention time of each elements, in our case: 60 seconds. To delete a set, the &lt;code&gt;nft delete set ${family} ${table_name} ${set_name}&lt;/code&gt; can be used.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nft &lt;span class="s1"&gt;'delete set inet filter blackhole'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let creates a new sec called &lt;code&gt;blackhole4&lt;/code&gt;, this one will store IPv4 addresses for 1 hour.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nft &lt;span class="s1"&gt;'add set inet filter blackhole4 {
&lt;/span&gt;&lt;span class="gp"&gt;    type ipv4_addr ;&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;span class="gp"&gt;    flags timeout;&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;span class="gp"&gt;    timeout 1h;&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;span class="go"&gt;  }'
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The specification and the content of a set can be returned via the &lt;a href="https://manpages.debian.org/trixie/nftables/nftables.8.en.html#SETS" rel="noopener noreferrer"&gt;&lt;code&gt;nft list set ${family} ${table_name} ${set_name}&lt;/code&gt;&lt;/a&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nft &lt;span class="s1"&gt;'list set inet filter blackhole4'&lt;/span&gt;
&lt;span class="go"&gt;table inet filter {
        set blackhole4 {
                type ipv4_addr
                timeout 1h
        }
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If not defined with the parameters &lt;code&gt;elements&lt;/code&gt;, a set is empty by default. To add a new element in it, the &lt;a href="https://manpages.debian.org/trixie/nftables/nftables.8.en.html#ELEMENTS" rel="noopener noreferrer"&gt;&lt;code&gt;nft element add ${family} ${table_name} ${set_name} { ELEMENT }&lt;/code&gt;&lt;/a&gt; can be executed. Let's try out with one IP address.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nft &lt;span class="s1"&gt;'add element inet filter blackhole4 { 1.1.1.1 }'&lt;/span&gt;
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nft &lt;span class="s1"&gt;'list set inet filter blackhole4'&lt;/span&gt;
&lt;span class="go"&gt;table inet filter {
        set blackhole4 {
                type ipv4_addr
                timeout 1h
                elements = { 1.1.1.1 expires 59m58s166ms }
        }
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same can be done for IPv6.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nft &lt;span class="s1"&gt;'add set inet filter blackhole6 { type ipv6_addr ; flags timeout; timeout 3600s; }'&lt;/span&gt;
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nft &lt;span class="s1"&gt;'list set inet filter blackhole6'&lt;/span&gt;
&lt;span class="go"&gt;table inet filter {
        set blackhole6 {
                type ipv6_addr
                timeout 1h
        }
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to support IPv4 or IPv6 range (&lt;code&gt;1.2.3.4/32&lt;/code&gt; or &lt;code&gt;ffce::/64&lt;/code&gt; for example), the flag &lt;code&gt;interval&lt;/code&gt; is required.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nft &lt;span class="s1"&gt;'delete set inet filter blackhole4'&lt;/span&gt;
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nft &lt;span class="s1"&gt;'add set inet filter blackhole4 {
&lt;/span&gt;&lt;span class="gp"&gt;    type ipv4_addr ;&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;span class="gp"&gt;    flags timeout ;&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;span class="gp"&gt;    timeout 1h ;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;    flags interval;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;  }'

&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nft &lt;span class="s1"&gt;'delete set inet filter blackhole4'&lt;/span&gt;
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nft &lt;span class="s1"&gt;'add set inet filter blackhole6 { 
&lt;/span&gt;&lt;span class="gp"&gt;    type ipv6_addr ;&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;span class="gp"&gt;    flags timeout;&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;span class="gp"&gt;    timeout 1h;&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;span class="gp"&gt;    flags interval;&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;span class="go"&gt;  }'
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, IP addresses ranges can be added.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nft &lt;span class="s1"&gt;'add element inet filter blackhole4 { 1.2.3.4/24 }'&lt;/span&gt;
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nft &lt;span class="s1"&gt;'list set inet filter blackhole4'&lt;/span&gt;
&lt;span class="go"&gt;table inet filter {
        set blackhole4 {
                type ipv4_addr
                flags interval,timeout
                timeout 1h
                elements = { 1.2.3.0/24 expires 59m57s486ms }
        }
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why using sets? It will make your life easier. You can create many of them, and adds IP addresses directly from the command line. The only drawback is due to the retention, if you plan to have fixed IP addresses in those set, you can use instead anonymous sets or configure the &lt;code&gt;elements&lt;/code&gt; parameters with the list of persistent elements.&lt;/p&gt;

&lt;p&gt;A rule can use those named sets by prefixing their name with &lt;code&gt;@&lt;/code&gt; during the rule definition. For example, if we want to block all IP addresses from &lt;code&gt;blackhole4&lt;/code&gt; and &lt;code&gt;blockhole6&lt;/code&gt; sets, we can use the following rules.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nft &lt;span class="s1"&gt;'add rule inet filter input log ip saddr @blackhole4 drop'&lt;/span&gt;
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nft &lt;span class="s1"&gt;'add rule inet filter input log ip6 saddr @blackhole6 drop'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A &lt;a href="https://manpages.debian.org/trixie/nftables/nftables.8.en.html#LOG_STATEMENT" rel="noopener noreferrer"&gt;&lt;code&gt;log&lt;/code&gt;&lt;/a&gt; statement in the rule. Everytime a packet will reach this rule, it will also be logged. Then, we can allow HTTP/80 and HTTP/443&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nft &lt;span class="s1"&gt;'add rule inet filter input tcp dport 80 accept'&lt;/span&gt;
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nft &lt;span class="s1"&gt;'add rule inet filter input tcp dport 443 accept'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here the final rules defined in the input chain.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nft list chain inet filter input
&lt;span class="go"&gt;table inet filter {
        chain input {
&lt;/span&gt;&lt;span class="gp"&gt;                type filter hook input priority filter;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;policy accept&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="go"&gt;                tcp dport 22 accept
                log ip saddr @blackhole4 drop
                log ip6 saddr @blackhole6 drop
                tcp dport 80 accept
                tcp dport 443 accept
        }
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you feel confident enough, you can now switch the default policy to &lt;code&gt;drop&lt;/code&gt;. In fact, if your services are simply listening to TCP/22, TCP/80 and TCP/443, it will change nothing, but all other potential daemon listening to other ports will not be visible anymore from the outside world.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nft &lt;span class="s1"&gt;'add chain inet filter input { policy drop ; }'&lt;/span&gt;
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nft &lt;span class="s1"&gt;'list chain inet filter input'&lt;/span&gt;
&lt;span class="go"&gt;nft 'list chain inet filter input'
table inet filter {
        chain input {
&lt;/span&gt;&lt;span class="gp"&gt;                type filter hook input priority filter;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;policy drop&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="go"&gt;                tcp dport 22 accept
                log ip saddr @blackhole4 drop
                log ip6 saddr @blackhole6 drop
                tcp dport 80 accept
                tcp dport 443 accept
        }
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ah! But we did something very bad! By default, we are blocking everything on all interfaces, including the loopback (&lt;code&gt;lo&lt;/code&gt;). It can be a problem!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nft &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="s1"&gt;'list chain inet filter input'&lt;/span&gt;
&lt;span class="go"&gt;table inet filter {                                                                                      
&lt;/span&gt;&lt;span class="gp"&gt;        chain input { #&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;handle 1               
&lt;span class="gp"&gt;                type filter hook input priority filter;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;policy drop&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="gp"&gt;                tcp dport 22 accept #&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;handle 19
&lt;span class="gp"&gt;                log ip saddr @blackhole4 drop #&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;handle 20
&lt;span class="gp"&gt;                iif "lo" accept #&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;handle 28
&lt;span class="gp"&gt;                log ip6 saddr @blackhole6 drop #&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;handle 21
&lt;span class="gp"&gt;                tcp dport 80 accept #&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;handle 22
&lt;span class="gp"&gt;                tcp dport 443 accept #&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;handle 23
&lt;span class="go"&gt;        }
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We want to add this rule before the SSH rule (handle 19), to do that, &lt;code&gt;insert&lt;/code&gt; command will be used instead of &lt;code&gt;add&lt;/code&gt;. To match an interface, &lt;code&gt;iif&lt;/code&gt; or &lt;code&gt;iifname&lt;/code&gt;&amp;nbsp;keywords. Here, &lt;code&gt;iif&lt;/code&gt; will do the job. &lt;code&gt;iifname&lt;/code&gt; is useful when some rules are required for non-existing interfaces (an &lt;a href="https://serverfault.com/a/985167" rel="noopener noreferrer"&gt;answer from StackOverflow&lt;/a&gt; about the difference between those two keywords is great).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nft &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="s1"&gt;'insert rule inet filter input handle 19 iif lo accept'&lt;/span&gt;
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nft &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="s1"&gt;'list chain inet filter input'&lt;/span&gt;                                                                                                                                                      
&lt;span class="go"&gt;table inet filter {
&lt;/span&gt;&lt;span class="gp"&gt;        chain input { #&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;handle 1
&lt;span class="gp"&gt;                type filter hook input priority filter;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;policy drop&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="gp"&gt;                iif "lo" accept #&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;handle 31
&lt;span class="gp"&gt;                tcp dport 22 accept #&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;handle 19
&lt;span class="gp"&gt;                log ip saddr @blackhole4 drop #&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;handle 20
&lt;span class="gp"&gt;                log ip6 saddr @blackhole6 drop #&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;handle 21
&lt;span class="gp"&gt;                tcp dport 80 accept #&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;handle 22
&lt;span class="gp"&gt;                tcp dport 443 accept #&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;handle 23
&lt;span class="go"&gt;        }
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, the rule has been added before the SSH one with the handle 31. That's pretty cool, but we would also like to have some statistics regarding each rule usage.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nft &lt;span class="s1"&gt;'replace rule inet filter input handle 19 tcp dport 22 counter accept'&lt;/span&gt;
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nft &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="s1"&gt;'list chain inet filter input'&lt;/span&gt;
&lt;span class="go"&gt;table inet filter {
&lt;/span&gt;&lt;span class="gp"&gt;        chain input { #&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;handle 1
&lt;span class="gp"&gt;                type filter hook input priority filter;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;policy drop&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="gp"&gt;                iif "lo" accept #&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;handle 31
&lt;span class="gp"&gt;                tcp dport 22 counter packets 0 bytes 0 accept #&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;handle 19
&lt;span class="gp"&gt;                log ip saddr @blackhole4 drop #&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;handle 20
&lt;span class="gp"&gt;                log ip6 saddr @blackhole6 drop #&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;handle 21
&lt;span class="gp"&gt;                tcp dport 80 accept #&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;handle 22
&lt;span class="gp"&gt;                tcp dport 443 accept #&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;handle 23
&lt;span class="go"&gt;        }
}

&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nc &lt;span class="nt"&gt;-zv&lt;/span&gt; localhost 22
&lt;span class="go"&gt;Connection to 127.0.0.1 22 port [tcp/ssh] succeeded!

&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nft &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="s1"&gt;'list chain inet filter input'&lt;/span&gt;
&lt;span class="go"&gt;table inet filter {
&lt;/span&gt;&lt;span class="gp"&gt;        chain input { #&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;handle 1
&lt;span class="gp"&gt;                type filter hook input priority filter;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;policy drop&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="gp"&gt;                iif "lo" accept #&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;handle 31
&lt;span class="gp"&gt;                tcp dport 22 counter packets 17 bytes 996 accept #&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;handle 19
&lt;span class="gp"&gt;                log ip saddr @blackhole4 drop #&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;handle 20
&lt;span class="gp"&gt;                log ip6 saddr @blackhole6 drop #&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;handle 21
&lt;span class="gp"&gt;                tcp dport 80 accept #&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;handle 22
&lt;span class="gp"&gt;                tcp dport 443 accept #&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;handle 23
&lt;span class="go"&gt;        }
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before deploying this kind of configuration, always test it many tests and don't hesitate to read the documentation or ask questions on mailing-list (&lt;a href="https://www.nftables.org/mailinglists.html" rel="noopener noreferrer"&gt;nftables mailing-list&lt;/a&gt;or &lt;a href="https://www.mail-archive.com/nanog%40nanog.org/maillist.html" rel="noopener noreferrer"&gt;NANOG&lt;/a&gt;) or forums. Be aware a wrong firewall configuration can have disastrous effect on your network. If you are happy with your rules, and you want to have it enabled when the system is booting, don't forget to activate the service with &lt;code&gt;systemd&lt;/code&gt; on Debian (or with the service manager of your distribution).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;systemctl status nftables
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;nftables
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;systemctl start nftables
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Configuration and Syntax
&lt;/h1&gt;

&lt;p&gt;The main nftables configuration can be found in &lt;a href="https://sources.debian.org/src/nftables/1.1.6-1/debian/nftables.conf" rel="noopener noreferrer"&gt;&lt;code&gt;/etc/nftables.conf&lt;/code&gt;&lt;/a&gt;  on most of the distributions but it can also be stored in &lt;code&gt;/etc/nftables.rules.d&lt;/code&gt; directory (e.g. Gentoo). This file will contain the firewall rules loaded nftables at boot time, then, this is critical to be sure the rules have been tested first to avoid being blocked if you are using SSH for example.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;distribution&lt;/th&gt;
&lt;th&gt;file or directory&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Debian-like&lt;/td&gt;
&lt;td&gt;&lt;a href="https://sources.debian.org/src/nftables/1.1.6-1/debian/nftables.conf" rel="noopener noreferrer"&gt;&lt;code&gt;/etc/nftables.conf&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gentoo&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/etc/nftables.rules&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gentoo&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/etc/nftables.rules.d&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Alpine&lt;/td&gt;
&lt;td&gt;&lt;a href="https://gitlab.alpinelinux.org/alpine/aports/-/blob/master/main/nftables/nftables.nft" rel="noopener noreferrer"&gt;&lt;code&gt;/etc/nftables.nft&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ArchLinux&lt;/td&gt;
&lt;td&gt;&lt;a href="https://gitlab.archlinux.org/archlinux/packaging/packages/nftables/-/blob/main/nftables.conf?ref_type=heads" rel="noopener noreferrer"&gt;&lt;code&gt;/etc/nftables.conf&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;On Debian - and probably on other distribution as well - one can find few use cases and examples in &lt;a href="https://sources.debian.org/src/nftables/1.1.6-1/examples" rel="noopener noreferrer"&gt;&lt;code&gt;/usr/share/doc/nftables/examples&lt;/code&gt;&lt;/a&gt; directory. It can be helpful if you are starting with &lt;a href="https://manpages.debian.org/trixie/nftables/nft.8.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;nftables&lt;/code&gt;&lt;/a&gt; and you have some trouble to understand how to configure it correctly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;find /usr/share/doc/nftables/examples/ | &lt;span class="nb"&gt;sort&lt;/span&gt;
&lt;span class="go"&gt;/usr/share/doc/nftables/examples/
/usr/share/doc/nftables/examples/all-in-one.nft
/usr/share/doc/nftables/examples/arp-filter.nft
/usr/share/doc/nftables/examples/bridge-filter.nft
/usr/share/doc/nftables/examples/ct_helpers.nft
/usr/share/doc/nftables/examples/inet-filter.nft
/usr/share/doc/nftables/examples/inet-nat.nft
/usr/share/doc/nftables/examples/ipv4-filter.nft
/usr/share/doc/nftables/examples/ipv4-mangle.nft
/usr/share/doc/nftables/examples/ipv4-nat.nft
/usr/share/doc/nftables/examples/ipv4-raw.nft
/usr/share/doc/nftables/examples/ipv6-filter.nft
/usr/share/doc/nftables/examples/ipv6-mangle.nft
/usr/share/doc/nftables/examples/ipv6-nat.nft
/usr/share/doc/nftables/examples/ipv6-raw.nft
/usr/share/doc/nftables/examples/load_balancing.nft
/usr/share/doc/nftables/examples/nat.nft
/usr/share/doc/nftables/examples/netdev-ingress.nft
/usr/share/doc/nftables/examples/overview.nft
/usr/share/doc/nftables/examples/pf.os
/usr/share/doc/nftables/examples/README
/usr/share/doc/nftables/examples/secmark.nft
/usr/share/doc/nftables/examples/sets_and_maps.nft
/usr/share/doc/nftables/examples/sysvinit
/usr/share/doc/nftables/examples/sysvinit/nftables.init
/usr/share/doc/nftables/examples/sysvinit/README
/usr/share/doc/nftables/examples/workstation.nft
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Most of the distributions are using an ultra-permissive ruleset by default, it allows everything from everywhere. Even activated, in this case, the firewall is mostly useless. You can see below the content of &lt;a href="https://sources.debian.org/src/nftables/1.1.6-1/debian/nftables.conf" rel="noopener noreferrer"&gt;&lt;code&gt;/etc/nftables.conf&lt;/code&gt;&lt;/a&gt; on Debian.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/sbin/nft -f
&lt;/span&gt;
&lt;span class="n"&gt;flush&lt;/span&gt; &lt;span class="n"&gt;ruleset&lt;/span&gt;

&lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="n"&gt;inet&lt;/span&gt; &lt;span class="n"&gt;filter&lt;/span&gt; {
        &lt;span class="n"&gt;chain&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; {
                &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="n"&gt;filter&lt;/span&gt; &lt;span class="n"&gt;hook&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="n"&gt;priority&lt;/span&gt; &lt;span class="n"&gt;filter&lt;/span&gt;;
        }
        &lt;span class="n"&gt;chain&lt;/span&gt; &lt;span class="n"&gt;forward&lt;/span&gt; {
                &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="n"&gt;filter&lt;/span&gt; &lt;span class="n"&gt;hook&lt;/span&gt; &lt;span class="n"&gt;forward&lt;/span&gt; &lt;span class="n"&gt;priority&lt;/span&gt; &lt;span class="n"&gt;filter&lt;/span&gt;;
        }
        &lt;span class="n"&gt;chain&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt; {
                &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="n"&gt;filter&lt;/span&gt; &lt;span class="n"&gt;hook&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="n"&gt;priority&lt;/span&gt; &lt;span class="n"&gt;filter&lt;/span&gt;;
        }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two methods exist to load this code, the first one is to simply execute the file (note the shebang pointing to the &lt;code&gt;nft&lt;/code&gt; command in the first line of the configuration).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/etc/nftables.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second method is to load it manually with &lt;code&gt;nft&lt;/code&gt; and the &lt;code&gt;-f&lt;/code&gt; flag followed by the path where the configuration is stored.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nft &lt;span class="nt"&gt;-f&lt;/span&gt; /etc/nftables.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In both case, the firewall will load the rules, and they can now be displayed with &lt;code&gt;nft&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nft list ruleset
&lt;span class="go"&gt;table inet filter {
        chain input {
&lt;/span&gt;&lt;span class="gp"&gt;                type filter hook input priority filter;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;policy accept&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="go"&gt;        }

        chain forward {
&lt;/span&gt;&lt;span class="gp"&gt;                type filter hook forward priority filter;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;policy accept&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="go"&gt;        }

        chain output {
&lt;/span&gt;&lt;span class="gp"&gt;                type filter hook output priority filter;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;policy accept&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="go"&gt;        }
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using a configuration file containing &lt;code&gt;nftables&lt;/code&gt; rules is easier to deal with than to hack and grind the command line interface. In fact, I usually prefer designing my firewall this way to avoid the annoying &lt;code&gt;index&lt;/code&gt; and &lt;code&gt;handle&lt;/code&gt; references to manage. Let re-create our previous rules from the command line section in &lt;code&gt;/tmp/nftables_rules.conf&lt;/code&gt; to improve it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/sbin/nft -f
&lt;/span&gt;
&lt;span class="n"&gt;flush&lt;/span&gt; &lt;span class="n"&gt;ruleset&lt;/span&gt;

&lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="n"&gt;inet&lt;/span&gt; &lt;span class="n"&gt;filter&lt;/span&gt; {
  &lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="n"&gt;blackhole4&lt;/span&gt; {
    &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="n"&gt;ipv4_addr&lt;/span&gt;
    &lt;span class="n"&gt;flags&lt;/span&gt; &lt;span class="n"&gt;interval&lt;/span&gt;,&lt;span class="n"&gt;timeout&lt;/span&gt;
    &lt;span class="n"&gt;timeout&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;
  }

  &lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="n"&gt;blackhole6&lt;/span&gt; {
    &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="n"&gt;ipv6_addr&lt;/span&gt;
    &lt;span class="n"&gt;flags&lt;/span&gt; &lt;span class="n"&gt;interval&lt;/span&gt;,&lt;span class="n"&gt;timeout&lt;/span&gt;
    &lt;span class="n"&gt;timeout&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;
  }

  &lt;span class="n"&gt;chain&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; {
    &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="n"&gt;filter&lt;/span&gt; &lt;span class="n"&gt;hook&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="n"&gt;priority&lt;/span&gt; &lt;span class="n"&gt;filter&lt;/span&gt;; &lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="n"&gt;drop&lt;/span&gt;;
    &lt;span class="n"&gt;iif&lt;/span&gt; &lt;span class="s2"&gt;"lo"&lt;/span&gt; &lt;span class="n"&gt;accept&lt;/span&gt;
    &lt;span class="n"&gt;tcp&lt;/span&gt; &lt;span class="n"&gt;dport&lt;/span&gt; &lt;span class="m"&gt;22&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt; &lt;span class="n"&gt;saddr&lt;/span&gt; @&lt;span class="n"&gt;blackhole4&lt;/span&gt; &lt;span class="n"&gt;drop&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="n"&gt;ip6&lt;/span&gt; &lt;span class="n"&gt;saddr&lt;/span&gt; @&lt;span class="n"&gt;blackhole6&lt;/span&gt; &lt;span class="n"&gt;drop&lt;/span&gt;
    &lt;span class="n"&gt;tcp&lt;/span&gt; &lt;span class="n"&gt;dport&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt; &lt;span class="n"&gt;accept&lt;/span&gt;
    &lt;span class="n"&gt;tcp&lt;/span&gt; &lt;span class="n"&gt;dport&lt;/span&gt; &lt;span class="m"&gt;443&lt;/span&gt; &lt;span class="n"&gt;accept&lt;/span&gt;
  }

  &lt;span class="n"&gt;chain&lt;/span&gt; &lt;span class="n"&gt;forward&lt;/span&gt; {
    &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="n"&gt;filter&lt;/span&gt; &lt;span class="n"&gt;hook&lt;/span&gt; &lt;span class="n"&gt;forward&lt;/span&gt; &lt;span class="n"&gt;priority&lt;/span&gt; &lt;span class="n"&gt;filter&lt;/span&gt;; &lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="n"&gt;accept&lt;/span&gt;;
  }

  &lt;span class="n"&gt;chain&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt; {
    &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="n"&gt;filter&lt;/span&gt; &lt;span class="n"&gt;hook&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="n"&gt;priority&lt;/span&gt; &lt;span class="n"&gt;filter&lt;/span&gt;; &lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="n"&gt;accept&lt;/span&gt;;
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What we can do with that? Well, we can group the duplicated rules or the rules having the same behaviors. For example, allowing TCP/80 and TCP/443 are related to the same service, why not grouping them together inside their own chain?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;  &lt;span class="c"&gt;# ...
&lt;/span&gt;  &lt;span class="n"&gt;chain&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt; {
    &lt;span class="n"&gt;counter&lt;/span&gt;
    &lt;span class="n"&gt;tcp&lt;/span&gt; &lt;span class="n"&gt;dport&lt;/span&gt; {&lt;span class="m"&gt;80&lt;/span&gt;, &lt;span class="m"&gt;443&lt;/span&gt;} &lt;span class="n"&gt;accept&lt;/span&gt;
  }
  &lt;span class="c"&gt;# ...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we can remove those rules from the input chain and use a &lt;a href="https://manpages.debian.org/trixie/nftables/nftables.8.en.html#VERDICT_STATEMENT" rel="noopener noreferrer"&gt;&lt;code&gt;jump&lt;/code&gt;&lt;/a&gt; or a &lt;a href="https://manpages.debian.org/trixie/nftables/nftables.8.en.html#VERDICT_STATEMENT" rel="noopener noreferrer"&gt;&lt;code&gt;goto&lt;/code&gt;&lt;/a&gt; statement. The &lt;a href="https://manpages.debian.org/trixie/nftables/nftables.8.en.html#VERDICT_STATEMENT" rel="noopener noreferrer"&gt;&lt;code&gt;jump&lt;/code&gt;&lt;/a&gt; statement is used to route a packet inside another chain and get it back if no changes happened. The &lt;a href="https://manpages.debian.org/trixie/nftables/nftables.8.en.html#VERDICT_STATEMENT" rel="noopener noreferrer"&gt;&lt;code&gt;goto&lt;/code&gt;&lt;/a&gt; statement will give the packet to another chain and will never get it back, the policy of this chain will be the final one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;  &lt;span class="c"&gt;# ...
&lt;/span&gt;  &lt;span class="n"&gt;chain&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; {
    &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="n"&gt;filter&lt;/span&gt; &lt;span class="n"&gt;hook&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="n"&gt;priority&lt;/span&gt; &lt;span class="n"&gt;filter&lt;/span&gt;; &lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="n"&gt;drop&lt;/span&gt;;
    &lt;span class="n"&gt;iif&lt;/span&gt; &lt;span class="s2"&gt;"lo"&lt;/span&gt; &lt;span class="n"&gt;accept&lt;/span&gt;
    &lt;span class="n"&gt;tcp&lt;/span&gt; &lt;span class="n"&gt;dport&lt;/span&gt; &lt;span class="m"&gt;22&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt; &lt;span class="n"&gt;saddr&lt;/span&gt; @&lt;span class="n"&gt;blackhole4&lt;/span&gt; &lt;span class="n"&gt;drop&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="n"&gt;ip6&lt;/span&gt; &lt;span class="n"&gt;saddr&lt;/span&gt; @&lt;span class="n"&gt;blackhole6&lt;/span&gt; &lt;span class="n"&gt;drop&lt;/span&gt;
    &lt;span class="n"&gt;jump&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;
  }
  &lt;span class="c"&gt;# ...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&amp;nbsp;nft &lt;span class="nt"&gt;-f&lt;/span&gt; /tmp/nftables_rules.conf
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nft list ruleset
&lt;span class="go"&gt;table inet filter {
        set blackhole4 {
                type ipv4_addr
                flags interval,timeout
                timeout 1h
        }

        set blackhole6 {
                type ipv6_addr
                flags interval,timeout
                timeout 1h
        }

        chain input {
&lt;/span&gt;&lt;span class="gp"&gt;                type filter hook input priority filter;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;policy drop&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="go"&gt;                iif "lo" accept
                tcp dport 22 counter packets 17 bytes 996 accept
                log ip saddr @blackhole4 drop
                log ip6 saddr @blackhole6 drop
                jump http
        }

        chain http {
                counter packets 4 bytes 216
                tcp dport 80 accept
                tcp dport 443 accept
        }

        chain forward {
&lt;/span&gt;&lt;span class="gp"&gt;                type filter hook forward priority filter;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;policy accept&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="go"&gt;        }

        chain output {
&lt;/span&gt;&lt;span class="gp"&gt;                type filter hook output priority filter;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;policy accept&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="go"&gt;        }
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  &lt;code&gt;netns&lt;/code&gt; Network Simulation
&lt;/h1&gt;

&lt;p&gt;Most of the readers should know &lt;a href="https://www.docker.com/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt;, perhaps &lt;a href="https://podman.io/" rel="noopener noreferrer"&gt;Podman&lt;/a&gt; or &lt;a href="https://linuxcontainers.org/" rel="noopener noreferrer"&gt;LxC&lt;/a&gt;/&lt;a href="https://linuxcontainers.org/incus/" rel="noopener noreferrer"&gt;Incus&lt;/a&gt;. Maybe &lt;a href="https://wiki.openvz.org/User_Guide/Operations_on_Containers" rel="noopener noreferrer"&gt;OpenVZ&lt;/a&gt; was one of your tool in the past. In fact, those projects are userland interfaces to control Linux &lt;a href="https://manpages.debian.org/trixie/manpages/namespaces.7.en.html" rel="noopener noreferrer"&gt;namespaces&lt;/a&gt; and &lt;a href="https://manpages.debian.org/trixie/manpages/cgroups.7.en.html" rel="noopener noreferrer"&gt;cgroups&lt;/a&gt;. All those features mixed together offer a way to isolate running processes from different point of view (pid, network, users, etc...).&lt;/p&gt;

&lt;p&gt;Simulating a network on Linux was never so easy. A huge effort was made by the Linux developers to create enough tools to deal with virtual network. A new network stack is created for each new &lt;code&gt;netns&lt;/code&gt; created. On most of the Linux distribution available, no namespaces are created. It can be checked using &lt;a href="https://manpages.debian.org/trixie/iproute2/ip-netns.8.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;ip netns&lt;/code&gt;&lt;/a&gt; command followed by the &lt;code&gt;list&lt;/code&gt; subcommand. It should return nothing, because no namespace are present.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To create such namespace, the command &lt;a href="https://manpages.debian.org/trixie/iproute2/ip-netns.8.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;ip netns&lt;/code&gt;&lt;/a&gt; and the subcommand &lt;code&gt;add&lt;/code&gt; followed by the name of the namespace. Let create 3 network namespace: &lt;code&gt;alice&lt;/code&gt;, &lt;code&gt;bob&lt;/code&gt; and &lt;code&gt;eve&lt;/code&gt;. As you can see with the last command, those namespaces are now returned when we invoke the &lt;code&gt;ip netns list&lt;/code&gt; command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns add alice
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns add bob
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns add eve
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns list
&lt;span class="go"&gt;eve
bob
alice
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These new network stacks are totally isolated from the rest of the system (in fact, not totally, because we are sharing the same kernel, and those namespaces are not working inside a virtual machine). Each of them are initialized with a  &lt;code&gt;lo&lt;/code&gt; (&lt;a href="https://manpages.debian.org/trixie/freebsd-manpages/lo.4freebsd.en.html" rel="noopener noreferrer"&gt;loopback&lt;/a&gt;) interface by default. To execute a command using a specific namespace, one can use &lt;code&gt;ip netns exec&lt;/code&gt; followed by the id of the namespace and the command to execute. On Linux, we can use &lt;a href="https://manpages.debian.org/trixie/net-tools/ifconfig.8.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;ifconfig&lt;/code&gt;&lt;/a&gt; (legacy) or &lt;a href="https://manpages.debian.org/trixie/iproute2/ip.8.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;ip&lt;/code&gt;&lt;/a&gt; (modern). I would recommend to use &lt;a href="https://manpages.debian.org/trixie/iproute2/ip.8.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;ip&lt;/code&gt;&lt;/a&gt; because of its modern integration with the recent kernel and the insane amount of features it already support. So in our case, if we want to see the interfaces created, we can invoke &lt;a href="https://manpages.debian.org/trixie/iproute2/ip-link.8.en.html#ip_link_show_-_display_device_attributes" rel="noopener noreferrer"&gt;&lt;code&gt;ip link show&lt;/code&gt;&lt;/a&gt;, it will print the complete list of devices present. The full command should look like that: &lt;a href="https://manpages.debian.org/trixie/iproute2/ip-netns.8.en.html#ip~8" rel="noopener noreferrer"&gt;&lt;code&gt;ip netns exec ${namespace_name} ip link show&lt;/code&gt;&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;alice ip &lt;span class="nb"&gt;link &lt;/span&gt;show
&lt;span class="gp"&gt;1: lo: &amp;lt;LOOPBACK&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
&lt;span class="go"&gt;    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;bob ip &lt;span class="nb"&gt;link &lt;/span&gt;show
&lt;span class="gp"&gt;1: lo: &amp;lt;LOOPBACK&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
&lt;span class="go"&gt;    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;eve ip &lt;span class="nb"&gt;link &lt;/span&gt;show
&lt;span class="gp"&gt;1: lo: &amp;lt;LOOPBACK&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
&lt;span class="go"&gt;    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we can simulate a real network. First thing to do, create a schema to see how the namespaces will communicate together.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fny9ncm62ot4i6c98x3n2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fny9ncm62ot4i6c98x3n2.png" alt="namespace schema configuration" width="681" height="321"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Not sure if this is really accurate, but as you can see, we will have 3 namespace, &lt;code&gt;alice&lt;/code&gt;&amp;nbsp;and &lt;code&gt;bob&lt;/code&gt; will be connected to &lt;code&gt;eve&lt;/code&gt; via a bridge. &lt;code&gt;nftables&lt;/code&gt; will also be enabled and active on the three namespaces. Now, how to create a virtual interface? To do that, we can use the &lt;a href="https://manpages.debian.org/trixie/manpages/veth.4.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;veth&lt;/code&gt;&lt;/a&gt; interface, it will provide a way to link two namespaces. You can also see it a bit like a virtual cable, with two ends, one plugged on one namespace, and the other, on the other namespace. The command to use when creating a new &lt;a href="https://manpages.debian.org/trixie/manpages/veth.4.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;veth&lt;/code&gt;&lt;/a&gt; interface is &lt;a href="https://manpages.debian.org/trixie/iproute2/ip-link.8.en.html#ip_link_add_-_add_virtual_link" rel="noopener noreferrer"&gt;&lt;code&gt;ip link add&lt;/code&gt;&lt;/a&gt;. The syntax is a bit unusual for Unix/Linux system, but it looks like that the following snippet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ip &lt;span class="nb"&gt;link &lt;/span&gt;add &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;veth_netns1_name&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; netns &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;netns1_name&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nb"&gt;type &lt;/span&gt;veth &lt;span class="se"&gt;\&lt;/span&gt;
  peer &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;veth_netns2_name&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; netns &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;netns2_name&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;${veth_netns1_name}&lt;/code&gt; will be used to rename the interface present in the &lt;code&gt;${netns1_name}&lt;/code&gt; namespace. &lt;code&gt;${veth_netns2_name}&lt;/code&gt; will be used to rename the interface created in the &lt;code&gt;${netns2_name}&lt;/code&gt; namespace. Let try those commands. As you can see, after executing those commands, the &lt;code&gt;eve@if2&lt;/code&gt; interface has been added in &lt;code&gt;alice&lt;/code&gt; namespace, the &lt;code&gt;eve@if3&lt;/code&gt; has been added in the &lt;code&gt;bob&lt;/code&gt; namespace, two interfaces called &lt;code&gt;alice@if2&lt;/code&gt; and &lt;code&gt;bob@if3&lt;/code&gt; have been added in the &lt;code&gt;eve&lt;/code&gt; namespace.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip &lt;span class="nb"&gt;link &lt;/span&gt;add eve netns alice &lt;span class="nb"&gt;type &lt;/span&gt;veth peer alice netns eve
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip &lt;span class="nb"&gt;link &lt;/span&gt;add eve netns bob &lt;span class="nb"&gt;type &lt;/span&gt;veth peer bob netns eve
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;alice ip &lt;span class="nb"&gt;link &lt;/span&gt;show
&lt;span class="gp"&gt;1: lo: &amp;lt;LOOPBACK&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
&lt;span class="go"&gt;    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
&lt;/span&gt;&lt;span class="gp"&gt;2: eve@if2: &amp;lt;BROADCAST,MULTICAST&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
&lt;span class="go"&gt;    link/ether be:af:4b:81:c7:de brd ff:ff:ff:ff:ff:ff link-netns eve

&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;bob ip &lt;span class="nb"&gt;link &lt;/span&gt;show
&lt;span class="gp"&gt;1: lo: &amp;lt;LOOPBACK&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
&lt;span class="go"&gt;    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
&lt;/span&gt;&lt;span class="gp"&gt;2: eve@if3: &amp;lt;BROADCAST,MULTICAST&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
&lt;span class="go"&gt;    link/ether 76:4c:0d:53:e5:4a brd ff:ff:ff:ff:ff:ff link-netns eve

&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;eve ip &lt;span class="nb"&gt;link &lt;/span&gt;show
&lt;span class="gp"&gt;1: lo: &amp;lt;LOOPBACK&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
&lt;span class="go"&gt;    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
&lt;/span&gt;&lt;span class="gp"&gt;2: alice@if2: &amp;lt;BROADCAST,MULTICAST&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
&lt;span class="go"&gt;    link/ether 3a:a9:54:25:ec:5a brd ff:ff:ff:ff:ff:ff link-netns alice
&lt;/span&gt;&lt;span class="gp"&gt;3: bob@if2: &amp;lt;BROADCAST,MULTICAST&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
&lt;span class="go"&gt;    link/ether 66:6a:89:88:06:7b brd ff:ff:ff:ff:ff:ff link-netns bob
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now all our namespaces are connected via &lt;code&gt;eve&lt;/code&gt;, we can create a &lt;a href="https://manpages.debian.org/trixie/iproute2/bridge.8.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;bridge&lt;/code&gt;&lt;/a&gt; in it. Again, the command &lt;code&gt;ip link add&lt;/code&gt; can be used here.  All new &lt;a href="https://manpages.debian.org/trixie/manpages/veth.4.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;veth&lt;/code&gt;&lt;/a&gt; interfaces added in the &lt;code&gt;eve&lt;/code&gt; namespace will be plugged into &lt;code&gt;bridge0&lt;/code&gt;. To do that, all those interfaces must have it has master.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;eve ip &lt;span class="nb"&gt;link &lt;/span&gt;add &lt;span class="nb"&gt;type &lt;/span&gt;bridge
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;eve ip &lt;span class="nb"&gt;link set &lt;/span&gt;dev alice master bridge0
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;eve ip &lt;span class="nb"&gt;link set &lt;/span&gt;dev bob master bridge0
&lt;span class="gp"&gt;1: lo: &amp;lt;LOOPBACK&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
&lt;span class="go"&gt;    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
&lt;/span&gt;&lt;span class="gp"&gt;2: alice@if2: &amp;lt;BROADCAST,MULTICAST&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;mtu 1500 qdisc noop master bridge0 state DOWN mode DEFAULT group default qlen 1000
&lt;span class="go"&gt;    link/ether 3a:a9:54:25:ec:5a brd ff:ff:ff:ff:ff:ff link-netns alice
&lt;/span&gt;&lt;span class="gp"&gt;3: bob@if2: &amp;lt;BROADCAST,MULTICAST&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;mtu 1500 qdisc noop master bridge0 state DOWN mode DEFAULT group default qlen 1000
&lt;span class="go"&gt;    link/ether 66:6a:89:88:06:7b brd ff:ff:ff:ff:ff:ff link-netns bob
&lt;/span&gt;&lt;span class="gp"&gt;4: bridge0: &amp;lt;BROADCAST,MULTICAST&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
&lt;span class="go"&gt;    link/ether 3a:a9:54:25:ec:5a brd ff:ff:ff:ff:ff:ff
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It looks good, &lt;code&gt;alice@if2&lt;/code&gt; and &lt;code&gt;bob@if2&lt;/code&gt; have been added into &lt;code&gt;bridge0&lt;/code&gt;. Now, we enabled all interface state (from &lt;code&gt;DOWN&lt;/code&gt; to &lt;code&gt;UP&lt;/code&gt;). The command is simple: &lt;a href="https://manpages.debian.org/trixie/iproute2/ip-link.8.en.html#ip_link_set_-_change_device_attributes" rel="noopener noreferrer"&gt;&lt;code&gt;ip link set dev ${device_name} up&lt;/code&gt;&lt;/a&gt;. Let execute that for the &lt;code&gt;alice&lt;/code&gt; namespace.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;alice ip &lt;span class="nb"&gt;link set &lt;/span&gt;dev eve up
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;alice ip &lt;span class="nb"&gt;link &lt;/span&gt;show 
&lt;span class="gp"&gt;1: lo: &amp;lt;LOOPBACK&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
&lt;span class="go"&gt;    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
&lt;/span&gt;&lt;span class="gp"&gt;2: eve@if2: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
&lt;span class="go"&gt;    link/ether be:af:4b:81:c7:de brd ff:ff:ff:ff:ff:ff link-netns eve
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can do the same for the &lt;code&gt;bob&lt;/code&gt; namespace...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;bob ip &lt;span class="nb"&gt;link set &lt;/span&gt;dev eve up
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;bob ip &lt;span class="nb"&gt;link &lt;/span&gt;show 
&lt;span class="gp"&gt;1: lo: &amp;lt;LOOPBACK&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
&lt;span class="go"&gt;    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
&lt;/span&gt;&lt;span class="gp"&gt;2: eve@if3: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
&lt;span class="go"&gt;    link/ether 76:4c:0d:53:e5:4a brd ff:ff:ff:ff:ff:ff link-netns eve
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And finally activate all interfaces in the &lt;code&gt;eve&lt;/code&gt; namespace.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;eve ip &lt;span class="nb"&gt;link set &lt;/span&gt;dev bridge0 up
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;eve ip &lt;span class="nb"&gt;link &lt;/span&gt;s dev alice up
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;eve ip &lt;span class="nb"&gt;link set &lt;/span&gt;dev bob up
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;eve ip &lt;span class="nb"&gt;link &lt;/span&gt;show 
&lt;span class="gp"&gt;1: lo: &amp;lt;LOOPBACK&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
&lt;span class="go"&gt;    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
&lt;/span&gt;&lt;span class="gp"&gt;2: alice@if2: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;mtu 1500 qdisc noqueue master bridge0 state UP mode DEFAULT group default qlen 1000
&lt;span class="go"&gt;    link/ether 3a:a9:54:25:ec:5a brd ff:ff:ff:ff:ff:ff link-netns alice
&lt;/span&gt;&lt;span class="gp"&gt;3: bob@if2: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;mtu 1500 qdisc noqueue master bridge0 state UP mode DEFAULT group default qlen 1000
&lt;span class="go"&gt;    link/ether 66:6a:89:88:06:7b brd ff:ff:ff:ff:ff:ff link-netns bob
&lt;/span&gt;&lt;span class="gp"&gt;4: bridge0: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
&lt;span class="go"&gt;    link/ether 3a:a9:54:25:ec:5a brd ff:ff:ff:ff:ff:ff
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's great, we can now configure IP addresses. &lt;code&gt;eve&lt;/code&gt; will act as a router here, so, we can give it &lt;code&gt;10.0.0.1/8&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fplkouuzxmnna9fj7v5l0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fplkouuzxmnna9fj7v5l0.png" alt="eve namespace with 10.0.0.1/8" width="681" height="321"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To configure the L3 network stack (e.g. IPv4, IPv6) the &lt;a href="https://manpages.debian.org/trixie/iproute2/ip-address.8.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;ip address&lt;/code&gt;&lt;/a&gt; command can be used. To add a new address simply use &lt;a href="https://manpages.debian.org/trixie/iproute2/ip-address.8.en.html#ip_address_add_-_add_new_protocol_address." rel="noopener noreferrer"&gt;&lt;code&gt;ip address add dev ${device} ${address}&lt;/code&gt;&lt;/a&gt;. To show the current configuration, one can use &lt;a href="https://manpages.debian.org/trixie/iproute2/ip-address.8.en.html#ip_address_show_-_look_at_protocol_addresses" rel="noopener noreferrer"&gt;&lt;code&gt;ip address show&lt;/code&gt;&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;eve ip address add dev bridge0 10.0.0.1/8
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;eve ip address show dev bridge0
&lt;span class="gp"&gt;4: bridge0: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;mtu 1500 qdisc noqueue state UP group default qlen 1000
&lt;span class="go"&gt;    link/ether 3a:a9:54:25:ec:5a brd ff:ff:ff:ff:ff:ff
    inet 10.0.0.1/8 scope global bridge0
       valid_lft forever preferred_lft forever
    inet6 fe80::38a9:54ff:fe25:ec5a/64 scope link proto kernel_ll 
       valid_lft forever preferred_lft forever
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;alice&lt;/code&gt; will have the &lt;code&gt;10.0.0.2/24&lt;/code&gt; IP address. It should now be able to reach &lt;code&gt;eve&lt;/code&gt; by using ICMP ECHO packets via &lt;a href="https://manpages.debian.org/trixie/inetutils-ping/ping.1.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;ping&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg4lcil3jfd4aq41gzsed.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg4lcil3jfd4aq41gzsed.png" alt="alice namespace with 10.0.0.2/8" width="681" height="321"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;alice ip address add dev eve 10.0.0.2/8
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;alice ip address show dev eve
&lt;span class="gp"&gt;2: eve@if2: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;mtu 1500 qdisc noqueue state UP group default qlen 1000
&lt;span class="go"&gt;    link/ether be:af:4b:81:c7:de brd ff:ff:ff:ff:ff:ff link-netns eve
    inet 10.0.0.2/8 scope global eve
       valid_lft forever preferred_lft forever
    inet6 fe80::bcaf:4bff:fe81:c7de/64 scope link proto kernel_ll 
       valid_lft forever preferred_lft forever

&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;alice ping &lt;span class="nt"&gt;-c&lt;/span&gt; 1 10.0.0.1
&lt;span class="go"&gt;PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
64 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=0.046 ms

--- 10.0.0.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.046/0.046/0.046/0.000 ms
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works! &lt;code&gt;bob&lt;/code&gt; will receive &lt;code&gt;10.0.0.3/8&lt;/code&gt; IP address. Let do the same test with &lt;a href="https://manpages.debian.org/trixie/inetutils-ping/ping.1.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;ping&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fngpwq3nqmzp3y3t142m3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fngpwq3nqmzp3y3t142m3.png" alt="bob namespace with 10.0.0.3/8" width="681" height="321"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;bob ip address add dev eve 10.0.0.3/8
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;bob ip address show dev eve
&lt;span class="gp"&gt;2: eve@if3: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;mtu 1500 qdisc noqueue state UP group default qlen 1000
&lt;span class="go"&gt;    link/ether 76:4c:0d:53:e5:4a brd ff:ff:ff:ff:ff:ff link-netns eve
    inet 10.0.0.3/8 scope global eve
       valid_lft forever preferred_lft forever
    inet6 fe80::744c:dff:fe53:e54a/64 scope link proto kernel_ll 
       valid_lft forever preferred_lft forever

&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;bob ping &lt;span class="nt"&gt;-c&lt;/span&gt; 1 10.0.0.1
&lt;span class="go"&gt;PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
64 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=0.084 ms

--- 10.0.0.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.084/0.084/0.084/0.000 ms
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our isolated test network is ready to test our firewalls configuration. By default, &lt;code&gt;nftables&lt;/code&gt; is disabled on each namespace, then, we need to enable it. I assume there the &lt;code&gt;/etc/nftables.conf&lt;/code&gt; contains the standard &lt;code&gt;nftables&lt;/code&gt; configuration. If it's not the case on your side,  you can easily recreate this ruleset. Let enable &lt;code&gt;nftables&lt;/code&gt; on &lt;code&gt;alice&lt;/code&gt; namespace first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;alice nft list ruleset
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;alice nft &lt;span class="nt"&gt;-f&lt;/span&gt; /etc/nftables.conf
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;alice nft list ruleset
&lt;span class="go"&gt;table inet filter {
        chain input {
&lt;/span&gt;&lt;span class="gp"&gt;                type filter hook input priority filter;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;policy accept&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="go"&gt;        }

        chain forward {
&lt;/span&gt;&lt;span class="gp"&gt;                type filter hook forward priority filter;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;policy accept&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="go"&gt;        }

        chain output {
&lt;/span&gt;&lt;span class="gp"&gt;                type filter hook output priority filter;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;policy accept&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="go"&gt;        }
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, we can enable it on &lt;code&gt;bob&lt;/code&gt; namespace.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;bob nft list ruleset
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;bob nft &lt;span class="nt"&gt;-f&lt;/span&gt; /etc/nftables.conf
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;bob nft list ruleset
&lt;span class="go"&gt;table inet filter {
        chain input {
&lt;/span&gt;&lt;span class="gp"&gt;                type filter hook input priority filter;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;policy accept&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="go"&gt;        }

        chain forward {
&lt;/span&gt;&lt;span class="gp"&gt;                type filter hook forward priority filter;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;policy accept&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="go"&gt;        }

        chain output {
&lt;/span&gt;&lt;span class="gp"&gt;                type filter hook output priority filter;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;policy accept&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="go"&gt;        }
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, &lt;code&gt;eve&lt;/code&gt; can also have its own firewall enabled.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;eve nft list ruleset
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;eve nft &lt;span class="nt"&gt;-f&lt;/span&gt; /etc/nftables.conf
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;eve nft list ruleset
&lt;span class="go"&gt;table inet filter {
        chain input {
&lt;/span&gt;&lt;span class="gp"&gt;                type filter hook input priority filter;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;policy accept&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="go"&gt;        }

        chain forward {
&lt;/span&gt;&lt;span class="gp"&gt;                type filter hook forward priority filter;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;policy accept&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="go"&gt;        }

        chain output {
&lt;/span&gt;&lt;span class="gp"&gt;                type filter hook output priority filter;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;policy accept&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="go"&gt;        }
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The firewalls are all ready, we can now inject our configuration for each namespace and see what will happen. To simulate a server (e.g. ssh for example) or a client, we can use &lt;a href="https://manpages.debian.org/trixie/netcat-openbsd/nc.1.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;nc&lt;/code&gt;&lt;/a&gt;. If we want to do something more complex, like simulating a port scanner, we can use &lt;a href="https://manpages.debian.org/trixie/nmap/nmap.1.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;nmap&lt;/code&gt;&lt;/a&gt;. Let simulate &lt;a href="https://manpages.debian.org/trixie/openssh-server/sshd.8.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;sshd&lt;/code&gt;&lt;/a&gt; on each namespace. You can start one new terminal for each one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;alice nc &lt;span class="nt"&gt;-vkl&lt;/span&gt; 22
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;bob nc &lt;span class="nt"&gt;-kl&lt;/span&gt; 22
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;eve nc &lt;span class="nt"&gt;-vkl&lt;/span&gt; 22
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;alice nc &lt;span class="nt"&gt;-zv&lt;/span&gt; 10.0.0.1 22
&lt;span class="go"&gt;Connection to 10.0.0.1 22 port [tcp/ssh] succeeded!

&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;bob nc &lt;span class="nt"&gt;-zv&lt;/span&gt; 10.0.0.1 22
&lt;span class="go"&gt;Connection to 10.0.0.1 22 port [tcp/ssh] succeeded!

&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;eve nc &lt;span class="nt"&gt;-zv&lt;/span&gt; 10.0.0.2 22
&lt;span class="go"&gt;Connection to 10.0.0.2 22 port [tcp/ssh] succeeded!

&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;eve nc &lt;span class="nt"&gt;-zv&lt;/span&gt; 10.0.0.3 22
&lt;span class="go"&gt;Connection to 10.0.0.3 22 port [tcp/ssh] succeeded!
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let update the input chain policy on &lt;code&gt;alice&lt;/code&gt; by dropping everything by default&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;alice nft &lt;span class="s1"&gt;'add chain inet filter input { policy drop; }'&lt;/span&gt;
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;alice nft list chain inet filter input
&lt;span class="go"&gt;table inet filter {
        chain input {
&lt;/span&gt;&lt;span class="gp"&gt;                type filter hook input priority filter;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;policy drop&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="go"&gt;        }
}

&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;alice ping &lt;span class="nt"&gt;-c1&lt;/span&gt; 10.0.0.1
&lt;span class="go"&gt;PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.

--- 10.0.0.1 ping statistics ---
1 packets transmitted, 0 received, 100% packet loss, time 0ms

&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;alice nft &lt;span class="s1"&gt;'add rule inet filter input icmp type { * } accept'&lt;/span&gt;
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;alice ping &lt;span class="nt"&gt;-c1&lt;/span&gt; 10.0.0.1
&lt;span class="go"&gt;PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
64 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=0.067 ms

--- 10.0.0.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.067/0.067/0.067/0.000 ms

&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;eve nc &lt;span class="nt"&gt;-w3&lt;/span&gt; &lt;span class="nt"&gt;-zv&lt;/span&gt; 10.0.0.2 22
&lt;span class="go"&gt;nc: connect to 10.0.0.2 port 22 (tcp) timed out: Operation now in progress

&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;alice nft &lt;span class="s1"&gt;'add rule inet filter input tcp dport ssh accept'&lt;/span&gt;
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;alice nft &lt;span class="nt"&gt;-a&lt;/span&gt; list chain inet filter input
&lt;span class="go"&gt;table inet filter {
&lt;/span&gt;&lt;span class="gp"&gt;        chain input { #&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;handle 1
&lt;span class="gp"&gt;                type filter hook input priority filter;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;policy drop&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="gp"&gt;                icmp type { * } accept #&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;handle 8
&lt;span class="gp"&gt;                tcp dport 22 accept #&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;handle 9
&lt;span class="go"&gt;        }
}

&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;eve nc &lt;span class="nt"&gt;-w3&lt;/span&gt; &lt;span class="nt"&gt;-zv&lt;/span&gt; 10.0.0.2 22
&lt;span class="go"&gt;Connection to 10.0.0.2 22 port [tcp/ssh] succeeded!
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;eve&lt;/code&gt; is now able to ping &lt;code&gt;alice&lt;/code&gt; because the ICMP packets are allowed on &lt;code&gt;alice&lt;/code&gt; side.  If you want to simulate a real router with &lt;code&gt;eve&lt;/code&gt;, you will also need to check your kernel configuration with &lt;a href="https://manpages.debian.org/trixie/procps/sysctl.8.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;sysctl&lt;/code&gt;&lt;/a&gt; or via &lt;a href="https://manpages.debian.org/trixie/manpages/sysfs.5.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;sysfs&lt;/code&gt;&lt;/a&gt;. You will probably need to set the following parameters. If you want more details about them, you can check the &lt;a href="https://docs.kernel.org/networking/ip-sysctl.html" rel="noopener noreferrer"&gt;IP Sysctl Documentation&lt;/a&gt; from Kernel.org.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;net.ipv4.ip_forward&lt;/code&gt;: Forward Packets between interfaces;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;net.ipv4.conf.alice.forwarding&lt;/code&gt;: Enable IP forwarding on this interface. This controls whether packets received &lt;em&gt;on&lt;/em&gt; this interface can be forwarded;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;net.ipv4.conf.all.forwarding&lt;/code&gt;: Enable IP forwarding on this interface. This controls whether packets received &lt;em&gt;on&lt;/em&gt; this interface can be forwarded;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;net.ipv4.conf.bob.forwarding&lt;/code&gt;: Enable IP forwarding on this interface. This controls whether packets received &lt;em&gt;on&lt;/em&gt; this interface can be forwarded;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;net.ipv4.conf.bridge0.forwarding&lt;/code&gt;: Enable IP forwarding on this interface. This controls whether packets received &lt;em&gt;on&lt;/em&gt; this interface can be forwarded;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;net.ipv6.conf.all.forwarding&lt;/code&gt;: Enable global IPv6 forwarding between all interfaces.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you prefer using &lt;code&gt;sysfs&lt;/code&gt;, don't forget to run the command with the correct network namespace, below an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;eve find /sys/devices/virtual/net/ &lt;span class="nt"&gt;-maxdepth&lt;/span&gt; 1
&lt;span class="go"&gt;/sys/devices/virtual/net/
/sys/devices/virtual/net/bridge0
/sys/devices/virtual/net/alice
/sys/devices/virtual/net/bob
/sys/devices/virtual/net/lo

&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;alice find /sys/devices/virtual/net/ &lt;span class="nt"&gt;-maxdepth&lt;/span&gt; 1
&lt;span class="go"&gt;/sys/devices/virtual/net/
/sys/devices/virtual/net/eve
/sys/devices/virtual/net/lo

&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;bob find /sys/devices/virtual/net/ &lt;span class="nt"&gt;-maxdepth&lt;/span&gt; 1
&lt;span class="go"&gt;/sys/devices/virtual/net/
/sys/devices/virtual/net/eve
/sys/devices/virtual/net/lo
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before cleaning up everything, and to give you a proof this network is really isolated from the rest of the system, you can try to &lt;code&gt;ping&lt;/code&gt; with setting any network namespace.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ping &lt;span class="nt"&gt;-c&lt;/span&gt; 1 10.0.0.1
&lt;span class="go"&gt;PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.

--- 10.0.0.1 ping statistics ---
1 packets transmitted, 0 received, 100% packet loss, time 0ms

&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ping &lt;span class="nt"&gt;-c1&lt;/span&gt; 10.0.0.2
&lt;span class="go"&gt;PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.

--- 10.0.0.2 ping statistics ---
1 packets transmitted, 0 received, 100% packet loss, time 0ms

&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ping &lt;span class="nt"&gt;-c1&lt;/span&gt; 10.0.0.3
&lt;span class="go"&gt;PING 10.0.0.3 (10.0.0.3) 56(84) bytes of data.

--- 10.0.0.3 ping statistics ---
1 packets transmitted, 0 received, 100% packet loss, time 0ms
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you are done with these last tests, you can delete all namespaces one by one, nothing will be saved, and your system will be back like before. Neat, right? How to do that? You just have to invoke &lt;code&gt;ip netns delete ${namespace_name}&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns delete alice
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns delete bob
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns delete eve
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip netns list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why using network namespaces to test a firewall configuration? Well, because it's cheap, it's highly flexible, and you don't risk to cut your connection with the ssh server because you are controlling the namespace directly from the host itself. It can also be used to simulate different kind of traffic behavior with the help of &lt;a href="https://manpages.debian.org/trixie/iproute2/tc.8.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;tc&lt;/code&gt;&lt;/a&gt;, create a VPN with &lt;a href="https://manpages.debian.org/trixie/wireguard-tools/wg.8.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;wg&lt;/code&gt;&lt;/a&gt; or configure &lt;a href="https://manpages.debian.org/trixie/freebsd-manpages/vxlan.4freebsd.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;vxlan&lt;/code&gt;&lt;/a&gt; for example. Anyway, this is a cool way to learn how to configure a network and simulate it without paying a shit ton of money in real hardware.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;This first post on &lt;code&gt;nftables&lt;/code&gt; was inspired by one training session I gave ~5 years ago to one of my developers team when they wanted to know more about network usage. Indeed, we were working in a company selling network solution, but most of the developers were totally unaware of the complexity of the network. Furthermore, a big part of the equipments and devices were (and still are) crazy expensive, then it's kinda challenging to train people with their own individual system.&lt;/p&gt;

&lt;p&gt;Linux (or BSD systems) are offering there a great alternative. If you don't know, a big part of the modern firewall, router, or switch appliances are based on modified FOSS. Back 20 years ago, when I began my career, we were dealing with Alcatel-Lucent Core Routers, all of them - at this time - were using a custom version of RHEL (I can't remember the name of the OS, after a quick research, it could be &lt;a href="https://infocenter.nokia.com/public/7210SAS229R1A/index.jsp?topic=%2Fcom.nokia.TSR_Basic_System_Guide%2Fconfiguration_f-ai9j4okjrn.html" rel="noopener noreferrer"&gt;TiMOS&lt;/a&gt;). &lt;a href="https://en.wikipedia.org/wiki/Redback_Networks" rel="noopener noreferrer"&gt;Redback&lt;/a&gt;, now part of Ericsson, were also selling their core network equipments on a modified version of NetBSD. So, why not using FOSS to learn how to deploy and use a network like a netadmin?&lt;/p&gt;

&lt;p&gt;Anyway, I hope this long will motivate you to test &lt;code&gt;nftables&lt;/code&gt; and perhaps from &lt;code&gt;iptables&lt;/code&gt;. I also hope it will motivate you to learn more about networking protocols and how to use them in your environment. As usual, here a list of links to help you improve your &lt;code&gt;nftables&lt;/code&gt; knowledge.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.nftables.org/" rel="noopener noreferrer"&gt;Official &lt;code&gt;nftables&lt;/code&gt; website&lt;/a&gt;, where you will find all official information related to nftables, lot of examples and the documentation;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://wiki.nftables.org/wiki-nftables/index.php/Main_Page" rel="noopener noreferrer"&gt;Official &lt;code&gt;nftables&lt;/code&gt; wiki&lt;/a&gt; is a gold mine, where you will find a lot of snippets, examples, uses cases and best practices;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://wiki.gentoo.org/wiki/Nftables" rel="noopener noreferrer"&gt;&lt;code&gt;nftables&lt;/code&gt; documentation&lt;/a&gt; from the Gentoo Wiki, where you will find a lot of examples and some use cases;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://wiki.archlinux.org/title/Nftables" rel="noopener noreferrer"&gt;&lt;code&gt;nftables&lt;/code&gt; documentation&lt;/a&gt; from the Arch Linux Wiki, where you will also find a lot of examples and use cases;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://wiki.alpinelinux.org/wiki/Nftables" rel="noopener noreferrer"&gt;&lt;code&gt;nftables&lt;/code&gt; documentation&lt;/a&gt; from the Alpine Linux Wiki, another great place to learn more about &lt;code&gt;nftables&lt;/code&gt;;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://documentation.ubuntu.com/security/security-features/network/firewall/nftables/" rel="noopener noreferrer"&gt;&lt;code&gt;nftables&lt;/code&gt; documentation&lt;/a&gt; from the official Ubuntu documentation, a good place to learn how &lt;code&gt;nftables&lt;/code&gt; has been integrated in Ubuntu;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/8/html/configuring_and_managing_networking/getting-started-with-nftables_configuring-and-managing-networking" rel="noopener noreferrer"&gt;&lt;code&gt;nftables&lt;/code&gt; configuration&lt;/a&gt; from the official Red Hat documentation, nice to have if you are using Fedora or RHEL and you would like to use &lt;code&gt;nftables&lt;/code&gt; by default;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf-next.git" rel="noopener noreferrer"&gt;Linux kernel &lt;code&gt;nftables&lt;/code&gt; source code&lt;/a&gt;, where you will learn how &lt;code&gt;nftables&lt;/code&gt; has been implemented in C and integrated in the Linux kernel;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://git.netfilter.org/nftables/" rel="noopener noreferrer"&gt;&lt;code&gt;nftables&lt;/code&gt; userland source code&lt;/a&gt;, where you will find the source code of the &lt;code&gt;nft&lt;/code&gt; command and all other scripts or tools deployed by default on your favorite distribution.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As final note regarding &lt;code&gt;nftables&lt;/code&gt;, like any software, it has bugs and can also have security issues, because it is running as kernel module, it can have a big impact on all your running application. In fact, &lt;a href="https://lore.kernel.org/linux-cve-announce/?q=nftables" rel="noopener noreferrer"&gt;&lt;code&gt;nftables&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://lore.kernel.org/linux-cve-announce/?q=netfilter" rel="noopener noreferrer"&gt;&lt;code&gt;netfilter&lt;/code&gt;&lt;/a&gt; have been - and are still - impacted by that. One in charge of servers with &lt;code&gt;nftables&lt;/code&gt; (and more generally with a Linux kernel installed) should always follow the &lt;a href="https://lore.kernel.org/linux-cve-announce/" rel="noopener noreferrer"&gt;Linux Kernel CVE announce&lt;/a&gt; page, and be ready to plan an upgrade in case of problem. &lt;/p&gt;

&lt;p&gt;Happy hack and have fun!&lt;/p&gt;




&lt;p&gt;Image Cover by &lt;a href="https://unsplash.com/@gxjansen?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Guido Jansen&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/yellow-fire-digital-wallpaper-Nz-zAt4qiuU?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>linux</category>
      <category>firewall</category>
      <category>sysadmin</category>
      <category>nftables</category>
    </item>
    <item>
      <title>Arguments Parsing in Dart</title>
      <dc:creator>Mathieu Kerjouan</dc:creator>
      <pubDate>Wed, 17 Jun 2026 08:00:00 +0000</pubDate>
      <link>https://dev.to/niamtokik/arguments-parsing-in-dart-jc1</link>
      <guid>https://dev.to/niamtokik/arguments-parsing-in-dart-jc1</guid>
      <description>&lt;p&gt;This is a quick and dirty note about parsing command line arguments in Dart. If you are familiar with C programming, you should already know the &lt;a href="https://manpages.debian.org/trixie/manpages-dev/getopt.3.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;getopt&lt;/code&gt;&lt;/a&gt; function, or if you are doing a bit of shell scripting, the &lt;a href="https://manpages.debian.org/trixie/util-linux/getopt.1.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;getopt&lt;/code&gt;&lt;/a&gt; command. In Dart, the main package to do that is called &lt;a href="https://pub.dev/packages/args" rel="noopener noreferrer"&gt;&lt;code&gt;args&lt;/code&gt;&lt;/a&gt; and it is maintained by the Dart team. The &lt;a href="https://pub.dev/documentation/args/latest/" rel="noopener noreferrer"&gt;API documentation&lt;/a&gt; is containing examples and the descriptions of the difference classes to use. The source code is available on &lt;a href="https://github.com/dart-lang/core/tree/main/pkgs/args" rel="noopener noreferrer"&gt;dart-lang/core&lt;/a&gt; repository at Github. Another package called &lt;a href="https://pub.dev/packages/capp" rel="noopener noreferrer"&gt;&lt;code&gt;capp&lt;/code&gt;&lt;/a&gt; is regrouping a lot of feature in one single module, including interactive console. We will not talk about this one today.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart create arg_parse
&lt;span class="go"&gt;Creating arg_parse using template console...

  .gitignore
  analysis_options.yaml
  CHANGELOG.md
  pubspec.yaml
  README.md
  bin/arg_parse.dart
  lib/arg_parse.dart
  test/arg_parse_test.dart

Running pub get...                     0.3s
  Resolving dependencies...
  Downloading packages...
  Changed 48 dependencies!
  1 package has newer versions incompatible with dependency constraints.
  Try `dart pub outdated` for more information.

Created project arg_parse in arg_parse! In order to get started, run the following commands:

  cd arg_parse
  dart run

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;arg_parse
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart pub add args
&lt;span class="go"&gt;Resolving dependencies... 
Downloading packages... 
  args 2.7.0 (from transitive dependency to direct dependency)
  package_config 2.2.0 (3.0.0 available)
Changed 1 dependency!
1 package has newer versions incompatible with dependency constraints.
Try `dart pub outdated` for more information.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let modify the entry-point in &lt;code&gt;bin/arg_parse.dart&lt;/code&gt; and imports &lt;code&gt;args.dart&lt;/code&gt; module.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:args/args.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Parsing
&lt;/h1&gt;

&lt;p&gt;Parsing a command line with the &lt;code&gt;args&lt;/code&gt; package is simple. The first step is to instantiate a new &lt;a href="https://pub.dev/documentation/args/latest/args/ArgParser-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;ArgParser&lt;/code&gt;&lt;/a&gt; object where all the parsing logic will be stored. The &lt;a href="https://pub.dev/documentation/args/latest/args/ArgParser/ArgParser.html" rel="noopener noreferrer"&gt;constructor&lt;/a&gt; can deal with trailing options via the &lt;code&gt;allowTrailingOptions&lt;/code&gt; parameter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ArgParser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;allowTrailingOptions:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This newly created object defines 4 type of arguments when it comes to parse data:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Commands are special keywords which can become subcommands with their own arguments and options. Those are created with the &lt;a href="https://pub.dev/documentation/args/latest/args/ArgParser/addCommand.html" rel="noopener noreferrer"&gt;&lt;code&gt;addCommand&lt;/code&gt;&lt;/a&gt; method;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Flags are boolean key, if a flag is present, it will exist in the parsed result. Flags can be created with the &lt;a href="https://pub.dev/documentation/args/latest/args/ArgParser/addFlag.html" rel="noopener noreferrer"&gt;&lt;code&gt;addFlag&lt;/code&gt;&lt;/a&gt; method;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Options are key/value arguments, they can be created with the &lt;a href="https://pub.dev/documentation/args/latest/args/ArgParser/addOption.html" rel="noopener noreferrer"&gt;&lt;code&gt;addOption&lt;/code&gt;&lt;/a&gt; method;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Multi Options are multi key/value arguments, it means this kind of argument can be seen many time. a Multi Option can be created with &lt;a href="https://pub.dev/documentation/args/latest/args/ArgParser/addMultiOption.html" rel="noopener noreferrer"&gt;&lt;code&gt;addMultiOption&lt;/code&gt;&lt;/a&gt; method;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The best way to learn is to try. Let  reproduce a small subset of the arguments defined by &lt;a href="https://manpages.debian.org/trixie/curl/curl.1.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;curl&lt;/code&gt;&lt;/a&gt; using the &lt;code&gt;args&lt;/code&gt; package:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://manpages.debian.org/trixie/curl/curl.1.en.html#compressed" rel="noopener noreferrer"&gt;&lt;code&gt;--compressed&lt;/code&gt;&lt;/a&gt;: flag with long argument only;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addFlag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s"&gt;'compressed'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;help:&lt;/span&gt; &lt;span class="s"&gt;'(HTTP) Request a compressed response using one of the algorithms curl supports, and automatically decompress the content.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;defaultsTo:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://manpages.debian.org/trixie/curl/curl.1.en.html#connect" rel="noopener noreferrer"&gt;&lt;code&gt;--connect-timeout &amp;lt;seconds&amp;gt;&lt;/code&gt;&lt;/a&gt;: option with long argument only and accepting positive decimal values;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addOption&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s"&gt;'connect-timeout'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;help:&lt;/span&gt; &lt;span class="s"&gt;'Maximum time in seconds that you allow curl&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s"&gt;s connection to take. This only limits the connection phase, so if curl connects within the given period it continues - if not it exits.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;defaultsTo:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;valueHelp:&lt;/span&gt; &lt;span class="s"&gt;'timeout'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://manpages.debian.org/trixie/curl/curl.1.en.html#c," rel="noopener noreferrer"&gt;&lt;code&gt;-c, --cookie-jar &amp;lt;filename&amp;gt;&lt;/code&gt;&lt;/a&gt;: option with short and long arguments, only accepting filename;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addOption&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s"&gt;'cookie-jar'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;help:&lt;/span&gt; &lt;span class="s"&gt;'(HTTP) Specify to which file you want curl to write all cookies after a completed operation.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;abbr:&lt;/span&gt; &lt;span class="s"&gt;'c'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;valueHelp:&lt;/span&gt; &lt;span class="s"&gt;'filename'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://manpages.debian.org/trixie/curl/curl.1.en.html#v," rel="noopener noreferrer"&gt;&lt;code&gt;-v, --verbose&lt;/code&gt;&lt;/a&gt;: short and long flag with short floag repetition feature (&lt;code&gt;-vvvv&lt;/code&gt;);
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addMultiOption&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s"&gt;'verbose'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;help:&lt;/span&gt; &lt;span class="s"&gt;'Make curl output verbose information during the operation. Useful for debugging and seeing what&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s"&gt;s going on under the hood.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;abbr:&lt;/span&gt; &lt;span class="s"&gt;'v'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;splitCommas:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;allowed:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;defaultsTo:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://manpages.debian.org/trixie/curl/curl.1.en.html#V," rel="noopener noreferrer"&gt;&lt;code&gt;-V, --version&lt;/code&gt;&lt;/a&gt;: short and long flag only argument.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addFlag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s"&gt;'version'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;help:&lt;/span&gt; &lt;span class="s"&gt;'Display information about curl and the libcurl version it uses.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;abbr:&lt;/span&gt; &lt;span class="s"&gt;'V'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;defaultsTo:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this time, it is possible to display the usage via the &lt;a href="https://pub.dev/documentation/args/latest/args/ArgParser/usage.html" rel="noopener noreferrer"&gt;&lt;code&gt;usage&lt;/code&gt;&lt;/a&gt; property. Let put everything in a function and simply print the usage to see what will happen.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;ArgParser&lt;/span&gt; &lt;span class="nf"&gt;createParser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ArgParser&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addFlag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;'help'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;help:&lt;/span&gt; &lt;span class="s"&gt;'print usage'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;abbr:&lt;/span&gt; &lt;span class="s"&gt;'h'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;defaultsTo:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addFlag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;'compressed'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;help:&lt;/span&gt; &lt;span class="s"&gt;'(HTTP) Request a compressed response using one of the algorithms curl supports,'&lt;/span&gt;
      &lt;span class="s"&gt;'and automatically decompress the content.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;defaultsTo:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addOption&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;'connect-timeout'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;help:&lt;/span&gt; &lt;span class="s"&gt;'Maximum time in seconds that you allow curl&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s"&gt;s connection to take. '&lt;/span&gt;
      &lt;span class="s"&gt;'This only limits the connection phase, so if curl connects within the '&lt;/span&gt;
      &lt;span class="s"&gt;'given period it continues - if not it exits.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;valueHelp:&lt;/span&gt; &lt;span class="s"&gt;'timeout'&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addOption&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;'cookie-jar'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;help:&lt;/span&gt; &lt;span class="s"&gt;'(HTTP) Specify to which file you want curl to write all cookies after '&lt;/span&gt;
      &lt;span class="s"&gt;'a completed operation.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;abbr:&lt;/span&gt; &lt;span class="s"&gt;'c'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;valueHelp:&lt;/span&gt; &lt;span class="s"&gt;'filename'&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addMultiOption&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;'verbose'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;help:&lt;/span&gt; &lt;span class="s"&gt;'Make curl output verbose information during the operation. '&lt;/span&gt;
      &lt;span class="s"&gt;'Useful for debugging and seeing what&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s"&gt;s going on under the hood.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;abbr:&lt;/span&gt; &lt;span class="s"&gt;'v'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;splitCommas:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;allowed:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;defaultsTo:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addFlag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;'version'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;help:&lt;/span&gt; &lt;span class="s"&gt;'Display information about curl and the libcurl version it uses.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;abbr:&lt;/span&gt; &lt;span class="s"&gt;'V'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;defaultsTo:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;                 
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;createParser&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;                                                                         
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;                              
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart run bin/arg_parse.dart 
&lt;span class="go"&gt;-h, --[no-]help                    print usage
    --[no-]compressed              (HTTP) Request a compressed response using one of the algorithms curl supports,and automatically decompress the content.
&lt;/span&gt;&lt;span class="gp"&gt;    --connect-timeout=&amp;lt;timeout&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;Maximum &lt;span class="nb"&gt;time &lt;/span&gt;&lt;span class="k"&gt;in &lt;/span&gt;seconds that you allow curl&lt;span class="s1"&gt;'s connection to take. This only limits the connection phase, so if curl connects within the given period it continues - if not it exits.
&lt;/span&gt;&lt;span class="gp"&gt;-c, --cookie-jar=&amp;lt;filename&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s1"&gt;(HTTP) Specify to which file you want curl to write all cookies after a completed operation.
&lt;/span&gt;&lt;span class="go"&gt;-v, --verbose                      Make curl output verbose information during the operation. Useful for debugging and seeing what's going on under the hood.
-V, --[no-]version                 Display information about curl and the libcurl version it uses.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Interesting, but not really useful. Let parse the arguments directly using the &lt;a href="https://pub.dev/documentation/args/latest/args/ArgParser/parse.html" rel="noopener noreferrer"&gt;&lt;code&gt;parse()&lt;/code&gt;&lt;/a&gt; method. It will return a &lt;a href="https://pub.dev/documentation/args/latest/args/ArgResults-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;ArgResults&lt;/code&gt;&lt;/a&gt; object in case of success. For debugging purpose, let creates a new function called &lt;code&gt;showParser()&lt;/code&gt; to show the data stored in the &lt;code&gt;ArgResults&lt;/code&gt; object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;showResults&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ArgResults&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"arguments: &lt;/span&gt;&lt;span class="si"&gt;${result.arguments}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"help: &lt;/span&gt;&lt;span class="si"&gt;${result.flag('help')}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"compressed: &lt;/span&gt;&lt;span class="si"&gt;${result.flag('compressed')}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"connect-timeout: &lt;/span&gt;&lt;span class="si"&gt;${result.option('connect-timeout')}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"cookie-jar: &lt;/span&gt;&lt;span class="si"&gt;${result.option('cookie-jar')}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"verbose: &lt;/span&gt;&lt;span class="si"&gt;${result.multiOption('verbose')}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"version: &lt;/span&gt;&lt;span class="si"&gt;${result.flag('version')}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can now modify our &lt;code&gt;main()&lt;/code&gt; entry-point function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;createParser&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;ArgResults&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;                                                          
  &lt;span class="n"&gt;showResults&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great, it's time to play with our application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart run bin/arg_parse.dart 
&lt;span class="go"&gt;arguments: []
help: false
compressed: false
connect-timeout: null
cookie-jar: null
verbose: []
version: false

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart run bin/arg_parse.dart &lt;span class="nt"&gt;-h&lt;/span&gt;
&lt;span class="go"&gt;arguments: [-h]
help: true
compressed: false
connect-timeout: null
cookie-jar: null
verbose: []
version: false

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart run bin/arg_parse.dart &lt;span class="nt"&gt;--help&lt;/span&gt;
&lt;span class="go"&gt;arguments: [--help]
help: true
compressed: false
connect-timeout: null
cookie-jar: null
verbose: []
version: false

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart run bin/arg_parse.dart &lt;span class="nt"&gt;--compressed&lt;/span&gt;
&lt;span class="go"&gt;arguments: [--compressed]
help: false
compressed: true
connect-timeout: null
cookie-jar: null
verbose: []
version: false

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart run bin/arg_parse.dart &lt;span class="nt"&gt;--connect-timeout&lt;/span&gt; 10
&lt;span class="go"&gt;arguments: [--connect-timeout, 10]
help: false
compressed: false
connect-timeout: 10
cookie-jar: null
verbose: []
version: false

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart run bin/arg_parse.dart &lt;span class="nt"&gt;--cookie-jar&lt;/span&gt; ./test.txt
&lt;span class="go"&gt;arguments: [--cookie-jar, ./test.txt]
help: false
compressed: false
connect-timeout: null
cookie-jar: ./test.txt
verbose: []
version: false

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart run bin/arg_parse.dart &lt;span class="nt"&gt;-c&lt;/span&gt; ./test.txt
&lt;span class="go"&gt;arguments: [-c, ./test.txt]
help: false
compressed: false
connect-timeout: null
cookie-jar: ./test.txt
verbose: []
version: false

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart run bin/arg_parse.dart &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;span class="go"&gt;Unhandled exception:
FormatException: Missing argument for "-v".
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;0      Parser._validate &lt;span class="o"&gt;(&lt;/span&gt;package:args/src/parser.dart:324:7&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;#&lt;/span&gt;1      Parser._readNextArgAsValue &lt;span class="o"&gt;(&lt;/span&gt;package:args/src/parser.dart:128:5&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;#&lt;/span&gt;2      Parser._handleSoloOption &lt;span class="o"&gt;(&lt;/span&gt;package:args/src/parser.dart:163:7&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;#&lt;/span&gt;3      Parser._parseSoloOption &lt;span class="o"&gt;(&lt;/span&gt;package:args/src/parser.dart:146:12&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;#&lt;/span&gt;4      Parser.parse &lt;span class="o"&gt;(&lt;/span&gt;package:args/src/parser.dart:89:11&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;#&lt;/span&gt;5      ArgParser.parse &lt;span class="o"&gt;(&lt;/span&gt;package:args/src/arg_parser.dart:362:42&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;#&lt;/span&gt;6      main &lt;span class="o"&gt;(&lt;/span&gt;file:///home/user/tmp/dart/arg_parse/bin/arg_parse.dart:5:31&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;#&lt;/span&gt;7      _delayEntrypointInvocation.&amp;lt;anonymous closure&amp;gt; &lt;span class="o"&gt;(&lt;/span&gt;dart:isolate-patch/isolate_patch.dart:312:33&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;#&lt;/span&gt;8      _RawReceivePort._handleMessage &lt;span class="o"&gt;(&lt;/span&gt;dart:isolate-patch/isolate_patch.dart:193:12&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart run bin/arg_parse.dart &lt;span class="nt"&gt;-V&lt;/span&gt;
&lt;span class="go"&gt;arguments: [-V]
help: false
compressed: false
connect-timeout: null
cookie-jar: null
verbose: []
version: true
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, the &lt;code&gt;--verbose&lt;/code&gt; multi option is crashing. The main is reason is because a MultiOption should always have a value, the same command will pass if we add more &lt;code&gt;v&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart run bin/arg_parse.dart &lt;span class="nt"&gt;-vvvv&lt;/span&gt;
&lt;span class="go"&gt;arguments: [-vvvv]
help: false
compressed: false
connect-timeout: null
cookie-jar: null
verbose: [vvv]
version: false
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Well, it seems the Multi Option is not the good one to use for it and I'm currently not sure if it could be correctly supported with the &lt;code&gt;args&lt;/code&gt; package, perhaps adding &lt;code&gt;addMultiFlag&lt;/code&gt; methods could help. Too much work for this post, maybe for another one. Right now, let convert this argument to a simple Flag.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;  &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addFlag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;'verbose'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;help:&lt;/span&gt; &lt;span class="s"&gt;'Make curl output verbose information during the operation. '&lt;/span&gt;
      &lt;span class="s"&gt;'Useful for debugging and seeing what&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s"&gt;s going on under the hood.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;abbr:&lt;/span&gt; &lt;span class="s"&gt;'v'&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Validation
&lt;/h1&gt;

&lt;p&gt;It is possible to validate every arguments using a &lt;code&gt;callback&lt;/code&gt; function, but the document recommend to avoid doing that.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The callback argument is invoked with the option's value when the option is parsed. Note that this makes argument parsing order-dependent in ways that are often surprising, and its use is discouraged in favor of reading values from the &lt;code&gt;ArgResults&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The validation of each parsed values should be done outside of the parser and probably result in a new specific objects containing the valid values.&lt;/p&gt;

&lt;p&gt;Data validation and sanitization in Dart will be the main subject of another article, so, we will see that later!&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;The &lt;a href="https://pub.dev/packages/args" rel="noopener noreferrer"&gt;&lt;code&gt;args&lt;/code&gt;&lt;/a&gt; package is doing the job for most of the common use case, but in some situation where you will need a custom way to parse arguments, another solution will probably be required. Anyway, Dart was not created to deal with command line tools, and this package will probably fit in all your small projects, even like a small web server. Here a list of interesting resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://pub.dev/packages/args#dispatching-commands" rel="noopener noreferrer"&gt;Commands Dispatch&lt;/a&gt; can be used to create subcommands;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://pub.dev/packages/args" rel="noopener noreferrer"&gt;`args package&lt;/a&gt; show a complete use case with good examples, a must read;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://pub.dev/documentation/args/latest/" rel="noopener noreferrer"&gt;&lt;code&gt;args&lt;/code&gt; API documentation&lt;/a&gt; is complete, as usual;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/dart-lang/core/blob/main/pkgs/args/example/arg_parser/example.dart" rel="noopener noreferrer"&gt;&lt;code&gt;pkgs/args/example/arg_parser/example.dart&lt;/code&gt;&lt;/a&gt; example from the &lt;code&gt;dart-lang/core&lt;/code&gt; repository show mostly all parser use case, you'll like it.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hack well and have fun!&lt;/p&gt;




&lt;p&gt;Cover Image by &lt;a href="https://unsplash.com/@zoeey97?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Zoha Gohar&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/two-crows-silhouetted-against-a-blue-spotlight-rf2Z1dsQCT4?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dart</category>
      <category>cli</category>
      <category>parser</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Platform Identification in Dart</title>
      <dc:creator>Mathieu Kerjouan</dc:creator>
      <pubDate>Tue, 16 Jun 2026 08:00:00 +0000</pubDate>
      <link>https://dev.to/niamtokik/platform-identification-in-dartt-3b9i</link>
      <guid>https://dev.to/niamtokik/platform-identification-in-dartt-3b9i</guid>
      <description>&lt;p&gt;During the last weeks, I tried to learn a lot from Dart and Flutter, but even after a bit less than 1 month, I still don't know some basic stuff. When I was dealing with command line arguments parsing, I was thinking "how to get the goddamn program name". You know, the string you can simply have with &lt;code&gt;argv[0]&lt;/code&gt; &lt;sup id="fnref1"&gt;1&lt;/sup&gt; in C or with the functions exported by the &lt;a href="https://www.erlang.org/doc/apps/erts/init.html" rel="noopener noreferrer"&gt;&lt;code&gt;init&lt;/code&gt;&lt;/a&gt; module in Erlang. In Dart, you will require &lt;code&gt;dart:io&lt;/code&gt; package and the &lt;a href="https://api.dart.dev/dart-io/Platform-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;Platform&lt;/code&gt;&lt;/a&gt; class.&lt;/p&gt;

&lt;p&gt;Let write a small program to print out what kind of information this class store.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'dart:io'&lt;/span&gt; &lt;span class="kd"&gt;show&lt;/span&gt; &lt;span class="n"&gt;Platform&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;systemInfo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Platform.&lt;/span&gt;&lt;span class="si"&gt;${key}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;${info[key]}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;systemInfo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"environment"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Platform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"executable"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Platform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;executable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"executableArguments"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Platform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;executableArguments&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"isAndroid"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Platform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isAndroid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"isFuchsia"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Platform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isFuchsia&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"isIOS"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Platform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isIOS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"isLinux"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Platform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isLinux&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"isWindows"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Platform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isWindows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"lineTerminator"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Platform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;lineTerminator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"localeName"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Platform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;localeName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"localHostname"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Platform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;localHostname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"numberOfProcessors"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Platform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;numberOfProcessors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"operatingSystem"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Platform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;operatingSystem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"operatingSystemVersion"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Platform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;operatingSystemVersion&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"packageConfig"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Platform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;packageConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"pathSeparator"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Platform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;pathSeparator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"resolvedExecutable"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Platform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;resolvedExecutable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"script"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Platform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"version"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Platform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;version&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The function &lt;code&gt;systemInfo()&lt;/code&gt; returns a &lt;code&gt;Map&amp;lt;String, dynamic&amp;gt;&lt;/code&gt; where the keys of the maps are simply a reference to the name of the &lt;code&gt;Platform&lt;/code&gt; class attributes. Nothing complex there. When executed it will print the content of all attributes line by line. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://api.dart.dev/dart-io/Platform/environment.html" rel="noopener noreferrer"&gt;&lt;code&gt;Platform.environment&lt;/code&gt;&lt;/a&gt; returns the environment in a &lt;code&gt;Map&amp;lt;String, String&amp;gt;&lt;/code&gt;;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;Platform.environment: {
  SHELL: /bin/bash,
&lt;/span&gt;&lt;span class="c"&gt;  ...
&lt;/span&gt;&lt;span class="go"&gt;}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://api.dart.dev/dart-io/Platform/executable.html" rel="noopener noreferrer"&gt;&lt;code&gt;Platform.executable&lt;/code&gt;&lt;/a&gt; returns the name of the executable used to execute the application as a &lt;code&gt;String&lt;/code&gt;, in our case, I am using &lt;code&gt;dart run&lt;/code&gt; command (and then use the Dart VM);
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;Platform.executable: /.asdf/installs/flutter/3.41.7-stable/bin/cache/dart-sdk/bin/dart
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://api.dart.dev/dart-io/Platform/executableArguments.html" rel="noopener noreferrer"&gt;Platform.executableArguments&lt;/a&gt; returns the arguments passed to the application as a &lt;code&gt;List&amp;lt;String&amp;gt;&lt;/code&gt;, again, in our case, we are using the Dart VM, most of the arguments directly related to it;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;Platform.executableArguments: [
  --resolved_executable_name=/.asdf/installs/flutter/3.41.7-stable/bin/cache/dart-sdk/bin/dart, 
  --executable_name=/.asdf/installs/flutter/3.41.7-stable/bin/cache/dart-sdk/bin/dart,
  --packages=.dart_tool/package_config.json
]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://api.dart.dev/dart-io/Platform/isLinux.html" rel="noopener noreferrer"&gt;&lt;code&gt;Platform.isLinux&lt;/code&gt;&lt;/a&gt; returns a &lt;code&gt;Bool&lt;/code&gt;, it's true if it's a Linux system, else it's false. Other attributes like that are exposed to check easily what kind of systems is being used;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;Platform.isLinux: true
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://api.dart.dev/dart-io/Platform/localeName.html" rel="noopener noreferrer"&gt;&lt;code&gt;Platform.localeName&lt;/code&gt;&lt;/a&gt; returns the locale environment as &lt;code&gt;String&lt;/code&gt;, useful for the localization;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;Platform.localeName: en_US.UTF-8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://api.dart.dev/dart-io/Platform/localHostname.html" rel="noopener noreferrer"&gt;&lt;code&gt;Platform.localHostname&lt;/code&gt;&lt;/a&gt; returns the name of the host (defined in &lt;code&gt;/etc/hosts&lt;/code&gt; or in &lt;code&gt;/etc/hostname&lt;/code&gt;) as &lt;code&gt;String&lt;/code&gt;;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;Platform.localHostname: localhost
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://api.dart.dev/dart-io/Platform/numberOfProcessors.html" rel="noopener noreferrer"&gt;&lt;code&gt;Platform.numberOfProcessors&lt;/code&gt;&lt;/a&gt; returns the number of available CPU as &lt;code&gt;int&lt;/code&gt;, no distinctions between a real CPU or a core;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;Platform.numberOfProcessors: 16
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://api.dart.dev/dart-io/Platform/operatingSystem.html" rel="noopener noreferrer"&gt;&lt;code&gt;Platform.operatingSystem&lt;/code&gt;&lt;/a&gt; returns the code name of the system as a &lt;code&gt;String&lt;/code&gt;;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;Platform.operatingSystem: linux
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://api.dart.dev/dart-io/Platform/operatingSystemVersion.html" rel="noopener noreferrer"&gt;&lt;code&gt;Platform.operatingSystemVersion&lt;/code&gt;&lt;/a&gt; returns the version of the system used as &lt;code&gt;String&lt;/code&gt;, for Linux, it will be the version of the kernel, the same kind of string that could be returned by &lt;a href="https://manpages.debian.org/trixie/coreutils/uname.1.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;uname&lt;/code&gt;&lt;/a&gt;;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;Platform.operatingSystemVersion: Linux 6.19.13+parrot7-amd64 #&lt;/span&gt;1 SMP PREEMPT_DYNAMIC Parrot 6.19.13-1parrot1 &lt;span class="o"&gt;(&lt;/span&gt;2026-04-30&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://api.dart.dev/dart-io/Platform/resolvedExecutable.html" rel="noopener noreferrer"&gt;&lt;code&gt;Platform.resolvedExecutable&lt;/code&gt;&lt;/a&gt; returns the full path of the executable as a &lt;code&gt;String&lt;/code&gt;;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;Platform.resolvedExecutable: /.asdf/installs/flutter/3.41.7-stable/bin/cache/dart-sdk/bin/dart
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://api.dart.dev/dart-io/Platform/script.html" rel="noopener noreferrer"&gt;&lt;code&gt;Platform.script&lt;/code&gt;&lt;/a&gt; returns the script (Dart application) running in the current isolate (in our the default one) as &lt;code&gt;String&lt;/code&gt;;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;Platform.script: file:///tmp/dart/system_info/.dart_tool/pub/bin/system_info/system_info.dart-3.11.5.snapshot
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://api.dart.dev/dart-io/Platform/version.html" rel="noopener noreferrer"&gt;&lt;code&gt;Platform.version&lt;/code&gt;&lt;/a&gt; returns the Dart version used for this application as &lt;code&gt;String&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;Platform.version: 3.11.5 (stable) (Wed Apr 15 00:36:32 2026 -0700) on "linux_x64"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sadly, one will not find a lot of resources about platform detection, at least, directly on Dart. Some posts can be found on the web regarding Flutter integration on different platform, but not so much when it comes to create a command line interface tool. Here a list containing other resources.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://api.dart.dev/dart-io/Platform-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;Platform&lt;/code&gt; class API Documentation&lt;/a&gt; from &lt;code&gt;dart:io&lt;/code&gt; module;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://medium.com/@abhishek17600031/flutter-understanding-dart-io-platform-defaulttargetplatform-and-kisweb-and-when-to-use-each-bac1a5d10f73" rel="noopener noreferrer"&gt;Flutter: Understanding dart:io Platform, defaultTargetPlatform, and kIsWeb (and When to Use Each)&lt;/a&gt; on Medium&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://medium.com/@chetan.akarte/how-do-you-detect-the-host-platform-from-dart-code-d0694529ac51" rel="noopener noreferrer"&gt;How do you detect the host platform from Dart code?&lt;/a&gt; on Medium&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.zetcode.com/dart/platform/" rel="noopener noreferrer"&gt;Dart Platform&lt;/a&gt; on ZetCode&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy hack and have fun!&lt;/p&gt;




&lt;p&gt;Cover Image by &lt;a href="https://unsplash.com/@goraivis?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;GoRaivis Photography&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/a-black-and-white-photo-of-a-train-platform-Jsus4t5Q2Mc?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;The function called at program startup is named main. The implementation declares no prototype for this function. It can be defined with no parameters or with two parameters (referred to here as &lt;code&gt;argc&lt;/code&gt; and &lt;code&gt;argv&lt;/code&gt;, though any names may be used, as they are local to the function in which they are declared). [...] The value of &lt;code&gt;argc&lt;/code&gt; shall be nonnegative. &lt;code&gt;argv[argc]&lt;/code&gt; shall be a null pointer. From &lt;a href="http://flash-gordon.me.uk/ansi.c.txt" rel="noopener noreferrer"&gt;ANSI-C Specification&lt;/a&gt;&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>dart</category>
      <category>cli</category>
      <category>platform</category>
      <category>system</category>
    </item>
    <item>
      <title>SHA-2 Hash Functions in Dart</title>
      <dc:creator>Mathieu Kerjouan</dc:creator>
      <pubDate>Mon, 15 Jun 2026 08:00:00 +0000</pubDate>
      <link>https://dev.to/niamtokik/sha-2-hash-functions-in-dart-2eg</link>
      <guid>https://dev.to/niamtokik/sha-2-hash-functions-in-dart-2eg</guid>
      <description>&lt;p&gt;Hash functions are one way functions, they are taking an input and will return always the same output based on the input. One of the one used hash function is SHA-256, which is part of the &lt;a href="https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf" rel="noopener noreferrer"&gt;SHA-2 FIPS 180-4 specification&lt;/a&gt;. You already know it if you are using Bitcoin and many other crypto-currencies/blockchains. Unfortunately, there are no cryptographic library implementation directly available in the Dart SDK, and we must use external packages.&lt;/p&gt;

&lt;p&gt;Instead of re-implementing our own, we will use external packages. At this time, two packages look promising, &lt;a href="https://pub.dev/packages/cryptography" rel="noopener noreferrer"&gt;&lt;code&gt;cryptography&lt;/code&gt; &lt;/a&gt;, &lt;a href="https://pub.dev/packages/crypto" rel="noopener noreferrer"&gt;&lt;code&gt;crypto&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://pub.dev/packages/hashlib" rel="noopener noreferrer"&gt;&lt;code&gt;hashlib&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://pub.dev/packages/hash" rel="noopener noreferrer"&gt;&lt;code&gt;hash&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://pub.dev/packages/sodium" rel="noopener noreferrer"&gt;&lt;code&gt;sodium&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let start with a new sandbox project and compare each ones.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart create hashing
&lt;span class="go"&gt;Creating hashing using template console...

  .gitignore
  analysis_options.yaml
  CHANGELOG.md
  pubspec.yaml
  README.md
  bin/hashing.dart
  lib/hashing.dart
  test/hashing_test.dart

Running pub get...                     |-0.8s
  Resolving dependencies...
  Downloading packages...
  Changed 48 dependencies!
  1 package has newer versions incompatible with dependency constraints.
  Try `dart pub outdated` for more information.

Created project hashing in hashing! In order to get started, run the following commands:

  cd hashing
  dart run

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;hashing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This program should be simple, the idea is to return a hash from stdin as hexacimal string. This kind of program already exists and are installed by default on probably all Linux distribution via the &lt;a href="https://manpages.debian.org/trixie/perl/shasum.1.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;shasum&lt;/code&gt;&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo test&lt;/span&gt; | sha224
&lt;span class="go"&gt;52f1bf093f4b7588726035c176c0cdb4376cfea53819f1395ac9e6ec  -

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo test&lt;/span&gt; | &lt;span class="nb"&gt;sha256sum&lt;/span&gt;
&lt;span class="go"&gt;f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2  -

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo test&lt;/span&gt; | &lt;span class="nb"&gt;sha384sum&lt;/span&gt; 
&lt;span class="go"&gt;109bb6b5b6d5547c1ce03c7a8bd7d8f80c1cb0957f50c4f7fda04692079917e4f9cad52b878f3d8234e1a170b154b72d  -

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo test&lt;/span&gt; | &lt;span class="nb"&gt;sha512sum&lt;/span&gt; 
&lt;span class="go"&gt;0e3e75234abc68f4378a86b3f4b32a198ba301845b0cd6e50106e874345700cc6663a86c1ea125dc5e92be17c98f9a0f85ca9d5f595db2012f7cc3571945c123  -
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or it can also be done using &lt;a href="https://manpages.debian.org/trixie/openssl/openssl.1ssl.en.html" rel="noopener noreferrer"&gt;OpenSSL&lt;/a&gt; or &lt;a href="https://man.openbsd.org/openssl" rel="noopener noreferrer"&gt;LibreSSL&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo test&lt;/span&gt; | openssl dgst &lt;span class="nt"&gt;-hex&lt;/span&gt; &lt;span class="nt"&gt;-sha224&lt;/span&gt;
&lt;span class="go"&gt;SHA2-224(stdin)= 52f1bf093f4b7588726035c176c0cdb4376cfea53819f1395ac9e6ec

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo test&lt;/span&gt; | openssl dgst &lt;span class="nt"&gt;-hex&lt;/span&gt; &lt;span class="nt"&gt;-sha256&lt;/span&gt;
&lt;span class="go"&gt;SHA2-256(stdin)= f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo test&lt;/span&gt; | openssl dgst &lt;span class="nt"&gt;-hex&lt;/span&gt; &lt;span class="nt"&gt;-sha384&lt;/span&gt;
&lt;span class="go"&gt;SHA2-384(stdin)= 109bb6b5b6d5547c1ce03c7a8bd7d8f80c1cb0957f50c4f7fda04692079917e4f9cad52b878f3d8234e1a170b154b72d

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo test&lt;/span&gt; | openssl dgst &lt;span class="nt"&gt;-hex&lt;/span&gt; &lt;span class="nt"&gt;-sha512&lt;/span&gt;
&lt;span class="go"&gt;SHA2-512(stdin)= 0e3e75234abc68f4378a86b3f4b32a198ba301845b0cd6e50106e874345700cc6663a86c1ea125dc5e92be17c98f9a0f85ca9d5f595db2012f7cc3571945c123
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The behavior should be the same, except it will use stdin by default and simply print the hash in hexadecimal without other information.&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;code&gt;lib/crypto.dart&lt;/code&gt; Interface
&lt;/h1&gt;

&lt;p&gt;The &lt;a href="https://pub.dev/packages/crypto" rel="noopener noreferrer"&gt;&lt;code&gt;crypto&lt;/code&gt;&lt;/a&gt; package supports only Hash function, including SHA-2 (&lt;a href="https://pub.dev/documentation/crypto/latest/crypto/sha224-constant.html" rel="noopener noreferrer"&gt;&lt;code&gt;SHA-224&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://pub.dev/documentation/crypto/latest/crypto/sha256-constant.html" rel="noopener noreferrer"&gt;&lt;code&gt;SHA-256&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://pub.dev/documentation/crypto/latest/crypto/sha384-constant.html" rel="noopener noreferrer"&gt;&lt;code&gt;SHA-384&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://pub.dev/documentation/crypto/latest/crypto/sha512-constant.html" rel="noopener noreferrer"&gt;&lt;code&gt;SHA-512&lt;/code&gt;&lt;/a&gt;). Few &lt;a href="https://github.com/dart-lang/core/blob/main/pkgs/crypto/example/example.dart" rel="noopener noreferrer"&gt;examples&lt;/a&gt; are available from its &lt;a href="https://github.com/dart-lang/core/blob/main/pkgs/crypto" rel="noopener noreferrer"&gt;repository&lt;/a&gt;. This package is maintained by the Dart Team and is including as core module. Let add it in our project to see what kind of dependencies it requires.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart pub add crypto
&lt;span class="go"&gt;"crypto" is already in "dependencies". Will try to update the constraint.
Resolving dependencies... 
Downloading packages... 
  package_config 2.2.0 (3.0.0 available)
Got dependencies!
1 package has newer versions incompatible with dependency constraints.
Try `dart pub outdated` for more information.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'dart:convert'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:crypto/crypto.dart'&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;crypto&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;sha224&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;crypto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sha224&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;crypto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;sha384&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;crypto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sha384&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;sha512&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;crypto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sha512&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;dynamic&lt;/span&gt; &lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;convert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  &lt;code&gt;lib/cryptography.dart&lt;/code&gt; Interface
&lt;/h1&gt;

&lt;p&gt;The &lt;a href="https://pub.dev/packages/cryptography" rel="noopener noreferrer"&gt;&lt;code&gt;cryptography&lt;/code&gt;&lt;/a&gt; package is supporting all version of &lt;code&gt;SHA-2&lt;/code&gt; (&lt;a href="https://pub.dev/documentation/cryptography/latest/cryptography/Sha224-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;SHA-224&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://pub.dev/documentation/cryptography/latest/cryptography/Sha256-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;SHA-256&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://pub.dev/documentation/cryptography/latest/cryptography/Sha384-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;SHA-384&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://pub.dev/documentation/cryptography/latest/cryptography/Sha512-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;SHA-512&lt;/code&gt;&lt;/a&gt;). It does not require a lot of dependencies and seem to be used by a lot of projects. Few &lt;a href="https://pub.dev/packages/cryptography#examples" rel="noopener noreferrer"&gt;examples&lt;/a&gt; can be found. Let add it as dependency in our project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart pub add cryptography
&lt;span class="go"&gt;Resolving dependencies... 
Downloading packages... 
+ cryptography 2.9.0
+ ffi 2.2.0
  package_config 2.2.0 (3.0.0 available)
Changed 2 dependencies!
1 package has newer versions incompatible with dependency constraints.
Try `dart pub outdated` for more information.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'dart:convert'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'dart:async'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:cryptography/cryptography.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To make things easier, the module &lt;code&gt;lib/cryptography.dart&lt;/code&gt; will export 4 functions called &lt;code&gt;sha224()&lt;/code&gt;, &lt;code&gt;sha256()&lt;/code&gt;, &lt;code&gt;sha384()&lt;/code&gt; and &lt;code&gt;sha512()&lt;/code&gt;. All of them will take a &lt;code&gt;List&amp;lt;int&amp;gt;&lt;/code&gt; as input and will return a &lt;code&gt;Future&amp;lt;List&amp;lt;int&amp;gt;&amp;gt;&lt;/code&gt; as output. The next modules will follow the same pattern.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;sha224&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Sha224&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Sha256&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;sha384&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Sha384&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;sha512&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Sha512&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An object for each kind of hash function must be instantiated first. To avoid duplicated and annoying code, the private function &lt;code&gt;_hash()&lt;/code&gt; was created. The first argument is the input as &lt;code&gt;List&amp;lt;int&amp;gt;&lt;/code&gt; and the second argument is the instantiated object (all hash objects are using the same methods).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;dynamic&lt;/span&gt; &lt;span class="n"&gt;algorithm&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;sink&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;algorithm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newHashSink&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;sink&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;sink&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;sink&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;hash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The implementation can use a synchronous or an asynchronous method to hash the data. In our case, we are using the asynchronous with the help of a &lt;a href="https://api.dart.dev/stable/3.11.5/dart-async/Stream-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;Stream&lt;/code&gt;&lt;/a&gt; and a &lt;a href="https://api.dart.dev/stable/3.11.5/dart-core/Sink-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;sink&lt;/code&gt;&lt;/a&gt;. The &lt;code&gt;sink&lt;/code&gt; is created with the help of &lt;a href="https://pub.dev/documentation/cryptography/latest/cryptography/HashAlgorithm/newHashSink.html" rel="noopener noreferrer"&gt;&lt;code&gt;newHashSink()&lt;/code&gt;&lt;/a&gt; method&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;code&gt;lib/hashlib.dart&lt;/code&gt; Interface
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart pub add hashlib
&lt;span class="go"&gt;Resolving dependencies... 
Downloading packages... 
+ hashlib 2.3.4
+ hashlib_codecs 3.1.2
  package_config 2.2.0 (3.0.0 available)
Changed 2 dependencies!
1 package has newer versions incompatible with dependency constraints.
Try `dart pub outdated` for more information.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let edit &lt;code&gt;lib/hashlib.dart&lt;/code&gt;. Firstly, we need to import the &lt;code&gt;hashlib&lt;/code&gt; module.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'dart:convert'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:hashlib/hashlib.dart'&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, we will reuse the same kind of interface as functions we created before.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;sha224&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sha224&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;sha384&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sha384&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;sha512&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sha512&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we will create our &lt;code&gt;_hash()&lt;/code&gt; function. This time, the second argument is waiting for an object callback from &lt;code&gt;hashlib&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;dynamic&lt;/span&gt; &lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromIterable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;digest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byteStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;digest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The callback reads the list of integer with the help of the &lt;a href="https://pub.dev/documentation/hashlib/latest/hashlib/HashBase/byteStream.html" rel="noopener noreferrer"&gt;&lt;code&gt;byteStream()&lt;/code&gt;&lt;/a&gt;&amp;nbsp;method and returns a &lt;code&gt;Future&amp;lt;String&amp;gt;&lt;/code&gt; instead of a &lt;code&gt;List&amp;lt;int&amp;gt;&lt;/code&gt;. I don't think it was necessary to convert it, it would have been slower for no real benefit.&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;code&gt;lib/sodium.dart&lt;/code&gt; Interface
&lt;/h1&gt;

&lt;p&gt;The &lt;a href="https://libsodium.gitbook.io/" rel="noopener noreferrer"&gt;&lt;code&gt;libsodium&lt;/code&gt;&lt;/a&gt; library &lt;a href="https://libsodium.gitbook.io/doc/advanced/sha-2_hash_function#sha-256" rel="noopener noreferrer"&gt;supports SHA-2&lt;/a&gt; but the &lt;a href="https://pub.dev/packages/sodium" rel="noopener noreferrer"&gt;&lt;code&gt;sodium&lt;/code&gt;&lt;/a&gt; package in Dart &lt;a href="https://github.com/Skycoder42/libsodium_dart_bindings#considered-for-the-future" rel="noopener noreferrer"&gt;does not support it&lt;/a&gt; algorithms for now. Let just add it anyway just to see the list of dependency required.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart pub add sodium
&lt;span class="go"&gt;Resolving dependencies... 
Downloading packages... 
+ archive 4.0.9
+ code_assets 1.0.0 (1.2.1 available)
+ csslib 1.0.2
+ freezed_annotation 3.1.0
+ hooks 1.0.3 (2.0.2 available)
+ html 0.15.6
+ json_annotation 4.12.0
  package_config 2.2.0 (3.0.0 available)
+ posix 6.5.0
+ record_use 0.6.0
+ sodium 4.0.2+1
Changed 10 dependencies!
3 packages have newer versions incompatible with dependency constraints.
Try `dart pub outdated` for more information.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, instead of SHA-2, it offers an implementation of &lt;a href="https://www.rfc-editor.org/rfc/rfc7693.txt" rel="noopener noreferrer"&gt;&lt;code&gt;Blake2b&lt;/code&gt;&lt;/a&gt; via the &lt;a href="https://pub.dev/documentation/sodium/latest/sodium/GenericHash-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;GenericHash&lt;/code&gt;&lt;/a&gt; class. Sadly, I was expecting a lot from this module because I've already used &lt;a href="https://nacl.cr.yp.to/" rel="noopener noreferrer"&gt;&lt;code&gt;NaCl&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://libsodium.net/" rel="noopener noreferrer"&gt;&lt;code&gt;libsodium&lt;/code&gt;&lt;/a&gt; in the past.&lt;/p&gt;

&lt;h1&gt;
  
  
  Entry-point &lt;code&gt;bin/hashing.dart&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;The main entry-point is defined in &lt;code&gt;bin/hashing.dart&lt;/code&gt;. Let import few modules first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'dart:io'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'dart:convert'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'dart:async'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:hashing/cryptography.dart'&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;cryptography&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:hashing/crypto.dart'&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;crypto&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:hashing/hashlib.dart'&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It will be a command line tool, an &lt;code&gt;usage()&lt;/code&gt; function can be nice to have.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"usage: hashing HASH MODULE"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More than half of the functions created will return a &lt;code&gt;List&amp;lt;int&amp;gt;&lt;/code&gt; and did not find a way to convert that in hexadecimal string in the SDK. To fix that, the &lt;code&gt;toHex()&lt;/code&gt; function will convert each integers present in the list as 8 bits hexadecimal string and join them. I don't know also if a Dart offers a way to identify the default endianness of the system, a parameter called &lt;code&gt;little&lt;/code&gt; has been created to deal with that. It assumes the data are in &lt;code&gt;little-endian&lt;/code&gt; and will convert them to &lt;code&gt;big-endian&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;toHex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;little&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mh"&gt;0x0f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;little&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;join&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;hex()&lt;/code&gt; function is converting an integer to its hexadecimal representation as &lt;code&gt;String&lt;/code&gt;. This is a simple switch/case and throwing an error if the integer is not present in the statement.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"0"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"2"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"3"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"4"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"5"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"6"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"7"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"8"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"9"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"b"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"c"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"d"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"e"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"f"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The arguments passed to the program must be "parsed". Again, a simple switch/case will do the job. At least, everybody can easily understand what I want to do there.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;switcher&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"sha224"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"cryptography"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; 
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;toHex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;cryptography&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sha224&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"sha256"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"cryptography"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; 
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;toHex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;cryptography&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"sha384"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"cryptography"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; 
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;toHex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;cryptography&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sha384&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"sha512"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"cryptography"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; 
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;toHex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;cryptography&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sha512&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"sha224"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"crypto"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; 
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;toHex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;crypto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sha224&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"sha256"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"crypto"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; 
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;toHex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;crypto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"sha384"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"crypto"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; 
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;toHex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;crypto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sha384&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"sha512"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"crypto"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; 
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;toHex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;crypto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sha512&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"sha224"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"hashlib"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; 
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sha224&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"sha256"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"hashlib"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; 
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"sha384"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"hashlib"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;     
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sha384&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"sha512"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"hashlib"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; 
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sha512&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, the &lt;code&gt;main()&lt;/code&gt; function, reading the input from &lt;code&gt;stdin&lt;/code&gt; and forwarding it to the &lt;code&gt;switcher()&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;stdin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toList&lt;/span&gt;&lt;span class="p"&gt;())[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;digest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;switcher&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;digest&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;            
    &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;      
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;                         
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Test
&lt;/h1&gt;

&lt;p&gt;Let build this project and test it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart build cli
&lt;span class="go"&gt;The `dart build cli` command is in preview at the moment.
&lt;/span&gt;&lt;span class="gp"&gt;See documentation on https://dart.dev/interop/c-interop#&lt;/span&gt;native-assets.
&lt;span class="go"&gt;
Deleting output directory: /tmp/dart/hashing/build/cli/linux_x64/.
Running build hooks... 
Running link hooks... 
Copying 1 build assets:
package:sodium/libsodium
Generated: /tmp/dart/hashing/build/cli/linux_x64/bundle/bin/hashing
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Are you lazy? That's my case today. We will use &lt;a href="https://manpages.debian.org/trixie/findutils/xargs.1.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;xargs&lt;/code&gt;&lt;/a&gt; to do most of the jobs for us.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"cryptography&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;crypto&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;hashlib&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="gp"&gt;  | xargs -I%i sh -c 'echo %i: $&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;echo test&lt;/span&gt; | ./build/cli/linux_x64/bundle/bin/hashing sha224 %i&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="s1"&gt;'
&lt;/span&gt;&lt;span class="go"&gt;cryptography: 52f1bf093f4b7588726035c176c0cdb4376cfea53819f1395ac9e6ec
crypto: 52f1bf093f4b7588726035c176c0cdb4376cfea53819f1395ac9e6ec
hashlib: 52f1bf093f4b7588726035c176c0cdb4376cfea53819f1395ac9e6ec

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"cryptography&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;crypto&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;hashlib&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 8
&lt;span class="gp"&gt;  | xargs -I%i sh -c 'echo %i: $&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;echo test&lt;/span&gt; | ./build/cli/linux_x64/bundle/bin/hashing sha256 %i&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="s1"&gt;'
&lt;/span&gt;&lt;span class="go"&gt;cryptography: f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2
crypto: f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2
hashlib: f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"cryptography&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;crypto&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;hashlib&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="gp"&gt;  | xargs -I%i sh -c 'echo %i: $&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;echo test&lt;/span&gt; | ./build/cli/linux_x64/bundle/bin/hashing sha384 %i&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="s1"&gt;'
&lt;/span&gt;&lt;span class="go"&gt;cryptography: 109bb6b5b6d5547c1ce03c7a8bd7d8f80c1cb0957f50c4f7fda04692079917e4f9cad52b878f3d8234e1a170b154b72d
crypto: 109bb6b5b6d5547c1ce03c7a8bd7d8f80c1cb0957f50c4f7fda04692079917e4f9cad52b878f3d8234e1a170b154b72d
hashlib: 109bb6b5b6d5547c1ce03c7a8bd7d8f80c1cb0957f50c4f7fda04692079917e4f9cad52b878f3d8234e1a170b154b72d

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"cryptography&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;crypto&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;hashlib&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="gp"&gt;  | xargs -I%i sh -c 'echo %i: $&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;echo test&lt;/span&gt; | ./build/cli/linux_x64/bundle/bin/hashing sha512 %i&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="s1"&gt;'
&lt;/span&gt;&lt;span class="go"&gt;cryptography: 0e3e75234abc68f4378a86b3f4b32a198ba301845b0cd6e50106e874345700cc6663a86c1ea125dc5e92be17c98f9a0f85ca9d5f595db2012f7cc3571945c123
crypto: 0e3e75234abc68f4378a86b3f4b32a198ba301845b0cd6e50106e874345700cc6663a86c1ea125dc5e92be17c98f9a0f85ca9d5f595db2012f7cc3571945c123
hashlib: 0e3e75234abc68f4378a86b3f4b32a198ba301845b0cd6e50106e874345700cc6663a86c1ea125dc5e92be17c98f9a0f85ca9d5f595db2012f7cc3571945c123
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output is identical for all commands. So, it looks good to me.&lt;/p&gt;

&lt;h1&gt;
  
  
  Performance
&lt;/h1&gt;

&lt;p&gt;The performance difference when a program is executed with the Dart VM and as native application is real. Here a few examples.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;time echo test&lt;/span&gt; | openssl dgst &lt;span class="nt"&gt;-sha224&lt;/span&gt; &lt;span class="nt"&gt;-binary&lt;/span&gt; | &lt;span class="nb"&gt;base64&lt;/span&gt;
&lt;span class="go"&gt;UvG/CT9LdYhyYDXBdsDNtDds/qU4GfE5Wsnm7A==

real    0m0,007s
user    0m0,002s
sys     0m0,007s

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;time echo test&lt;/span&gt; | dart run bin/hashing.dart sha224 cryptography
&lt;span class="go"&gt;Running build hooks... 
52f1bf093f4b7588726035c176c0cdb4376cfea53819f1395ac9e6ec

real    0m0,944s
user    0m1,343s
sys     0m0,194s

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;time echo test&lt;/span&gt; | ./build/cli/linux_x64/bundle/bin/hashing sha224 cryptography
&lt;span class="go"&gt;52f1bf093f4b7588726035c176c0cdb4376cfea53819f1395ac9e6ec

real    0m0,007s
user    0m0,000s
sys     0m0,009s
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When using the Dart Virtual Machine, the code is taking more than 1 second to hash the string "test". I think its due to the VM initialization or the Dart application trying to see what kind of changes have been made. Anyway, the code generated by Dart in a native format is still a bit slow compared to OpenSSL though.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Choosing a cryptographic library nowadays is hard. 10 years ago, only few projects were available, and most of the time, we were using &lt;a href="https://www.openssl.org/" rel="noopener noreferrer"&gt;OpenSSL&lt;/a&gt; or one of its fork like &lt;a href="https://www.libressl.org/" rel="noopener noreferrer"&gt;LibreSSL&lt;/a&gt;. &lt;a href="https://gnutls.org/" rel="noopener noreferrer"&gt;GnuTLS&lt;/a&gt;, &lt;a href="https://www.bearssl.org/" rel="noopener noreferrer"&gt;BearSSL&lt;/a&gt;, &lt;a href="https://github.com/cuberite/polarssl" rel="noopener noreferrer"&gt;PolarSSL&lt;/a&gt; or &lt;a href="https://www.wolfssl.com/" rel="noopener noreferrer"&gt;WolfSSL&lt;/a&gt; were also other alternatives to consider. Instead of reusing the already deeply tested and checked libraries, all "modern languages" decided to reimplement their own.&lt;/p&gt;

&lt;p&gt;The Dart team only support the &lt;code&gt;crypto&lt;/code&gt; package offering hashing algorithms, but any other cryptographic functions. If you only need hashing, use this one. If you need more, you should probably test &lt;code&gt;cryptography&lt;/code&gt; and &lt;code&gt;hashlib&lt;/code&gt; packages. Finally, if you don't care about SHA-2 and you just want something portable, you can check &lt;code&gt;libsodium&lt;/code&gt; package.&lt;/p&gt;




&lt;p&gt;Cover Image by &lt;a href="https://unsplash.com/@noahdavis?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Noah Näf&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/assorted-umbrellas-hanging-on-ceiling-qhfxY3X6JV0?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dart</category>
      <category>cryptography</category>
      <category>security</category>
      <category>sha</category>
    </item>
    <item>
      <title>Sandbox#1: Flutter Application Design First Steps</title>
      <dc:creator>Mathieu Kerjouan</dc:creator>
      <pubDate>Fri, 12 Jun 2026 09:00:00 +0000</pubDate>
      <link>https://dev.to/niamtokik/sandbox1-flutter-application-design-first-steps-35ge</link>
      <guid>https://dev.to/niamtokik/sandbox1-flutter-application-design-first-steps-35ge</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Epic things start with small humble steps. Pay respect to your beginnings. And if you're just starting out, know that it's OK to be sucky. To be small. To be messy and chaotic. Just make sure to never ever stop dreaming.&lt;/p&gt;

&lt;p&gt;-- Vishen Lakhiani&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let start another chapter of this journey with Dart by creating a mobile application with &lt;a href="https://flutter.dev/" rel="noopener noreferrer"&gt;Flutter&lt;/a&gt;. For this post, a really simple application will be created called &lt;code&gt;sandbox&lt;/code&gt;. Instead of adding some interactive part, like sending/receiving data from a backend, let simply design an application based on a wireframe.&lt;/p&gt;

&lt;h1&gt;
  
  
  Bootstrapping
&lt;/h1&gt;

&lt;p&gt;As usual, I'm using &lt;code&gt;asdf&lt;/code&gt; to install Flutter on my laptop but it can also be installed manually by following the &lt;a href="https://docs.flutter.dev/install/quick" rel="noopener noreferrer"&gt;Flutter Quick Install documentation&lt;/a&gt;. This new project will be called &lt;code&gt;sandbox&lt;/code&gt;, a good name for a draft.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;flutter create sandbox
&lt;span class="go"&gt;Creating project sandbox...
Resolving dependencies in `sandbox`... 
Downloading packages... 
Got dependencies in `sandbox`.
Wrote 131 files.

All done!
You can find general documentation for Flutter at: https://docs.flutter.dev/
Detailed API documentation is available at: https://api.flutter.dev/
If you prefer video documentation, consider: https://www.youtube.com/c/flutterdev

In order to run your application, type:

&lt;/span&gt;&lt;span class="gp"&gt;  $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;sandbox
&lt;span class="gp"&gt;  $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;flutter run
&lt;span class="go"&gt;
Your application code is in sandbox/lib/main.dart.

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;sandbox
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Wireframe and Prototype
&lt;/h1&gt;

&lt;p&gt;My career is from network and system administration, backend and distributed development, when it comes to design something, I'm thinking like a technician/engineer: I don't care about the look, it should work. In a previous publication, rock-paper-scissors data structure and primitive have been designed, why not starting with a kind of interface for this game?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1f69igd0hrdsbrxifzzo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1f69igd0hrdsbrxifzzo.png" alt="Prototype made with Figma" width="435" height="846"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Creating a prototype or a wireframe with Figma is painful. Really. I still don't believe such kind of "application" can be so successful. Anyway, after a long fight, and without any idea how to expert a layer correctly, I finally decided to do a screenshot of the page. Yeah. Ridiculous. If you are a developer at Figma, are you really using your software? I mean, exporting a selected area can be done quite easily with &lt;a href="https://app.diagrams.net/" rel="noopener noreferrer"&gt;draw.io&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Anyway, the goal here is to reproduce this with Flutter. No need for interactive feature, just a raw prototype.&lt;/p&gt;

&lt;h1&gt;
  
  
  First Version
&lt;/h1&gt;

&lt;p&gt;Someone said "start humble", this is the first version, with lot of duplicated code an crappy colors, but it should give the big picture of the application.&lt;/p&gt;

&lt;p&gt;The top bar will be used for the menu, to switch to another mode or game for example. It contains the title of the current page. At this time, it will do nothing.&lt;/p&gt;

&lt;p&gt;Then comes the opponent container. This part will be empty at first, but in the future, it could be an animated area, where the final shape of the opponent will be loaded.&lt;/p&gt;

&lt;p&gt;The third container will contain the final result, when both players selected the shape, and the result should be displayed. If this application is used also by someone else (remotely), one player can see "loss", while the other one will see "win". If the shapes are the same, both players will see "draw".&lt;/p&gt;

&lt;p&gt;The fourth container is where the player will select the action. Three shapes are available, rock, paper and scissors. When tapping on one of them, it will send it to a remote server (if playing against someone else), or simply update the state of the application (if playing against the machine).&lt;/p&gt;

&lt;p&gt;Finally, the last container is the bottom bar. It will be removed in the second version because I think an user can click by mistake on one of the button instead of selecting one shape. Let begins the implementation. Nothing really fancy at first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:flutter/material.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;runApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;MyApp&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The interesting part is present in &lt;code&gt;MyApp()&lt;/code&gt; class where we are defining the different layer of the application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyApp&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatelessWidget&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;MyApp&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;MaterialApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;debugShowCheckedModeBanner:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;home:&lt;/span&gt; &lt;span class="n"&gt;Scaffold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;appBar:&lt;/span&gt; &lt;span class="n"&gt;AppBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;leading:&lt;/span&gt; &lt;span class="n"&gt;IconButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;onPressed:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="nl"&gt;icon:&lt;/span&gt; &lt;span class="n"&gt;Icon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Icons&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;menu&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
          &lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Jan Ken Pon"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="n"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;children:&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Widget&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;[&lt;/span&gt;
            &lt;span class="n"&gt;Expanded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Center&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;InkWell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                  &lt;span class="nl"&gt;onTap:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
                  &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Ink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="nl"&gt;height:&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;infinity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="nl"&gt;width:&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;infinity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="nl"&gt;color:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;grey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Center&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"opponent choice"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
                  &lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="p"&gt;),&lt;/span&gt;
              &lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;Expanded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Center&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;InkWell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                  &lt;span class="nl"&gt;onTap:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
                  &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Ink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="nl"&gt;height:&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;infinity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="nl"&gt;width:&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;infinity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="nl"&gt;color:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;amber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Center&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"result"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
                  &lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="p"&gt;),&lt;/span&gt;
              &lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;Expanded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Center&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Row&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                  &lt;span class="nl"&gt;children:&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Widget&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;[&lt;/span&gt;
                    &lt;span class="n"&gt;Expanded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                      &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;InkWell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="nl"&gt;onTap:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
                        &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Ink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                          &lt;span class="nl"&gt;height:&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;infinity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                          &lt;span class="nl"&gt;width:&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;infinity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                          &lt;span class="nl"&gt;color:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                          &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Center&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"rock"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
                        &lt;span class="p"&gt;),&lt;/span&gt;
                      &lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="n"&gt;Expanded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                      &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;InkWell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="nl"&gt;onTap:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
                        &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Ink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                          &lt;span class="nl"&gt;height:&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;infinity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                          &lt;span class="nl"&gt;width:&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;infinity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                          &lt;span class="nl"&gt;color:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;green&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                          &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Center&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"paper"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
                        &lt;span class="p"&gt;),&lt;/span&gt;
                      &lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="n"&gt;Expanded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                      &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;InkWell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="nl"&gt;onTap:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
                        &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Ink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                          &lt;span class="nl"&gt;height:&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;infinity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                          &lt;span class="nl"&gt;width:&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;infinity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                          &lt;span class="nl"&gt;color:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;red&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                          &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Center&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"scissors"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
                        &lt;span class="p"&gt;),&lt;/span&gt;
                      &lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="p"&gt;),&lt;/span&gt;
                  &lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="p"&gt;),&lt;/span&gt;
              &lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nl"&gt;bottomNavigationBar:&lt;/span&gt; &lt;span class="n"&gt;BottomNavigationBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;items:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="n"&gt;BottomNavigationBarItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;icon:&lt;/span&gt; &lt;span class="n"&gt;Icon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Icons&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;star&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"Home"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;BottomNavigationBarItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="nl"&gt;icon:&lt;/span&gt; &lt;span class="n"&gt;Icon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Icons&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;star&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
              &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"Jan Ken Pon"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I know, this is a disgusting piece of code. The level of indentation is insane and the duplicated code is crazy, but at least, it works. What kind of classes I used here?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://api.flutter.dev/flutter/material/Scaffold-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;Scaffold&lt;/code&gt;&lt;/a&gt; class is used to configure the visual layout. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://api.flutter.dev/flutter/material/AppBar-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;AppBar()&lt;/code&gt;&lt;/a&gt; class is instantiated in the &lt;code&gt;Scaffold&lt;/code&gt; object (&lt;a href="https://api.flutter.dev/flutter/material/Scaffold/appBar.html" rel="noopener noreferrer"&gt;&lt;code&gt;appBar&lt;/code&gt;&lt;/a&gt; attribute) and configure the top bar of the application. The menu button on the left currently don't work. The title is a simple &lt;code&gt;Text()&lt;/code&gt; object;&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;AppBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;leading:&lt;/span&gt; &lt;span class="n"&gt;IconButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;),&lt;/span&gt;
 &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://api.flutter.dev/flutter/material/BottomNavigationBar-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;BottomNavigationBar&lt;/code&gt;&lt;/a&gt; is also instantiated in &lt;code&gt;Scaffold&lt;/code&gt; in the &lt;a href="https://api.flutter.dev/flutter/material/Scaffold/bottomNavigationBar.html" rel="noopener noreferrer"&gt;&lt;code&gt;bottomNavigationBar&lt;/code&gt;&lt;/a&gt; attribute. This object is also made of a list of Widgets defined in the &lt;code&gt;items&lt;/code&gt; attribute. At least 2 items must be created, in our case, two &lt;a href="https://api.flutter.dev/flutter/widgets/BottomNavigationBarItem-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;BottomNavigationBarItem&lt;/code&gt;&lt;/a&gt; are created;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;BottomNavigationBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;items:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
   &lt;span class="n"&gt;BottomNavigationBarItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
     &lt;span class="c1"&gt;// ...&lt;/span&gt;
   &lt;span class="p"&gt;),&lt;/span&gt;
   &lt;span class="n"&gt;BottomNavigationBarItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
     &lt;span class="c1"&gt;// ...&lt;/span&gt;
   &lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://api.flutter.dev/flutter/widgets/Column-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;Column&lt;/code&gt;&lt;/a&gt; class is used in the &lt;a href="https://api.flutter.dev/flutter/material/Scaffold/body.html" rel="noopener noreferrer"&gt;&lt;code&gt;body&lt;/code&gt;&lt;/a&gt; attribute of the &lt;code&gt;Scaffold&lt;/code&gt; object. This is where the user will be able to see the actions and play with the application. A list of &lt;code&gt;Widget&lt;/code&gt; can be provided using the &lt;code&gt;children&lt;/code&gt;&amp;nbsp;attributes, each one will divide the available space. So, in our case, we need 3 "lines", one for the opponent, one for the result and the last one for the user selection;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;children:&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Widget&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;[&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://api.flutter.dev/flutter/widgets/Container-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;Expanded&lt;/code&gt;&lt;/a&gt; class is used on every "line" to use all the space available from each line;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Expanded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Center&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://api.flutter.dev/flutter/widgets/Center-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;Center&lt;/code&gt;&lt;/a&gt; is then used to vertically and horizontally center all elements;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Center&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;InkWell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://api.flutter.dev/flutter/material/InkWell-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;InkWell&lt;/code&gt;&lt;/a&gt; classes are then used to define a place that can react to user touch, a bit like a button, but with more flexibility;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;InkWell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;onTap:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Ink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://api.flutter.dev/flutter/material/Ink-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;Ink&lt;/code&gt;&lt;/a&gt; classes are finally used to set the &lt;a href="https://api.flutter.dev/flutter/material/Ink/height.html" rel="noopener noreferrer"&gt;&lt;code&gt;height&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://api.flutter.dev/flutter/material/Ink/width.html" rel="noopener noreferrer"&gt;&lt;code&gt;width&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://api.flutter.dev/flutter/material/MaterialApp/color.html" rel="noopener noreferrer"&gt;&lt;code&gt;color&lt;/code&gt;&lt;/a&gt; of each active elements;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Ink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;height:&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;infinity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;width:&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;infinity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;color:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;amber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Center&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://api.flutter.dev/flutter/widgets/Row-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;Row&lt;/code&gt;&lt;/a&gt; is also used for the shapes user layer, where the user can select &lt;code&gt;rock&lt;/code&gt;, &lt;code&gt;paper&lt;/code&gt; or &lt;code&gt;scissors&lt;/code&gt;. The goal of this class is to split the available space in rows.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Row&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;children:&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Widget&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;[&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;This application can be compiled and installed on the Android emulator. Here a screenshot of this first application draft from Android (API 36.0).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb3p82ep5tl05fdfv2xhe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb3p82ep5tl05fdfv2xhe.png" alt="Design First Version" width="800" height="1778"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The colors have been set simply to see where the important information will be displayed. The same colors will stay there until we have something functional.&lt;/p&gt;

&lt;p&gt;With this first version, we also have an idea of the essential bricks we can reuse in the future. It means the next iteration will remove some part of this design, create reusable functions and likely add a bit of interactivity.&lt;/p&gt;

&lt;p&gt;Have fun!&lt;/p&gt;




&lt;p&gt;Cover Image by &lt;a href="https://unsplash.com/@tetrakiss?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Arseny Togulev&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/white-robot-MECKPoKJYjM?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>android</category>
      <category>mobile</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Encoding and Decoding JSON in Dart</title>
      <dc:creator>Mathieu Kerjouan</dc:creator>
      <pubDate>Thu, 11 Jun 2026 08:00:00 +0000</pubDate>
      <link>https://dev.to/niamtokik/encoding-and-decoding-json-in-dart-b8m</link>
      <guid>https://dev.to/niamtokik/encoding-and-decoding-json-in-dart-b8m</guid>
      <description>&lt;p&gt;Is it really necessary to introduce &lt;a href="https://json.org/" rel="noopener noreferrer"&gt;JSON&lt;/a&gt; in 2026? Any developers had to deal with JSON at least one time in his career. Anyway, JSON (JavaScript Object Notation) is an open standard format designed for web development usage. In fine, it became one of the most used data format.&lt;/p&gt;

&lt;p&gt;Before working on more challengin but interesting serializer like &lt;a href="https://cbor.io/" rel="noopener noreferrer"&gt;CBOR&lt;/a&gt; or &lt;a href="https://protobuf.dev/" rel="noopener noreferrer"&gt;Protocol Buffer&lt;/a&gt;, let take a moment to learn how use JSON in Dart.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'dart:convert'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When one needs to encode or decode JSON, two ways existing, using the &lt;a href="https://api.dart.dev/dart-convert/JsonCodec-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;JsonCodec()&lt;/code&gt;&lt;/a&gt; class or use the specific class for encoding (&lt;a href="https://api.dart.dev/dart-convert/JsonEncoder-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;JsonEncoder()&lt;/code&gt;&lt;/a&gt;) or for decoding (&lt;a href="https://api.dart.dev/dart-convert/JsonDecoder-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;JsonDecoder()&lt;/code&gt;&lt;/a&gt;). In both case, the result will be the same.&lt;/p&gt;

&lt;h1&gt;
  
  
  Encoding
&lt;/h1&gt;

&lt;p&gt;JSON encoder is directly integrated in the Dart SDK, and can be included in your code by simply importing &lt;a href="https://api.dart.dev/dart-convert/" rel="noopener noreferrer"&gt;&lt;code&gt;dart:convert&lt;/code&gt;&lt;/a&gt;&amp;nbsp;package.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'dart:convert'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let create a first function called &lt;code&gt;encode()&lt;/code&gt; to encode any kind of object using the &lt;a href="https://api.dart.dev/dart-convert/JsonCodec/encode.html" rel="noopener noreferrer"&gt;&lt;code&gt;json.encode()&lt;/code&gt;&lt;/a&gt; interface.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-- &lt;/span&gt;&lt;span class="si"&gt;${label}&lt;/span&gt;&lt;span class="s"&gt;:"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;encoded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;encoded&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let create another function called &lt;code&gt;encode2()&lt;/code&gt;. This one will instantiate an object from the &lt;a href="https://api.dart.dev/dart-convert/JsonEncoder-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;JsonEncoder()&lt;/code&gt;&lt;/a&gt; class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;encode2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;encoder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JsonEncoder&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-- &lt;/span&gt;&lt;span class="si"&gt;${label}&lt;/span&gt;&lt;span class="s"&gt;:"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;encoded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;encoder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;convert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;encoded&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great, we have two ways to encode our objects, the main entry-point is still missing though. Let fix that.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"== encode with JsonCodec"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"integer(1)"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"string(test)"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;([],&lt;/span&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"list()"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"list(1,2,3)"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;({},&lt;/span&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"map()"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s"&gt;"a"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"map(a, 1)"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.001&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"float(0.001)"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"bool(true)"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"bool(false)"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"encode with JsonEncoder"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;encode2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"integer(1)"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;encode2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"string(test)"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;encode2&lt;/span&gt;&lt;span class="p"&gt;([],&lt;/span&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"list()"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;encode2&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"list(1,2,3)"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;encode2&lt;/span&gt;&lt;span class="p"&gt;({},&lt;/span&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"map()"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;encode2&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s"&gt;"a"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"map(a, 1)"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;encode2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.001&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"float(0.001)"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;encode2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"bool(true)"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;encode2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"bool(false)"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The time has come to execute this program and see the result.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart run bin/encode.dart
&lt;span class="go"&gt;== encode with JsonCodec
-- integer(1):
1

-- string(test):
"test"

-- list():
[]

-- list(1,2,3):
[1,2,3]

-- map():
{}

-- map(a, 1):
{"a":1}

-- float(0.001):
0.001

-- bool(true):
true

-- bool(false):
false

encode with JsonEncoder
-- integer(1):
1

-- string(test):
"test"

-- list():
[]

-- list(1,2,3):
[1,2,3]

-- map():
{}

-- map(a, 1):
{"a":1}

-- float(0.001):
0.001

-- bool(true):
true

-- bool(false):
false
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It looks okay, the main Dart types are supported, but what will happen if we give an unsupported type to the encoder?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code is simple, we pass a raw &lt;code&gt;Object()&lt;/code&gt;  to &lt;a href="https://api.dart.dev/dart-convert/JsonCodec/encode.html" rel="noopener noreferrer"&gt;&lt;code&gt;json.encode()&lt;/code&gt;&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart bin/encode_exception.dart
&lt;span class="go"&gt;Uncaught Error, error: Error: Converting object to an encodable object failed: Instance of 'Object'
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Doh, it produces an error! In fact, this is normal, the JSON encoder does not know how to deal with such objects. JSON only support a subset of all Dart type, so, in this case, we need to find a way to convert (serialize) similar objects to something compatible with JSON. In most of the case, it will be converted as &lt;code&gt;String()&lt;/code&gt;, perhaps using Base64 or something similar.&lt;/p&gt;

&lt;p&gt;To illustrate that, let create a new class called &lt;code&gt;Tuple&lt;/code&gt;, it will contain two public attributes, a &lt;code&gt;left&lt;/code&gt; and a &lt;code&gt;right&lt;/code&gt; one. Both are &lt;a href="https://api.dart.dev/dart-core/String-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;String()&lt;/code&gt;&lt;/a&gt;. To represent this kind of object in JSON we can easily use a Map containing at least two fields, one for the left and one of the right. Adding a method called &lt;code&gt;toJson()&lt;/code&gt; here will help us.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Tuple&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;Tuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;right&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;toJson&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"_object"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Tuple"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"left"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"right"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The JSON encoder needs a way to know how to convert this new object, it can be done by creating a simple function like the following one. The idea is to check the type of the object passed as argument and to return another object compatible with the JSON format.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;toEncodable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;Tuple&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toJson&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="kt"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;toEncodable()&lt;/code&gt; function can be passed to the &lt;a href="https://api.dart.dev/dart-convert/JsonCodec/JsonCodec.html" rel="noopener noreferrer"&gt;&lt;code&gt;JsonCodec&lt;/code&gt;&lt;/a&gt; constructor via the &lt;a href="https://api.dart.dev/dart-convert/JsonEncoder/JsonEncoder.html" rel="noopener noreferrer"&gt;&lt;code&gt;toEncodable&lt;/code&gt;&lt;/a&gt; attribute. The rest is pure testing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;encoder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JsonCodec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;toEncodable:&lt;/span&gt; &lt;span class="n"&gt;toEncodable&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;encoded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;encoder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kt"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="n"&gt;Tuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Left1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Right1"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;encoded&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let execute the code and check the result.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart bin/encode_encable.dart
&lt;span class="go"&gt;[1,"test","Instance of 'Object'",{"_object":"Tuple","left":"Left1","right":"Right1"}]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our object is correctly converted to a JSON map containing a left and right field. Regarding the raw object, we have now a string containing &lt;code&gt;"Instance of 'Object'"&lt;/code&gt;, that's the direct result of the &lt;code&gt;toString()&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;Last trick, it is also possible to encode a JSON using indentation via the &lt;a href="https://api.dart.dev/dart-convert/JsonEncoder/JsonEncoder.withIndent.html" rel="noopener noreferrer"&gt;&lt;code&gt;JsonEncoder.withIndent()&lt;/code&gt;&lt;/a&gt; constructor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'dart:convert'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JsonEncoder&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withIndent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;" "&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;convert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let execute our code, the output should be a JSON with indentation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart bin/encode_indent.dart
&lt;span class="go"&gt;{
 "test": [
  1,
  2,
  3
 ]
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's right! It works! Next step, decoding JSON.&lt;/p&gt;

&lt;h1&gt;
  
  
  Decoding
&lt;/h1&gt;

&lt;p&gt;Decoding a JSON String is similar than encoding Dart objects, one can use &lt;a href="https://api.dart.dev/dart-convert/JsonCodec-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;JsonCodec&lt;/code&gt;&lt;/a&gt; or create a &lt;a href="https://api.dart.dev/dart-convert/JsonDecoder-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;JsonDecoder&lt;/code&gt;&lt;/a&gt; object, the result will be the same.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s"&gt;'{"test": 1}'&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;JsonDecoder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;convert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s"&gt;'{"test": 1}'&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let execute this code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart run bin/decode.dart
&lt;span class="go"&gt;{test: 1}
{test: 1}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without surprise, it returns a &lt;a href="https://api.dart.dev/dart-core/Map-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;Map()&lt;/code&gt;&lt;/a&gt;, but, what if we want to return a specific Object instead? Let take our &lt;code&gt;Tuple&lt;/code&gt; class back from the Encoding section.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Tuple&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;Tuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;right&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;toJson&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"_object"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Tuple"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"left"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"right"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://api.dart.dev/dart-convert/JsonDecoder-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;JsonDecoder&lt;/code&gt;&lt;/a&gt; class can accept a &lt;a href="https://api.dart.dev/dart-convert/JsonDecoder/JsonDecoder.html" rel="noopener noreferrer"&gt;&lt;code&gt;reviver()&lt;/code&gt;&lt;/a&gt; function passed as parameter and can be used for this kind of situation. When some data looks like it's a specific object, the &lt;code&gt;reviver()&lt;/code&gt; function will be able to return the correct instantiated object. Here an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="kt"&gt;Function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;reviver&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--&amp;gt; key: &lt;/span&gt;&lt;span class="si"&gt;$key&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--&amp;gt; value: &lt;/span&gt;&lt;span class="si"&gt;$value&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"_object"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"tuple"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; 
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Tuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"left"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"right"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The snippet above will check each value returned by the decoder, and if one of these values are matching a specific kind of pattern (in our case if the value is a Map containing a field &lt;code&gt;object&lt;/code&gt; containing the string &lt;code&gt;tuple&lt;/code&gt;), then it will create the object. Let check that with a new main entry-point.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;toEncode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"1"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; 
      &lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"data"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}]&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;decoder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JsonDecoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reviver&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;decoder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;convert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;toEncode&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"==&amp;gt; final: &lt;/span&gt;&lt;span class="si"&gt;$x&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we can execute the code and check the output.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart bin/decode_reviver.dart
&lt;span class="gp"&gt;--&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;key: data
&lt;span class="gp"&gt;--&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;value: 2
&lt;span class="gp"&gt;--&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;key: _object
&lt;span class="gp"&gt;--&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;value: tuple
&lt;span class="gp"&gt;--&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;key: left
&lt;span class="gp"&gt;--&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;value: left
&lt;span class="gp"&gt;--&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;key: right
&lt;span class="gp"&gt;--&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;value: right
&lt;span class="gp"&gt;--&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;key: tuple
&lt;span class="gp"&gt;--&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;value: &lt;span class="o"&gt;{&lt;/span&gt;_object: tuple, left: left, right: right&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="gp"&gt;--&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;key: &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;span class="gp"&gt;--&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;value: &lt;span class="o"&gt;{&lt;/span&gt;data: 2, tuple: Instance of &lt;span class="s1"&gt;'Tuple'&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="gp"&gt;--&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;key: 0
&lt;span class="gp"&gt;--&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;value: &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;data: 2, tuple: Instance of &lt;span class="s1"&gt;'Tuple'&lt;/span&gt;&lt;span class="o"&gt;}}&lt;/span&gt;
&lt;span class="gp"&gt;--&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;key: 1
&lt;span class="gp"&gt;--&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;value: &lt;span class="o"&gt;[{&lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;data: 2, tuple: Instance of &lt;span class="s1"&gt;'Tuple'&lt;/span&gt;&lt;span class="o"&gt;}}]&lt;/span&gt;
&lt;span class="gp"&gt;--&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;key: null
&lt;span class="gp"&gt;--&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;value: &lt;span class="o"&gt;{&lt;/span&gt;1: &lt;span class="o"&gt;[{&lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;data: 2, tuple: Instance of &lt;span class="s1"&gt;'Tuple'&lt;/span&gt;&lt;span class="o"&gt;}}]}&lt;/span&gt;
&lt;span class="gp"&gt;==&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;final: &lt;span class="o"&gt;{&lt;/span&gt;1: &lt;span class="o"&gt;[{&lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;data: 2, tuple: Instance of &lt;span class="s1"&gt;'Tuple'&lt;/span&gt;&lt;span class="o"&gt;}}]}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, the returned value by the decoder includes an instance of a &lt;code&gt;Tuple&lt;/code&gt; class. The code is still a bit messy, not enough check, but the idea is there.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;This post was a small one, JSON is a simple format, and the interfaces used to encode/decode it are relatively easy to use, at least, for simple use cases. JSON can also be specified using schema or used as serializer, but it will be seen in another publication. As usual, for the ones would like to dive deeper in the subject, here few interesting links for you.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dart.dev/learn/tutorial/data-and-json" rel="noopener noreferrer"&gt;Official JSON Dart Tutorial&lt;/a&gt;, where you can see more complex examples;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://api.dart.dev/dart-convert/JsonCodec-class.html" rel="noopener noreferrer"&gt;JsonCodec class&lt;/a&gt;, where you will find the complete documentation API for the JsonCodec class;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://api.dart.dev/dart-convert/JsonDecoder-class.html" rel="noopener noreferrer"&gt;JsonDecoder class&lt;/a&gt;, where you will learn how to use the JsonDecoder class;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://api.dart.dev/dart-convert/JsonEncoder-class.html" rel="noopener noreferrer"&gt;JsonEncoder class&lt;/a&gt;, where the full description of the JsonEncoder class can be found;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/dart-lang/sdk/blob/fc3da898ea1664b8a8633f6ec03d2c2290bb3d01/sdk/lib/convert/json.dart" rel="noopener noreferrer"&gt;&lt;code&gt;dart:convert&lt;/code&gt; JSON source code&lt;/a&gt;, where you will find the full documentation of the &lt;code&gt;convert&lt;/code&gt; package, containing also the classes for Base64 or UTF8/Unicode;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dev.to/avwerosuoghene/passing-data-from-dart-to-json-for-backend-integration-5bn2a"&gt;Passing Data from Dart to JSON for Backend Integration&lt;/a&gt; by &lt;a class="mentioned-user" href="https://dev.to/avwerosuoghene"&gt;@avwerosuoghene&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://medium.com/@codewithif/mastering-json-serialization-in-dart-flutter-from-basics-to-complex-models-4c764400c2d6" rel="noopener noreferrer"&gt;Mastering JSON Models in Dart &amp;amp; Flutter: From Basics to Complex Models&lt;/a&gt; by &lt;a href="https://medium.com/@codewithif" rel="noopener noreferrer"&gt;Iftekhar Ahmed&lt;/a&gt; on Medium&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://medium.com/@vikranthsalian/how-to-parse-json-in-dart-flutter-60c17143198e" rel="noopener noreferrer"&gt;How to Parse JSON in Dart/Flutter&lt;/a&gt; by &lt;a href="https://medium.com/@vikranthsalian" rel="noopener noreferrer"&gt;Vikranh Salian&lt;/a&gt; on Medium&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://medium.com/@bhavesh.sachala/mastering-json-encoding-and-decoding-in-flutter-dart-a-comprehensive-guide-for-api-integration-b5153aa6f1bc" rel="noopener noreferrer"&gt;Mastering JSON Encoding and Decoding in Flutter/Dart: A Comprehensive Guide for API Integration&lt;/a&gt; by &lt;a href="https://medium.com/@bhavesh.sachala" rel="noopener noreferrer"&gt;Bhaveshh Sachala&lt;/a&gt; on Medium &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Have fun!&lt;/p&gt;




&lt;p&gt;Cover Image by &lt;a href="https://unsplash.com/@dieter_muenchen?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Dieter K&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/a-black-and-white-photo-of-a-row-of-sinks-M4G5BSUo1p4?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>json</category>
      <category>dart</category>
      <category>serialization</category>
      <category>serializer</category>
    </item>
  </channel>
</rss>
