Skip to content

Commit

Permalink
Gemini Chatbot
Browse files Browse the repository at this point in the history
  • Loading branch information
AyeshaIftikhar committed Mar 23, 2024
1 parent 3d58d29 commit 9881d56
Show file tree
Hide file tree
Showing 5 changed files with 342 additions and 115 deletions.
2 changes: 1 addition & 1 deletion android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ android {
applicationId "com.example.flutter_ai"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
minSdkVersion flutter.minSdkVersion
minSdkVersion 21
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
Expand Down
3 changes: 2 additions & 1 deletion android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:label="flutter_ai"
android:label="AI with Flutter"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity
Expand Down
117 changes: 4 additions & 113 deletions lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_ai/services/gemini_services.dart';
import 'package:flutter_ai/widgets/content_widget.dart';

import 'screens/home_screen.dart';

void main() {
runApp(const MyApp());
Expand All @@ -12,121 +12,12 @@ class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter with AI',
debugShowCheckedModeBanner: false,
home: const MyHomePage(title: 'Flutter with AI'),
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(title: 'Flutter with AI'),
debugShowCheckedModeBanner: false,
);
}
}

class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;

@override
State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
List<Map<String, dynamic>> prompts = <Map<String, dynamic>>[];
TextEditingController query = TextEditingController();
bool addPrompt = false, loading = false;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: prompts.isEmpty
? const Center(
child: Text('No content has been generated yet'),
)
: ListView.builder(
itemCount: prompts.length,
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 10),
itemBuilder: (context, index) {
return ContentWidget(prompts: prompts[index]);
},
),
floatingActionButton: addPrompt
? null
: FloatingActionButton.extended(
onPressed: () {
setState(() {
addPrompt = !addPrompt;
});
},
tooltip: 'Increment',
icon: const Icon(Icons.add),
label: const Text('Add Prompt'),
),
bottomNavigationBar: addPrompt
? Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
left: 15,
right: 15,
),
child: Padding(
padding: const EdgeInsets.only(bottom: 20),
child: Row(
children: [
Expanded(
child: TextFormField(
controller: query,
maxLines: null,
keyboardType: TextInputType.multiline,
textInputAction: TextInputAction.send,
onChanged: (value) => setState(() {}),
decoration: InputDecoration(
isDense: true,
hintText: 'Write something here...',
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: const BorderSide(color: Colors.grey),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: const BorderSide(color: Colors.grey),
),
),
),
),
if (query.text.isNotEmpty)
Padding(
padding: const EdgeInsets.only(left: 10),
child: InkWell(
onTap: () async {
setState(() {
loading = true;
});
String text = await GeminiServices.generateText(
query.text,
);
prompts.add({'query': query.text, 'content': text});
query.clear();
loading = false;
addPrompt = false;
setState(() {});
},
child: CircleAvatar(
radius: 30,
child: loading
? const CircularProgressIndicator()
: const Icon(Icons.send),
),
),
),
],
),
),
)
: null,
);
}
}
209 changes: 209 additions & 0 deletions lib/screens/gemini_chat.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
import 'dart:convert';

import 'package:flutter/rendering.dart';
import 'package:flutter_ai/services/gemini_services.dart';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';

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

@override
State<GeminiMsgScreen> createState() => _GeminiMsgScreenState();
}

class _GeminiMsgScreenState extends State<GeminiMsgScreen> {
final TextEditingController chatController = TextEditingController();
final ScrollController scrollController = ScrollController();
List<Map<String, dynamic>> chatHistory = [];

sendMessage() {
FocusManager.instance.primaryFocus?.unfocus();
if (chatController.text.isNotEmpty) {
chatHistory.add({
"time": DateTime.now(),
"message": chatController.text,
"isSender": true,
});
chatController.clear();
}
if (mounted) setState(() {});
if (scrollController.hasClients) {
scrollController.jumpTo(scrollController.position.maxScrollExtent);
}
if (mounted) setState(() {});
getResponses();
}

void getResponses() async {
final url =
"https://generativelanguage.googleapis.com/v1beta2/models/chat-bison-001:generateMessage?key=${GeminiServices.apikey}";
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
};
chatHistory.add({
"time": DateTime.now(),
"message": 'typing',
"isSender": false,
});
if (scrollController.hasClients) {
scrollController.jumpTo(scrollController.position.maxScrollExtent);
}
final response = await http.post(uri, body: jsonEncode(request));
chatHistory.removeLast();
chatHistory.add({
"time": DateTime.now(),
"message": json.decode(response.body)["candidates"][0]["content"],
"isSender": false,
});
if (mounted) setState(() {});
if (scrollController.hasClients) {
scrollController.animateTo(
scrollController.position.maxScrollExtent,
duration: const Duration(microseconds: 1),
curve: Curves.linear,
);
}
if (mounted) setState(() {});
}

double previousScrollPosition = 0.0;
ScrollDirection scrollDirection = ScrollDirection.idle;
void onScroll() {
double currentScrollPosition = scrollController.position.pixels;
if (currentScrollPosition > previousScrollPosition) {
scrollDirection = ScrollDirection.forward;
if (mounted) setState(() {});
} else if (currentScrollPosition < previousScrollPosition) {
scrollDirection = ScrollDirection.reverse;
if (mounted) setState(() {});
}

previousScrollPosition = currentScrollPosition;
}

@override
void initState() {
scrollController.addListener(onScroll);
if (mounted) setState(() {});
super.initState();
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Chat with Gemini AI')),
bottomNavigationBar: Padding(
padding: EdgeInsets.only(
left: 15,
right: 15,
bottom: MediaQuery.of(context).viewInsets.bottom,
),
child: Padding(
padding: const EdgeInsets.only(bottom: 20),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
child: TextFormField(
maxLines: null,
controller: chatController,
keyboardType: TextInputType.multiline,
onChanged: (value) => setState(() {}),
textInputAction: TextInputAction.send,
textCapitalization: TextCapitalization.sentences,
decoration: InputDecoration(
isDense: true,
hintText: "Type a message",
contentPadding: const EdgeInsets.all(12.0),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: const BorderSide(color: Colors.grey),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: const BorderSide(color: Colors.grey),
),
),
),
),
if (chatController.text.isNotEmpty)
Padding(
padding: const EdgeInsets.only(left: 10),
child: InkWell(
onTap: () => sendMessage(),
child: const CircleAvatar(child: Icon(Icons.send)),
),
),
],
),
),
),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 20),
child: chatHistory.isEmpty
? const Center(
child: Text('Start conservating with Gemini...'),
)
: ListView.builder(
itemCount: chatHistory.length,
controller: scrollController,
padding: const EdgeInsets.only(top: 10, bottom: 10),
physics: const BouncingScrollPhysics(),
itemBuilder: (context, index) {
return Container(
padding: const EdgeInsets.only(
left: 14,
right: 14,
top: 10,
bottom: 10,
),
child: Align(
alignment: (chatHistory[index]["isSender"]
? Alignment.topRight
: Alignment.topLeft),
child: Container(
padding: const EdgeInsets.all(16),
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"]
? Colors.pink
: Colors.white,
),
child: Text(
chatHistory[index]["message"],
style: TextStyle(
fontSize: 15,
color: chatHistory[index]["isSender"]
? Colors.white
: Colors.black,
),
),
),
),
);
},
),
),
);
}
}
Loading

0 comments on commit 9881d56

Please sign in to comment.