<?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: Song Yang</title>
    <description>The latest articles on DEV Community by Song Yang (@songyang-dev).</description>
    <link>https://dev.to/songyang-dev</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1077484%2F0e836106-e137-455b-bacb-e459d917cc2f.jpeg</url>
      <title>DEV Community: Song Yang</title>
      <link>https://dev.to/songyang-dev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/songyang-dev"/>
    <language>en</language>
    <item>
      <title>How to use Lottie animations in Flutter 3.16</title>
      <dc:creator>Song Yang</dc:creator>
      <pubDate>Thu, 14 Mar 2024 00:04:13 +0000</pubDate>
      <link>https://dev.to/songyang-dev/how-to-use-lottie-animations-in-flutter-316-53n3</link>
      <guid>https://dev.to/songyang-dev/how-to-use-lottie-animations-in-flutter-316-53n3</guid>
      <description>&lt;p&gt;In this tutorial, we go over the basics of Lottie animations and their use inside Flutter. We will see how Lottie animation files are represented in JSON and .lottie (DotLottie) formats. Then, we will put them into practice in a Flutter mobile app.&lt;/p&gt;

&lt;p&gt;This is my system.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Software&lt;/th&gt;
&lt;th&gt;Version&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Flutter&lt;/td&gt;
&lt;td&gt;3.16.9&lt;/td&gt;
&lt;td&gt;Installed on Win 11 (not WSL)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Android SDK&lt;/td&gt;
&lt;td&gt;34&lt;/td&gt;
&lt;td&gt;Android Studio Hedgehog&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Lottie Animations
&lt;/h2&gt;

&lt;p&gt;Lottie animations are vector graphics animations. They are usually simple and short. Artists and designers can make the animations in Adobe After Effects or Figma, and then they can export them into a Lottie animation.&lt;/p&gt;

&lt;p&gt;Here is an &lt;a href="https://lottiefiles.com/animations/morphing-pyramid-segments-CHHdKw9CjV" rel="noopener noreferrer"&gt;example animation&lt;/a&gt; on LottieFiles, a website for sharing Lottie animations.&lt;/p&gt;

&lt;p&gt;The name "Lottie" is named after the film director &lt;a href="https://airbnb.io/lottie/#/?id=why-is-it-called-lottie" rel="noopener noreferrer"&gt;Lotte Reineger&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  File Formats
&lt;/h3&gt;

&lt;p&gt;Lottie animations are not stored like normal animations, that is, in video files or GIFs. This is because the size of the files is too big and the pixelated resolution goes against responsive design.&lt;/p&gt;

&lt;p&gt;In 2015, &lt;a href="https://lottiefiles.com/what-is-lottie" rel="noopener noreferrer"&gt;Hernan Torrisi&lt;/a&gt; developed a JSON format, now standardized by the &lt;a href="https://lottie.github.io/" rel="noopener noreferrer"&gt;Lottie Animation Community&lt;/a&gt;. The JSON format is minified for storage efficiency and rendered onto the screen by a player at runtime. Here is an &lt;a href="https://lottiefiles.github.io/lottie-docs/Introduction/" rel="noopener noreferrer"&gt;introduction to the JSON format&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In 2020, LottieFiles developed a binary file format called &lt;a href="https://dotlottie.io/" rel="noopener noreferrer"&gt;.lottie&lt;/a&gt; (DotLottie), which is a ZIP version of the JSON format. The biggest benefit of .lottie is its much smaller file size, up to 70-90% smaller than JSON!&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Lottie Animations
&lt;/h2&gt;

&lt;p&gt;To use Lottie animations, we need a Lottie player that can parse the animation data from JSON or .lottie and render it to the screen. In Flutter, the most popular package for this is called &lt;a href="https://pub.dev/packages/lottie" rel="noopener noreferrer"&gt;lottie&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup
&lt;/h3&gt;

&lt;p&gt;The animations we are using in this tutorial come from LottieFiles. Follow the links to download both the JSON and .lottie formats. In practice, we only need one format, but we will learn how to use both.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://lottiefiles.com/animations/day-and-night-toggle-tUzZELBsdB" rel="noopener noreferrer"&gt;Day and Night Toggle by Amanda - LottieFiles&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://lottiefiles.com/animations/morphing-pyramid-segments-CHHdKw9CjV" rel="noopener noreferrer"&gt;Morphing Pyramid Segments by Ision Industries - LottieFiles&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Create a Flutter project with &lt;code&gt;flutter create&lt;/code&gt;. Move the downloaded files into an &lt;code&gt;assets&lt;/code&gt; folder.&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%2Fs7ndf1d834098brotl9z.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%2Fs7ndf1d834098brotl9z.png" alt="file structure" width="533" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Modify the &lt;code&gt;pubspec.yaml&lt;/code&gt; accordingly. Import the Lottie animation packages and expose the asset path.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ...&lt;/span&gt;

&lt;span class="na"&gt;dependencies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# ...&lt;/span&gt;

    &lt;span class="c1"&gt;# animations&lt;/span&gt;
    &lt;span class="na"&gt;lottie&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;^3.1.0&lt;/span&gt;
    &lt;span class="na"&gt;dotlottie_loader&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;^0.0.3&lt;/span&gt; &lt;span class="c1"&gt;# only if you want to use .lottie&lt;/span&gt;

&lt;span class="c1"&gt;# ...&lt;/span&gt;

&lt;span class="na"&gt;flutter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

    &lt;span class="c1"&gt;# ...&lt;/span&gt;

    &lt;span class="c1"&gt;# To add assets to your application, add an assets section, like this:&lt;/span&gt;
    &lt;span class="na"&gt;assets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;assets/animations/&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Importing JSON
&lt;/h3&gt;

&lt;p&gt;To use the JSON format of a Lottie animation, it only takes one line.&lt;/p&gt;

&lt;p&gt;For example, this is in &lt;code&gt;main.dart&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="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="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:lottie/lottie.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;LottieExampleApp&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LottieExampleApp&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;LottieExampleApp&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;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;body:&lt;/span&gt; &lt;span class="n"&gt;Lottie&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;asset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'assets/animations/Pyramid.json'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;-- this line&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;&lt;code&gt;Lottie.asset()&lt;/code&gt; is a named constructor available in &lt;a href="https://pub.dev/packages/lottie" rel="noopener noreferrer"&gt;lottie&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This will give you something like this. By default, the animation loops forever. To change that behavior, there are adjustable parameters in the &lt;code&gt;Lottie.asset()&lt;/code&gt; method. For more fine-grained control, an &lt;code&gt;AnimationController&lt;/code&gt; is needed.&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%2Fdluln3wqh8hnpiz6g2oa.gif" 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%2Fdluln3wqh8hnpiz6g2oa.gif" alt="looping pyramid" width="426" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Importing .lottie
&lt;/h3&gt;

&lt;p&gt;Make sure the &lt;code&gt;dotlottie_loader&lt;/code&gt; package has been added to the &lt;code&gt;pubspec.yaml&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LottieExampleApp&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;LottieExampleApp&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;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;body:&lt;/span&gt; &lt;span class="n"&gt;DotLottieLoader&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromAsset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="s"&gt;"assets/animations/Pyramid.lottie"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;frameBuilder:&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="n"&gt;DotLottie&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;dotlottie&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// check whether the animation has been loaded&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;dotlottie&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="p"&gt;{&lt;/span&gt;
              &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Lottie&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dotlottie&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;animations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;single&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="c1"&gt;// what to render while it is loading&lt;/span&gt;
              &lt;span class="k"&gt;return&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;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;This produces the same thing as the JSON format but saves space on asset storage. Notice how the &lt;code&gt;DotLottieLoader&lt;/code&gt; still needs the &lt;code&gt;Lottie&lt;/code&gt; package. These two packages work together.&lt;/p&gt;

&lt;h2&gt;
  
  
  Animation Controllers
&lt;/h2&gt;

&lt;p&gt;Animation controllers are widgets that control animations. We will use them to play the light switch animation from night to day and vice versa when the user taps on it.&lt;/p&gt;

&lt;p&gt;To start, create a boilerplate animation controller widget using VS Code's Flutter extension. Simply type 'st' and select "Animation Controller". Alternatively, copy the following.&lt;/p&gt;

&lt;p&gt;This is in a different Dart file called &lt;code&gt;light_switch.dart&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LightSwitch&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatefulWidget&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;LightSwitch&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;State&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LightSwitch&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;createState&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;_LightSwitchState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;_LightSwitchState&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LightSwitch&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;SingleTickerProviderStateMixin&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;late&lt;/span&gt; &lt;span class="n"&gt;AnimationController&lt;/span&gt; &lt;span class="n"&gt;_controller&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;initState&lt;/span&gt;&lt;span class="p"&gt;()&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;initState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;_controller&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AnimationController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;vsync:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;dispose&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;_controller&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;dispose&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;dispose&lt;/span&gt;&lt;span class="p"&gt;();&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="c1"&gt;// widgets to show&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yes, animation controllers are just stateful widgets with a &lt;code&gt;SingleTickerProviderStateMixin&lt;/code&gt;. With this, we don't have to use &lt;code&gt;setState()&lt;/code&gt; at all!&lt;/p&gt;

&lt;p&gt;Next, let's set up the .lottie import wrapped inside a &lt;code&gt;GestureDetector&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="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;GestureDetector&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="c1"&gt;// TODO!&lt;/span&gt;
      &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;DotLottieLoader&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromAsset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;'assets/animations/Light switch.lottie'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;frameBuilder:&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="n"&gt;dotLottie&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;dotLottie&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="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Lottie&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="c1"&gt;// .lottie can contain more than one animation&lt;/span&gt;
              &lt;span class="c1"&gt;// pick one&lt;/span&gt;
              &lt;span class="n"&gt;dotLottie&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;animations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;single&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="nl"&gt;controller:&lt;/span&gt; &lt;span class="n"&gt;_controller&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="nl"&gt;onLoaded:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;composition&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// the content of this Lottie animation&lt;/span&gt;
                &lt;span class="n"&gt;_controller&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;duration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;composition&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;duration&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="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// show nothing while it is loading&lt;/span&gt;
            &lt;span class="k"&gt;return&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;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we add the &lt;code&gt;duration&lt;/code&gt; of the animation to the controller during the &lt;code&gt;onLoaded&lt;/code&gt; callback of the &lt;code&gt;Lottie.memory()&lt;/code&gt; factory method. Without setting a duration, the animation cannot play.&lt;/p&gt;

&lt;h3&gt;
  
  
  Responding to User Taps
&lt;/h3&gt;

&lt;p&gt;Let's write two methods that play the animation from night to day and vice versa.&lt;/p&gt;

&lt;p&gt;We are still in the stateful widget's &lt;code&gt;State&amp;lt;&amp;gt;&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="c1"&gt;/// Animate the switch to the day position&lt;/span&gt;
  &lt;span class="n"&gt;TickerFuture&lt;/span&gt; &lt;span class="n"&gt;animateToDay&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;_controller&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;animateTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_dayFrame&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;_composition&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;durationFrames&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// a ratio&lt;/span&gt;

  &lt;span class="c1"&gt;/// Animate the switch to the night position&lt;/span&gt;
  &lt;span class="n"&gt;TickerFuture&lt;/span&gt; &lt;span class="n"&gt;animateToNight&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;_controller&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;animateBack&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's define the private fields in the 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="c1"&gt;/// The frame on which the animation reaches the day state&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;_dayFrame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;/// Information about the Lottie file&lt;/span&gt;
  &lt;span class="kd"&gt;late&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;LottieComposition&lt;/span&gt; &lt;span class="n"&gt;_composition&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// ... inside .lottie import&lt;/span&gt;
              &lt;span class="nl"&gt;onLoaded:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;composition&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// the content of this Lottie animation&lt;/span&gt;
                &lt;span class="n"&gt;_controller&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;duration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;composition&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="n"&gt;_composition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;composition&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 frame number &lt;code&gt;60&lt;/code&gt; is found by watching the Lottie animation in a &lt;a href="https://lottiefiles.com/animations/day-and-night-toggle-tUzZELBsdB" rel="noopener noreferrer"&gt;player&lt;/a&gt;. In frame 60, the animation reaches a state where it is day. Frame 0 starts with the night.&lt;/p&gt;

&lt;p&gt;The two animation methods work by playing the animation&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;from frame 0 to 60: &lt;code&gt;animateToDay()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;from frame 60 to 0 in reverse: &lt;code&gt;animateToNight()&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The animation controller API takes in ratios when playing from frame number to frame number. As such, we give it the total frame count.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tying Things Up
&lt;/h3&gt;

&lt;p&gt;Finally, let's write the callback for the &lt;code&gt;GestureDetector&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="c1"&gt;/// Inner state for whether it is day or night&lt;/span&gt;
  &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;_isDay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;/// Change to day if night and vice versa.&lt;/span&gt;
  &lt;span class="c1"&gt;/// Ignore input if the animation is still in progress.&lt;/span&gt;
  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;changeDayNight&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;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_controller&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isAnimating&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_isDay&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;await&lt;/span&gt; &lt;span class="n"&gt;animateToNight&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="n"&gt;_isDay&lt;/span&gt; &lt;span class="o"&gt;=&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="k"&gt;else&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;animateToDay&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="n"&gt;_isDay&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="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;GestureDetector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;onTap:&lt;/span&gt; &lt;span class="n"&gt;changeDayNight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;TickerFuture&lt;/code&gt; is a Dart future. We wait for them to complete before switching the inner state Boolean so that the Boolean reflects the screen logic.&lt;/p&gt;

&lt;p&gt;And voila!&lt;br&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%2Fnvt6aszz5244owxg6j0a.gif" 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%2Fnvt6aszz5244owxg6j0a.gif" alt="final app" width="426" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you need to check out the full source code, click &lt;a href="https://github.com/songyang-dev/flutter-lottie-tutorial" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Feel free to comment and file issues!&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>lottie</category>
      <category>animation</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>3 Ways to Package Your Java Project into a JAR for Deployment</title>
      <dc:creator>Song Yang</dc:creator>
      <pubDate>Sun, 07 May 2023 16:51:42 +0000</pubDate>
      <link>https://dev.to/songyang-dev/3-ways-to-package-your-java-project-into-a-jar-for-deployment-h0e</link>
      <guid>https://dev.to/songyang-dev/3-ways-to-package-your-java-project-into-a-jar-for-deployment-h0e</guid>
      <description>&lt;p&gt;In this tutorial, we will look at 3 ways to package your Java project into a JAR file, so you can ship your application to your users.&lt;/p&gt;

&lt;p&gt;A JAR file is an archive that contains compiled Java source code and other things needed to run your program. With a functioning JAR, your user should only need a Java Runtime to run your code. They shouldn't need to compile your code by opening up an IDE or using a build tool. This makes it easy to deploy on different platforms. Hence, Java's slogan "&lt;a href="https://www.geeksforgeeks.org/why-is-java-write-once-and-run-anywhere/" rel="noopener noreferrer"&gt;Write once, run anywhere&lt;/a&gt;". &lt;/p&gt;

&lt;h2&gt;
  
  
  1. Building a single JAR
&lt;/h2&gt;

&lt;p&gt;When your project is simple and you don't have many dependencies, building a JAR from your project is very easy. The &lt;code&gt;jar&lt;/code&gt; task on Gradle is enough. Just make sure your &lt;code&gt;build.gradle&lt;/code&gt; has your application's entry point (main class/main method) defined.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gradle"&gt;&lt;code&gt;&lt;span class="n"&gt;jar&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// this is necessary to run your JAR&lt;/span&gt;
    &lt;span class="n"&gt;manifest&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="s1"&gt;'Main-Class'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'yourpackages.morepackages.YourMainClass'&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// the rest is to access resource files like images, etc.&lt;/span&gt;
    &lt;span class="n"&gt;duplicatesStrategy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DuplicatesStrategy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;EXCLUDE&lt;/span&gt;

    &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;configurations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;runtimeClasspath&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;collect&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isDirectory&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;zipTree&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;it&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;Then run this on the command line.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gradle jar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your JAR file is produced inside the directory depending on your project IDE. For projects initialized with &lt;code&gt;gradle&lt;/code&gt; only, that is &lt;code&gt;build/libs/&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Building a JAR and delivering it with other JARs
&lt;/h2&gt;

&lt;p&gt;It is possible that a fat JAR cannot be produced for reasons outside of your control. Fat JARs are supposed to contain everything you need to run the program, besides the Java Runtime. But sometimes, a certain dependency creates a problem with the building process. For example, building a Java project with JavaFX creates problems when attaching the JavaFX runtime to your JAR.&lt;/p&gt;

&lt;p&gt;To remedy this, you can deliver several JARs at once and provide a script that launches the Java Runtime properly by referencing the third-party JARs.&lt;/p&gt;

&lt;h3&gt;
  
  
  A. Acquire the third-party JARs
&lt;/h3&gt;

&lt;p&gt;For every third-party JARs you cannot integrate into your JAR, get the version of those JARs that you need. For example, download a version of the &lt;a href="https://gluonhq.com/products/javafx/" rel="noopener noreferrer"&gt;JavaFX runtime&lt;/a&gt; JAR to your computer.&lt;/p&gt;

&lt;h3&gt;
  
  
  B. Build your JAR alone
&lt;/h3&gt;

&lt;p&gt;This step is similar to the situation where you build a single JAR.&lt;/p&gt;

&lt;h3&gt;
  
  
  C. Write a shell script (aka command-line script)
&lt;/h3&gt;

&lt;p&gt;A shell script is a file that contains commands that you would run interactively on the command line. Instead of typing every command by hand one by one, you can write them into a file and run the file through the command line. The language of a shell depends on the system you intend to deploy your Java project. Here is a short list of commonly found shell languages.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Windows:

&lt;ul&gt;
&lt;li&gt;Batch (.bat)&lt;/li&gt;
&lt;li&gt;Bash (.sh), if &lt;a href="https://git-scm.com/downloads" rel="noopener noreferrer"&gt;Git Bash&lt;/a&gt; is installed&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Mac:

&lt;ul&gt;
&lt;li&gt;Z Shell (.zsh)&lt;/li&gt;
&lt;li&gt;Bash (.sh), if the Mac is set up properly&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Linux:

&lt;ul&gt;
&lt;li&gt;Bash (.sh)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Begin your shell script with a &lt;a href="https://en.wikipedia.org/wiki/Shebang_(Unix)" rel="noopener noreferrer"&gt;shebang&lt;/a&gt;. This tells the command line what the file is. For example, the following shebang is for a Bash file. It must always be the first line of a shell script.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a shell script. Usually, they are named &lt;code&gt;run.sh&lt;/code&gt; or &lt;code&gt;execute.sh&lt;/code&gt;. Inside it, you call the Java Runtime while referencing the third-party libraries' JARs and your JAR.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;java &lt;span class="nt"&gt;--module-path&lt;/span&gt; path/to/your/third-party-JARs &lt;span class="nt"&gt;--add-modules&lt;/span&gt; module.you.need,second.module.you.need &lt;span class="nt"&gt;-jar&lt;/span&gt; your-project/build/libs/YourOwnJAR.jar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this command, we call &lt;code&gt;java&lt;/code&gt; by giving it a module path. The parameter &lt;code&gt;path/to/your/third-party-JARs&lt;/code&gt; you supply here should be the location where you store your third-party JARs. Since we are deploying, we expect the JARs to be located in the same folder as the shell script. Therefore, we can use relative paths, e.g. &lt;code&gt;javafx-sdk-20.0.1/lib&lt;/code&gt;. This assumes you moved the JavaFX folder next to your shell script.&lt;/p&gt;

&lt;p&gt;Example file setup&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;your-current-working-directory/
|  YourOwnJAR.jar
|  javafx-sdk-20.0.1/
|  |  lib/
|  |  |  ... lots of JARs   
|  
|  run.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example &lt;code&gt;run.sh&lt;/code&gt; for a JavaFX project&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
java &lt;span class="nt"&gt;--module-path&lt;/span&gt; javafx-sdk-20.0.1/lib &lt;span class="nt"&gt;--add-modules&lt;/span&gt; javafx.controls,javafx.fxml,javafx.graphics,javafx.media &lt;span class="nt"&gt;-jar&lt;/span&gt; YourOwnJar.jar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Don't forget to grant permission to the script with &lt;code&gt;chmod&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x run.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To deliver this to your user, you would zip &lt;code&gt;your-current-working-directory&lt;/code&gt; and send the archive. You would notify your user to unzip the archive and run the shell script by double-clicking it or through the command line with &lt;code&gt;./run.sh&lt;/code&gt;. This assumes your user has the appropriate shell language and Java Runtime.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Delivering a native application
&lt;/h2&gt;

&lt;p&gt;This method is unlike the other two because it does not require the user to have anything installed, not even a Java Runtime! For situations where you want to deliver maximum application independence, this should be your go-to solution. For example, if you are dealing with people who have no idea what a shell script is or even what coding is, you should be sending everything as a self-contained double-clickable program.&lt;/p&gt;

&lt;p&gt;The way this last method works is by shipping the Java Runtime with your JAR and dependencies. For those who don't know, the Java Runtime is an application designed to run compiled Java code on a specific operating system. As developers, we use the runtime that comes with the Java Development Kit (JDK). Two limitations of this method are the rather large deployment binary and the breaking of the slogan "Write once, run anywhere".&lt;/p&gt;

&lt;p&gt;We will make use of the running example. This method builds upon Method 2.&lt;/p&gt;

&lt;h3&gt;
  
  
  A. Put all of your JARs (third-party or not) into a folder with your shell script
&lt;/h3&gt;

&lt;p&gt;This step is the same as all of Method 2.&lt;/p&gt;

&lt;h3&gt;
  
  
  B. Download a copy of the Java Runtime Environment (JRE)
&lt;/h3&gt;

&lt;p&gt;Next, you need to download a version of the Java Runtime. Head over to a distributor like the &lt;a href="https://adoptium.net/temurin/releases/" rel="noopener noreferrer"&gt;Eclipse Foundation&lt;/a&gt; and download a JRE that matches the version of your JDK. Although you can ship the JDK to your user, the file size would be very large (200+ MB) and it would contain many features that your user will never use. That's why we only need the JRE (40+ MB).&lt;/p&gt;

&lt;h3&gt;
  
  
  C. Move the JRE into your bundling folder
&lt;/h3&gt;

&lt;p&gt;Assuming you have the same file structure from Method 2, you only need to move the unzipped JRE into your folder. The final file structure should look like this.&lt;/p&gt;

&lt;p&gt;Example bundling folder structure&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;your-bundling-directory/
|  jre/ # &amp;lt;------------------------ moved here
|  YourOwnJAR.jar
|  javafx-sdk-20.0.1/
|  |  lib/
|  |  |  ... lots of JARs   
|  
|  run.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  D. Edit the shell script
&lt;/h3&gt;

&lt;p&gt;After being bundled (in later steps), the shell script cannot be directly run by the user clicking on it. As such, we need to make the script more independent.&lt;/p&gt;

&lt;p&gt;One dependence that the script from Method 2 has is the assumption that the user has the &lt;code&gt;java&lt;/code&gt; command installed on their machine. This time, we don't make this assumption and use the &lt;code&gt;java&lt;/code&gt; from the Java Runtime copied to &lt;code&gt;your-bundling-directory/jre/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Shell scripts like Bash are also dependent on where the user calls them from. After being bundled, the user no longer calls the &lt;code&gt;run.sh&lt;/code&gt; script directly. But it is still called by the bundling tool's runtime service when the user clicks on the executable. Therefore, we need to make sure the paths inside the script are all relative to the script's location.&lt;/p&gt;

&lt;p&gt;In Bash, there is a special variable called &lt;code&gt;BASH_SOURCE&lt;/code&gt; which contains the path to the directory where the Bash script is located. We will use this as a way to have relative paths.&lt;/p&gt;

&lt;p&gt;Declare a variable in your &lt;code&gt;run.sh&lt;/code&gt; called &lt;code&gt;HERE&lt;/code&gt;. Pay attention to the spacing around the assignment operator &lt;code&gt;=&lt;/code&gt;. It matters in Bash!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;HERE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BASH_SOURCE&lt;/span&gt;&lt;span class="p"&gt;%/*&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above assignment, we make use of another Bash feature, &lt;a href="https://www.linux.com/topic/desktop/all-about-curly-braces-bash/" rel="noopener noreferrer"&gt;string manipulation&lt;/a&gt;. This is done with the curly braces. In our case, we use the percent symbol &lt;code&gt;%&lt;/code&gt; to truncate the &lt;code&gt;/&lt;/code&gt; symbol and everything after inside the variable. &lt;code&gt;*&lt;/code&gt; is the wild card operator from regular expressions. Since &lt;code&gt;BASH_SOURCE&lt;/code&gt; contains a path to a directory, it ends with a slash on Linux and we don't want that.&lt;/p&gt;

&lt;p&gt;Modify the &lt;code&gt;java&lt;/code&gt; command of your &lt;code&gt;run.sh&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HERE&lt;/span&gt;&lt;span class="s2"&gt;/jre/bin/java"&lt;/span&gt; &lt;span class="nt"&gt;--module-path&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HERE&lt;/span&gt;&lt;span class="s2"&gt;/javafx-sdk-20.0.1/lib"&lt;/span&gt; &lt;span class="nt"&gt;--add-modules&lt;/span&gt; javafx.controls,javafx.fxml,javafx.graphics,javafx.media &lt;span class="nt"&gt;-jar&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HERE&lt;/span&gt;&lt;span class="s2"&gt;/YourOwnJAR.jar"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, all the paths starts with the &lt;code&gt;$HERE&lt;/code&gt; variable, which means it is all relative to the location of the &lt;code&gt;run.sh&lt;/code&gt; script. In Bash, we reference variable using the &lt;code&gt;$&lt;/code&gt; symbol followed by the name of the variable. Notice here we put all the paths containing &lt;code&gt;$HERE&lt;/code&gt; in double quotes. That is to take care of potential spaces inside path names, such as "Program Files".&lt;/p&gt;

&lt;p&gt;You may also notice the last argument is &lt;code&gt;"$@"&lt;/code&gt;. This is also one of the default variables in Bash. &lt;code&gt;$@&lt;/code&gt; is a special variable that represents all the parameters the &lt;code&gt;run.sh&lt;/code&gt; script has received through the command line. If your Java application takes command line arguments, this is absolutely necessary. Otherwise, it doesn't hurt to have it. This special variable is also enclosed in double quotes, because some arguments may contain white space themselves and that can cause argument splitting issues.&lt;/p&gt;

&lt;p&gt;In summary, this is what a &lt;code&gt;run.sh&lt;/code&gt; can look like for independent bundling.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nv"&gt;HERE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BASH_SOURCE&lt;/span&gt;&lt;span class="p"&gt;%/*&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;

&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HERE&lt;/span&gt;&lt;span class="s2"&gt;/jre/bin/java"&lt;/span&gt; &lt;span class="nt"&gt;--module-path&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HERE&lt;/span&gt;&lt;span class="s2"&gt;/javafx-sdk-20.0.1/lib"&lt;/span&gt; &lt;span class="nt"&gt;--add-modules&lt;/span&gt; javafx.controls,javafx.fxml,javafx.graphics,javafx.media &lt;span class="nt"&gt;-jar&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HERE&lt;/span&gt;&lt;span class="s2"&gt;/YourOwnJAR.jar"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Don't forget to test your shell script. Assuming you are outside the bundling directory, you can run the script through the command line.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./bundle/run.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  E. Use &lt;code&gt;warp-packer&lt;/code&gt; to produce a binary
&lt;/h3&gt;

&lt;p&gt;Download the &lt;a href="https://github.com/dgiagio/warp" rel="noopener noreferrer"&gt;&lt;code&gt;warp-packer&lt;/code&gt;&lt;/a&gt; tool to your system.&lt;/p&gt;

&lt;p&gt;If you put the tool next to your bundling directory, then you can run the following commands.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./warp-packer &lt;span class="nt"&gt;--arch&lt;/span&gt; linux-x64 &lt;span class="nt"&gt;--input_dir&lt;/span&gt; your-bundling-directory &lt;span class="nt"&gt;--exec&lt;/span&gt; run.sh &lt;span class="nt"&gt;--output&lt;/span&gt; app.bin
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x app.bin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the first command,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;--arch&lt;/code&gt;: the architecture you are deploying to. &lt;code&gt;linux-x64&lt;/code&gt; is the Linux operating system with &lt;code&gt;x64&lt;/code&gt; hardware.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--input_dir&lt;/code&gt;: the bundling directory you just prepared all this time&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--exec&lt;/code&gt;: the executable shell script that runs when the binary is clicked&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--output&lt;/code&gt;: the name of the produced binary with an extension of your choosing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The second command grants permission to run the binary.&lt;/p&gt;

&lt;p&gt;Test your binary.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./app.bin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the JavaFX example, the binary size is about 108M for one of my students' projects. This is quite expected if you have resources like images in your Java project. Even after &lt;code&gt;warp-packer&lt;/code&gt; compresses your files, most of the space is still taken by the Java Runtime Environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Competing tools for bundling Java
&lt;/h3&gt;

&lt;p&gt;In a previous version of this article, I recommended &lt;code&gt;jlink&lt;/code&gt; and &lt;code&gt;jpackage&lt;/code&gt; for bundling Java into a native application. For simple applications, that can still work. However, these default tools in Java are no longer as reliable and as flexible as before. You can still find tutorials on them, but they only teach you how to bundle "Hello World"-level apps with little dependencies. This article dives deeper into software deployment with problematic Java dependencies such as JavaFX. As of the update of this article, I still couldn't get the default JDK tools to work with JavaFX.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this blog, I explained 3 ways of deploying your Java project. Each method depends on your target user base. If they are okay with having some installed software on their machine. Delivering a JAR or a group of JARs + script is an acceptable solution. However, if you cannot expect your user to have any software installed, you should bundle everything into a standalone executable.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>java</category>
      <category>devops</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
