Build Your Own AI Powered ChatGPT Powered App Using Flutter

By Faheem Ahmed - january 04,2024
Build your own AI powered ChatGPT blog

Introduction

To build your own ChatGPT Powered Mobile app using Flutter, you don't need to be an expert in programming. Flutter is a user-friendly framework that allows you to create mobile applications with ease. You can start developing your app quickly with the easy-to-use interface and helpful documentation. Whether you are new or experienced, Flutter makes it easy to create amazing mobile apps. Today you will learn how to integrate OpenAI's API in your application and make a helpful recipe generator app.

Users can type in the ingredients they have on hand, and the app with the power of OpenAI API generates a tasty recipe that uses those ingredients

Things You are going to learn today

  • How to build your own ChatGPT-powered mobile app using Flutter.
  • Setting up a Flutter project and creating a basic UI.
  • Open AI key generation flow.
  • Integrating OpenAI’s API into the app to generate recipe suggestions.
  • Sending a POST request to OpenAI’s API with the necessary parameters.
  • Displaying the AI-generated recipe on the app’s interface.

Things You Need

  • OpenAPI Key
  • Basic Understanding of Flutter Framework
  • IDE like VSCODE or Android Studio

How to get your own OpenAI key?

OpenAI API is not freely available and comes with a price tag, which is based on tokens used and which model is used, you can access that information here: https://openai.com/pricing

You need to have an API Key in order to use it in your project, therefore first of all login to your OpenAI account by going to this website link:

https://platform.openai.com/api-keys

From this screen, you can obtain your personal key. Ensure it’s confidentiality as sharing it could lead to unauthorized usage, depleting your credits and incurring charges.

Getting Started

Set up your Flutter project:

First, make sure you have Flutter SDK installed and configured on your machine. If not, you can follow the official Flutter installation guide here: https://docs.flutter.dev/get-started/install

Create a new Flutter project using the following command in your VS code Terminal:

flutter create flutter_ai_recipe_app

In “main.dart” file, remove old code and replace with the below code:

				
					class MyApp extends StatelessWidget {
 const MyApp({super.key});
 @override
 Widget build(BuildContext context) {
   return MaterialApp(
     debugShowCheckedModeBanner: false,
     title: 'Magic AI Recipe App',
     theme: ThemeData(
       colorScheme: ColorScheme.fromSeed(
         seedColor: Colors.green,
         primary: Colors.black,
       ),
       useMaterial3: true,
     ),
     home: const RecipeScreen(),
   );
 }
}
				
			

In the above code we are basically just calling “RecipeScreen()” and setting the theme for our application.
Now, In your project create a new file named recipe_screen.dart and Make the UI by pasting this code in your editor:

				
					class RecipeScreen extends StatefulWidget {
 const RecipeScreen({super.key});

 @override
 State<RecipeScreen> createState() =>
     _RecipeScreenState(); // Creating the state for this widget
}

class _RecipeScreenState extends State<RecipeScreen> {
 late final TextEditingController controller; // Controller for the text field
 late final FocusNode focusNode; // Focus node for the text field
 final List<String> ingredients = <String>[]; // List to store the ingredients
 String response = ''; // String to store the response from the AI

 @override
 void initState() {
   super.initState();
   controller = TextEditingController(); // Initializing the text controller
   focusNode = FocusNode(); // Initializing the focus node
 }

 @override
 void dispose() {
   controller.dispose(); // Disposing the text controller when not needed
   focusNode.dispose(); // Disposing the focus node when not needed
   super.dispose();
 }

 @override
 Widget build(BuildContext context) {
   return Scaffold(
     appBar: AppBar(
       title: const Text('Magic AI Recipe App'), // App bar title
     ),
     body: SafeArea(
       child: Padding(
         padding: const EdgeInsets.all(16.0),
         child: Column(
           children: [
             const Text(
           'Find your unique recipe! by typing your ingredients'), // Instruction text
             const SizedBox(height: 16.0),
             Row(
               children: [
                 Flexible(
                   child: TextFormField(
                     controller: controller, // Assigning the controller
                     focusNode: focusNode, // Assigning the focus node
                     decoration: const InputDecoration(
                       border: OutlineInputBorder(),
                       labelText: 'Type Your Ingredients', // Placeholder text
                     ),
                     onFieldSubmitted: (String value) {
                       // Action on submit
                       setState(() {
                         ingredients
                             .add(value); // Adding the ingredient to the list
                         controller.clear(); // Clearing the text field
                         focusNode
                             .requestFocus(); // Requesting focus for the text field
                       });
                     },
                   ),
                 ),
                 TextButton(
                   onPressed: () {
                     // Action on button press
                     setState(() {
                       ingredients.add(controller
                           .text); // Adding the ingredient to the list
                       controller.clear(); // Clearing the text field
                       focusNode
                           .requestFocus(); // Requesting focus for the text field
                     });
                   },
                   child: const Icon(Icons.add), // Button icon
                 ),
               ],
             ),
             const SizedBox(height: 5.0),
             Wrap(
               spacing: 8.0,
               children: [
                 for (final String ingredient
                     in ingredients) // Looping through the ingredients list
                   Chip(
                     label: Text(ingredient), // Displaying the ingredient
                     onDeleted: () {
                       // Action on delete
                       setState(() {
                         ingredients.remove(
                             ingredient); // Removing the ingredient from the list
                       });
                     },
                   ),
               ],
             ),
             const SizedBox(height: 16.0),
             Expanded(
                 child: SizedBox(
               child: Text(response), // Displaying the response from the AI
             )),
             Align(
                 alignment: Alignment.bottomCenter,
                 child: ElevatedButton(
                   onPressed: () async {
                     // Action on button press
                     setState(() => response =
                         'Thinking'); // Updating the response to 'Thinking'
                     dynamic temp = await Response().askAi(ingredients.join(
                         ingredients
                             .toString())); // Asking the AI for a recipe
                     print(temp);
                     setState(() => response = temp
                         .toString()); // Updating the response with the AI's response
                   },
                   child: const Text('Create Recipe'), // Button text
                 )),
           ],
         ),
       ),
     ),
   );
 }
}

				
			

In recipe_screen.dart create a stateful class "RecipeScreen" and create basic variables:

late final TextEditingController controller; //for the formField (ingredients)

late final FocusNode focusNode; // focus node for the formField

final List ingredients = [ ]; String response = '';

Let’s Understand these Variables:

  1. final List<String> ingredients = <String>[]; – It’s a list that will hold the ingredients for the recipe and initializes as an empty list of strings.
  2. String response = ”; – This string will hold the response from the server.

This Dart code creates a user interface for a recipe app in Flutter. It includes a text field for ingredient input, an add button, and a display area for added ingredients. Ingredients can be submitted through the text field or the add button, and can be removed by clicking on the delete button on each ingredient chip.

Next, Add “http” package in the pubspec.yaml file and run pub get command in terminal The http package is used for making HTTP requests.

				
					http: ^1.1.0

				
			
Integrating your chatGPT API

First of all, you need to create a separate dart file named response.dart, and write the following code:
Make sure you use your own key here.

				
					// Importing required libraries
import 'dart:convert';

import 'package:flutter_ai_recipe_app/models/recipe_model.dart';
import 'package:http/http.dart' as http;

// Class to handle responses from the OpenAI API
class Response {
 RecipeModel recipeModel = RecipeModel(); // Model to store the response
 var choices = <Choice>[]; // List to store the choices from the response
 String apiKey =
     'sk-ilYnnPBMlp10ORNS8JMfT3BlbkFJF32312FASDED3DFFF3'; // API key for OpenAI(change it)

 // Function to send a request to the OpenAI API and get a response
 Future<dynamic> askAi(String prompt) async {
   try {
     // Sending a POST request to the OpenAI API
     final response = await http.post(
       Uri.parse('https://api.openai.com/v1/chat/completions'),
       headers: {
         'content-type': 'application/json',
         'Authorization': 'Bearer $apiKey',
       },
       body: jsonEncode(
         {
           "model": "gpt-3.5-turbo",
           "temperature": 0,
           "max_tokens": 100,
           "top_p": 1,
           "messages": [
             {"role": "system", "content": "You are a helpful assistant."},
             {
               "role": "user",
               "content":
                   "Create a recipe using the following ingredients: $prompt"
             }
           ],
         },
       ),
     );
     print(response.body); // Printing the response body
     recipeModel = RecipeModel.fromJson(
         jsonDecode(response.body)); // Parsing the response body to the model
     choices = recipeModel.choices!; // Storing the choices from the response
     return choices[0]
         .message
         .content; // Returning the content of the first choice
   } catch (e) {
     print('Error occurred: $e'); // Printing the error if any
     rethrow;
   }
 }
}

				
			

This defines a Response class with a method askAi. This method sends a POST request to an OpenAI API, passing a prompt to create a recipe using provided ingredients, and uses an API key for authorization.

Now let’s understand this code:

HTTP: This package is used for making API requests in Dart applications.

Response class is responsible for communicating with the OpenAI API.It has three main properties:

recipeModel: An instance of RecipeModel used to store the response from the API.

choices: A list of Choice objects that store the different options provided by the API.

apiKey: The API key used to authenticate with the OpenAI API.

Now let’s talk about the askAi function:

Function askAi: 'askAi' takes a prompt as input and returns a Future, inside this function we are making “HTTP POST request” using the http.post method provided by the http package.Then we set the request headers, including content type and authorization using the API key

				
					final response = await http.post(
  Uri.parse('https://api.openai.com/v1/chat/completions'),
       headers: {
         'content-type': 'application/json',
         'Authorization': 'Bearer $apiKey',
       },
       body: jsonEncode(
         {
           "model": "gpt-3.5-turbo",
           "temperature": 0,
           "max_tokens": 100,
           "top_p": 1,
           "messages": [
             {"role": "system", "content": "You are a helpful assistant."},
             {
               "role": "user",
               "content":
                   "Create a recipe using the following ingredients: $prompt"
             }
           ],
         },
       ),
     );
 print(response.body); // Printing the response body

				
			

Let’s go to the body function and understand what's going on here.

This code will send POST request to the ‘OpenAI API’,

				
					 Uri.parse('https://api.openai.com/v1/chat/completions'),
				
			

Here is the URL where the request is sent.

				
					headers: {
         'content-type': 'application/json',
         'Authorization': 'Bearer $apiKey',
       },
				
			

This is the headers of the request. It tells the server that the content of the request is in JSON format and gives the API key for authorization.

				
					body: jsonEncode(
{
"model": "gpt-3.5-turbo",// tells the API to use the "gpt-3.5-turbo" model.
"temperature": 0,// controls the randomness of the AI's output (0 means deterministic).
"max_tokens": 100,// sets the maximum length of the output to 100 tokens.
"top_p": 1,//It is the  parameter related to the randomness of the output.
           "messages": [
//This is an array of messages that set the context for the AI
. The first message tells the AI that it's a helpful assistant.
The second message is a user's request to create a recipe with the provided ingredients ($prompt).
{"role": "system", "content": "You are a helpful assistant."},
{
 "role": "user",
"content":
"Create a recipe using the following ingredients: $prompt"
             }
           ],
         },
       ),
				
			

Here we have the body of request. It is converted to a JSON string using jsonEncode function, which commonly used to convert a Dart object into a JSON string.

Creating model

Now let’s create a model for the project.

RecipeModel class represents a response from the OpenAI API.
constructor that takes these six properties as optional parameters.
FactoryConstructor It has a factory constructor fromJson, which creates a RecipeModel instance from a JSON object.
toJson method converts a RecipeModel instance back into a JSON object.

				
					class RecipeModel {
// The unique identifier of the response.
 String? id;
 // The type of the object in the response.
 String? object;
 // The time the response was created.
 int? created;
 // The model used to generate the response.
 String? model;
 // The choices in the response.
 List<Choice>? choices;
 // The usage details of the API call.
 Usage? usage;


 RecipeModel({
   this.id,
   this.object,
   this.created,
   this.model,
   this.choices,
   this.usage,
 });

 factory RecipeModel.fromJson(Map<String, dynamic> json) => RecipeModel(
       id: json["id"],
       object: json["object"],
       created: json["created"],
       model: json["model"],
       choices:
           List<Choice>.from(json["choices"].map((x) => Choice.fromJson(x))),
       usage: Usage.fromJson(json["usage"]),
     );

 Map<String, dynamic> toJson() => {
       "id": id,
       "object": object,
       "created": created,
       "model": model,
       "choices": List<dynamic>.from(choices!.map((x) => x.toJson())),
       "usage": usage!.toJson(),
     };
}
				
			

The Choice class represents a choice in our context.

index: It is the integer representing the index of the choice.

message: This is an instance of the Message class, which represents the message which is associated with the choice.

finishReason: It’s the string representing the reason why the generation of the choice was finished.

				
					class Choice {
 int index;
 Message message;
 String finishReason;

 Choice({
   required this.index,
   required this.message,
   required this.finishReason,
 });

 factory Choice.fromJson(Map<String, dynamic> json) => Choice(
       index: json["index"],
       message: Message.fromJson(json["message"]),
       finishReason: json["finish_reason"],
     );

 Map<String, dynamic> toJson() => {
       "index": index,
       "message": message.toJson(),
       "finish_reason": finishReason,
     };
}
				
			

The Message class is a model that represents a message in the context.

role: It is the string that represents the role of the message sender. It could be "system", "user", or "assistant".

content: It is the string that represents the content of the message.

				
					class Message {
 String role;
 String content;

 Message({
   required this.role,
   required this.content,
 });

 factory Message.fromJson(Map<String, dynamic> json) => Message(
       role: json["role"],
       content: json["content"],
     );

 Map<String, dynamic> toJson() => {
       "role": role,
       "content": content,
     };
}


				
			

Now, Let’s take a look at the UI of the application, you just built:

This is the main screen of the app where you will type the ingredients and hit enter, and then tap the create recipe button.

Type and enter all the ingredients you have in hand and press the create recipe button and see the magic of AI as it generates a recipe for you.

Finally, the app will show you a recipe based on your ingredients you provided, it nicely divides the ingredients and instructions for you, and gives you accurate measurements of ingredients as well. Now, go ahead an experiment with other ingredients and see what the AI has to offer!

Conclusion

Today you learned how easy it is to build your very own Flutter Application that has AI powers and generates a recipe for you based on the items you provide to it.

You first started out with a default flutter project, then you learned how to build a User Interface that is simple and clear.

You learned how to get the API key and learn using that API key how you can send a prompt to OpenAI and get a response in JSON and then made a model that helps with parsing of data and how to display the response to the screen.

If this article has helped in any way and you have learned something new today, Kindly consider sharing the article so others can also benefit and learn how to build simple AI applications.

Subscribe to our website for more interesting articles on AI and other interesting fields of IT. You can find the code for this project here:

Repository Link:
Written by
Faheem Ahmed

AIM Digital

Copyright © 2023. AIM Digital. All Rights Reserved.

Email: youremail.com

Phone: yourphonenumber

wpChatIcon
wpChatIcon