<?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: Victor Ihedioha</title>
    <description>The latest articles on DEV Community by Victor Ihedioha (@apow).</description>
    <link>https://dev.to/apow</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%2F574017%2F4f1e6f23-176a-4650-834c-8ea307f86163.jpeg</url>
      <title>DEV Community: Victor Ihedioha</title>
      <link>https://dev.to/apow</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/apow"/>
    <language>en</language>
    <item>
      <title>Introducing the PII Mask Maven Dependency: Secure Your JSON Data with Ease</title>
      <dc:creator>Victor Ihedioha</dc:creator>
      <pubDate>Tue, 12 Nov 2024 09:19:25 +0000</pubDate>
      <link>https://dev.to/apow/introducing-the-pii-mask-maven-dependency-secure-your-json-data-with-ease-oie</link>
      <guid>https://dev.to/apow/introducing-the-pii-mask-maven-dependency-secure-your-json-data-with-ease-oie</guid>
      <description>&lt;p&gt;With privacy and data protection becoming increasingly important, I'm excited to announce the release of the new &lt;strong&gt;PII Masking Maven Dependency&lt;/strong&gt;. This dependency is designed to make it effortless for developers to add personal data masking to their applications, ensuring that sensitive information stays protected.&lt;/p&gt;

&lt;p&gt;The goal is to create an accessible, flexible, and powerful library that provides easy-to-use annotations for automatic data masking. I'm inviting the developer community to try it out, provide feedback, and join me in making this tool even better!&lt;/p&gt;




&lt;h2&gt;
  
  
  🎯 Why Use the PII Masking Library?
&lt;/h2&gt;

&lt;p&gt;Data masking is critical in today’s digital landscape for several reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Enhanced Security&lt;/strong&gt;: Protect users’ sensitive data from exposure in logs, exceptions, or debugging output.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regulatory Compliance&lt;/strong&gt;: Easily adhere to regulations like GDPR, HIPAA, and PCI-DSS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Developer-Friendly&lt;/strong&gt;: Automatically mask data without manual intervention or additional configuration—perfect for fast-paced development environments.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This PII Mask Maven Dependency offers a powerful and straightforward approach to masking sensitive fields in your data models using simple annotations.&lt;/p&gt;




&lt;h2&gt;
  
  
  📦 Key Features
&lt;/h2&gt;

&lt;p&gt;The library supports a variety of customization options to meet different data protection needs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Flexible Masking Options&lt;/strong&gt;: Choose masking patterns that best suit your data requirements.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Annotation-Based Configuration&lt;/strong&gt;: Mark fields with a single &lt;code&gt;@MaskData&lt;/code&gt; annotation to enable masking—no additional setup required.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integration with Jackson&lt;/strong&gt;: Works seamlessly with Jackson for automatic JSON serialization, ensuring that masked data is correctly represented in logs and outputs.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  🤝 How It Works
&lt;/h2&gt;

&lt;p&gt;The PII Masking Library offers simple annotations that can be applied directly to fields, allowing you to define:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Mask Type&lt;/strong&gt;: Choose to mask the beginning, end, middle, or both ends of a string field.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mask Length&lt;/strong&gt;: Specify the number of characters to mask for more granular control.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Example Usage
&lt;/h3&gt;

&lt;p&gt;Here’s how a model class can use the &lt;code&gt;@MaskData&lt;/code&gt; annotation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@MaskData&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MaskType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;MASK_END&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;creditCardNumber&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// getters and setters&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🚀 Join me in Building a Secure Data Ecosystem
&lt;/h2&gt;

&lt;p&gt;I believe that data security should be easy to implement, accessible, and highly customizable. This PII Masking Library is an open-source project, and we’re looking for developers to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Try It Out&lt;/strong&gt;: Integrate it into your applications and explore its capabilities.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Give Feedback&lt;/strong&gt;: Let us know what works and what doesn’t, and suggest improvements.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Collaborate&lt;/strong&gt;: Contribute to the project on GitHub, whether by submitting bug reports, suggesting features, or contributing code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Get Started&lt;/strong&gt; by adding this &lt;a href="https://github.com/Vicynet/pii-mask/packages/2296212?version=1.0.2" rel="noopener noreferrer"&gt;dependency&lt;/a&gt; to your Maven project and try out the powerful data masking capabilities today. If you’d like to collaborate or have questions, check out our GitHub repository and join the conversation.&lt;/p&gt;

</description>
      <category>java</category>
      <category>security</category>
      <category>productivity</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Harmonizing Technology and Faith: The Final Composition of the AI Bible Chat App</title>
      <dc:creator>Victor Ihedioha</dc:creator>
      <pubDate>Thu, 27 Jun 2024 09:45:00 +0000</pubDate>
      <link>https://dev.to/apow/harmonizing-technology-and-faith-the-final-composition-of-the-ai-bible-chat-app-i71</link>
      <guid>https://dev.to/apow/harmonizing-technology-and-faith-the-final-composition-of-the-ai-bible-chat-app-i71</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this final part of our series, we’ll see how the local database service, the Gemini AI service, and the user interface combine to create a seamless and interactive AI Bible chat app.&lt;/p&gt;

&lt;p&gt;At the time of creating this final write-up, I had made version 2 of the app, which includes an AI-created audio podcast.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Symphony of Services
&lt;/h2&gt;

&lt;p&gt;Our app is akin to an orchestra, where each service plays a crucial role in the overall performance. The local database service (sqflite) acts as the foundation, storing user data and scripture content for offline access. The Gemini AI service is the soloist, interpreting user queries and delivering insightful responses. The Gemini AI goes further to create delightful audio podcast content which is then transmitted through text-to-speech (TTS)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ldx06j13v665368unk6.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ldx06j13v665368unk6.jpg" alt="Image description" width="486" height="1080"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Local Database Service
&lt;/h2&gt;

&lt;p&gt;The sqflite service ensures that users have a smooth experience by providing quick access to saved scriptures and chats. It’s designed to be efficient and reliable, syncing with the cloud when online to update content and user data.&lt;/p&gt;

&lt;p&gt;The chat history is stored locally and can be retrieved at any time, allowing users to review past conversations and reflect on their learning journey.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkeskpzdy4p0j3gemxkdd.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkeskpzdy4p0j3gemxkdd.jpg" alt="Image description" width="486" height="1080"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Gemini AI Service
&lt;/h2&gt;

&lt;p&gt;Integrating the Gemini AI SDK allows the app to understand and respond to complex queries. It uses natural language processing to provide users with relevant scripture passages and interpretations, making the study of the Bible more engaging.&lt;/p&gt;

&lt;h2&gt;
  
  
  Continuous Chat
&lt;/h2&gt;

&lt;p&gt;To maintain a continuous chat experience, we keep the instance of the chat session alive throughout the app’s lifecycle. This allows users to pick up the conversation where they left off, ensuring continuity and context.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcm4ablb0bxpxqcsktqlq.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcm4ablb0bxpxqcsktqlq.jpg" alt="Image description" width="486" height="1080"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  User Interface Decisions
&lt;/h2&gt;

&lt;p&gt;The user interface (UI) is the conductor, guiding the user through their experience with clarity and purpose. I made several UI decisions with the user’s needs in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Simplicity:&lt;/strong&gt; The UI is clean and uncluttered, avoiding unnecessary distractions and focusing on the content.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Intuitive Navigation:&lt;/strong&gt; The navigation is intuitive, with clear labels and logical flow, making it easy for users to find what they’re looking for.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Responsive Design:&lt;/strong&gt; The app is responsive, providing a consistent experience across different devices and screen sizes.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;The AI Bible Chat app is not just a tool; it's a companion for those seeking wisdom and guidance through the scriptures. By combining the local database service, the Gemini AI service, and a thoughtfully designed UI, I have created an app that not only meets technical standards but also touches the hearts of its users.&lt;/p&gt;

&lt;p&gt;As we conclude this series, remember that the journey of innovation is ongoing. There’s always room for improvement, and feedback from users is invaluable. Let’s continue to explore, learn, and grow together in our quest to harmonize technology and faith.&lt;/p&gt;

&lt;p&gt;Here's a link to the previous article titled: &lt;strong&gt;&lt;a href="https://dev.to/apow/integrating-ai-with-grace-the-gemini-sdk-and-flutter-part-3-2n3f"&gt;Integrating AI with Grace: The Gemini SDK and Flutter - Part 3&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Download the APK app sample (arm64) &lt;a href="https://drive.google.com/file/d/10Y4-Qy5W-mBIywQqzwLddR4z0XrtSdtI/view?usp=sharing"&gt;here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>buildwithgemini</category>
      <category>flutter</category>
      <category>ai</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Integrating AI with Grace: The Gemini SDK and Flutter - Part 3</title>
      <dc:creator>Victor Ihedioha</dc:creator>
      <pubDate>Wed, 22 May 2024 21:08:18 +0000</pubDate>
      <link>https://dev.to/apow/integrating-ai-with-grace-the-gemini-sdk-and-flutter-part-3-2n3f</link>
      <guid>https://dev.to/apow/integrating-ai-with-grace-the-gemini-sdk-and-flutter-part-3-2n3f</guid>
      <description>&lt;p&gt;Welcome to the third part of our series on building an AI Bible chat app. In this segment, we’ll explore the integration of the Google Gemini AI SDK with Flutter, delve into prompt design, and discuss maintaining persistent chat sessions.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is Prompt Design and Why Gemini?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Prompt design&lt;/strong&gt; is creating and tweaking prompts given to a Large Language Model (Gemini AI Models) to get the desired type and output quality. This is a process of trial and error until you get a specific prompt that fits your use case. &lt;/p&gt;

&lt;p&gt;Interestingly, the prompt design for the AI Bible Chat App took more than half my day, before I could get the exact output I wanted.&lt;/p&gt;

&lt;h2&gt;
  
  
  Training on Google AI Studio
&lt;/h2&gt;

&lt;p&gt;I had to learn and experiment with &lt;a href="https://aistudio.google.com"&gt;Google AI Studio&lt;/a&gt;, a browser-based IDE for prototyping with Google's generative models.&lt;/p&gt;

&lt;p&gt;With Generative AI, there's I had no need for a large database of Scriptures, even more, the app is capable of delivering in-depth analysis of scriptures just by using the free access Gemini API, model 1.5 pro.&lt;/p&gt;

&lt;p&gt;The Google Gemini 1.5 pro model is multi-modal and takes texts and images as input and outputs text and input also, which is dependent on your prompt.&lt;/p&gt;

&lt;p&gt;You could experiment with Freeform (open-ended text)&lt;br&gt;
Structured (predefined format, examples of request and response), and&lt;br&gt;
Chat (enables a user to have a natural ongoing conversation with the model) type of prompt, depending on your use case. &lt;/p&gt;

&lt;p&gt;For our AI Bible Chat App, both the structured and chat types were applied for optimal output.&lt;/p&gt;
&lt;h2&gt;
  
  
  Integrating Google Gemini AI SDK in Flutter
&lt;/h2&gt;

&lt;p&gt;The Google Gemini AI SDK is a powerful tool that allows Flutter developers to incorporate AI functionalities into their apps. Here’s how you can integrate it into your Flutter project:&lt;/p&gt;
&lt;h2&gt;
  
  
  Setting Up Your API Key
&lt;/h2&gt;

&lt;p&gt;Before you can start using the Gemini AI SDK, you need to obtain an API key from Google AI Studio. This key will authenticate your requests to the AI services.&lt;/p&gt;
&lt;h2&gt;
  
  
  Installing the SDK
&lt;/h2&gt;

&lt;p&gt;Add the Gemini AI SDK to your pubspec.yaml file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dependencies:
  google_generative_ai: ^0.4.0 (or latest version)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run &lt;code&gt;flutter pub get&lt;/code&gt; to install the package.&lt;/p&gt;

&lt;h2&gt;
  
  
  Initializing the SDK
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  GenerativeModel _initializeAIModel() {
    const apiKey = 'YOUR_API_KEY';
    openModel ??= GenerativeModel(
        model: 'gemini-pro',
        apiKey: apiKey,
        generationConfig: GenerationConfig(maxOutputTokens: 1000));
    return openModel!;
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Maintaining Persistent Chat Sessions
&lt;/h2&gt;

&lt;p&gt;To keep chat sessions open for continuous interaction, you can use static variables to maintain the state across different user inputs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  static ChatSession? openChatSession;

  ChatSession _initializeAIChatSession(
      String userQuery, String companionResponse) {
    final newChatSession = _initializeAIModel().startChat(history: [
      Content.text(userQuery),
      Content.model([TextPart(companionResponse)])
    ]);
    openChatSession = newChatSession;
    return newChatSession;
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Integrating AI into your Flutter app with the Gemini AI SDK opens up a world of possibilities. By understanding prompt design and maintaining persistent chat sessions, you can create an engaging and interactive Bible study experience for your users.&lt;/p&gt;

&lt;p&gt;Stay tuned for the next article, &lt;strong&gt;Harmonizing Technology and Faith: The Final Composition of the AI Bible Chat App&lt;/strong&gt;, where we’ll dive into the user interface design and user experience considerations for our AI Bible chat app.&lt;/p&gt;

&lt;p&gt;Remember, the key to a successful Generative AI integration is a combination of technical know-how and creative prompt design. Keep experimenting and refining your approach to achieve the best results.&lt;/p&gt;

&lt;p&gt;Here's a link to the previous article &lt;a href="https://dev.to/apow/flutter-faith-crafting-an-ai-bible-chat-app-with-stacked-architecture-part-2-18no"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Download the APK app sample (arm64) &lt;a href="https://drive.google.com/file/d/1_kBvIbgYM8-VfWvVVYyt1RbCvGbkIi2K/view?usp=sharing"&gt;here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>buildwithgemini</category>
      <category>flutter</category>
      <category>ai</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Flutter &amp; Faith: Crafting an AI Bible Chat App with Stacked Architecture - Part 2</title>
      <dc:creator>Victor Ihedioha</dc:creator>
      <pubDate>Mon, 20 May 2024 08:51:41 +0000</pubDate>
      <link>https://dev.to/apow/flutter-faith-crafting-an-ai-bible-chat-app-with-stacked-architecture-part-2-18no</link>
      <guid>https://dev.to/apow/flutter-faith-crafting-an-ai-bible-chat-app-with-stacked-architecture-part-2-18no</guid>
      <description>&lt;h2&gt;
  
  
  Setting Up Flutter with Stacked for Optimal Performance
&lt;/h2&gt;

&lt;p&gt;Welcome back to the second installment of our technical deep dive into building an AI Bible chat app. In the first part, we introduced the concept and the technologies involved. Now, let’s roll up our sleeves and get into the nitty-gritty of setting up Flutter with the Stacked architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Stacked
&lt;/h2&gt;

&lt;p&gt;Stacked provides a clean architecture that separates UI and business logic, which is crucial for maintainability and scalability. It’s especially beneficial for complex state management and dependency injection, ensuring that our app remains performant and responsive. To me, one special use of Stacked is the elimination of boilerplate code and unnecessary repetitive logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Initializing the Project
&lt;/h2&gt;

&lt;p&gt;First, we set up our Flutter environment to use Stacked CLI&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Install Stacked CLI&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dart pub global activate stacked_cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might see an instruction asking you to add a pub path to your system or user path environment of your PC or Mac, please do so.&lt;/p&gt;

&lt;p&gt;Ensure you have the latest version of Flutter installed and your favorite IDE ready to go. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create a new Flutter project leveraging Stacked CLI&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;stacked create app companion --description MyApp --org com.myapp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above line tells Stacked CLI to create a new Flutter project called "&lt;strong&gt;companion&lt;/strong&gt;", with description, "&lt;strong&gt;MyApp&lt;/strong&gt;", and company domain, which is a unique identifier of your app, in case you decide to publish on the app stores "&lt;strong&gt;com.myapp&lt;/strong&gt;"&lt;/p&gt;

&lt;p&gt;Sit, watch, and marvel at how Stacked CLI creates your Flutter app with necessary starter dependencies, starter views, view-models, and test classes. What more can a productive mobile engineer ask for?&lt;/p&gt;

&lt;p&gt;You can add new views, services, dialogs, bottomsheets, widgets, and all come with their view-model and test classes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;stacked create view bible_chat
stacked create service bible_chat
stacked create dialog history_dialog
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you make changes to your models, modify any view, let's say your view now accepts a parameter, instead of executing the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flutter pub run build_runner build --delete-conflicting-outputs 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can run stacked generate.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;stacked generate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Preparing the Bible Data
&lt;/h2&gt;

&lt;p&gt;Our app’s core functionality revolves around providing users with scripture passages. To do this, we need a comprehensive dataset that includes the books of the Bible, chapters, verses, and various translations in different languages. "&lt;strong&gt;I have reasons I didn't engage the Gemini AI for this task&lt;/strong&gt;"&lt;/p&gt;

&lt;p&gt;The bible data will be initialised and used on the start of the project in a very fast way.&lt;/p&gt;

&lt;p&gt;Here's a sample JSON of our Bible data placed in &lt;strong&gt;assets/data/app_bible.json&lt;/strong&gt; in our project&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  {
    "versions": [
        {
            "name": "King James Vversion",
            "label": "KJV"
        },
        {
            "name": "New King James Vversion",
            "label": "NKJV"
        }
   ],
   "languages": [
        "English",
        "Spanish",
        "French",
        "Italian",
        "Portuguese",
        "Pidgin",
        "Igbo",
        "Yoruba",
        "Hausa"
  ],
 "books": [
    {
      "name": "Genesis",
      "chapters": [
        {
          "number": 1,
          "verses": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]
        },
        {
          "number": 2,
          "verses": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]
        }
      ]
    },
{
      "name": "Exodus",
      "chapters": [
        {
          "number": 1,
          "verses": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]
        },
        {
          "number": 2,
          "verses": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]
        }
      ]
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's what our Bible data service looks like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class BibleDataService {

  Map&amp;lt;String, dynamic&amp;gt; _bibleData = {};

  Future&amp;lt;Map&amp;lt;String, dynamic&amp;gt;?&amp;gt; readJson() async {
    try {
      final String response =
          await rootBundle.loadString('assets/data/app_bible.json');
      _bibleData = await jsonDecode(response);
      return _bibleData;
    } catch (e) {
      return null;
    }
  }

  List&amp;lt;String&amp;gt; getBooks() {
    return _bibleData['books']
        .map((book) =&amp;gt; book['name'])
        .whereType&amp;lt;String&amp;gt;()
        .toList();
  }

  List&amp;lt;int&amp;gt; getChapters(String bookName) {
    final book =
        _bibleData['books'].firstWhere((book) =&amp;gt; book['name'] == bookName);
    return book['chapters']
        .map((chapter) =&amp;gt; chapter['number'])
        .whereType&amp;lt;int&amp;gt;()
        .toList();
  }

  List&amp;lt;int&amp;gt; getVerses(String bookName, int chapterNumber) {
    final book =
        _bibleData['books'].firstWhere((book) =&amp;gt; book['name'] == bookName);
    final chapter = book['chapters']
        .firstWhere((chapter) =&amp;gt; chapter['number'] == chapterNumber);
    return chapter['verses'].cast&amp;lt;int&amp;gt;();
  }

  List&amp;lt;String&amp;gt; getVersions() {
    return _bibleData['versions']
        .map((version) =&amp;gt; version['label'])
        .whereType&amp;lt;String&amp;gt;()
        .toList();
  }

  List&amp;lt;String&amp;gt; getLanguages() {
    return _bibleData['languages']
        .map((language) =&amp;gt; language)
        .whereType&amp;lt;String&amp;gt;()
        .toList();
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: You could create models that mock this same exact data, making it even more easier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Chat Models for a seamless chat experience
&lt;/h2&gt;

&lt;p&gt;We need models to represent conversations and sessions in our app, so the user can have continuous conversations with Gemini AI and access the history of conversations with Gemini AI in our app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;conversation.dart&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Conversation {
  int? id;
  String? conversationId;
  String? role;
  String? message;
  DateTime? timestamp;

  Conversation({
    this.id,
    this.conversationId,
    this.role,
    this.message,
    this.timestamp,
  });

  Conversation.fromJson(Map&amp;lt;String, dynamic&amp;gt; json) {
    id = json['id'];
    conversationId = json['conversationId'];
    role = json['role'];
    message = json['message'];
    timestamp = DateTime.parse(json['timestamp']);
  }

  Map&amp;lt;String, dynamic&amp;gt; toJson() {
    final Map&amp;lt;String, dynamic&amp;gt; data = &amp;lt;String, dynamic&amp;gt;{};
    data['id'] = id;
    data['conversationId'] = conversationId;
    data['role'] = role;
    data['message'] = message;
    data['timestamp'] = timestamp?.toIso8601String();
    return data;
  }

  factory Conversation.fromMap(Map&amp;lt;String, dynamic&amp;gt; map) {
    return Conversation(
      id: map['id'],
      conversationId: map['conversationId'],
      role: map['role'],
      message: map['message'],
      timestamp: DateTime.parse(map['timestamp']),
    );
  }

  Map&amp;lt;String, dynamic&amp;gt; toMap() {
    return {
      'id': id,
      'conversationId': conversationId,
      'role': role,
      'message': message,
      'timestamp': timestamp?.toIso8601String(),
    };
  }
}

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;session.dart&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Session {
  String? sessionId;
  String? conversationId;
  String? title;
  DateTime? timestamp;

  Session({
    this.sessionId,
    this.conversationId,
    this.title,
    this.timestamp,
  });

  Session.fromJson(Map&amp;lt;String, dynamic&amp;gt; json) {
    sessionId = json['sessionId'];
    conversationId = json['conversationId'];
    title = json['title'];
    timestamp = DateTime.parse(json['timestamp']);
  }

  Map&amp;lt;String, dynamic&amp;gt; toJson() {
    final Map&amp;lt;String, dynamic&amp;gt; data = &amp;lt;String, dynamic&amp;gt;{};
    data['sessionId'] = sessionId;
    data['conversationId'] = conversationId;
    data['title'] = title;
    data['timestamp'] = timestamp?.toIso8601String();
    return data;
  }

  factory Session.fromMap(Map&amp;lt;String, dynamic&amp;gt; map) {
    return Session(
      sessionId: map['sessionId'],
      conversationId: map['conversationId'],
      title: map['title'],
      timestamp: DateTime.parse(map['timestamp']),
    );
  }

  Map&amp;lt;String, dynamic&amp;gt; toMap() {
    return {
      'sessionId': sessionId,
      'conversationId': conversationId,
      'title': title,
      'timestamp': timestamp?.toIso8601String(),
    };
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;session_conversations.dart&lt;/strong&gt; (To identify group of conversations with the associated session)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class SessionConversations {
  Session? session;
  List&amp;lt;Conversation&amp;gt;? conversations;

  SessionConversations({
    this.session,
    this.conversations,
  });

  SessionConversations.fromJson(Map&amp;lt;String, dynamic&amp;gt; json) {
    session = json['session'];
    if (json['conversations'] != null) {
      conversations = &amp;lt;Conversation&amp;gt;[];
      json['conversations'].forEach((v) {
        conversations!.add(Conversation.fromJson(v));
      });
    }
  }

  Map&amp;lt;String, dynamic&amp;gt; toJson() {
    final Map&amp;lt;String, dynamic&amp;gt; data = &amp;lt;String, dynamic&amp;gt;{};
    data['session'] = session;
    if (conversations != null) {
      data['conversations'] =
          conversations!.map((conversation) =&amp;gt; conversation.toJson()).toList();
    }
    return data;
  }

  factory SessionConversations.fromMap(Map&amp;lt;String, dynamic&amp;gt; map) {
    return SessionConversations(
      session: map['session'],
      conversations: List&amp;lt;Conversation&amp;gt;.from(map['conversations']
          ?.map((conversation) =&amp;gt; Conversation.fromMap(conversation))),
    );
  }

  Map&amp;lt;String, dynamic&amp;gt; toMap() {
    return {
      'session': session,
      'conversations':
          conversations?.map((conversation) =&amp;gt; conversation.toMap()).toList(),
    };
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;sessions.dart&lt;/strong&gt; (History of all conversations by the user)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Sessions {
  List&amp;lt;SessionConversations&amp;gt;? sessions;

  Sessions({
    this.sessions,
  });

  Sessions.fromJson(Map&amp;lt;String, dynamic&amp;gt; json) {
    if (json['sessions'] != null) {
      sessions = &amp;lt;SessionConversations&amp;gt;[];
      json['conversations'].forEach((v) {
        sessions!.add(SessionConversations.fromJson(v));
      });
    }
  }

  Map&amp;lt;String, dynamic&amp;gt; toJson() {
    final Map&amp;lt;String, dynamic&amp;gt; data = &amp;lt;String, dynamic&amp;gt;{};
    if (sessions != null) {
      data['sessions'] =
          sessions!.map((sessions) =&amp;gt; sessions.toJson()).toList();
    }
    return data;
  }

  factory Sessions.fromMap(Map&amp;lt;String, dynamic&amp;gt; map) {
    return Sessions(
      sessions: List&amp;lt;SessionConversations&amp;gt;.from(map['sessions']
          ?.map((sessions) =&amp;gt; SessionConversations.fromMap(sessions))),
    );
  }

  Map&amp;lt;String, dynamic&amp;gt; toMap() {
    return {
      'sessions': sessions?.map((sessions) =&amp;gt; sessions.toMap()).toList(),
    };
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Local Storage with sqflite
&lt;/h2&gt;

&lt;p&gt;With our models in place, we can now set up sqflite for local storage. This will allow users to access history of conversations even when offline, and also pickup from where they left off.&lt;/p&gt;

&lt;p&gt;1_bible_chat.sql (assets/sql/1_bible_chat.sql)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE TABLE session (
    sessionId TEXT PRIMARY KEY,
    conversationId TEXT UNIQUE,
    title TEXT,
    timestamp DATETIME
);

CREATE TABLE conversation (
    id INTEGER PRIMARY KEY,
    conversationId TEXT,
    role VARCHAR(50),
    message TEXT,
    timestamp DATETIME,
    FOREIGN KEY (conversationId) REFERENCES session(conversationId)
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A glance at our database service class. You are to have other classes that will involve fetching and inserting data into the database&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class DatabaseService {
  final _logger = getLogger('DatabaseService');
  final _databaseMigrationService = locator&amp;lt;DatabaseMigrationService&amp;gt;();

  late final Database _database;
  final String _sessionTable = 'session';
  final String _conversationTable = 'conversation';

  Future&amp;lt;void&amp;gt; init() async {
    _logger.i('Initializing database');
    final directory = await getApplicationDocumentsDirectory();
    _database = await openDatabase(
      '${directory.path}/bible_chat',
      version: 1,
    );
    try {
      _logger.i('Creating database tables');
      // Apply migration on every start
      await _databaseMigrationService.runMigration(
        _database,
        migrationFiles: [
          '1_bible_chat.sql',
        ],
        verbose: true,
      );
      _logger.i('Database tables created');
    } catch (e, s) {
      _logger.v('Error creating database tables', e, s);
    }
  }

  Future&amp;lt;void&amp;gt; createConversation(Conversation conversation) async {
    _logger.i('storing conversation data');
    try {
      await _database.insert(
        _conversationTable,
        conversation.toMap(),
        conflictAlgorithm: ConflictAlgorithm.ignore,
      );
      _logger.i('Conversation data stored');
    } catch (e) {
      _logger.e('error trying to store a conversation data');
    }
  }

  Future&amp;lt;void&amp;gt; createSession(Session session) async {
    _logger.i('storing session data');
    try {
      await _database.insert(
        _sessionTable,
        session.toMap(),
        conflictAlgorithm: ConflictAlgorithm.ignore,
      );
      _logger.i('Session data stored');
    } catch (e) {
      _logger.e('error trying to store a session data');
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;In this article, we’ve set up our Flutter project with the Stacked architecture and prepared our Bible data for use within the app. We’ve also implemented local storage using sqflite, ensuring users enjoy a seamless offline experience.&lt;/p&gt;

&lt;p&gt;Stay tuned for the next part, where we’ll dive into integrating the Gemini AI SDK to bring our chat app to life with intelligent scripture search capabilities.&lt;/p&gt;

&lt;p&gt;Remember, the journey of learning and development is continuous. As we build and improve our app, we’re also refining our skills and pushing the boundaries of what’s possible with technology.&lt;/p&gt;

&lt;p&gt;Here's a link to the previous article &lt;strong&gt;&lt;a href="https://dev.to/apow/building-an-ai-powered-bible-chat-app-a-technical-journey-with-gemini-ai-sdk-and-flutter-3mil"&gt;https://dev.to/apow/building-an-ai-powered-bible-chat-app-a-technical-journey-with-gemini-ai-sdk-and-flutter-3mil&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I will be updating this article with a link to the next article titled: &lt;strong&gt;Integrating AI with Grace: The Gemini SDK and Flutter - Part 3&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Download the APK app sample (arm64) &lt;a href="https://drive.google.com/file/d/1_kBvIbgYM8-VfWvVVYyt1RbCvGbkIi2K/view?usp=sharing"&gt;here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>buildwithgemini</category>
      <category>flutter</category>
      <category>ai</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Building an AI-Powered Bible Chat App: A Technical Journey with Gemini AI SDK and Flutter</title>
      <dc:creator>Victor Ihedioha</dc:creator>
      <pubDate>Sat, 18 May 2024 11:33:28 +0000</pubDate>
      <link>https://dev.to/apow/building-an-ai-powered-bible-chat-app-a-technical-journey-with-gemini-ai-sdk-and-flutter-3mil</link>
      <guid>https://dev.to/apow/building-an-ai-powered-bible-chat-app-a-technical-journey-with-gemini-ai-sdk-and-flutter-3mil</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;With the integration of artificial intelligence (AI) becoming a game-changer in today's world, I recently embarked on creating a mobile application – an AI-powered Bible chat experience. In this article, I’ll share my journey of creating a simple yet effective AI-powered Bible chat app using the &lt;a href="https://pub.dev/packages/google_generative_ai"&gt;Gemini AI SDK&lt;/a&gt; and Flutter, focusing on Stacked architecture for state management and &lt;a href="https://pub.dev/packages/sqflite"&gt;sqflite&lt;/a&gt; for local storage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conceptualizing the AI Bible Chat App
&lt;/h2&gt;

&lt;p&gt;The idea was to create an application which could provide users with an interactive and personalized Bible study experience. The app would leverage AI to understand natural language queries and provide contextually relevant scripture passages, interpretations, and study materials.&lt;/p&gt;

&lt;p&gt;The app utilizes the Gemini large language model API to process these queries and generate informative responses based on the vast amount of biblical text it has been trained on.&lt;/p&gt;

&lt;p&gt;To enhance the user experience, I implemented an SQLite local database using the sqflite package. This allows the app to store user queries and corresponding Gemini responses. This cached data enables features like revisiting past conversations and potentially offering offline functionality in the future.&lt;/p&gt;

&lt;p&gt;Stacked, a state management solution by Filledstacks, was chosen for its ease of use and focus on reactive programming. Stacked helps manage the application state efficiently, particularly when dealing with user interactions and updates from the Gemini API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing the Right Tools
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Frontend&lt;/strong&gt;: &lt;strong&gt;Flutter&lt;/strong&gt; – A framework from Google, emerged as the ideal framework for its cross-platform capabilities and its expressive and flexible UI, enabling the building of beautiful and performant applications for iOS, Android, Web, and Desktop (MacOS, Windows, and Linux) using a single codebase.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;State Management&lt;/strong&gt;: &lt;strong&gt;Stacked&lt;/strong&gt; – A reactive state management solution built for Flutter, simplifying app creation, state handling and data flow within the app.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;AI Model&lt;/strong&gt;: &lt;strong&gt;Gemini&lt;/strong&gt; – A large language model from Google AI, trained on a massive dataset of text and code, allowing it to generate creative text formats and answer questions in an informative way.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Local Database&lt;/strong&gt;: &lt;strong&gt;sqflite&lt;/strong&gt; – An ORM (Object Relational Mapper) for Flutter that facilitates interaction with SQLite, a lightweight and embedded relational database engine, for storing user queries and Gemini responses on user devices.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Designing the App with Stacked
&lt;/h2&gt;

&lt;p&gt;Stacked architecture allowed for a clean separation of concerns, making the codebase more readable and easier to manage. It follows the MVVM pattern (Model-View-ViewModel), which fits perfectly with the reactive nature of Flutter apps.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class BibleDataService {
  final _databaseService = locator&amp;lt;DatabaseService&amp;gt;();

  Future&amp;lt;List&amp;lt;SessionConversations&amp;gt;?&amp;gt; 
     getAllSessionsWithConversations() async {
      var allSessionsWithConversations =
        await _databaseService.fetchAllSessionsWithConversations();
    return allSessionsWithConversations;
  }
}
&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;class BibleChatViewModel extends FormViewModel {
  final _bibleDataService = locator&amp;lt;BibleDataService&amp;gt;();
  final _logger = getLogger('BibleChatViewModel');

  List&amp;lt;String&amp;gt; get books =&amp;gt; _books;
  List&amp;lt;String&amp;gt; _books = [];

  void fetchBibleBooks() {
    _books = _bibleDataService.getBooks();
    rebuildUi();
  }
}
&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;class BibleChatView extends StackedView&amp;lt;BibleChatViewModel&amp;gt; {
  const BibleChatView({super.key});

  @override
  Widget builder(
    BuildContext context,
    BibleChatViewModel viewModel,
    Widget? child,
  ) {
    return Scaffold(
      backgroundColor: kcChatBackground,
      appBar: ChatAppBar(
        title: viewModel.sessionConversation?.session?.title.toString() ??
            'No title',
        onHistoryPressed: viewModel.showHistoryDialog,
      ),
      body: SafeArea(
        child: Column(
          children: [
            Expanded(
                child: ChatComponent(
              conversations: viewModel.sessionConversation!.conversations!,
            )),
            const ChatBoxComponent()
          ],
        ),
      ),
    );
  }

  @override
  BibleChatViewModel viewModelBuilder(
    BuildContext context,
  ) =&amp;gt;
      BibleChatViewModel();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Integrating Gemini AI SDK
&lt;/h2&gt;

&lt;p&gt;The Gemini AI SDK was integrated to handle natural language inputs. It processed user queries and matched them with relevant Bible and scripture content, using a combination of machine learning models and a structured database of scriptures.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  GenerativeModel initializeAIModel() {
    const apiKey = 'YOUR_API_KEY';
    openModel ??= GenerativeModel(
        model: 'gemini-pro',
        apiKey: apiKey,
        generationConfig: GenerationConfig(maxOutputTokens: 1000));
    return openModel!;
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Local Storage with sqflite
&lt;/h2&gt;

&lt;p&gt;To enhance the user experience with offline capabilities, sqflite was used for local storage. It stored user chat history with Gemini, allowing for a seamless experience even without an internet connection.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Future&amp;lt;void&amp;gt; init() async {
    _logger.i('Initializing database');
    final directory = await getApplicationDocumentsDirectory();
    _database = await openDatabase(
      '${directory.path}/bible_chat',
      version: 1,
    );
    try {
      _logger.i('Creating database tables');
      await _databaseMigrationService.runMigration(
        _database,
        migrationFiles: [
          '1_bible_chat.sql',
        ],
        verbose: true,
      );
      _logger.i('Database tables created');
    } catch (e, s) {
      _logger.v('Error creating database tables', e, s);
    }
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;The development of the AI Bible chat app was a fulfilling project that combined my passion for software engineering with the power of AI. By utilizing Flutter, Gemini AI SDK, Stacked, and sqflite, I was able to create an app that not only met the technical requirements but also provided a meaningful experience to its users.&lt;/p&gt;

&lt;p&gt;The journey doesn’t end here, though. The field of AI is constantly advancing, and there’s always room for improvement and innovation. I look forward to exploring new ways to enhance the app and bring even more value to users across the world.&lt;/p&gt;

&lt;p&gt;This article is a high-level overview of the technical approach taken to build the AI Bible chat app. It’s intended to inspire and guide those who are interested in developing similar applications. Remember, the best technical articles are those that inform, engage, and challenge the reader to think differently about the problems they’re solving.&lt;/p&gt;

&lt;p&gt;Here's a link to the next article titled: &lt;strong&gt;&lt;a href="https://dev.to/apow/flutter-faith-crafting-an-ai-bible-chat-app-with-stacked-architecture-part-2-18no"&gt;Flutter &amp;amp; Faith: Crafting an AI Bible Chat App with Stacked Architecture - Part 2&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Download the APK app sample (arm64) &lt;a href="https://drive.google.com/file/d/10Y4-Qy5W-mBIywQqzwLddR4z0XrtSdtI/view?usp=sharing"&gt;here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>gemini</category>
      <category>flutter</category>
      <category>ai</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
