Building an AI Chatbot using Flutter with Makersuite and Palm API: A Step-by-Step Guide

Esther Irawati Setiawan
Google Developer Experts
8 min readSep 28, 2023

Artificial intelligence (AI) has revolutionized how we interact with technology. One of the intriguing innovations in the world of AI is the chatbot. Chatbots are computer programs designed to communicate with humans through text or voice. They can be found in various applications, from customer service to personal assistants that aid daily tasks.

As a popular application UI development framework, Flutter enables us to construct AI chatbot applications with appealing and responsive displays. By combining the power of Flutter and Artificial Intelligence, you can create an app that understands and responds to user conversations in a manner resembling human interaction.

This article will guide you through creating a user-friendly AI chatbot application using Flutter. We’ll walk you through the development process from start to finish, including implementing AI to comprehend and respond to user conversations.

Obtaining the API Key

First, visit the Makersuite website at https://makersuite.google.com. If you haven’t signed in with your Google account, Makersuite will direct you to the sign-in page. After signing in, the webpage will redirect you to the Makersuite’s home page.

On the left side, there is a navigation to the page to obtain the API key.

After clicking, the page will change, and you can see options to generate a key for a new or existing project. Choose ‘Create API key in new project’.

Wait for a moment, and the API key will be generated. Copy that key for use when accessing the PaLM API.

If you’re familiar with Flutter, you can skip to Setup the API call part.

Flutter Installation

The first step we need to take to install Flutter on Android Studio is to visit the website https://docs.flutter.dev/get-started/install and download the Flutter SDK compatible with the operating system in use. Once the Flutter SDK has been downloaded, extract the file to a desired directory (for example, C:\flutter).

After that, we must open our computer's “Environment Variable” settings. In the search menu, type “env,” and the “Edit the system Environment Variable” option will appear.

After entering that menu, click the Environment Variables button, select the Path variable, and click Edit. Once in the edit path menu, click the New button and add the bin path from the Flutter folder extracted in the previous step (for example, C:\flutter\bin).

Flutter Installation on Android Studio and Creating a Flutter Project

The first step is to open the Android Studio application. Once inside the application, select the plugins menu, search for, and install the Flutter & Dart plugins.

Creating the UI

Before starting to code the Flutter application UI, we can install the dependencies required for this sample project. The additional dependencies this project uses are gradient_borders for display and http for making API calls. Dependencies can be added to the pubspec.yaml file, as shown in the code snippet below.

dependencies:
flutter:
sdk: flutter
gradient_borders: ^1.0.0
http: ^1.1.0

In the next step, create a file as shown in the image below in the ‘lib’ folder. The ‘ui’ folder contains the code for the UI of each page, and the ‘widgets’ folder is for custom widgets that we will create and use.

Populate the gradient_text.dart file in the widgets folder with the code snippet below.

class GradientText extends StatelessWidget {
const GradientText(
this.text, {
required this.gradient,
this.style,
});

final String text;
final TextStyle? style;
final Gradient gradient;

@override
Widget build(BuildContext context) {
return ShaderMask(
blendMode: BlendMode.srcIn,
shaderCallback: (bounds) => gradient.createShader(
Rect.fromLTWH(0, 0, bounds.width, bounds.height),
),
child: Text(text, style: style),
);
}
}

In the main.dart folder, input the code as shown below, which determines the application name, the theme used, and the route for the application pages.

void main() {
runApp(const MyApp());
}

class MyApp extends StatelessWidget {
const MyApp({super.key});

@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Chat Bot',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.orangeAccent),
useMaterial3: true,
),
initialRoute: HomePage.routeName,
routes: {
HomePage.routeName: (context) => const HomePage(),
ChatPage.routeName: (context) => const ChatPage(),
},
);
}
}

In the home.dart file, create a stateless widget, assign a routeName, and a UI card containing the “Ask Now” button, which is used to navigate to the chat view. An example is given in the code below.

class HomePage extends StatelessWidget {
static const routeName = '/home';
const HomePage({super.key});

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("AI ChatBot", style: TextStyle(fontWeight: FontWeight.bold),),
),
body: Container(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Card(
child: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
//get colors from hex
Color(0xFFF69170),
Color(0xFF7D96E6),
]
),
borderRadius: BorderRadius.all(Radius.circular(10.0)),
),
child: Row(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Padding(
padding: EdgeInsets.only(top: 16.0, left: 16.0),
child: (
Text("Hi! You Can Ask Me", style: const TextStyle(fontSize: 20.0, fontWeight: FontWeight.bold, color: Colors.white))
),
),
const Padding(
padding: EdgeInsets.only(left: 16.0),
child: (
Text("Anything", style: const TextStyle(fontSize: 20.0, fontWeight: FontWeight.bold, color: Colors.white))
),
),
Padding(
padding: const EdgeInsets.only(top: 8.0, left: 16.0, bottom: 16.0),
child: (
TextButton(
onPressed: (){
Navigator.pushNamed(context, '/chat');
},
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all<Color>(Colors.white),
foregroundColor: MaterialStateProperty.all<Color>(Colors.black),
),
child: const Padding(
padding: EdgeInsets.symmetric(horizontal: 16.0),
child: GradientText(
"Ask Now",
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.bold,
),
gradient: LinearGradient(
colors: [
Color(0xFFF69170),
Color(0xFF7D96E6),
]
),
),
)
)
),
),
],
),
const Padding(
padding: EdgeInsets.only(left: 16.0),
child: DecoratedBox(
decoration: BoxDecoration(
image: DecorationImage(image: AssetImage("assets/images/icon.png"), fit: BoxFit.cover),
),
child: SizedBox(height: 150, width: 150,),
),
)
],
),
),
),
const Padding(
padding: EdgeInsets.only(top: 16.0, left: 16.0),
child: Text("Recent Chats", style: TextStyle(fontSize: 20.0, fontWeight: FontWeight.bold),),
),
],
),
),
),
);
}
}
UI of HomePage

Next, in the chat.dart file, create a stateful widget with a route name that looks like a typical chat application. Also, create an empty variable of type List<Map<String, dynamic>>, which will be used to store the chat history. An example of the code snippet and the chat application appearance can be seen below.

class ChatPage extends StatefulWidget {
static const routeName = '/chat';
const ChatPage({super.key});

@override
State<ChatPage> createState() => _ChatPageState();
}

class _ChatPageState extends State<ChatPage> {
final TextEditingController _chatController = TextEditingController();
final ScrollController _scrollController = ScrollController();
List<Map<String, dynamic>> _chatHistory = [];

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Chat", style: TextStyle(fontWeight: FontWeight.bold),),
),
body: Stack(
children: [
Container(
//get max height
height: MediaQuery.of(context).size.height - 160,
child: ListView.builder(
itemCount: _chatHistory.length,
shrinkWrap: false,
controller: _scrollController,
padding: const EdgeInsets.only(top: 10,bottom: 10),
physics: const BouncingScrollPhysics(),
itemBuilder: (context, index){
return Container(
padding: EdgeInsets.only(left: 14,right: 14,top: 10,bottom: 10),
child: Align(
alignment: (_chatHistory[index]["isSender"]?Alignment.topRight:Alignment.topLeft),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 2,
blurRadius: 5,
offset: const Offset(0, 3),
),
],
color: (_chatHistory[index]["isSender"]?Color(0xFFF69170):Colors.white),
),
padding: EdgeInsets.all(16),
child: Text(_chatHistory[index]["message"], style: TextStyle(fontSize: 15, color: _chatHistory[index]["isSender"]?Colors.white:Colors.black)),
),
),
);
},
),
),
Align(
alignment: Alignment.bottomCenter,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
height: 60,
width: double.infinity,
color: Colors.white,
child: Row(
children: [
Expanded(
child: Container(
decoration: const BoxDecoration(
border: GradientBoxBorder(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Color(0xFFF69170),
Color(0xFF7D96E6),
]
),
),
borderRadius: BorderRadius.all(Radius.circular(50.0)),
),
child: Padding(
padding: const EdgeInsets.all(4.0),
child: TextField(
decoration: const InputDecoration(
hintText: "Type a message",
border: InputBorder.none,
contentPadding: EdgeInsets.all(8.0),
),
controller: _chatController,
),
),
),
),
const SizedBox(width: 4.0,),
MaterialButton(
onPressed: (){

},
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(80.0)),
padding: const EdgeInsets.all(0.0),
child: Ink(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Color(0xFFF69170),
Color(0xFF7D96E6),
]
),
borderRadius: BorderRadius.all(Radius.circular(50.0)),
),
child: Container(
constraints: const BoxConstraints(minWidth: 88.0, minHeight: 36.0), // min sizes for Material buttons
alignment: Alignment.center,
child: const Icon(Icons.send, color: Colors.white,)
),
),
)
],
),
),
)
],
),
);
}
UI Chat

In theonPressed event of the send button, provide a code setState after adding to the chat history based on what the user typed in the textfield. An example of the code snippet can be seen below.

onPressed: (){
setState(() {
if(_chatController.text.isNotEmpty){
_chatHistory.add({
"time": DateTime.now(),
"message": _chatController.text,
"isSender": true,
});
_chatController.clear();

}
});
_scrollController.jumpTo(
_scrollController.position.maxScrollExtent,
);
},

Setup the API call

Afterward, create an async function to retrieve a response from the API and execute setState after adding the API’s response to the chat history. Don’t forget to call the function in the onPressed event of the send button. The code snippet for the function and the added onPressed event can be seen below. (Remember to insert the obtained API Key into the URL).

void getAnswer() async {
final url = "https://generativelanguage.googleapis.com/v1beta2/models/chat-bison-001:generateMessage?key=<INSERT API KEY>";
final uri = Uri.parse(url);
List<Map<String,String>> msg = [];
for (var i = 0; i < _chatHistory.length; i++) {
msg.add({"content": _chatHistory[i]["message"]});
}

Map<String, dynamic> request = {
"prompt": {
"messages": [msg]
},
"temperature": 0.25,
"candidateCount": 1,
"topP": 1,
"topK": 1
};

final response = await http.post(uri, body: jsonEncode(request));

setState(() {
_chatHistory.add({
"time": DateTime.now(),
"message": json.decode(response.body)["candidates"][0]["content"],
"isSender": false,
});
});

_scrollController.jumpTo(
_scrollController.position.maxScrollExtent,
);
}
onPressed: (){
setState(() {
if(_chatController.text.isNotEmpty){
_chatHistory.add({
"time": DateTime.now(),
"message": _chatController.text,
"isSender": true,
});
_chatController.clear();

}
});
_scrollController.jumpTo(
_scrollController.position.maxScrollExtent,
);

getAnswer();
},

After that, the application can be run, and you can try chatting with the bot.

Conclusion

Artificial Intelligence (AI) has revolutionized our interaction with technology, with chatbots being a notable innovation in this domain. These chatbots, designed to communicate with humans through text or voice, are now integrated into various applications ranging from customer service to daily personal assistance. Flutter, a renowned UI development framework, facilitates the creation of visually appealing and responsive AI chatbot applications. By harnessing the power of Flutter combined with AI capabilities, one can develop applications that understand and respond to user interactions in a human-like manner. This article offers a step-by-step guide on creating an easy-to-use AI chatbot using Flutter, covering the entire development process, including the integration of AI to comprehend and reply to user dialogues.

--

--