<?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: Tarlan Isaev 🍓</title>
    <description>The latest articles on DEV Community by Tarlan Isaev 🍓 (@organicnz).</description>
    <link>https://dev.to/organicnz</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%2F485668%2F0d7d04d9-3d09-4b1f-85e9-fb71a9b1f664.jpeg</url>
      <title>DEV Community: Tarlan Isaev 🍓</title>
      <link>https://dev.to/organicnz</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/organicnz"/>
    <language>en</language>
    <item>
      <title>Cross-posting automation across multiple social media platforms with Make.com and Airtable</title>
      <dc:creator>Tarlan Isaev 🍓</dc:creator>
      <pubDate>Wed, 08 Mar 2023 08:29:42 +0000</pubDate>
      <link>https://dev.to/organicnz/cross-posting-automation-across-multiple-social-media-platforms-with-makecom-and-airtable-1if3</link>
      <guid>https://dev.to/organicnz/cross-posting-automation-across-multiple-social-media-platforms-with-makecom-and-airtable-1if3</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--snGY8sXh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/48v2vo2704rznjn8gzio.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--snGY8sXh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/48v2vo2704rznjn8gzio.png" alt="Image description" width="880" height="479"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Social media has become an essential part of our lives, and it’s no longer just a tool for personal communication. It has become an integral part of businesses’ marketing strategies, and for good reason. Social media platforms provide a powerful way to reach a vast audience, increase brand awareness, and engage with customers.&lt;/p&gt;

&lt;p&gt;However, managing multiple social media accounts can be a time-consuming and challenging task. Each platform has its own requirements and best practices for posting content, and keeping up with all of them can be overwhelming. That’s where scenario blueprints come in.&lt;/p&gt;

&lt;p&gt;Scenario blueprints allow you to automate the process of cross-posting content to multiple social media platforms. With just a few clicks, you can schedule posts in advance and have them automatically posted to all of your social media accounts at once, without having to manually post to each platform separately.&lt;/p&gt;

&lt;p&gt;This can save a significant amount of time and streamline your social media management. Rather than having to log in to each account separately and post the same content individually, you can manage all of your social media accounts in one place and post to multiple platforms simultaneously.&lt;/p&gt;

&lt;p&gt;Furthermore, using scenario blueprints ensures consistency in your social media posts. Consistency is essential in social media management, and cross-posting content to multiple platforms using a single tool ensures that your content is consistently posted across all platforms. This can help to increase engagement and reach across all of your social media accounts.&lt;/p&gt;

&lt;p&gt;Customization is another benefit of using scenario blueprints. With Make.com’s scenario editor, you can customize your cross-posting settings for each social media platform. This allows you to tailor your posts to each platform’s specific requirements and audience, while still saving time by using a single tool to manage all of your accounts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tJcZFen7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9e9p0oo4j5vjnwzv3g64.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tJcZFen7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9e9p0oo4j5vjnwzv3g64.png" alt="Image description" width="880" height="545"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Make.com is a powerful automation tool that can help streamline your workflow and manage multiple social media accounts efficiently. One of the key features of Make.com is its ability to get data from Airtable, which is a cloud-based database tool. By combining the power of Make.com with Airtable, you can automate workflows based on data stored in your Airtable database.&lt;/p&gt;

&lt;p&gt;To import a scenario blueprint and set up cross-posting across various social media platforms, follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Log in to your Make.com account and click on the “Scenarios” tab at the top of the page.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8M6BXswM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qtall9cou609twfsh3os.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8M6BXswM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qtall9cou609twfsh3os.png" alt="Image description" width="880" height="482"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click on the “Import” button in the bottom middle part of the screen. Also, we can export our blueprint in json format. Don’t forget to press on and set the scheduling time.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HhHo0UBc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qov0snxx6wiwcjyiu9zt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HhHo0UBc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qov0snxx6wiwcjyiu9zt.png" alt="Image description" width="880" height="481"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Select the scenario blueprint file that you want to import from this link on Google Drive.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Once the file is imported, click on the scenario name to open it in the scenario editor.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add the “Social Media” action to your scenario by clicking on it in the sidebar.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click on the “Configure” button next to the “Social Media” action to open the settings window.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Select the social media platforms that you want to cross-post to and enter your login credentials for each platform.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Customize the message and other settings as needed for each social media platform on Airtable.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9vKGtadT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fl2vxu3cr7bk2dimmk35.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9vKGtadT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fl2vxu3cr7bk2dimmk35.png" alt="Image description" width="880" height="545"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Select channels several channels where you’d like to share your post and make the status Approved.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bHIIlhnK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t94gz7m81vg093fd0zpa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bHIIlhnK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t94gz7m81vg093fd0zpa.png" alt="Image description" width="880" height="541"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Now select the Status to Approved and move to Approved for Publishing table.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5fJ1mLlS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u1w3rvwnhzybpuejo5s4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5fJ1mLlS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u1w3rvwnhzybpuejo5s4.png" alt="Image description" width="880" height="541"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Save your changes and run the scenario to start cross-posting to your social media accounts.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By following these steps, you can set up Make.com with Airtable and import a scenario blueprint to automate cross-posting across various social media platforms. This can save you time and ensure consistency in your social media posts, ultimately increasing engagement and reach across all of your social media accounts.&lt;/p&gt;

&lt;p&gt;That’s it! With these steps, you can easily import a scenario blueprint to Make.com and set up cross-posting via several social media platforms to increase your online presence and reach&lt;/p&gt;

&lt;p&gt;To summarize, scenario blueprints provide an excellent solution for managing multiple social media accounts efficiently. By automating the process of cross-posting content to multiple platforms, you can save time, ensure consistency, and increase engagement across all of your social media accounts. With the power of Make.com’s scenario editor and Airtable, you can customize your posts to each platform’s specific needs while streamlining your social media management.&lt;/p&gt;

&lt;p&gt;Resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;a href="https://www.youtube.com/@BusinessAutomatedTutorials"&gt;Youtube channel&lt;/a&gt; Business Automated Tutorials helped me understand all the settings&lt;/li&gt;
&lt;li&gt;Airtable of &lt;a href="https://airtable.com/invite/l?inviteId=invc7wrpAsjcY0N3z&amp;amp;inviteToken=ba151e9f7bbe789a4fb7f7e3cac024d0bc14461670f4e2bb674a2ad6c0af52d6&amp;amp;utm_medium=email&amp;amp;utm_source=product_team&amp;amp;utm_content=transactional-alerts"&gt;Foodshare Social Media Calendar&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://drive.google.com/file/d/1rtLrpuZvw1RL8yRJfi0lScXw69qtG8jF/view?usp=share_link"&gt;Blueprint&lt;/a&gt; to download&lt;/li&gt;
&lt;li&gt;ChatGPT&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Please let me know what you think. Thank you :)&lt;/p&gt;

&lt;h1&gt;
  
  
  make #airtable #smm #socialmedia #twitter #instagram #facebook #linkedin #telegram
&lt;/h1&gt;

</description>
    </item>
    <item>
      <title>Supabase: count and discount raws from the likes table</title>
      <dc:creator>Tarlan Isaev 🍓</dc:creator>
      <pubDate>Sun, 19 Feb 2023 14:40:18 +0000</pubDate>
      <link>https://dev.to/organicnz/count-and-discount-raws-from-the-likes-table-36jj</link>
      <guid>https://dev.to/organicnz/count-and-discount-raws-from-the-likes-table-36jj</guid>
      <description>&lt;p&gt;Hi guys. Here's how I solved the count or discount problem of raws from the &lt;code&gt;likes&lt;/code&gt; table with a unique &lt;code&gt;post_id&lt;/code&gt; that matches a unique &lt;code&gt;profile_id&lt;/code&gt; column.&lt;/p&gt;

&lt;p&gt;This function is designed to be called as a trigger function, and it takes no parameters. Instead, it uses the &lt;code&gt;NEW&lt;/code&gt; row variable to get the &lt;code&gt;post_id&lt;/code&gt; value of the row that was just inserted or updated in the &lt;code&gt;likes&lt;/code&gt; table. It then updates the &lt;code&gt;post_like_counter&lt;/code&gt; column in the &lt;code&gt;posts&lt;/code&gt; table using the &lt;code&gt;post_id&lt;/code&gt; value from the &lt;code&gt;NEW&lt;/code&gt; row.&lt;/p&gt;

&lt;p&gt;Assuming the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  The &lt;code&gt;likes&lt;/code&gt; table has columns &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;profile_id&lt;/code&gt;, and &lt;code&gt;post_id&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  The &lt;code&gt;posts&lt;/code&gt; table has columns &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;post_like_counter&lt;/code&gt;, and &lt;code&gt;profile_id&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  The &lt;code&gt;profiles&lt;/code&gt; table has columns &lt;code&gt;id&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  Each profile can only like a post once&lt;/li&gt;
&lt;li&gt;  You want to update the &lt;code&gt;post_like_counter&lt;/code&gt; column in the &lt;code&gt;posts&lt;/code&gt; table with the count of likes for each post, but only for likes made by profiles that exist in the &lt;code&gt;profiles&lt;/code&gt; table&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, the trigger function returns the &lt;code&gt;NEW&lt;/code&gt; row to indicate that the trigger executed successfully. You can use this function as a trigger function to automatically update the &lt;code&gt;post_like_counter&lt;/code&gt; column whenever a row is inserted or updated in the &lt;code&gt;likes&lt;/code&gt; table.&lt;/p&gt;

&lt;p&gt;You can use the following PostgreSQL query to achieve that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-- Create function counter that increments post likes
CREATE OR REPLACE FUNCTION get_count()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
BEGIN
UPDATE posts
SET post_like_counter = (
SELECT count(*)
FROM (
SELECT DISTINCT ON (likes.profile_id) likes.post_id
FROM likes
WHERE likes.post_id = NEW.post_id
AND likes.profile_id IN (SELECT id FROM profiles)
) AS unique_likes
)
WHERE id = NEW.post_id;

RETURN NEW;
END;
$$;

-- Trigger the function every time a user is added/liked a post in the likes table
CREATE trigger on_post_user_liked
after insert on likes
for each row execute procedure public.get_count();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fzlffpz6er9pjv3g85euw.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%2Fzlffpz6er9pjv3g85euw.png" alt=" " width="800" height="437"&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%2F62ra1hujm5ue0n8gx0jz.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%2F62ra1hujm5ue0n8gx0jz.png" alt=" " width="800" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To create a function that decrements the &lt;code&gt;post_like_counter&lt;/code&gt; counter, you can modify the existing function to subtract 1 from the &lt;code&gt;post_like_counter&lt;/code&gt; instead of counting the likes. &lt;/p&gt;

&lt;p&gt;Here's the function named &lt;code&gt;get_uncount&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-- Create function counter that decrements the value in the 'post_like_counter' column in the 'posts' table
CREATE OR REPLACE FUNCTION decrement_count()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
BEGIN
UPDATE posts
SET post_like_counter = post_like_counter - 1
WHERE id = OLD.post_id;

RETURN OLD;
END;
$$;

-- Trigger the function every time a user unliked/ delited post in the `likes` table
CREATE TRIGGER on_post_user_unliked
AFTER DELETE ON likes
FOR EACH ROW EXECUTE PROCEDURE public.decrement_count();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2F0zy83vr1japfgbw2ufkw.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%2F0zy83vr1japfgbw2ufkw.png" alt=" " width="800" height="438"&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%2F7myv0lm1f9j4s24sndko.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%2F7myv0lm1f9j4s24sndko.png" alt=" " width="800" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Please let me know if there is a more elegant way to count the likes. Thanks :)&lt;/p&gt;

</description>
      <category>codenewbie</category>
      <category>frontend</category>
      <category>discuss</category>
      <category>career</category>
    </item>
    <item>
      <title>Adapted Firestore data migration to Supabase</title>
      <dc:creator>Tarlan Isaev 🍓</dc:creator>
      <pubDate>Wed, 25 Jan 2023 13:30:27 +0000</pubDate>
      <link>https://dev.to/organicnz/adapted-firestore-data-migration-to-supabase-4ge8</link>
      <guid>https://dev.to/organicnz/adapted-firestore-data-migration-to-supabase-4ge8</guid>
      <description>&lt;p&gt;As I continue my journey with data migration with my awesome dev team, I am adapting another tutorial and providing some SQL snippets for reference. I have previously mentioned that this task is quite difficult for me, but I am finding it to be an enjoyable learning experience. I am discovering new and useful knowledge, and I hope that it is not too intimidating for those who are also learning. Despite the challenges, I am eager to keep pushing forward and making progress. Data migration can be a complex process, but with dedication and perseverance, it is possible to master. I will keep sharing my experiences and insights along the way, in the hopes that it will be helpful for others who are also working on data migration projects :)&lt;/p&gt;




&lt;p&gt;Please note that before updating the column, make sure to take the backup of the table or if you are using it in production take care of the down time as well.&lt;/p&gt;

&lt;p&gt;Supabase provides several tools to convert data from a Firebase Firestore database to a Supabase PostgreSQL database. The process copies the entire contents of a single Firestore collection to a single PostgreSQL table.&lt;/p&gt;

&lt;p&gt;The Firestore collection is "flattened" and converted to a table with basic columns of one of the following types: text, numeric, boolean, or jsonb. If your structure is more complex, you can write a program to split the newly-created json file into multiple, related tables before you import your json file(s) to Supabase.&lt;/p&gt;

&lt;p&gt;Set up the migration tool &lt;br&gt;
Clone the firebase-to-supabase repository:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git clone https://github.com/supabase-community/firebase-to-supabase.git&lt;br&gt;
&lt;/code&gt;&lt;br&gt;
In the /firestore directory, create a file named supabase-service.json with the following contents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "host": "database.server.com",
  "password": "secretpassword",
  "user": "postgres",
  "database": "postgres",
  "port": 5432
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Go to the Database settings for your project in the Supabase Dashboard.&lt;br&gt;
Under Connection Info, copy the Host string and replace the entry in your supabase-service.json file.&lt;br&gt;
Enter the password you used when you created your Supabase project in the password entry in the supabase-service.json file.&lt;/p&gt;

&lt;p&gt;Generate a Firebase private key [#generate-firebase-private-key]&lt;br&gt;
Log in to your Firebase Console and open your project.&lt;br&gt;
Click the gear icon next to Project Overview in the sidebar and select Project Settings.&lt;br&gt;
Click Service Accounts and select Firebase Admin SDK.&lt;br&gt;
Click Generate new private key.&lt;br&gt;
Rename the downloaded file to firebase-service.json.&lt;br&gt;
Command line options&lt;br&gt;
List all Firestore collections&lt;br&gt;
node collections.js&lt;/p&gt;

&lt;p&gt;Dump Firestore collection to JSON file&lt;/p&gt;

&lt;p&gt;&lt;code&gt;node firestore2json.js &amp;lt;collectionName&amp;gt; [&amp;lt;batchSize&amp;gt;] [&amp;lt;limit&amp;gt;]&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;batchSize (optional) defaults to 1000&lt;/li&gt;
&lt;li&gt;output filename is .json&lt;/li&gt;
&lt;li&gt;limit (optional) defaults to 0 (no limit)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In my case, I dumped Users collection from Firestore:&lt;br&gt;
&lt;code&gt;node firestore2json.js Users 1000 0&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Customize the JSON file with hooks&lt;/p&gt;

&lt;p&gt;I've done this step on VS Code. You can customize the way your JSON file is written using a custom hook. A common use for this is to "flatten" the JSON file, or to split nested data into separate, related database tables. For example, you could take a Firestore document that looks like this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;[{ "user": "mark", "score": 100, "items": ["hammer", "nail", "glue"] }]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;And split it into two files (one table for users and one table for items):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[{ "user": "mark", "score": 100 }]

[
  { "user": "mark", "item": "hammer" },
  { "user": "mark", "item": "nail" },
  { "user": "mark", "item": "glue" }
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Import JSON file to Supabase (PostgreSQL) [#import-to-supabase]#&lt;br&gt;
&lt;code&gt;node json2supabase.js &amp;lt;path_to_json_file&amp;gt; [&amp;lt;primary_key_strategy&amp;gt;] [&amp;lt;primary_key_name&amp;gt;]&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; The full path of the file you created in the previous step (Dump Firestore collection to JSON file ), such as ./my_collection.json&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.tooptional"&gt;&lt;/a&gt; Is one of:&lt;/li&gt;
&lt;li&gt;none (default) No primary key is added to the table.&lt;/li&gt;
&lt;li&gt;smallserial Creates a key using (id SMALLSERIAL PRIMARY KEY) (autoincrementing 2-byte integer).&lt;/li&gt;
&lt;li&gt;serial Creates a key using (id SERIAL PRIMARY KEY) (autoincrementing 4-byte integer).&lt;/li&gt;
&lt;li&gt;bigserial Creates a key using (id BIGSERIAL PRIMARY KEY) (autoincrementing 8-byte integer).&lt;/li&gt;
&lt;li&gt;uuid Creates a key using (id UUID PRIMARY KEY DEFAULT uuid_generate_v4()) (randomly generated UUID).&lt;/li&gt;
&lt;li&gt;firestore_id Creates a key using (id TEXT PRIMARY KEY) (uses existing firestore_id random text as key).&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.tooptional"&gt;&lt;/a&gt; Name of primary key. Defaults to "id".&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I imported using this command:&lt;br&gt;
&lt;code&gt;node json2supabase.js ./Users.json uuid id 1000 0&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If you get duplicates in your table, you can use the following SQL snippets, such as this one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DELETE FROM users_duplicate s
USING
(SELECT firestore_id, 
max (ctid) as max_ctid
FROM users_duplicate
GROUP by firestore_id, email) t
WHERE s.ctid &amp;lt;&amp;gt; t.max_ctid 
AND s.firestore_id=t.firestore_id;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that I renamed the 'users' table with a low case and created a duplicate 'users_duplicate' table in case I screw something up and need to return it to its original state. This way, we can copy an exact copy of any table.﻿&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%2Fujacknkjaij55l2bdiy3.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%2Fujacknkjaij55l2bdiy3.png" alt="Image description" width="800" height="423"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;﻿When you're done with migration you can add a foreigh key relation to 'user' column and matching by email from 'profiles' or 'users' table.&lt;br&gt;
﻿&lt;br&gt;
﻿﻿&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%2Fz535uv33tqldcmgu8tag.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%2Fz535uv33tqldcmgu8tag.png" alt="Image description" width="800" height="423"&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%2Fyvppqptks1qkef0wzrsq.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%2Fyvppqptks1qkef0wzrsq.png" alt="Image description" width="800" height="424"&gt;&lt;/a&gt;&lt;br&gt;
﻿&lt;br&gt;
Supabase allows you to write values as NULL and in the beginning I enabled the Allow Nullable option, after filling all users in the 'profile' table I disabled it right on the fly - very handy option :)&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%2Fhf41gvf57kzgltthylmn.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%2Fhf41gvf57kzgltthylmn.png" alt="Image description" width="800" height="423"&gt;&lt;/a&gt; ﻿&lt;br&gt;
﻿&lt;br&gt;
&lt;strong&gt;Custom hooks&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Hooks are used to customize the process of exporting a collection of Firestore documents to JSON. They can be used for:&lt;/p&gt;

&lt;p&gt;Customizing or modifying keys&lt;br&gt;
Calculating data&lt;br&gt;
Flattening nested documents into related SQL tables&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Write a custom hook&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Create a .js file for your collection&lt;/p&gt;

&lt;p&gt;If your Firestore collection is called users, create a file called users.js in the current folder.&lt;/p&gt;

&lt;p&gt;Construct your .js file&lt;/p&gt;

&lt;p&gt;The basic format of a hook file looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module.exports = (collectionName, doc, recordCounters, writeRecord) =&amp;gt; {
  // modify the doc here
  return doc
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Parameters&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;collectionName: The name of the collection you are processing.&lt;/li&gt;
&lt;li&gt;doc: The current document (JSON object) being processed.&lt;/li&gt;
&lt;li&gt;recordCounters: An internal object that keeps track of how many records have been processed in each collection.&lt;/li&gt;
&lt;li&gt;writeRecord: This function automatically handles the process of writing data to other JSON files (useful for "flatting" your document into separate JSON files to be written to separate database tables). writeRecord takes the following parameters:&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;name: Name of the JSON file to write to.&lt;/li&gt;
&lt;li&gt;doc: The document to write to the file.&lt;/li&gt;
&lt;li&gt;recordCounters: The same recordCounters object that was passed to this hook (just passes it on).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Examples&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Add a new (unique) numeric key to a collection&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module.exports = (collectionName, doc, recordCounters, writeRecord) =&amp;gt; {
  doc.unique_key = recordCounter[collectionName] + 1
  return doc
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Add a timestamp of when this record was dumped from Firestore
module.exports = (collectionName, doc, recordCounters, writeRecord) =&amp;gt; {
  doc.dump_time = new Date().toISOString()
  return doc
}

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

&lt;/div&gt;



&lt;p&gt;Flatten JSON into separate files&lt;/p&gt;

&lt;p&gt;Flatten the users collection into separate files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[
  {
    "uid": "abc123",
    "name": "mark",
    "score": 100,
    "weapons": ["toothpick", "needle", "rock"]
  },
  {
    "uid": "xyz789",
    "name": "chuck",
    "score": 9999999,
    "weapons": ["hand", "foot", "head"]
  }
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The users.js hook file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module.exports = (collectionName, doc, recordCounters, writeRecord) =&amp;gt; {
  for (let i = 0; i &amp;lt; doc.weapons.length; i++) {
    const weapon = {
      uid: doc.uid,
      weapon: doc.weapons[i],
    }
    writeRecord('weapons', weapon, recordCounters)
  }
  delete doc.weapons // moved to separate file
  return doc
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result is two separate JSON files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[
  { "uid": "abc123", "name": "mark", "score": 100 },
  { "uid": "xyz789", "name": "chuck", "score": 9999999 }
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[
  { "uid": "abc123", "weapon": "toothpick" },
  { "uid": "abc123", "weapon": "needle" },
  { "uid": "abc123", "weapon": "rock" },
  { "uid": "xyz789", "weapon": "hand" },
  { "uid": "xyz789", "weapon": "foot" },
  { "uid": "xyz789", "weapon": "head" }
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here are the SQL snippets in the Supabase interface. You can combine them into multiple queries for the same table in one snippet. &lt;br&gt;
﻿&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%2Firr714n2em87cammh1fb.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%2Firr714n2em87cammh1fb.png" alt="Image description" width="800" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In case you want to delete duplicates based on values of multiple columns, here is the query template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DELETE FROM users_duplicate
WHERE id IN
    (SELECT firestore_id
    FROM 
        (SELECT firestore_id,
         ROW_NUMBER() OVER( PARTITION BY firestore_id,
         email
        ORDER BY firestore_id ) AS row_num
        FROM users_duplicate ) t
        WHERE t.row_num &amp;gt; 1 );
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Find duplicates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT firestore_id, 
COUNT(1) 
FROM users_duplicate 
GROUP by firestore_id

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

&lt;/div&gt;



&lt;p&gt;Identify each record in a table with PostgresQL inbuilt column ctid:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT firestore_id, 
ctid
FROM users_duplicate; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;List down required i.e. distinct rows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT firestore_id, 
max (ctid) as max_ctid
FROM users_duplicate
GROUP by firestore_id;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can delete duplicate records with below query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DELETE FROM users_duplicate s
USING
(SELECT firestore_id, 
max (ctid) as max_ctid
FROM users_duplicate
GROUP by firestore_id, email) t
WHERE s.ctid &amp;lt;&amp;gt; t.max_ctid 
AND s.firestore_id=t.firestore_id;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Get column names:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT column_name, data_type
FROM information_schema.columns
WHERE table_name = 'users_duplicate';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update from one table to another based on a id match:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UPDATE profiles
SET email = (
SELECT
email
FROM
auth.users
WHERE
profiles.id = auth.users.id
-- AND auth.users.id = 8
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update first_name in profiles table from another table users_duplicate matching by email&lt;br&gt;
UPDATE profiles&lt;br&gt;
SET first_name = users_duplicate.first_name&lt;br&gt;
FROM users_duplicate&lt;br&gt;
WHERE profiles.email = users_duplicate.email;&lt;br&gt;
);&lt;/p&gt;

&lt;p&gt;Update rows in several columns at profiles table from another table users_duplicate matching by email:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UPDATE profiles
SET
first_name = users_duplicate.first_name,
second_name = users_duplicate.second_name,
username = users_duplicate.username,
avatar_url = users_duplicate.avatar_url,
phone_number = users_duplicate.phone_number,
user_metro_station = users_duplicate.user_metro_station,
birth_date = users_duplicate.birth_date,
user_location = users_duplicate.user_location,
liked_post = users_duplicate.liked_post,
about_me = users_duplicate.about_me,
user_address = users_duplicate.user_address,
created_time = users_duplicate.created_time,
firestore_id = users_duplicate.firestore_id
FROM users_duplicate
WHERE profiles.email = users_duplicate.email;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update at admin table updated_at column with null value to the curront stams:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UPDATE admin
SET updated_at = (now() AT TIME ZONE 'utc'::timestamptz)
WHERE updated_at IS NULL;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update at posts from null to empty string:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UPDATE posts
SET post_views = '0'::numeric
WHERE post_views IS NULL;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update null value in column five_star to false boolean at post table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UPDATE posts
SET five_star = 'false'::boolean
WHERE five_star IS NULL;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update column gif_url2 at posts table from another gif_url column:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gif_url table if value is null
UPDATE posts
SET gif_url_2 = gif_url
WHERE gif_url_2 IS NULL;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This query will delete all rows in the "chat_messages" table where the "text" column is not unique, by keeping only the row with the minimum "id" for each group of duplicate rows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DELETE FROM chat_messages
WHERE text IN (
SELECT text
FROM chat_messages
GROUP BY text
HAVING COUNT(*) &amp;gt; 1
) AND id NOT IN (
SELECT MIN(id)
FROM chat_messages
WHERE text IN (
SELECT text
FROM chat_messages
GROUP BY text
HAVING COUNT(*) &amp;gt; 1
)
GROUP BY text
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Delete all rows in the "chat_messages" table where the "text" column is not unique, by keeping only the row with the minimum "id" for each group of duplicate rows where value of the text column is null:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DELETE FROM chat_messages
WHERE text IS NULL AND id NOT IN (
SELECT MIN(id)
FROM chat_messages
WHERE text IS NULL
GROUP BY text
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Working with timestamptz data format&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To update the "last_message_timee" column in the "chats" table with the value of the "_seconds" key from the "last_message_time" column, converted to a timestamptz value, you can use the following query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UPDATE chats SET last_message_timee = to_timestamp(jsonb_extract_path_text(last_message_time, '_seconds')::bigint) AT TIME ZONE 'UTC'

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

&lt;/div&gt;



&lt;p&gt;This query uses the jsonb_extract_path_text() function to extract the value of the "_seconds" key from the "last_message_time" column, and then uses the to_timestamp() function to convert the seconds to a timestamptz value, and the AT TIME ZONE 'UTC' is used to set the time zone to UTC, and update the last_message_timee column with the result.&lt;br&gt;
﻿&lt;br&gt;
﻿﻿&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%2F7v5xk7lcidtz89970i11.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%2F7v5xk7lcidtz89970i11.png" alt="Image description" width="800" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we can delete 'last_massage_time' and rename another 'last_massage_timee' column with converted data to 'last_massage_time'.&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%2Fxgiqxpw8s4bhb6qank6b.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%2Fxgiqxpw8s4bhb6qank6b.png" alt="Image description" width="800" height="424"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To convert jsonb to timestamptz type of data manualy we can use this awesome resource:
&lt;a href="https://www.unixtimestamp.com/index.php" rel="noopener noreferrer"&gt;unixtimestamp.com
&lt;/a&gt;
For example we have &lt;code&gt;last_message_time&lt;/code&gt; column with jsonb data type:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "_seconds": 1655911605,
  "_nanoseconds": 442000000
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;I don't need nanoseconds and just simply converting seconds &lt;code&gt;1655911605&lt;/code&gt; to timestamptz:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Format  Seconds
GMT Wed Jun 22 2022 15:26:45 GMT+0000
Your Time Zone  Wed Jun 22 2022 18:26:45 GMT+0300 (Moscow Standard Time)
Relative    7 months ago
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fok1el61rzfda2elunpv0.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%2Fok1el61rzfda2elunpv0.png" alt="Image description" width="800" height="537"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In conclusion, data migration can be a daunting task, but it is also an exciting opportunity to learn and grow as a data professional. I find that by approaching the process piece by piece and utilizing helpful tutorials and SQL snippets, I'm able to make steady progress. I'm reminded that even though there may be challenges and obstacles along the way, the end result is worth the effort. So grateful for the opportunity to take on this project and I'm looking forward to continuing this exciting journey. I encourage others who may be facing similar challenges to not be discouraged and to keep pushing forward, as the rewards of a successfully completed data migration project on Supabase are well worth the effort.&lt;/p&gt;

&lt;p&gt;Thank you, guys :) &lt;/p&gt;

</description>
      <category>discuss</category>
      <category>community</category>
      <category>networking</category>
    </item>
    <item>
      <title>Adapted Firebase Auth Migration for the FlutterFlow project</title>
      <dc:creator>Tarlan Isaev 🍓</dc:creator>
      <pubDate>Mon, 02 Jan 2023 20:01:08 +0000</pubDate>
      <link>https://dev.to/organicnz/adapted-firebase-auth-migration-6ap</link>
      <guid>https://dev.to/organicnz/adapted-firebase-auth-migration-6ap</guid>
      <description>&lt;p&gt;Huge thanks to the FlutterFlow team for providing this great feature. Supabase promises very bright opportunities and a future for developers as an open-source project. I know it's going to be difficult, but I think now is a good time to start migrating the whole Foodshare Firebase to Supabase piece by piece :)&lt;/p&gt;

&lt;p&gt;Supabase provides several tools to help migrate auth users from a Firebase project to a Supabase project. There are two parts to the migration process:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;firestoreusers2json (TypeScript, JavaScript) exports users from an existing Firebase project to a .json file on your local system.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;import_users (TypeScript, JavaScript) imports users from a saved .json file into your Supabase project (inserting those users into the auth.users table of your PostgreSQL database instance).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Set up the migration tool&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before we start we need to Google auth in terminal:&lt;br&gt;
&lt;code&gt;gcloud auth login&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Set your project:&lt;br&gt;
&lt;code&gt;gcloud config set project &amp;lt;your-project&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Install npm moment module to avoid an error with it:&lt;br&gt;
&lt;code&gt;npm i moment&lt;/code&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Clone the firebase-to-supabase repository:
&lt;code&gt;git clone https://github.com/supabase-community/firebase-to-supabase.git&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;CD to /auth directory and in the /auth directory, create a file named supabase-service.json with the following contents:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;{&lt;br&gt;
  "host": "database.server.com",&lt;br&gt;
  "password": "secretpassword",&lt;br&gt;
  "user": "postgres",&lt;br&gt;
  "database": "postgres",&lt;br&gt;
  "port": 5432&lt;br&gt;
}&lt;/code&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Go to the Database settings for your project in the Supabase Dashboard.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Under Connection Info, copy the Host string and replace the entry in your supabase-service.json file.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enter the password you used when you created your Supabase project in the password entry in the supabase-service.json file.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Generate a Firebase private key&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Log in to your Firebase Console and open your project.&lt;/li&gt;
&lt;li&gt;Click the gear icon next to Project Overview in the sidebar and select Project Settings.&lt;/li&gt;
&lt;li&gt;Click Service Accounts and select Firebase Admin SDK.&lt;/li&gt;
&lt;li&gt;Click Generate new private key.&lt;/li&gt;
&lt;li&gt;Rename the downloaded file to firebase-service.json.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Save your Firebase password hash parameters&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Log in to your Firebase Console and open your project.&lt;/li&gt;
&lt;li&gt;Select Authentication (Build section) in the sidebar.&lt;/li&gt;
&lt;li&gt;Select Users in the top menu.&lt;/li&gt;
&lt;li&gt;At the top right of the users list, open the menu (3 dots) and click Password hash parameters.&lt;/li&gt;
&lt;li&gt;Copy and save the parameters for base64_signer_key, base64_salt_separator, rounds, and mem_cost.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;hash_config {&lt;br&gt;
  algorithm: SCRYPT,&lt;br&gt;
  base64_signer_key: XXXX/XXX+XXXXXXXXXXXXXXXXX+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==,&lt;br&gt;
  base64_salt_separator: Aa==,&lt;br&gt;
  rounds: 8,&lt;br&gt;
  mem_cost: 14,&lt;br&gt;
}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Command line options&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Dump Firestore users to a JSON file #&lt;/p&gt;

&lt;p&gt;&lt;code&gt;node firestoreusers2json.js [&amp;lt;filename.json&amp;gt;] [&amp;lt;batch_size&amp;gt;]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In my case, it was: &lt;code&gt;node firestoreusers2json.js users.json 1000&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;filename.json: (optional) output filename (defaults to ./users.json)&lt;br&gt;
batchSize: (optional) number of users to fetch in each batch (defaults to 100)&lt;br&gt;
If an error like the one below occurs, check your JSON dump file for a missing comma. This is easily detected by the highlighted line in VS Code. &lt;br&gt;
`&lt;code&gt;node:events:491&lt;/code&gt;&lt;br&gt;
      throw er; // Unhandled 'error' event check out your dumped file`&lt;/p&gt;

&lt;p&gt;Import JSON users file to Supabase Auth (PostgreSQL: auth.users) &lt;/p&gt;

&lt;p&gt;&lt;code&gt;node import_users.js &amp;lt;path_to_json_file&amp;gt; [&amp;lt;batch_size&amp;gt;]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In my case: &lt;code&gt;node import_users.js ./users.json 1000&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;path_to_json_file: full local path and filename of .json input file (of users)&lt;/li&gt;
&lt;li&gt;batch_size: (optional) number of users to process in a batch (defaults to 100)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;br&gt;
For more advanced migrations, including the use of a middleware server component for verifying a user's existing Firebase password and updating that password in your Supabase project the first time a user logs in, see the firebase-to-supabase repo.&lt;/p&gt;

&lt;p&gt;Thank you :)&lt;/p&gt;

</description>
      <category>css</category>
      <category>frontend</category>
      <category>webdev</category>
    </item>
    <item>
      <title>FlutterFlow | Codemagic CI with Google Play Store</title>
      <dc:creator>Tarlan Isaev 🍓</dc:creator>
      <pubDate>Tue, 03 May 2022 12:00:37 +0000</pubDate>
      <link>https://dev.to/organicnz/flutterflow-codemagic-ci-with-google-play-store-4b7l</link>
      <guid>https://dev.to/organicnz/flutterflow-codemagic-ci-with-google-play-store-4b7l</guid>
      <description>&lt;p&gt;**TL:DR :) &lt;/p&gt;

&lt;p&gt;Setting up a Flutter project**&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Files preparation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before listing our app on Codemagic we shoul prepare our project for the delivery. Here's my "build.gradle" file with the complete code.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Path: ../android/app/build.gradle&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;`def localProperties = new Properties()&lt;br&gt;
def localPropertiesFile = rootProject.file('local.properties')&lt;br&gt;
if (localPropertiesFile.exists()) {&lt;br&gt;
    localPropertiesFile.withReader('UTF-8') { reader -&amp;gt;&lt;br&gt;
        localProperties.load(reader)&lt;br&gt;
    }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;def flutterRoot = localProperties.getProperty('flutter.sdk')&lt;br&gt;
if (flutterRoot == null) {&lt;br&gt;
    throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;def flutterVersionCode = localProperties.getProperty('flutter.versionCode')&lt;br&gt;
if (flutterVersionCode == null) {&lt;br&gt;
    flutterVersionCode = '1'&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;def flutterVersionName = localProperties.getProperty('flutter.versionName')&lt;br&gt;
if (flutterVersionName == null) {&lt;br&gt;
    flutterVersionName = '1.0'&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;apply plugin: 'com.android.application'&lt;br&gt;
apply plugin: 'kotlin-android'&lt;br&gt;
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"&lt;br&gt;
apply plugin: 'com.google.gms.google-services'  // Google Services plugin&lt;/p&gt;

&lt;p&gt;def keystoreProperties = new Properties()&lt;br&gt;
def keystorePropertiesFile = rootProject.file('key.properties')&lt;br&gt;
if (keystorePropertiesFile.exists()) {&lt;br&gt;
    keystoreProperties.load(new FileInputStream(keystorePropertiesFile))&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;android {&lt;br&gt;
    compileSdkVersion 31&lt;br&gt;
    ndkVersion '23.1.7779620'&lt;br&gt;
    sourceSets {&lt;br&gt;
        main.java.srcDirs += 'src/main/kotlin'&lt;br&gt;
    }&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;lintOptions {
    disable 'InvalidPackage'
}

defaultConfig {
    // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
    applicationId "com.flutterflow.foodshare"
                minSdkVersion 21
    targetSdkVersion 31
    versionCode flutterVersionCode.toInteger()
    versionName flutterVersionName
               ndk {
                       debugSymbolLevel 'FULL'
              }
}
signingConfigs {
    release {
        keyAlias keystoreProperties['keyAlias']
        keyPassword keystoreProperties['keyPassword']
        storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
        storePassword keystoreProperties['storePassword']
    }
}

buildTypes {
    release {
        // TODO: Add your own signing config for the release build.
        // Signing with the debug keys for now, so `flutter run --release` works.
        signingConfig signingConfigs.release
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;/p&gt;

&lt;p&gt;flutter {&lt;br&gt;
    source '../..'&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;dependencies {&lt;br&gt;
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"&lt;br&gt;
}`&lt;/p&gt;

&lt;p&gt;Acording to our delivering build we have to increase our "version: 1.0.0+1" build in root folder of our project "../pubspec.yaml". The second build is going to be "version: 1.0.0+2" and so on and so forth.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Path: ../pubspec.yaml&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;...&lt;br&gt;
 version: 1.0.0+13&lt;br&gt;
...&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Version inconsistency between local and Codemagic&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;First, you need to make sure that the gradlew file isn’t in ".gitignore". Look for "../android/gradlew", and if it’s in ".gitignore", delete it from there. I simply commented two lines. Then add "!gradle-wrapper.jar" to a new line in ".gitignore" to create an exception so that "gradle-wrapper.jar" would also be excluded from ".gitignore".&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Path: "../android/.gitignore"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;`gradle-wrapper.jar&lt;br&gt;
!gradle-wrapper.jar&lt;br&gt;
/.gradle&lt;br&gt;
/captures/&lt;br&gt;
/gradlew&lt;br&gt;
/gradlew.bat&lt;/p&gt;

&lt;h1&gt;
  
  
  /gradlew
&lt;/h1&gt;

&lt;h1&gt;
  
  
  /gradlew.bat
&lt;/h1&gt;

&lt;p&gt;/local.properties&lt;br&gt;
GeneratedPluginRegistrant.java`&lt;/p&gt;

&lt;p&gt;Change "distributionUrl" to version "7.2"  in "../android/gradle/wrapper/gradle-wrapper.properties"&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Path: ../android/gradle/wrapper/gradle-wrapper.properties&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;distributionBase=GRADLE_USER_HOME&lt;br&gt;
distributionPath=wrapper/dists&lt;br&gt;
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip&lt;br&gt;
zipStoreBase=GRADLE_USER_HOME&lt;br&gt;
zipStorePath=wrapper/dists&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In our case, run "./gradlew wrapper --gradle-version 7.2" inside "../android" locally to create gradlew and gradle-wrapper.properties files in your repository.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Change icons&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A bit annoyng part for me is changing icons in a few folders. Otherwise we will see the default flutter icon on installed app. Hopefully it will be fixed soon by FlutterFlow team :) &lt;/p&gt;

&lt;p&gt;Following path "../android/app/src/main/res" and directories within it: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;../android/app/src/main/res/mipmap-hdpi&lt;/li&gt;
&lt;li&gt;../android/app/src/main/res/mipmap-mdpi&lt;/li&gt;
&lt;li&gt;../android/app/src/main/res/mipmap-xhdpi&lt;/li&gt;
&lt;li&gt;../android/app/src/main/res/mipmap-xxhdpi&lt;/li&gt;
&lt;li&gt;../android/app/src/main/res/mipmap-xxxhdpi&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4-IlTCww--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cp85d9c3e38zvy75dcr9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4-IlTCww--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cp85d9c3e38zvy75dcr9.png" alt="Image description" width="880" height="535"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For toasting those icons I've been using online tools. Not sure which platforms in particular. Here's a few anyway :)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://romannurik.github.io/AndroidAssetStudio/"&gt;https://romannurik.github.io/AndroidAssetStudio/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://appicon.co/"&gt;https://appicon.co/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://makeappicon.com/"&gt;https://makeappicon.com/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Codemagic configuration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oQvJE2dt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5p3olbjx8vkbirw3f0su.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oQvJE2dt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5p3olbjx8vkbirw3f0su.png" alt="Image description" width="880" height="467"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'll just simply share my configuration to make things way easier 🙂&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build for platforms&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Android
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RdyfC_cn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3rvn2mj6aurgv1bayc4h.png" alt="Image description" width="880" height="466"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Run build on&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Free - macOS Standard VM (2.3GHz Quad Core / 8GB)&lt;/li&gt;
&lt;li&gt;No "Build triggers"&lt;/li&gt;
&lt;li&gt;No "Environment variables"&lt;/li&gt;
&lt;li&gt;No "Dependency caching"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--K8V5SF3R--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hlldt5srnyak6g6et3in.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--K8V5SF3R--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hlldt5srnyak6g6et3in.png" alt="Image description" width="880" height="566"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Add custom settings&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No "Post-clone script"&lt;/li&gt;
&lt;li&gt;No "Pre-test script"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Tests&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No tests are covered yet&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;*&lt;em&gt;Add custom settings for post-tests *&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No "Post-test script"&lt;/li&gt;
&lt;li&gt;No "Pre-build script"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Build&lt;/strong&gt;&lt;br&gt;
In my case I changed a few settings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Flutter version: channel Beta&lt;/li&gt;
&lt;li&gt;Mode: release&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;*&lt;em&gt;Add custom settings for post-build scripts *&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No "Post-build script"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Distribution&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;Enable Android code signing. *&lt;/em&gt;&lt;br&gt;
Was covered in &lt;a href="https://dev.to/organicnz/build-and-release-an-android-app-to-google-play-store-555g"&gt;Build and release an Android app to Google Play Store&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keystore*: upload keystore&lt;/li&gt;
&lt;li&gt;Keystore password: add keystore password&lt;/li&gt;
&lt;li&gt;Key alias: upload&lt;/li&gt;
&lt;li&gt;Key password: add key password&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Enable Google Play publishing&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add "Credentials (.json)*" key. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KMzyYgbJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/woaov8ktycbkuiaprylc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KMzyYgbJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/woaov8ktycbkuiaprylc.png" alt="Image description" width="880" height="465"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you don't have the json key or file is corrupted you should  generated it on "&lt;a href="https://console.cloud.google.com/iam-admin/serviceaccounts/details/111665568618235174091/permissions?project=your-project"&gt;Google Cloud Platform&lt;/a&gt;" at "IAM &amp;amp; Admin" section and "Service accounts".&lt;/p&gt;

&lt;p&gt;Press "Create Service Account". Here's my overview. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5SQqTyDJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wrq1qji2vp9bvxgk4tpy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5SQqTyDJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wrq1qji2vp9bvxgk4tpy.png" alt="Image description" width="880" height="464"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add the service account name and click "Done".&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RY5oUi0u--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bniu9y2kqovzdyx7vjp3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RY5oUi0u--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bniu9y2kqovzdyx7vjp3.png" alt="Image description" width="880" height="466"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Grant this service account access to project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cloud Build Editor&lt;/li&gt;
&lt;li&gt;Firebase Service Management Service Agent &lt;/li&gt;
&lt;li&gt;Service Account Token Creator&lt;/li&gt;
&lt;li&gt;Service Account User &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finalise it - click "Done".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--56EtMldY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v7n6v55r7au8suqiv8e4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--56EtMldY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v7n6v55r7au8suqiv8e4.png" alt="Image description" width="880" height="467"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Create new or add JSON key in key category. For my project I added a new key. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4wsr7UeF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/47s9wptpc2rq3lgtvrw8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4wsr7UeF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/47s9wptpc2rq3lgtvrw8.png" alt="Image description" width="880" height="466"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Generated and added key should look like this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7zMTaOtf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sbvpb06idyam03uuqlmi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7zMTaOtf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sbvpb06idyam03uuqlmi.png" alt="Image description" width="880" height="465"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Firebase App Distribution&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Disabled&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Notifications&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enable email publishing and add your email.&lt;/li&gt;
&lt;li&gt;Slack is disabled.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Post-publish script&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No any script&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Configs in yaml format&lt;br&gt;
Here's the complete config codemagic.yaml file &lt;br&gt;
Track: internal &lt;/p&gt;

&lt;p&gt;`# Automatically generated on 2021-11-13 UTC from &lt;a href="https://codemagic.io/app/6179aca0a05d5e8795c3cfa0/settings"&gt;https://codemagic.io/app/6179aca0a05d5e8795c3cfa0/settings&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Note that this configuration is not an exact match to UI settings. Review and adjust as necessary.
&lt;/h1&gt;

&lt;p&gt;workflows:&lt;br&gt;
  default-workflow:&lt;br&gt;
    name: Default Workflow&lt;br&gt;
    max_build_duration: 30&lt;br&gt;
    environment:&lt;br&gt;
      vars:&lt;br&gt;
        FCI_KEYSTORE_PATH: /tmp/keystore.keystore&lt;br&gt;
        FCI_KEYSTORE: Encrypted(Z0FBQUF...2TGxYYVBBPT0=)&lt;br&gt;
        FCI_KEYSTORE_PASSWORD: Encrypted(Z0FBQUF...g0Ni1xVlE9PQ==)&lt;br&gt;
        FCI_KEY_PASSWORD: Encrypted(Z0FBQUFB...FMZHc9PQ==)&lt;br&gt;
        FCI_KEY_ALIAS: Encrypted(Z0FBQUF...ZWWQ5aWc9PQ==)&lt;br&gt;
      flutter: beta&lt;br&gt;
      xcode: latest&lt;br&gt;
      cocoapods: default&lt;br&gt;
    scripts:&lt;br&gt;
      - |&lt;br&gt;
        # set up key.properties&lt;br&gt;
        echo $FCI_KEYSTORE | base64 --decode &amp;gt; $FCI_KEYSTORE_PATH&lt;br&gt;
        cat &amp;gt;&amp;gt; "$FCI_BUILD_DIR/android/key.properties" &amp;lt;
        storePassword=$FCI_KEYSTORE_PASSWORD&lt;br&gt;
        keyPassword=$FCI_KEY_PASSWORD&lt;br&gt;
        keyAlias=$FCI_KEY_ALIAS&lt;br&gt;
        storeFile=/tmp/keystore.keystore&lt;br&gt;
        EOF&lt;br&gt;
      - |&lt;br&gt;
        # set up local properties&lt;br&gt;
        echo "flutter.sdk=$HOME/programs/flutter" &amp;gt; "$FCI_BUILD_DIR/android/local.properties"&lt;br&gt;
      - flutter packages pub get&lt;br&gt;
      - flutter build appbundle --release&lt;br&gt;
    artifacts:&lt;br&gt;
      - build/&lt;strong&gt;/outputs/apk/&lt;/strong&gt;/&lt;em&gt;.apk&lt;br&gt;
      - build/&lt;/em&gt;&lt;em&gt;/outputs/bundle/&lt;/em&gt;&lt;em&gt;/&lt;/em&gt;.aab&lt;br&gt;
      - build/&lt;strong&gt;/outputs/&lt;/strong&gt;/mapping.txt&lt;br&gt;
      - flutter_drive.log&lt;br&gt;
    publishing:&lt;br&gt;
      email:&lt;br&gt;
        recipients:&lt;br&gt;
          - &lt;a href="mailto:tamerlanium@gmail.com"&gt;tamerlanium@gmail.com&lt;/a&gt;&lt;br&gt;
      google_play:&lt;br&gt;
        credentials: Encrypted(Z0FBQUF...VNUdUMU09)&lt;br&gt;
        track: internal&lt;br&gt;
        in_app_update_priority: 0`&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Last step&lt;/strong&gt;&lt;br&gt;
Run following "flutter clean" command on terminal and commit the changes then rerun your Codemagic build.    &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YF3ffz8G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o61d057d0uzfghz8yvmk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YF3ffz8G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o61d057d0uzfghz8yvmk.png" alt="Image description" width="880" height="466"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Troubleshooting&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Error caused by dependencies or could be something else&lt;/strong&gt;&lt;br&gt;
If you encounter an error with dependencies pls try out to clean your project through the terminal:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sudo flutter clean &amp;amp;&amp;amp; sudo rm ios/Podfile &amp;amp;&amp;amp; sudo rm ios/Podfile.lock pubspec.lock &amp;amp;&amp;amp; sudo rm -rf ios/Pods ios/Runner.xcworkspace &amp;amp;&amp;amp; sudo rm -Rf ios/.symlinks &amp;amp;&amp;amp; sudo rm -Rf ios/Flutter/Flutter.framework &amp;amp;&amp;amp; sudo rm -Rf ios/Flutter/Flutter.podspec&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;and&lt;/p&gt;

&lt;p&gt;&lt;code&gt;flutter pub get &amp;amp;&amp;amp; flutter packages pub run build_runner build --delete-conflicting-outputs&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;then once again commit the changes and rerun your Codemagic build. &lt;/p&gt;

&lt;p&gt;That should help you to set up the CI/CD pipeline :)   &lt;/p&gt;

&lt;p&gt;Thank you :)  &lt;/p&gt;

&lt;p&gt;﻿ &lt;/p&gt;

</description>
    </item>
    <item>
      <title>FlutterFlow | Build and release an Android app on Google Play Store</title>
      <dc:creator>Tarlan Isaev 🍓</dc:creator>
      <pubDate>Tue, 03 May 2022 11:51:24 +0000</pubDate>
      <link>https://dev.to/organicnz/build-and-release-an-android-app-to-google-play-store-555g</link>
      <guid>https://dev.to/organicnz/build-and-release-an-android-app-to-google-play-store-555g</guid>
      <description>&lt;p&gt;Geez I was facing an issue all day long with signing my android app today, but finally overcame the pain lol :) &lt;/p&gt;

&lt;p&gt;*&lt;em&gt;Preparation before the flight :)  *&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Before signing my app I increased "compileSdkVersion 30", "minSdkVersion 23", "targetSdkVersion 30". Also added "signingConfigs"  a few "def" properties and change "signingConfigs" to "release" mode in "build.grudle" file.&lt;/p&gt;

&lt;p&gt;Here's the complete "build.grudle" file:&lt;/p&gt;

&lt;p&gt;// Beginning | sorry the code snippet isn't properly displayed for some reason. Pls copy as it's. &lt;br&gt;
def localProperties = new Properties()&lt;br&gt;
def localPropertiesFile = rootProject.file('local.properties')&lt;br&gt;
if (localPropertiesFile.exists()) {&lt;br&gt;
    localPropertiesFile.withReader('UTF-8') { reader -&amp;gt;&lt;br&gt;
        localProperties.load(reader)&lt;br&gt;
    }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;def flutterRoot = localProperties.getProperty('flutter.sdk')&lt;br&gt;
if (flutterRoot == null) {&lt;br&gt;
    throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;def flutterVersionCode = localProperties.getProperty('flutter.versionCode')&lt;br&gt;
if (flutterVersionCode == null) {&lt;br&gt;
    flutterVersionCode = '1'&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;def flutterVersionName = localProperties.getProperty('flutter.versionName')&lt;br&gt;
if (flutterVersionName == null) {&lt;br&gt;
    flutterVersionName = '1.0'&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;apply plugin: 'com.android.application'&lt;br&gt;
apply plugin: 'kotlin-android'&lt;br&gt;
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"&lt;br&gt;
apply plugin: 'com.google.gms.google-services'  // Google Services plugin&lt;/p&gt;

&lt;p&gt;def keystoreProperties = new Properties()&lt;br&gt;
def keystorePropertiesFile = rootProject.file('key.properties')&lt;br&gt;
if (keystorePropertiesFile.exists()) {&lt;br&gt;
    keystoreProperties.load(new FileInputStream(keystorePropertiesFile))&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;android {&lt;br&gt;
    compileSdkVersion 30&lt;br&gt;
    ndkVersion '23.1.7779620'&lt;br&gt;
    sourceSets {&lt;br&gt;
        main.java.srcDirs += 'src/main/kotlin'&lt;br&gt;
    }&lt;br&gt;
    lintOptions {&lt;br&gt;
        disable 'InvalidPackage'&lt;br&gt;
    }&lt;br&gt;
    defaultConfig {&lt;br&gt;
        // TODO: Specify your own unique Application ID (&lt;a href="https://developer.android.com/studio/build/application-id.html" rel="noopener noreferrer"&gt;https://developer.android.com/studio/build/application-id.html&lt;/a&gt;).&lt;br&gt;
        applicationId "com.flutterflow.foodshare"&lt;br&gt;
        minSdkVersion 23&lt;br&gt;
        targetSdkVersion 30&lt;br&gt;
        versionCode flutterVersionCode.toInteger()&lt;br&gt;
        versionName flutterVersionName&lt;br&gt;
                    ndk {&lt;br&gt;
                               debugSymbolLevel 'FULL'&lt;br&gt;
                    }&lt;br&gt;
    }&lt;br&gt;
    signingConfigs {&lt;br&gt;
        release {&lt;br&gt;
            keyAlias keystoreProperties['keyAlias']&lt;br&gt;
            keyPassword keystoreProperties['keyPassword']&lt;br&gt;
            storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null&lt;br&gt;
            storePassword keystoreProperties['storePassword']&lt;br&gt;
        }&lt;br&gt;
    }&lt;br&gt;
    buildTypes {&lt;br&gt;
        release {&lt;br&gt;
            // TODO: Add your own signing config for the release build.&lt;br&gt;
            // Signing with the debug keys for now, so 'flutter run --release' works.&lt;br&gt;
                                   signingConfig signingConfigs.release&lt;br&gt;
        }&lt;br&gt;
    }&lt;br&gt;
}&lt;br&gt;
flutter {&lt;br&gt;
    source '../..'&lt;br&gt;
}&lt;br&gt;
dependencies {&lt;br&gt;
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"&lt;br&gt;
    implementation 'io.card:android-sdk:5.+'&lt;br&gt;
}&lt;br&gt;
// End&lt;/p&gt;

&lt;p&gt;Created file "key.properties" with these properties where storePassword and keyPassword are your later generated password for key. You'll put them on the terminal. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;storePassword=your_password&lt;br&gt;
keyPassword=your_password&lt;br&gt;
keyAlias=upload&lt;br&gt;
storeFile=/Users/organic/Development/foodshare-flutter/android/app/upload-keystore.jks&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F4l4ih3dhd7wvevshsxoy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F4l4ih3dhd7wvevshsxoy.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fkzdh3ja6xwjl7okx9det.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fkzdh3ja6xwjl7okx9det.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Signing the app&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before building the app we must sign it by the key, however I got this error initially:  &lt;/p&gt;

&lt;p&gt;&lt;code&gt;Java Algorithm HmacPBESHA256 not available&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;How did I solve it? Instead of the default command on Mac/Linux, I used the following command and it helped after a few hours of the struggle: &lt;/p&gt;

&lt;p&gt;&lt;code&gt;/Users/organic/Library/Java/JavaVirtualMachines/openjdk-16.0.1/Contents/Home/bin/keytool -genkey -v -keystore android/app/upload-keystore.jks -keyalg RSA -keysize 2048 -validity 10000 -alias upload -storetype JKS&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;That command creates and stores key's stores the upload-keystore.jks file in your home directory. If you want to store it elsewhere, change the argument you pass to the -keystore parameter. &lt;strong&gt;However, keep the keystore file private; don’t check it into public source control!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If I've understood it correctly the Openjdk v11 doesn't allow to issue JKS keys and it's only available from the version Openjdk v12. In my case I've used Openjdk v16 installed by Homebrew. &lt;/p&gt;

&lt;p&gt;Make sure you you've selected the right JDK (Java Development Kit) home path. In my case it locates in the directory below:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;/Users/organic/Library/Java/JavaVirtualMachines/openjdk-16.0.1/Contents/Home&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F07m5h80nyj1tysa002m1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F07m5h80nyj1tysa002m1.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Creation of the lightweight bundle.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Congrats, our key is generated and we're ready to build appbundle release 🙂&lt;/p&gt;

&lt;p&gt;Use this following command on your app folder: &lt;/p&gt;

&lt;p&gt;&lt;code&gt;flutter build appbundle --release --build-name=foodshare-1.0.0 --build-number=2&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Just wait until it's finally baked and you'll see the complete file "app-release.aab" by path "../your-app/build/app/outputs/bundle/release/app-release.aab". &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fkc9bjqrgf8ouljxjnh1i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fkc9bjqrgf8ouljxjnh1i.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Production release time.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Drop your app bundle there to upload and send it for moderation 🙂&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fmvja9kwnhb8re0jm5wf9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fmvja9kwnhb8re0jm5wf9.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now it looks complete I hope ahah &lt;/p&gt;

&lt;p&gt;Pls let me know if something is wrong here :)&lt;br&gt;
Thank you everyone :)&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;UPD: *&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;1. If you reset your repository to HEAD commit:  *&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sudo git reset --hard HEAD&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You should copy key and properties files each time "upload-keystore.jks" to the "../your-flutterflow-project/android/app/upload-keystore.jks" directory and "key.properties" to "../your-flutterflow-project/android/key.properties" directory in case to be able to create the app builds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.Reseting the upload key through Google Play Developer Support.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Open a ticket on Google Play Developer Support and they will help ti reset the upload key for your app, &lt;strong&gt;com.flutterflow.yourapp&lt;/strong&gt;. The new upload key will be used to sign APKs that you upload to Play.&lt;/p&gt;

&lt;p&gt;Google recommend that you adjust your planning to include a buffer period of 48 hours from when the upload key has been reset before you can use the new upload key. You can learn more about using app signing by Google Play &lt;a href="https://support.google.com/googleplay/android-developer/answer/7384423" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here’s how to generate and register a new upload key:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Follow the instructions in the &lt;a href="https://developer.android.com/studio/publish/app-signing.html#generate-key" rel="noopener noreferrer"&gt;Android Studio Help Center&lt;/a&gt; to generate a new key. It must be different from any previous keys. Alternatively, you can use the following command line to generate a new key:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;keytool -genkeypair -alias upload -keyalg RSA -keysize 2048 -validity 9125 -keystore keystore.jks&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This key must be a 2048 bit RSA key and have 25-year validity.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Export the certificate for that key to PEM format:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;keytool -export -rfc -alias upload -file upload_certificate.pem -keystore keystore.jks&lt;/code&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Reply to the open ticket email and attach the &lt;strong&gt;upload_certificate.pem&lt;/strong&gt; file.&lt;/li&gt;
&lt;/ol&gt;

</description>
    </item>
    <item>
      <title>FlutterFlow | Popup BottomSheet on the markers for GoogleMap</title>
      <dc:creator>Tarlan Isaev 🍓</dc:creator>
      <pubDate>Tue, 03 May 2022 11:43:08 +0000</pubDate>
      <link>https://dev.to/organicnz/popup-bottomsheet-on-the-markers-for-googlemap-23a7</link>
      <guid>https://dev.to/organicnz/popup-bottomsheet-on-the-markers-for-googlemap-23a7</guid>
      <description>&lt;p&gt;While developing your app, it could be not quite obvious how to leverage "Markers" all configured at least for me 🙂 &lt;/p&gt;

&lt;p&gt;You may need to pass data to another page from the "GoogleMap" page. For example, sending a listing item from "ListView" or "Column" to the "Component" in my case. To pass any data from one page to another component or page, you will need a few parameters. You can think of a parameter as a variable that holds the value being passed from one page to another.&lt;/p&gt;

&lt;p&gt;Now, let's configure the essential parts of our app for passing data by creating a page "GoogleMaps" that sends the message from one page to another "Component". You can see the "Users" and "Posts" collections are being passed from one page to the next one down below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SqevO1iG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ueu6ar8uan67cnkppdru.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SqevO1iG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ueu6ar8uan67cnkppdru.png" alt="Image description" width="868" height="879"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here could be your "Query Collection" for any events.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NYveaHNf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qpvnowd6ychufc5cpnug.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NYveaHNf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qpvnowd6ychufc5cpnug.png" alt="Image description" width="880" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add Action in "GoogleMap".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--q90Ah8c4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4u5462dha6rsaeo0zmoe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--q90Ah8c4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4u5462dha6rsaeo0zmoe.png" alt="Image description" width="880" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Passing Parameters from the GoogleMap to the Component &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ucRfWn9o--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hg878ofoh5jsoot18na4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ucRfWn9o--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hg878ofoh5jsoot18na4.png" alt="Image description" width="880" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add the "Component" to the "Action Type" named "Bottom Sheet" on "GoogleMap".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Bn6rptfL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/exz0kw1yu0v6fx8f3hfg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Bn6rptfL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/exz0kw1yu0v6fx8f3hfg.png" alt="Image description" width="880" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the List or "Column" add "Query Collection". In case, to be able to set "Paths" to a variable. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--f8ww-JqX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qlg4esfrzl2nxntp6kqi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--f8ww-JqX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qlg4esfrzl2nxntp6kqi.png" alt="Image description" width="880" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now it's time to define the "Parameters" on the "Details Page" of the component.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5uMbjVPF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kr7ujr8rg61mg4wvv4up.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5uMbjVPF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kr7ujr8rg61mg4wvv4up.png" alt="Image description" width="880" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At this moment it should be all set to go. Click-clack - it works 😀&lt;/p&gt;

&lt;p&gt;Thank you Mike Startup for navigating me in the right direction :)  &lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
