<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Code with Nirmal]]></title><description><![CDATA[Software Engineer | Flutter Fanatic]]></description><link>https://blog.nirmalcode.com</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1648891332492/8LCoSXUbq.png</url><title>Code with Nirmal</title><link>https://blog.nirmalcode.com</link></image><generator>RSS for Node</generator><lastBuildDate>Sat, 11 Apr 2026 04:24:45 GMT</lastBuildDate><atom:link href="https://blog.nirmalcode.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[#1 - Whatsapp Opener Flutter App]]></title><description><![CDATA[Idea
Enter a phone number with the country code and press a button to open the Whatsapp app or web.
Techstack

Flutter

Packages

Flutter bloc

Freezed



VSCode


Let's code
Initial project setup
flutter create whatsapp_opener

Remove all the commen...]]></description><link>https://blog.nirmalcode.com/1-whatsapp-opener-flutter-app</link><guid isPermaLink="true">https://blog.nirmalcode.com/1-whatsapp-opener-flutter-app</guid><category><![CDATA[Flutter]]></category><category><![CDATA[Android]]></category><category><![CDATA[Mobile Development]]></category><category><![CDATA[flutter_bloc]]></category><category><![CDATA[Mobile apps]]></category><dc:creator><![CDATA[Nirmal Ariyathilake]]></dc:creator><pubDate>Sun, 05 Feb 2023 04:17:25 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1675465970032/bb1c4c0d-6541-40dc-8cc9-936e34c51bd6.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-idea">Idea</h2>
<p>Enter a phone number with the country code and press a button to open the Whatsapp app or web.</p>
<h2 id="heading-techstack">Techstack</h2>
<ul>
<li><p>Flutter</p>
</li>
<li><p>Packages</p>
<ul>
<li><p>Flutter bloc</p>
</li>
<li><p>Freezed</p>
</li>
</ul>
</li>
<li><p>VSCode</p>
</li>
</ul>
<h2 id="heading-lets-code">Let's code</h2>
<h3 id="heading-initial-project-setup">Initial project setup</h3>
<pre><code class="lang-bash">flutter create whatsapp_opener
</code></pre>
<p>Remove all the comments from <code>pubspec.yaml</code> and <code>main.dart</code> files.</p>
<blockquote>
<p>Hint: If you are using VSCode, use the <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=plibither8.remove-comments">Remove Comments</a> extension.</p>
</blockquote>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1675472110327/f2b3c913-93aa-4f47-92e4-5174811c4fee.png" alt class="image--center mx-auto" /></p>
<p>Update analysis options to maintain a clean flutter code</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="1daa3f3944acc7023ffe82191f4d04c9"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/NirmalAriyathilake/1daa3f3944acc7023ffe82191f4d04c9" class="embed-card">https://gist.github.com/NirmalAriyathilake/1daa3f3944acc7023ffe82191f4d04c9</a></div><p> </p>
<h3 id="heading-folder-structure">Folder structure</h3>
<ul>
<li><p>Application - BLOC notifiers</p>
</li>
<li><p>Presentation - Pages and Widgets</p>
</li>
</ul>
<pre><code class="lang-dart"><span class="hljs-comment">// lib/main.dart</span>

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

<span class="hljs-keyword">import</span> <span class="hljs-string">'presentation/app.dart'</span>;

<span class="hljs-keyword">void</span> main() {
  runApp(<span class="hljs-keyword">const</span> App());
}
</code></pre>
<pre><code class="lang-dart"><span class="hljs-comment">// lib/presentation/app.dart</span>

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

<span class="hljs-keyword">import</span> <span class="hljs-string">'presentation/app.dart'</span>;

<span class="hljs-keyword">void</span> main() {
  runApp(<span class="hljs-keyword">const</span> App());
}
</code></pre>
<pre><code class="lang-dart"><span class="hljs-comment">// lib/presentation/home_page.dart</span>

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

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

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span>  Scaffold(
      appBar: AppBar(),
    );
  }
}
</code></pre>
<h3 id="heading-now-lets-create-the-ui-for-the-home-page">Now let's create the UI for the home page</h3>
<ul>
<li><p>A text field to enter a number</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1675558695997/bae919d5-04a6-4695-a2d8-ec6db3c78d1b.png" alt class="image--center mx-auto" /></p>
</li>
</ul>
<pre><code class="lang-dart"><span class="hljs-comment">// lib/presentation/home_page.dart</span>

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

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

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Scaffold(
      appBar: AppBar(
        title: <span class="hljs-keyword">const</span> Text(<span class="hljs-string">"Whatsapp Opener"</span>),
        centerTitle: <span class="hljs-keyword">true</span>,
      ),
      body: Container(
        padding: <span class="hljs-keyword">const</span> EdgeInsets.symmetric(
          horizontal: <span class="hljs-number">10</span>,
          vertical: <span class="hljs-number">5</span>,
        ),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: [
            TextField(
              decoration: InputDecoration(labelText: <span class="hljs-string">'Enter number'</span>),
            ),
            ElevatedButton(
              onPressed: () {},
              child: <span class="hljs-keyword">const</span> Text(<span class="hljs-string">"Open Whatsapp"</span>),
            )
          ],
        ),
      ),
    );
  }
}
</code></pre>
<h3 id="heading-now-lets-create-the-logic">Now let's create the logic</h3>
<p>The main use case flow is,</p>
<ol>
<li><p>The user enters a number into a <code>Text Field</code></p>
</li>
<li><p>Then press the button</p>
</li>
<li><p>Whatsapp opens with a chat for the entered number</p>
</li>
</ol>
<p>To implement application logic I'm using the <a target="_blank" href="https://pub.dev/packages/flutter_bloc">flutter_bloc</a> package. Using that I'm going to,</p>
<ol>
<li><p>Hold the user entered number</p>
</li>
<li><p>Use that number to open the chat when the user presses the button</p>
</li>
</ol>
<blockquote>
<p>Hint: Install the <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=FelixAngelov.bloc">bloc</a> extension to use flutter_bloc easily</p>
</blockquote>
<p>To generate boilerplate codes and manage classes easily I'm using the <a target="_blank" href="https://pub.dev/packages/freezed">freezed</a> package with <a target="_blank" href="https://pub.dev/packages/freezed_annotation">freezed_annotation</a> and <a target="_blank" href="https://pub.dev/packages/build_runner">build_runner</a> packages.</p>
<pre><code class="lang-yaml"><span class="hljs-string">//</span> <span class="hljs-string">pubspec.yaml</span>

<span class="hljs-attr">name:</span> <span class="hljs-string">whatsapp_opener</span>
<span class="hljs-attr">description:</span> <span class="hljs-string">Open</span> <span class="hljs-string">whatsapp</span> <span class="hljs-string">chat</span> <span class="hljs-string">for</span> <span class="hljs-string">any</span> <span class="hljs-string">whatsapp</span> <span class="hljs-string">user</span> <span class="hljs-string">without</span> <span class="hljs-string">saving</span> <span class="hljs-string">the</span> <span class="hljs-string">number</span>

<span class="hljs-attr">publish_to:</span> <span class="hljs-string">'none'</span>

<span class="hljs-attr">version:</span> <span class="hljs-number">1.0</span><span class="hljs-number">.0</span><span class="hljs-string">+1</span>

<span class="hljs-attr">environment:</span>
  <span class="hljs-attr">sdk:</span> <span class="hljs-string">'&gt;=2.19.0 &lt;3.0.0'</span>

<span class="hljs-attr">dependencies:</span>
  <span class="hljs-attr">flutter:</span>
    <span class="hljs-attr">sdk:</span> <span class="hljs-string">flutter</span>

  <span class="hljs-attr">cupertino_icons:</span> <span class="hljs-string">^1.0.2</span>
  <span class="hljs-attr">flutter_bloc:</span> <span class="hljs-string">^8.1.1</span>
  <span class="hljs-attr">freezed_annotation:</span> <span class="hljs-string">^2.2.0</span>

<span class="hljs-attr">dev_dependencies:</span>
  <span class="hljs-attr">flutter_test:</span>
    <span class="hljs-attr">sdk:</span> <span class="hljs-string">flutter</span>

  <span class="hljs-attr">flutter_lints:</span> <span class="hljs-string">^2.0.0</span>
  <span class="hljs-attr">build_runner:</span> <span class="hljs-string">^2.3.3</span>
  <span class="hljs-attr">freezed:</span> <span class="hljs-string">^2.3.2</span>

<span class="hljs-attr">flutter:</span>
  <span class="hljs-attr">uses-material-design:</span> <span class="hljs-literal">true</span>
</code></pre>
<p>Now right-click on the application folder and select the <code>Cubit: New cubit</code> option. It will give a prompt to enter the notifier name. I'm calling this <code>AppActorNotifier</code>.</p>
<p>After entering the name for the cubit, the bloc extension will create 2 files.</p>
<pre><code class="lang-dart"><span class="hljs-comment">// lib/application/cubit/app_actor_notifier_cubit.dart</span>

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

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

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AppActorNotifierCubit</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Cubit</span>&lt;<span class="hljs-title">AppActorNotifierState</span>&gt; </span>{
  AppActorNotifierCubit() : <span class="hljs-keyword">super</span>(AppActorNotifierState.initial());
}

<span class="hljs-comment">// lib/application/cubit/app_actor_notifier_state.dart</span>

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

<span class="hljs-meta">@freezed</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AppActorNotifierState</span> <span class="hljs-title">with</span> <span class="hljs-title">_</span>$<span class="hljs-title">AppActorNotifierState</span> </span>{
  <span class="hljs-keyword">const</span> <span class="hljs-keyword">factory</span> AppActorNotifierState.initial() = _Initial;
}
</code></pre>
<p>Now you can see <code>part 'app_actor_notifier_cubit.freezed.dart';</code> this line gives an error. Because that file doesn't exist yet. To generate that file run the build_runner.</p>
<pre><code class="lang-bash">flutter pub run build_runner watch --delete-conflicting-outputs
</code></pre>
<blockquote>
<p>Note :</p>
<ul>
<li><p>watch : Build_runner is going to keep watching for any changes and re-run itself to update the generated files</p>
</li>
<li><p>--delete-conflicting-outputs : Replace the old generated files with the newer ones</p>
</li>
</ul>
</blockquote>
<p>Now let's update the state class to hold the number input. Also, I'm renaming the cubit folder to app_actor_notifier.</p>
<pre><code class="lang-dart"><span class="hljs-comment">// lib/application/app_actor_notifier/app_actor_notifier_state.dart</span>

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

<span class="hljs-meta">@freezed</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AppActorNotifierState</span> <span class="hljs-title">with</span> <span class="hljs-title">_</span>$<span class="hljs-title">AppActorNotifierState</span></span>{
  <span class="hljs-keyword">const</span> <span class="hljs-keyword">factory</span> AppActorNotifierState({
    <span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> number,
  }) = _AppActorNotifierState;

  <span class="hljs-keyword">factory</span> AppActorNotifierState.initial() =&gt; <span class="hljs-keyword">const</span> AppActorNotifierState(
    number: <span class="hljs-string">''</span>,
  );
}
</code></pre>
<p>Now let's create a method in the cubit to update the number value in the state when the user enters it.</p>
<pre><code class="lang-dart"><span class="hljs-comment">// lib/application/app_actor_notifier/app_actor_notifier_cubit.dart</span>

<span class="hljs-keyword">void</span> onNumberChanged(<span class="hljs-built_in">String</span> value) {
    emit(state.copyWith(number: value));
}
</code></pre>
<blockquote>
<p>Note :</p>
<ul>
<li><p>emit : emits the newest state</p>
</li>
<li><p>copyWith : Generated by freezed package to clone and create a new object with the updated values without changing other values in the object.</p>
</li>
</ul>
</blockquote>
<p>Now let's create the method to open the WhatsApp chat app with the number.</p>
<p>For this method, I'm using the <a target="_blank" href="https://pub.dev/packages/url_launcher">url_launcher</a> package.</p>
<pre><code class="lang-dart"><span class="hljs-comment">// lib/application/app_actor_notifier/app_actor_notifier_cubit.dart</span>

<span class="hljs-keyword">void</span> openWhatsappChat() {
    <span class="hljs-keyword">if</span> (state.number.isNotEmpty) {
      launchUrlString(<span class="hljs-string">'https://wa.me/<span class="hljs-subst">${state.number}</span>'</span>);
    }
}
</code></pre>
<blockquote>
<p>Note:</p>
<ul>
<li><p><a target="_blank" href="https://wa.me">https://wa.me</a> : Short url to open a whatsapp chat with a given number</p>
</li>
<li><p>state.number : Get number value from the state</p>
</li>
</ul>
<p>Hint: After installing a plugin make sure to rebuild your app ( Not hot restart) . Platform-specific codes from plugins only get included in the build time.  Otherwise, you'll get an <strong><em>Unimplemented</em></strong> <strong><em>Error.</em></strong></p>
</blockquote>
<p>Finally, we have,</p>
<pre><code class="lang-dart"><span class="hljs-comment">// lib/application/app_actor_notifier/app_actor_notifier_cubit.dart</span>

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

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

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AppActorNotifierCubit</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Cubit</span>&lt;<span class="hljs-title">AppActorNotifierState</span>&gt; </span>{
  AppActorNotifierCubit() : <span class="hljs-keyword">super</span>(AppActorNotifierState.initial());

  <span class="hljs-keyword">void</span> onNumberChanged(<span class="hljs-built_in">String</span> value) {
    emit(state.copyWith(number: value));
  }

  <span class="hljs-keyword">void</span> openWhatsappChat() {
    <span class="hljs-keyword">if</span> (state.number.isNotEmpty) {
      launchUrlString(<span class="hljs-string">'https://wa.me/<span class="hljs-subst">${state.number}</span>'</span>);
    }
  }
}
</code></pre>
<hr />
<p>Now let's use the <code>AppActorNotifier</code>,</p>
<p>First, we have to provide an instance of the <code>AppActorNotifierCubit</code> to our app.</p>
<pre><code class="lang-dart"><span class="hljs-comment">// lib/presentation/app.dart</span>

<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter_bloc/flutter_bloc.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:whatsapp_opener/application/app_actor_notifier/app_actor_notifier_cubit.dart'</span>;

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

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">App</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
  <span class="hljs-keyword">const</span> App({<span class="hljs-keyword">super</span>.key});

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> BlocProvider(
      create: (context) =&gt; AppActorNotifierCubit(),
      child: MaterialApp(
        debugShowCheckedModeBanner: <span class="hljs-keyword">false</span>,
        title: <span class="hljs-string">'Whatsapp Opener'</span>,
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: <span class="hljs-keyword">const</span> HomePage(),
      ),
    );
  }
}
</code></pre>
<p>Now we can bind our actor notifier to the home page.</p>
<pre><code class="lang-dart"><span class="hljs-comment">// lib/presentation/home_page.dart</span>

<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter_bloc/flutter_bloc.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:whatsapp_opener/application/app_actor_notifier/app_actor_notifier_cubit.dart'</span>;

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

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Scaffold(
      appBar: AppBar(
        title: <span class="hljs-keyword">const</span> Text(<span class="hljs-string">"Whatsapp Opener"</span>),
        centerTitle: <span class="hljs-keyword">true</span>,
      ),
      body: Container(
        padding: <span class="hljs-keyword">const</span> EdgeInsets.symmetric(
          horizontal: <span class="hljs-number">10</span>,
          vertical: <span class="hljs-number">5</span>,
        ),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: [
            TextField(
              decoration: <span class="hljs-keyword">const</span> InputDecoration(labelText: <span class="hljs-string">'Enter number'</span>),
              onChanged: (value) {
                <span class="hljs-keyword">if</span> (value.trim().isNotEmpty) {
                  context.read&lt;AppActorNotifierCubit&gt;().onNumberChanged(value);
                }
              },
            ),
            ElevatedButton(
              onPressed: () {
                context.read&lt;AppActorNotifierCubit&gt;().openWhatsappChat();
              },
              child: <span class="hljs-keyword">const</span> Text(<span class="hljs-string">"Open Whatsapp"</span>),
            )
          ],
        ),
      ),
    );
  }
}
</code></pre>
<blockquote>
<p>Note :</p>
<ul>
<li>context.read : Equal to BlocProvider.of(context) which looks up the closest ancestor instance of the specified type. Use to call events. <a target="_blank" href="https://bloclibrary.dev/#/flutterbloccoreconcepts?id=contextread">Learn more</a>.</li>
</ul>
</blockquote>
<h3 id="heading-validations">Validations</h3>
<ul>
<li><p>If the user enters an invalid number app should show an error</p>
</li>
<li><p>Submit button should stay disabled until the user enters a valid number</p>
</li>
</ul>
<pre><code class="lang-dart"><span class="hljs-comment">// lib/application/app_actor_notifier/app_actor_notifier_state.dart</span>

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

<span class="hljs-meta">@freezed</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AppActorNotifierState</span> <span class="hljs-title">with</span> <span class="hljs-title">_</span>$<span class="hljs-title">AppActorNotifierState</span></span>{
  <span class="hljs-keyword">const</span> <span class="hljs-keyword">factory</span> AppActorNotifierState({
    <span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> number,
    <span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> error,
  }) = _AppActorNotifierState;

  <span class="hljs-keyword">factory</span> AppActorNotifierState.initial() =&gt; <span class="hljs-keyword">const</span> AppActorNotifierState(
    number: <span class="hljs-string">''</span>,
    error: <span class="hljs-string">''</span>,
  );
}
</code></pre>
<pre><code class="lang-dart"><span class="hljs-comment">// lib/application/app_actor_notifier/app_actor_notifier_cubit.dart</span>

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

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

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AppActorNotifierCubit</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Cubit</span>&lt;<span class="hljs-title">AppActorNotifierState</span>&gt; </span>{
  AppActorNotifierCubit() : <span class="hljs-keyword">super</span>(AppActorNotifierState.initial());

  <span class="hljs-keyword">void</span> onNumberChanged(<span class="hljs-built_in">String</span> value) {
    <span class="hljs-keyword">if</span> (value.contains(<span class="hljs-built_in">RegExp</span>(<span class="hljs-string">r"^\+[\d\s]{2,}$"</span>))) {
      <span class="hljs-keyword">final</span> valueWithoutSpaces = value.replaceAll(<span class="hljs-built_in">RegExp</span>(<span class="hljs-string">r"\s+"</span>), <span class="hljs-string">""</span>);

      emit(state.copyWith(
        number: valueWithoutSpaces,
        error: <span class="hljs-string">''</span>,
      ));
    } <span class="hljs-keyword">else</span> {
      emit(state.copyWith(
        error: <span class="hljs-string">'Please enter a valid number'</span>,
      ));
    }
  }

  <span class="hljs-keyword">void</span> openWhatsappChat() {
    <span class="hljs-keyword">if</span> (state.number.isNotEmpty) {
      launchUrlString(<span class="hljs-string">'https://wa.me/<span class="hljs-subst">${state.number}</span>'</span>);
    }
  }
}
</code></pre>
<pre><code class="lang-dart"><span class="hljs-comment">// lib/presentation/home_page.dart</span>

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

<span class="hljs-keyword">import</span> <span class="hljs-string">'../application/app_actor_notifier/app_actor_notifier_cubit.dart'</span>;

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

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Scaffold(
      appBar: AppBar(
        title: <span class="hljs-keyword">const</span> Text(<span class="hljs-string">"Whatsapp Opener"</span>),
        centerTitle: <span class="hljs-keyword">true</span>,
      ),
      body: Container(
        padding: <span class="hljs-keyword">const</span> EdgeInsets.symmetric(
          horizontal: <span class="hljs-number">10</span>,
          vertical: <span class="hljs-number">5</span>,
        ),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: [
            TextField(
              decoration: InputDecoration(
                labelText: <span class="hljs-string">'Enter number'</span>,
                errorText: context
                    .select((AppActorNotifierCubit cubit) =&gt; cubit.state.error),
              ),
              onChanged: (value) {
                <span class="hljs-keyword">if</span> (value.trim().isNotEmpty) {
                  context.read&lt;AppActorNotifierCubit&gt;().onNumberChanged(value);
                }
              },
            ),
            ElevatedButton(
              onPressed: context.select((AppActorNotifierCubit cubit) =&gt;
                      cubit.state.error.isEmpty)
                  ? () {
                      context.read&lt;AppActorNotifierCubit&gt;().openWhatsappChat();
                    }
                  : <span class="hljs-keyword">null</span>,
              child: <span class="hljs-keyword">const</span> Text(<span class="hljs-string">"Open Whatsapp"</span>),
            )
          ],
        ),
      ),
    );
  }
}
</code></pre>
<blockquote>
<p>Note:</p>
<ul>
<li><p>context.watch : Provides the closest ancestor instance of the specified type and listens to changes on the instance. Equal to BlocProvider.of(context, listen: true). <a target="_blank" href="https://bloclibrary.dev/#/flutterbloccoreconcepts?id=contextwatch">Learn more</a>.</p>
</li>
<li><p>context.select : Same as context.watch, but allows you listen for changes in a smaller part of a state. <a target="_blank" href="https://bloclibrary.dev/#/flutterbloccoreconcepts?id=contextselect">Learn more</a>.</p>
</li>
</ul>
</blockquote>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Now we can open a chat with any Whatsapp user without saving the contact in the device.</p>
<p>GitHub: <a target="_blank" href="https://github.com/NirmalAriyathilake/blog_whatsapp_opener">https://github.com/NirmalAriyathilake/blog_whatsapp_opener</a></p>
<blockquote>
<p>“Good code is its own best documentation. As you're about to add a comment, ask yourself, 'How can I improve the code so that this comment isn't needed?' Improve the code and then document it to make it even clearer.” - Steve McConnell</p>
</blockquote>
]]></content:encoded></item></channel></rss>