<?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[Lam Nhan]]></title><description><![CDATA[Lam Nhan]]></description><link>https://blog.lamnhan.dev</link><generator>RSS for Node</generator><lastBuildDate>Sat, 02 May 2026 14:51:11 GMT</lastBuildDate><atom:link href="https://blog.lamnhan.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Flutter/Dart: Simplifying Isolate and Worker Usage]]></title><description><![CDATA[By using the isolate_manager v5.0.0, the Isolate on the VM and Worker on the Web will be implemented more easily than ever.
This package uses js_interop and the web package under the hood, so the app can freely compile to WASM.
Firstly, add an annota...]]></description><link>https://blog.lamnhan.dev/isolate-and-worker-in-flutter</link><guid isPermaLink="true">https://blog.lamnhan.dev/isolate-and-worker-in-flutter</guid><category><![CDATA[Flutter]]></category><category><![CDATA[Dart]]></category><dc:creator><![CDATA[Lam Nhan]]></dc:creator><pubDate>Wed, 15 May 2024 15:34:43 GMT</pubDate><content:encoded><![CDATA[<p>By using the <a target="_blank" href="https://pub.dev/packages/isolate_manager">isolate_manager v5.0.0</a>, the Isolate on the VM and Worker on the Web will be implemented more easily than ever.</p>
<p>This package uses <code>js_interop</code> and the <code>web</code> package under the hood, so the app can freely compile to WASM.</p>
<p>Firstly, add an annotation to a function that you want to use as a Worker:</p>
<pre><code class="lang-dart"><span class="hljs-meta">@isolateManagerWorker</span>
<span class="hljs-built_in">int</span> add(<span class="hljs-built_in">List</span>&lt;<span class="hljs-built_in">int</span>&gt; params){
  <span class="hljs-keyword">return</span> params[<span class="hljs-number">0</span>] + params[<span class="hljs-number">1</span>]
}

<span class="hljs-meta">@isolateManagerWorker</span>
<span class="hljs-built_in">int</span> subtract(<span class="hljs-built_in">List</span>&lt;<span class="hljs-built_in">int</span>&gt; params) {
  <span class="hljs-keyword">return</span> params[<span class="hljs-number">0</span>] - params[<span class="hljs-number">1</span>];
}
</code></pre>
<p>Note that those functions can not be depended on any Flutter UI library like <code>dart:ui</code> or <code>dart:io</code> which are not supported when compiling to <code>js</code>.</p>
<p>Then create an <code>IsolateManager</code> instance:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">final</span> isolatedAdd = IsolateManager.create(
                      add, 
                      workerName: <span class="hljs-string">'add'</span>, 
                      concurrent: <span class="hljs-number">2</span>,
                   );

<span class="hljs-keyword">final</span> isolatedSubtract = IsolateManager.create(
                      subtract, 
                      workerName: <span class="hljs-string">'subtract'</span>, 
                      concurrent: <span class="hljs-number">2</span>,
                   );
</code></pre>
<p>Run this command to generate the Workers:</p>
<pre><code class="lang-bash">dart run isolate_manager:generate
</code></pre>
<p>Now we can use it to compute something like:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">final</span> addResult = <span class="hljs-keyword">await</span> isolatedAdd([<span class="hljs-number">50</span>, <span class="hljs-number">30</span>]); <span class="hljs-comment">// 80</span>
<span class="hljs-keyword">final</span> subtractResult = <span class="hljs-keyword">await</span> isolatedSubtract([<span class="hljs-number">50</span>, <span class="hljs-number">30</span>]); <span class="hljs-comment">//20</span>
</code></pre>
<p>Or</p>
<pre><code class="lang-dart">StreamBuilder(
  stream: isolatedAdd.stream,
  builder: (context, snapshot) {
    <span class="hljs-keyword">if</span> (!snapshot.hasData) {
      <span class="hljs-keyword">return</span> <span class="hljs-keyword">const</span> Center(
        child: CircularProgressIndicator(),
      );
    }
    <span class="hljs-keyword">return</span> Text(<span class="hljs-string">'Isolated Add: <span class="hljs-subst">${snapshot.data}</span>'</span>);
  },
),
</code></pre>
<p>That’s it. Those isolated functions will work on both VM (via Isolate) and Web (via Worker).</p>
<p>Thank you for your reading.</p>
]]></content:encoded></item><item><title><![CDATA[Đa Ngôn Ngữ Cho Ứng Dụng Flutter Với LanguageHelper]]></title><description><![CDATA[PHẦN 1: CÁC BƯỚC THIẾT LẬP VÀ SỬ DỤNG CƠ BẢN
GIỚI THIỆU
Có nhiều cách để có thể sử dụng được nhiều ngôn ngữ (hay còn gọi là localization) cho ứng dụng Flutter. Ở bài viết này, mình sẽ giới thiệu thư viện language_helper và cách sử dụng của nó để có t...]]></description><link>https://blog.lamnhan.dev/da-ngon-ngu-cho-ung-dung-flutter-voi-languagehelper</link><guid isPermaLink="true">https://blog.lamnhan.dev/da-ngon-ngu-cho-ung-dung-flutter-voi-languagehelper</guid><category><![CDATA[Flutter]]></category><category><![CDATA[languages]]></category><category><![CDATA[localization]]></category><category><![CDATA[i18n]]></category><category><![CDATA[translation]]></category><dc:creator><![CDATA[Lam Nhan]]></dc:creator><pubDate>Sun, 03 Mar 2024 07:39:39 GMT</pubDate><content:encoded><![CDATA[<h1 id="heading-phan-1-cac-buoc-thiet-lap-va-su-dung-co-ban">PHẦN 1: CÁC BƯỚC THIẾT LẬP VÀ SỬ DỤNG CƠ BẢN</h1>
<h2 id="heading-gioi-thieu">GIỚI THIỆU</h2>
<p>Có nhiều cách để có thể sử dụng được nhiều ngôn ngữ (hay còn gọi là localization) cho ứng dụng Flutter. Ở bài viết này, mình sẽ giới thiệu thư viện <a target="_blank" href="https://pub.dev/packages/language_helper">language_helper</a> và cách sử dụng của nó để có thể tạo ứng dụng đa ngôn ngữ một cách đơn giản và hiệu quả nhất.</p>
<h2 id="heading-tinh-nang">TÍNH NĂNG</h2>
<ul>
<li><p>Dễ dàng kiểm soát và thay đổi ngôn ngữ trong ứng dụng. Tự động áp dụng ngôn ngữ hiện tại của máy cho ứng dụng.</p>
</li>
<li><p>Có thể kiểm soát bản dịch với <code>LanguageConditions</code> (như dịch số nhiều trong tiếng Anh).</p>
</li>
<li><p>Sử dụng generator để tách chuỗi cần thiết để tiện cho việc dịch (không cần sử dụng build_runner nên chạy rất nhanh).</p>
</li>
<li><p>Có thể sử dụng <code>Language Helper Translator</code> trên Chat GPT-4 để hỗ trợ dễ hơn cho việc dịch.</p>
</li>
</ul>
<h2 id="heading-cai-dat">CÀI ĐẶT</h2>
<p>Chỉ có bước này là bắt buộc trong quá trình phát triển ứng dụng, những bước còn lại có thể thực hiện sau khi ứng dụng đã sẵn sàng cho việc phát hành.</p>
<ul>
<li><p>Thêm <code>language_helper</code> vào project:</p>
<pre><code class="lang-bash">  flutter pub add language_helper
</code></pre>
</li>
<li><p>Thêm khởi tạo ngôn ngữ vào project:</p>
<pre><code class="lang-dart">  main() <span class="hljs-keyword">async</span> {
    WidgetsFlutterBinding.ensureInitialized();
    <span class="hljs-keyword">await</span> LanguageHelper.instance.initial(data: []);
    runApp(<span class="hljs-keyword">const</span> MyApp());
  }
</code></pre>
</li>
<li><p>Thêm <code>.tr</code>, <code>.trP</code> hay <code>.trF</code> vào những chuỗi cần dịch:</p>
<ul>
<li><p>Cơ bản:</p>
<pre><code class="lang-dart">  Text(<span class="hljs-string">'Chữ cần dịch'</span>.tr)
</code></pre>
</li>
<li><p>Dịch có điều kiện:</p>
<pre><code class="lang-dart">  Text(<span class="hljs-string">'Xin chào @{name}'</span>.trP({<span class="hljs-string">'name'</span>: name}))
</code></pre>
</li>
<li><p>Số nhiều:</p>
<pre><code class="lang-dart">  <span class="hljs-keyword">final</span> data = {
      <span class="hljs-string">'We have @{number} dollar'</span>: LanguageConditions(
          param: <span class="hljs-string">'number'</span>,
          conditions: {
              <span class="hljs-string">'0'</span>: <span class="hljs-string">'We have zero dollar'</span>,
              <span class="hljs-string">'1'</span>: <span class="hljs-string">'We have one dollar'</span>,
              <span class="hljs-string">'_'</span>: <span class="hljs-string">'We have @{number} dollars'</span>
          },
      ),
  }

  Text(<span class="hljs-string">'We have @{number} dollar'</span>.trP({<span class="hljs-string">'number'</span>: number}))
</code></pre>
</li>
</ul>
</li>
</ul>
<h2 id="heading-generator">GENERATOR</h2>
<p>Generate:</p>
<pre><code class="lang-bash">dart run language_helper:generate
</code></pre>
<p>Đường dẫn sau khi chạy:</p>
<pre><code class="lang-bash">.lib
|--- resources
|    |--- language_helper
|    |    |--- language_data.dart
|    |    |--- languages
|    |    |    |--- _generated.dart   ; This file will be overwritten when re-generating
</code></pre>
<h2 id="heading-dich-va-hieu-chinh">DỊCH VÀ HIỆU CHỈNH</h2>
<p>Tạo thêm tệp với ngôn ngữ tương ứng như <code>en.dart</code>, <code>vi.dart</code>,... ở trong thư mục <code>languages</code>.</p>
<p>Mở <a target="_blank" href="https://chat.openai.com/g/g-qoPMopEAb-language-helper-translator">Language Helper Translator</a> trên Chat-GPT 4, sao chép nội dung trong tệp <code>_generated.dart</code>, dán vào với theo mẫu sau và tiến hành dịch:</p>
<pre><code class="lang-dart">Đây là bản dịch của ứng dụng [Tên ứng dụng và mục đích cơ bản của ứng dụng]. Dịch sang [Ngôn ngữ đích]:

```dart
<span class="hljs-keyword">const</span> analysisLanguageData = &lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt;{
  <span class="hljs-string">'Xin chào @{name}'</span>: <span class="hljs-string">'Xin chào @{name}'</span>,
  <span class="hljs-string">'Chúng ta có @{number} đô-la'</span>: <span class="hljs-string">'Chúng ta có @{number} đô-la'</span>,
};
```
</code></pre>
<p>Sao chép kết quả và dán vào tệp ngôn ngữ tương ứng. Chẳng hạn như tệp <code>en.dart</code>:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">const</span> en = &lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt;{
  <span class="hljs-string">'Xin chào @{name}'</span>: <span class="hljs-string">'Xin chào @{name}'</span>,
  <span class="hljs-string">'Chúng ta có @{number} đô-la'</span>: <span class="hljs-string">'We have @{number} dollar'</span>,
};
</code></pre>
<p>Hiệu chỉnh lại kết quả cần thiết như số nhiều, bạn có thể hiệu chỉnh trực tiếp ở biến <code>en</code> hoặc tạo biến mới như <code>enOverride</code> như sau:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">final</span> enOverride = &lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt;{
  <span class="hljs-string">'Chúng ta có @{number} đô-la'</span>: LanguageCondition(
    param: <span class="hljs-string">'number'</span>,
    conditions: {
      <span class="hljs-string">'0'</span>: <span class="hljs-string">'We have zero dollar'</span>,
      <span class="hljs-string">'1'</span>: <span class="hljs-string">'We have one dollar'</span>,
      <span class="hljs-string">'_'</span>: <span class="hljs-string">'We have @{number} dollars'</span>,
    }
  ),
};
</code></pre>
<p>Thêm ngôn ngữ vừa dịch vào tệp <code>language_data.dart</code>:</p>
<pre><code class="lang-dart">LanguageData data = {
  LanguageCodes.vi: analysisLanguageData,
  LanguageCodes.en: en,
};

LanguageData dataOverrides = {
  LanguageCodes.en: enOverride,
};
</code></pre>
<p>Cập nhật lại trong project:</p>
<pre><code class="lang-dart">main() <span class="hljs-keyword">async</span> {
  WidgetsFlutterBinding.ensureInitialized();
  <span class="hljs-keyword">await</span> LanguageHelper.instance.initial(
      data: [LanguageDataProvider.data(data)]
      dataOverrides: [LanguageDataProvider.data(dataOverrides)],   
  );
  runApp(<span class="hljs-keyword">const</span> MyApp());
}
</code></pre>
<h2 id="heading-cap-nhat-widget-khi-ngon-ngu-thay-doi">CẬP NHẬT WIDGET KHI NGÔN NGỮ THAY ĐỔI</h2>
<p>Sử dụng <code>LanguageBuilder</code> hoặc <code>Tr</code> cho những Widget cần thay đổi khi ứng dụng thay đổi ngôn ngữ. Chỉ có <code>LanguageBuilder</code> cần thiết được cập nhật nên bạn có không cần quan tâm nếu sử dụng <code>LanguageBuilder</code> ở nhiều nơi dẫn đến dư thừa.</p>
<p>Ở <code>MaterialApp</code>:</p>
<pre><code class="lang-dart"><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> LanguageBuilder(
      builder: (context) {
        <span class="hljs-keyword">return</span> MaterialApp(
          localizationsDelegates: languageHelper.delegates,
          supportedLocales: languageHelper.locales,
          locale: languageHelper.locale,
          home: <span class="hljs-keyword">const</span> HomePage(),
        );
      }
    );
  }
}
</code></pre>
<p>Ở <code>Widget</code>:</p>
<pre><code class="lang-dart">LanguageBuilder(
    builder: (context) {
        <span class="hljs-keyword">return</span> Scaffold(
          body: Column(
            children: [
              Text(<span class="hljs-string">'Xin chào @{name}'</span>.tr),
              Text(<span class="hljs-string">'Chúng ta có @{number} đô-la'</span>.tr),
            ],
          ),
        );
    },
),
</code></pre>
<p>Sử dụng <code>Tr</code> (Phiên bản thu gọn của <code>LanguageBuilder</code>):</p>
<pre><code class="lang-dart">Tr((_) =&gt; Text(<span class="hljs-string">'Xin chào @{name}'</span>.tr)),
</code></pre>
<h2 id="heading-kiem-soat-ngon-ngu">KIỂM SOÁT NGÔN NGỮ</h2>
<p>Thay đổi ngôn ngữ:</p>
<pre><code class="lang-dart">languageHelper.change(LanguageCodes.vi);
</code></pre>
<p>Thêm bản dịch mới:</p>
<pre><code class="lang-dart">languageHelper.addData(LanguageDataProvider.data(newLanguageData));
languageHelper.addDataOverrides(LanguageDataProvider.data(newLanguageDataOverrides));
</code></pre>
<h2 id="heading-ket-luan">KẾT LUẬN</h2>
<p>Ở phần mở đầu, chúng ta đã hoàn thành việc cài đặt và cách sử dụng căn bản cho <a target="_blank" href="https://pub.dev/packages/language_helper">language_helper</a>. Ở phần tiếp theo, chúng ta sẽ tìm hiểu cách thiết lập để có thể sử dụng được cho ứng dụng phức tạp hơn với JSON.</p>
]]></content:encoded></item><item><title><![CDATA[How To Use OTP For Email Authentication In Flutter/Dart With Shared Hosting]]></title><description><![CDATA[Introduction
In the Flutter application, when we need to authenticate Email via OTP, we have many ways to achieve this, such as:

Send OTP directly from within the Flutter app. This method will not be safe because it is easy to reveal the email confi...]]></description><link>https://blog.lamnhan.dev/how-to-use-otp-for-email-authentication-in-flutter-dart-with-shared-hosting</link><guid isPermaLink="true">https://blog.lamnhan.dev/how-to-use-otp-for-email-authentication-in-flutter-dart-with-shared-hosting</guid><category><![CDATA[Flutter]]></category><category><![CDATA[Dart]]></category><category><![CDATA[authentication]]></category><category><![CDATA[email]]></category><category><![CDATA[shared hosting]]></category><dc:creator><![CDATA[Lam Nhan]]></dc:creator><pubDate>Mon, 04 Dec 2023 07:54:48 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1701676391155/cb9c38f9-7314-42b7-92fc-dadd43d3e90a.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>In the Flutter application, when we need to authenticate Email via OTP, we have many ways to achieve this, such as:</p>
<ul>
<li><p>Send OTP directly from within the Flutter app. This method will not be safe because it is easy to reveal the email configuration.</p>
</li>
<li><p>Send OTP via API call to the Server, the Server will be responsible for generating the OTP code and sending it to the user's email. In this way, we have two cases:</p>
<ul>
<li><p>The OTP when the user enters from Email will be re-authenticated at the Server. In this way, we need to store the Email on the Server. Therefore, with this method, we need more configuration to use it and can affect the privacy policy if not set up carefully.</p>
</li>
<li><p>OTP when the user enters from Email will be authenticated on the Client side. With this method, the Server only makes a request to send OTP to the user's email, and at the same time sends OTP to the application and the application will be responsible for re-authenticating the OTP when the user enters the code. <em>This is also the way I will guide in this article.</em></p>
</li>
</ul>
</li>
</ul>
<p>Currently, Shared Hosting is very cheap and popular in the market, so this is the goal I aim for. Besides, I chose the PHP language to perform the task of sending OTP because this is a very portable language, and can be used anywhere in Shared Hosting without any additional configuration (Javascript is supported but must be used with Nodejs and must configure Shared Hosting to run a Nodejs application).</p>
<p>The package mentioned is <a target="_blank" href="https://pub.dev/packages/auth_email">auth_email</a>, I will give more specific instructions in this article.</p>
<h2 id="heading-configuration-and-usage"><strong>Configuration And Usage</strong></h2>
<h3 id="heading-server">Server</h3>
<p>On the Server side, the package uses PHP as the language and also uses the <a target="_blank" href="https://github.com/PHPMailer/PHPMailer">PHPMailer</a> library to send emails to users.</p>
<p>First, you need to download <a target="_blank" href="https://raw.githubusercontent.com/vnniz/auth_email/main/server/php/releases/v0.0.4.zip">v0.0.4</a> and unzip it, you will get a folder named <code>src</code> with the following structure:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1701585777559/ba4cd3b2-046f-4fd5-a5b1-bda958b9e7f8.png?auto=compress,format&amp;format=webp" alt class="image--center mx-auto" /></p>
<p>And here is the content in <code>index.php</code>:</p>
<pre><code class="lang-dart"><span class="hljs-comment">// Set the SMTP server to send through</span>
$HOST = <span class="hljs-string">'example.com'</span>;
<span class="hljs-comment">// SMTP username</span>
$USER_NAME = <span class="hljs-string">'auth@example.com'</span>;
<span class="hljs-comment">// SMTP password</span>
$PASSWORD = <span class="hljs-string">'password'</span>;
<span class="hljs-comment">// TCP port to connect to</span>
$PORT = <span class="hljs-number">587</span>;
<span class="hljs-comment">// Send from</span>
$SEND_FROM = $USER_NAME;

<span class="hljs-comment">// Default subject for the email</span>
$DEFAULT_SUBJECT = <span class="hljs-string">'Verify Email'</span>;
<span class="hljs-comment">// Default body for the email</span>
$DEFAULT_BODY = <span class="hljs-string">'Please use this OTP to verify your email for the &lt;b&gt;{appName}&lt;/b&gt;, do not share this code to anyone: &lt;b&gt;{otp}&lt;/b&gt;'</span>;
<span class="hljs-comment">// Default length of the OTP</span>
$DEFAULT_OTP_LENGTH = <span class="hljs-number">6</span>;

<span class="hljs-comment">// Server key in SHA256 encryption (Ex: authemailtestkey)</span>
$SERVER_SHA256_KEY = <span class="hljs-string">'6955c3a2dbfd121697623896b38f5eb759d2cd503476980e14b9beb0cc036c4d'</span>;

<span class="hljs-comment">// Secure your applications</span>
$ALLOWED_APPS = [
    <span class="hljs-comment">// App name. Must be the same as the `appName` on the client side</span>
    <span class="hljs-string">'Auth Email Test'</span> =&gt; [
        <span class="hljs-comment">// Allow/Disallow the app using the modified subject</span>
        <span class="hljs-string">'modifiedSubject'</span> =&gt; <span class="hljs-keyword">false</span>,
        <span class="hljs-comment">// Allow/Disallow the app using the modified body</span>
        <span class="hljs-string">'modifiedBody'</span> =&gt; <span class="hljs-keyword">false</span>,
        <span class="hljs-comment">// Allow/Disallow the app using the modified OTP length</span>
        <span class="hljs-string">'modifiedOtpLength'</span> =&gt; <span class="hljs-keyword">false</span>,
    ],
];
</code></pre>
<p>With <code>$DEFAULT_BODY</code>, <code>{appName}</code> and <code>{otp}</code> will be replaced by the <code>appName</code> taken from the Client and the OTP is automatically generated at the Server.</p>
<p>The server only allows applications named in <code>$ALLOWED_APPS</code> to call to send an email.</p>
<p>After the configuration is complete, upload <code>PHPMailer,</code> <code>config.php</code>, and <code>index.php</code> to your Shared Hosting. For example, where you upload is <a target="_blank" href="https://example.com/auth/email">https://example.com/auth/email</a> to prepare for the next step.</p>
<p>So, we have completed the installation on the Server side.</p>
<h3 id="heading-client"><strong>Client</strong></h3>
<p>Add this package to your app:</p>
<pre><code class="lang-dart">flutter pub add auth_email
</code></pre>
<p>Create a new instance for <code>AuthEmail</code>:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">final</span> authEmail = AuthEmail(
    <span class="hljs-comment">// App name. Must be available in `$ALLOWED_APPS` on the Server.</span>
    appName: <span class="hljs-string">'Auth Email Test'</span>,
    <span class="hljs-comment">// Uploaded path.</span>
    server: <span class="hljs-string">'https://example.com/auth/email'</span>,
    <span class="hljs-comment">// Normal form of the server key.</span>
    serverKey: <span class="hljs-string">'authemailtestkey'</span>,
    <span class="hljs-comment">// Print the debug log.</span>
    isDebug: <span class="hljs-keyword">true</span>,
);
</code></pre>
<p>Next, OTP will be looked like this:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">final</span> <span class="hljs-built_in">bool</span> result = <span class="hljs-keyword">await</span> authEmail.sendOTP(
    email: <span class="hljs-string">'exampleclient@gmail.com'</span>, 
    subject: <span class="hljs-string">'Verify Email'</span>,
    body: <span class="hljs-string">'Please use this OTP to verify your email for the &lt;b&gt;{appName}&lt;/b&gt;, do not share this code to anyone: &lt;b&gt;{otp}&lt;/b&gt;'</span>,
    otpLength: <span class="hljs-number">6</span>,
);
</code></pre>
<p>Notice that you can only change the <code>subject</code>, <code>body</code> and <code>otpLength</code> when the configuration on the Server side allows to change these values.</p>
<p>And here is the way to re-authenticate an OTP inputted by the user:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">final</span> <span class="hljs-built_in">bool</span> isVerified = authEmail.verifyOTP(
    email: <span class="hljs-string">'exampleclient@gmail.com'</span>, 
    otp: <span class="hljs-string">'&lt;code&gt;'</span>
);
</code></pre>
<p><code>isVerified</code> will be <code>true</code> if the inputted OTP is right and <code>false</code> if it's wrong.</p>
<p>So, you can confirm whether the Email the user entered is a valid Email or not.</p>
<h2 id="heading-additional-information">Additional Information</h2>
<p>Before sending OTP, you can check whether the user's email is correct or not with this method:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">final</span> isValid = AuthEmail.isValidEmail(<span class="hljs-string">'exampleclient@gmail.com'</span>);
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Thus, we can simply and safely send and confirm OTP in the Flutter/Dart application when using Shared Hosting with PHP programming language.</p>
]]></content:encoded></item><item><title><![CDATA[Hướng Dẫn Sử Dụng OTP Để Xác Thực Email Trong Flutter/Dart Với Shared Hosting]]></title><description><![CDATA[Giới Thiệu
Trong ứng dụng Flutter, khi cần xác thực Email thông qua OTP, chúng ta có nhiều cách để đạt được điều này, chẳng hạn như:

Gửi OTP trực tiếp từ trong ứng dụng Flutter. Cách này sẽ không an toàn vì rất dễ để lộ cấu hình của email.

Gửi OTP ...]]></description><link>https://blog.lamnhan.dev/xac-thuc-email-qua-otp-trong-flutter-khi-dung-shared-hosting</link><guid isPermaLink="true">https://blog.lamnhan.dev/xac-thuc-email-qua-otp-trong-flutter-khi-dung-shared-hosting</guid><category><![CDATA[Flutter]]></category><category><![CDATA[Auth ]]></category><category><![CDATA[email]]></category><category><![CDATA[shared hosting]]></category><category><![CDATA[PHP]]></category><category><![CDATA[Dart]]></category><category><![CDATA[phpMailer]]></category><dc:creator><![CDATA[Lam Nhan]]></dc:creator><pubDate>Sun, 03 Dec 2023 08:14:36 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1701617641430/c5dd1f58-733d-42fb-84a1-0ec1e0346557.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-gioi-thieu">Giới Thiệu</h2>
<p>Trong ứng dụng Flutter, khi cần xác thực Email thông qua OTP, chúng ta có nhiều cách để đạt được điều này, chẳng hạn như:</p>
<ul>
<li><p>Gửi OTP trực tiếp từ trong ứng dụng Flutter. Cách này sẽ không an toàn vì rất dễ để lộ cấu hình của email.</p>
</li>
<li><p>Gửi OTP thông qua API gọi đến Server, Server sẽ chịu trách nhiệm tạo mã OTP và gửi đến email của người dùng. Ở cách này chúng ta có hai trường hơp:</p>
<ul>
<li><p>OTP khi người dùng nhập từ Email sẽ được xác thực lại ở Server, với cách này thì chúng ta cần lưu trữ Email ở Server. Do đó với cách này, chúng ta cần cấu hình nhiều hơn để sử dụng được và có thể ảnh hưởng đến chính sách bảo mật nếu không được thiết lập cẩn thận.</p>
</li>
<li><p>OTP khi người dùng nhập từ Email sẽ được xác thực ở phía Client. Với cách này thì Server chỉ thực hiện yêu cầu gửi OTP đến email người dùng, đồng thời gửi OTP đến ứng dụng và ứng dụng sẽ chịu trách nhiệm xác thực OTP khi người dùng nhập mã. <em>Đây cũng chính là cách mình sẽ hướng dẫn ở trong bài viết này.</em></p>
</li>
</ul>
</li>
</ul>
<p>Hiện nay, Shared Hosting có giá rất rẻ và rất phổ biến trên thị trường, do đó đây là mục tiêu mà mình hướng đến. Bên cạnh đó, mình chọn ngôn ngữ PHP để thực hiện nhiệm vụ gửi OTP vì đây là ngôn ngữ rất lưu động, có thể sử dụng bất kỳ đâu ở Shared Hosting mà không cần cấu hình gì thêm (Javascript được hỗ trợ nhưng phải sử dụng với Nodejs và phải cấu hình Shared Hosting để chạy được ứng dụng Nodejs).</p>
<p>Package được đề cập đến là <a target="_blank" href="https://pub.dev/packages/auth_email">auth_email</a>, mình sẽ hướng dẫn cụ thể hơn ở bài viết này.</p>
<h2 id="heading-cau-hinh-va-su-dung">Cấu Hình Và Sử Dụng</h2>
<h3 id="heading-server">Server</h3>
<p>Ở phía Server, package sử dụng PHP làm ngôn ngữ, đồng thời sử dụng thư viện <a target="_blank" href="https://github.com/PHPMailer/PHPMailer">PHPMailer</a> để gửi email tới người dùng.</p>
<p>Trước tiên, bạn cần tải <a target="_blank" href="https://raw.githubusercontent.com/vnniz/auth_email/main/server/php/releases/v0.0.4.zip">v0.0.4</a> về và giải nén, khi đó bạn sẽ nhận được được thư mục có tên <code>src</code> với cấu trúc như sau:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1701585777559/ba4cd3b2-046f-4fd5-a5b1-bda958b9e7f8.png" alt class="image--center mx-auto" /></p>
<p>Và đây là nội dung trong file <code>index.php</code>:</p>
<pre><code class="lang-php"><span class="hljs-comment">// Set the SMTP server to send through</span>
$HOST = <span class="hljs-string">'example.com'</span>;
<span class="hljs-comment">// SMTP username</span>
$USER_NAME = <span class="hljs-string">'auth@example.com'</span>;
<span class="hljs-comment">// SMTP password</span>
$PASSWORD = <span class="hljs-string">'password'</span>;
<span class="hljs-comment">// TCP port to connect to</span>
$PORT = <span class="hljs-number">587</span>;
<span class="hljs-comment">// Send from</span>
$SEND_FROM = $USER_NAME;

<span class="hljs-comment">// Tiêu đề mặc định cho Email</span>
$DEFAULT_SUBJECT = <span class="hljs-string">'Verify Email'</span>;
<span class="hljs-comment">// Nội dung mặc định cho Email</span>
$DEFAULT_BODY = <span class="hljs-string">'Please use this OTP to verify your email for the &lt;b&gt;{appName}&lt;/b&gt;, do not share this code to anyone: &lt;b&gt;{otp}&lt;/b&gt;'</span>;
<span class="hljs-comment">// Độ dài mặc định của OTP</span>
$DEFAULT_OTP_LENGTH = <span class="hljs-number">6</span>;

<span class="hljs-comment">// Client key ở mã hóa SHA256 (VD: authemailtestkey)</span>
$SERVER_SHA256_KEY = <span class="hljs-string">'6955c3a2dbfd121697623896b38f5eb759d2cd503476980e14b9beb0cc036c4d'</span>;

<span class="hljs-comment">// Bảo mật cho ứng dụng của bạn</span>
$ALLOWED_APPS = [
    <span class="hljs-comment">// Tên ứng dụng. `appName` ở client phải trùng với tên ứng dụng này</span>
    <span class="hljs-string">'Auth Email Test'</span> =&gt; [
        <span class="hljs-comment">// Cho phép/Không cho phép ứng dụng sử dụng tiêu đề tùy chỉnh</span>
        <span class="hljs-string">'modifiedSubject'</span> =&gt; <span class="hljs-literal">false</span>,
        <span class="hljs-comment">// Cho phép/Không cho phép ứng dụng sử dụng nội dung tùy chỉnh</span>
        <span class="hljs-string">'modifiedBody'</span> =&gt; <span class="hljs-literal">false</span>,
        <span class="hljs-comment">// Cho phép/Không cho phép ứng dụng sử dụng độ dài OTP tùy chỉnh</span>
        <span class="hljs-string">'modifiedOtpLength'</span> =&gt; <span class="hljs-literal">false</span>,
    ],
];
</code></pre>
<p>Bạn hãy cấu hình dựa trên thông tin từ nhà cung cấp email, với số lượng ít thì bạn có thể sử dụng Gmail cho việc xác thực này. Mình sẽ có một bài viết cụ thể hơn về cách sử dụng SMTP trong Gmail.</p>
<p>Với <code>$DEFAULT_BODY</code>, <code>{appName}</code> và <code>{otp}</code> sẽ được thay thế bởi <code>appName</code> được lấy từ Client và <code>OTP</code> được tự động tạo ở Server.</p>
<p>Server chỉ cho phép ứng dụng có tên trong <code>$ALLOWED_APPS</code> gọi tới để gửi email.</p>
<p>Sau khi cấu hình xong, bạn hãy tải <code>PHPMailer</code>, <code>config.php</code> và <code>index.php</code> lên Shared Hosting của bạn. Ví dụ nơi bạn tải lên là <code>https://example.com/auth/email</code> để chuẩn bị cho bước tiếp theo.</p>
<p>Như vậy, chúng ta đã hoàn thiện việc cài đặt ở phía Server.</p>
<h3 id="heading-client">Client</h3>
<p>Thêm package vào ứng dụng:</p>
<pre><code class="lang-powershell">flutter pub add auth_email
</code></pre>
<p>Tạo instance mới cho <code>AuthEmail</code>:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">final</span> authEmail = AuthEmail(
    <span class="hljs-comment">// Tên của ứng dụng. Phải có trong `$ALLOWED_APPS` ở server.</span>
    appName: <span class="hljs-string">'Auth Email Test'</span>,
    <span class="hljs-comment">// URL của server.</span>
    server: <span class="hljs-string">'https://example.com/auth/email'</span>,
    <span class="hljs-comment">// Key của server ở dạng thường.</span>
    serverKey: <span class="hljs-string">'authemailtestkey'</span>,
    <span class="hljs-comment">// In log để debug</span>
    isDebug: <span class="hljs-keyword">true</span>,
);
</code></pre>
<p>Tiếp theo, OTP sẽ được gửi như sau:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">final</span> <span class="hljs-built_in">bool</span> result = <span class="hljs-keyword">await</span> authEmail.sendOTP(
    email: <span class="hljs-string">'exampleclient@gmail.com'</span>, 
    subject: <span class="hljs-string">'Xác Thực'</span>,
    body: <span class="hljs-string">'Vui lòng sử dụng OTP này để xác thực cho ứng dụng &lt;b&gt;{appName}&lt;/b&gt;, không chia sẻ với bất kỳ ai: &lt;b&gt;{otp}&lt;/b&gt;'</span>,
    otpLength: <span class="hljs-number">6</span>,
);
</code></pre>
<p>Lưu ý là bạn chỉ có thể thay đổi <code>subject</code>, <code>body</code> và <code>otpLength</code> khi cấu hình ở Server cho phép thay đổi những thông tin này.</p>
<p>Và đây là cách để xác thực OTP sau khi người dùng nhập mã từ Email:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">final</span> <span class="hljs-built_in">bool</span> isVerified = authEmail.verifyOTP(
    email: <span class="hljs-string">'exampleclient@gmail.com'</span>, 
    otp: <span class="hljs-string">'&lt;code&gt;'</span>
);
</code></pre>
<p><code>isVerified</code> sẽ trả về <code>true</code> nếu OTP đã nhập đúng và <code>false</code> nếu OTP sai.</p>
<p>Như vậy, bạn đã có thể xác nhận được Email người dùng nhập có là Email khả dụng hay không rồi.</p>
<h2 id="heading-thong-tin-them">Thông Tin Thêm</h2>
<p>Trước khi thực hiện việc gửi OTP, bạn có thể kiểm tra xem Email người dùng có chính xác hay chưa bằng hàm:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">final</span> isValid = AuthEmail.isValidEmail(<span class="hljs-string">'exampleclient@gmail.com'</span>);
</code></pre>
<h2 id="heading-ket-thuc">Kết Thúc</h2>
<p>Như vậy, chúng ta đã có thể gửi và xác nhận OTP một cách đơn giản, an toàn trong ứng dụng Flutter (Dart) khi sử dụng Shared Hosting với ngôn ngữ PHP.</p>
]]></content:encoded></item></channel></rss>