Here in this blog we will be looking at how we can build an entire application of Text to Image .We will use the open ai api for the generation of the images from the text . also if there is other available api for the text completion and code completion then we will be exploring it.
First thing to start you should head over to the open AI official website create an account if you haven't. If you have already created an account for it then head over to it by logging.and under the developer section get an apikey.This will be your one time api key . so copy that api key and store it somewhere you will not get the apikeys once it is lost.
Some of the Sample Photo is Below
In flutter Create the flutter projects . and the folder structure is like this
.Here place the API key inside the API key folder. Generate Your Own because i will delete my apikey so the above API key wont work.
Also I have used shared preference and dark mode but this is optional . U can watch this video on YouTube for the minimalistic UI . I have skipped the shared preference and the login part in the YouTube video . If You Want the Straight Forward code then continue .
Here is list of code of all files inside of the lib folder.
The code for the main.dart file is as follow::
import 'package:flutter/material.dart'; | |
import 'package:o2openai/login/login.dart'; | |
import 'Pages/HomePage.dart'; | |
import 'package:o2openai/Themes/themes_const.dart'; | |
import 'package:shared_preferences/shared_preferences.dart'; | |
Future<void> main() async { | |
WidgetsFlutterBinding.ensureInitialized(); | |
SharedPreferences sp = await SharedPreferences.getInstance(); | |
bool darkmode = sp.getBool("darkmode") ?? false; | |
bool islogin = sp.getBool("islogin") ?? false; | |
String name = sp.getString("name") ?? ""; | |
String imagepath = sp.getString("imagepath") ?? ""; | |
runApp(MyApp( | |
darkmode: darkmode, | |
islogin: islogin, | |
name: name, | |
imagepath: imagepath, | |
)); | |
} | |
// it think now this recording is far good then other | |
//now i think this is good recording than other | |
class MyApp extends StatefulWidget { | |
bool darkmode; | |
bool islogin; | |
String? name; | |
String? imagepath; | |
MyApp({ | |
super.key, | |
required this.darkmode, | |
required this.islogin, | |
this.name, | |
this.imagepath, | |
}); | |
@override | |
State<MyApp> createState() => _MyAppState(); | |
} | |
class _MyAppState extends State<MyApp> { | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
theme: widget.darkmode ? customdarktheme : customlighttheme, | |
debugShowCheckedModeBanner: false, | |
home: Scaffold( | |
body: widget.islogin | |
? HomePage( | |
darkmode: widget.darkmode, | |
islogin: widget.islogin, | |
) | |
: LoginScreen( | |
darkmode: widget.darkmode, | |
)), | |
); | |
} | |
} |
The code for the the themes_const under the themes file is as follow this is optional you can skip it .I have also skipped it in the YouTube Video.
import 'package:flutter/material.dart'; | |
final ThemeData customlighttheme = ThemeData( | |
//custom theme for the container and text | |
brightness: Brightness.light, | |
scaffoldBackgroundColor: Colors.grey[300], | |
appBarTheme: const AppBarTheme( | |
backgroundColor: Color.fromARGB(255, 220, 220, 222), | |
foregroundColor: Colors.black), | |
//elevated button | |
elevatedButtonTheme: ElevatedButtonThemeData( | |
style: ButtonStyle( | |
backgroundColor: | |
MaterialStateProperty.all<Color>(Color.fromARGB(255, 186, 17, 102)), | |
foregroundColor: MaterialStateProperty.all<Color>(Colors.white), | |
), | |
), | |
); | |
//// | |
final ThemeData customdarktheme = ThemeData( | |
brightness: Brightness.dark, | |
scaffoldBackgroundColor: const Color.fromARGB(255, 52, 53, 65), | |
appBarTheme: const AppBarTheme( | |
backgroundColor: Color.fromARGB(255, 62, 63, 75), | |
foregroundColor: Colors.white), | |
elevatedButtonTheme: ElevatedButtonThemeData( | |
style: ButtonStyle( | |
backgroundColor: | |
MaterialStateProperty.all<Color>(Color.fromARGB(210, 186, 17, 101)), | |
foregroundColor: MaterialStateProperty.all<Color>(Colors.white), | |
), | |
), | |
); | |
// rgba(62,63,75,255) | |
// rgba(52,53,65,255) | |
// rgba(247,247,248,255) | |
// rgba(255,255,255,255) |
The code for the login.dart under login folder is as follow the name and password is 'admin' and 'admin' to successfully login. Shared preference packages is used for it to save login data locally. It was just me practicing Shared preference so i have used it but u may or may not used it also in the YouTube video above i have skipped that part.
import 'package:flutter/material.dart'; | |
import 'package:o2openai/main.dart'; | |
import 'package:shared_preferences/shared_preferences.dart'; | |
class LoginScreen extends StatefulWidget { | |
bool? darkmode; | |
LoginScreen({super.key, this.darkmode}); | |
@override | |
State<LoginScreen> createState() => _LoginScreenState(); | |
} | |
class _LoginScreenState extends State<LoginScreen> { | |
TextEditingController namecontroller = TextEditingController(); | |
TextEditingController passwordcontroller = TextEditingController(); | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar( | |
title: const Text("Login"), | |
), | |
body: Center( | |
child: Column( | |
children: [ | |
textformfield( | |
hinttext: "Name", | |
controller: namecontroller, | |
textinputtype: TextInputType.emailAddress, | |
obscuretext: false, | |
prefixicon: Icons.email, | |
suffixicon: Icons.visibility, | |
suffixicononpressed: () {}, | |
), | |
textformfield( | |
hinttext: "Password", | |
controller: passwordcontroller, | |
textinputtype: TextInputType.visiblePassword, | |
obscuretext: true, | |
prefixicon: Icons.lock, | |
suffixicon: Icons.visibility, | |
suffixicononpressed: () {}, | |
), | |
//login button | |
ElevatedButton( | |
onPressed: () async { | |
if (namecontroller.text == "admin" && | |
passwordcontroller.text == "admin") { | |
SharedPreferences sp = await SharedPreferences.getInstance(); | |
sp.setBool("islogin", true); | |
sp.setString("name", 'ADMIN'); | |
//go to runapp | |
Navigator.pushReplacement( | |
context, | |
MaterialPageRoute( | |
builder: (context) => | |
MyApp(darkmode: widget.darkmode!, islogin: true), | |
)); | |
} else {} | |
}, | |
child: const Text("Login"), | |
), | |
], | |
)), | |
); | |
} | |
} | |
Widget textformfield({ | |
required String? hinttext, | |
required TextEditingController? controller, | |
required TextInputType? textinputtype, | |
required bool? obscuretext, | |
required IconData? prefixicon, | |
required IconData? suffixicon, | |
required Function? suffixicononpressed, | |
}) { | |
return TextFormField( | |
key: UniqueKey(), | |
validator: (value) { | |
if (value!.isEmpty) { | |
return "Please enter some text"; | |
} | |
return null; | |
}, | |
controller: controller, | |
keyboardType: textinputtype, | |
obscureText: obscuretext!, | |
decoration: InputDecoration( | |
hintText: hinttext, | |
prefixIcon: Icon(prefixicon), | |
suffixIcon: IconButton( | |
onPressed: suffixicononpressed as void Function()?, | |
icon: Icon(suffixicon), | |
), | |
), | |
); | |
} |
Also In the Pages all the screen are separately made .first HomePage which shows the HomeScreen and by navigating it we can go to three differrent screens first screen is for text to image conversion the second screen is for the text completion and the third screen is for the code completion which is incomplete . Also in the above YouTube Video there is only about the text to image generation part.In Future if i made for the text completion and the code completion then i will update the video above .
The code for the HomePages are as follow.
import 'package:flutter/material.dart'; | |
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; | |
import '../main.dart'; | |
import 'o1ImageGenerationScreen.dart'; | |
import 'package:shared_preferences/shared_preferences.dart'; | |
import 'o2TextCompletionScreen.dart'; | |
class HomePage extends StatefulWidget { | |
bool? darkmode; | |
bool? islogin; | |
HomePage({ | |
super.key, | |
this.darkmode, | |
this.islogin, | |
}); | |
@override | |
State<HomePage> createState() => _HomePageState(); | |
} | |
// Qwermnbv@#123 | |
class _HomePageState extends State<HomePage> { | |
IconData _iconlight = Icons.wb_sunny; | |
IconData _icondark = Icons.nights_stay; | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar( | |
actions: [ | |
IconButton( | |
onPressed: () async { | |
SharedPreferences sp = await SharedPreferences.getInstance(); | |
sp.setBool("darkmode", !widget.darkmode!); | |
//rerun the entire app from myapp | |
Navigator.pushReplacement( | |
context, | |
MaterialPageRoute( | |
builder: (BuildContext context) => MyApp( | |
darkmode: !widget.darkmode!, | |
islogin: widget.islogin!, | |
))); | |
}, | |
icon: Icon(widget.darkmode! ? _icondark : _iconlight)) | |
], | |
elevation: 0, | |
leading: IconButton( | |
onPressed: () {}, | |
icon: const Icon( | |
FontAwesomeIcons.sun, | |
), | |
), | |
title: const Text("Open AI API BETA", | |
style: TextStyle( | |
fontSize: 20, | |
fontWeight: FontWeight.bold, | |
)), | |
centerTitle: true, | |
), | |
body: Center( | |
child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [ | |
customButton( | |
tittletext: "Image Generations", | |
subtitletext: "Generate any images easily", | |
customicon: FontAwesomeIcons.solidImages, | |
colorone: const Color.fromARGB(135, 110, 5, 229), | |
colortwo: const Color.fromARGB(129, 56, 16, 188), | |
// textColor: Colors.black, | |
onPressed: () { | |
Navigator.push(context, MaterialPageRoute( | |
builder: (context) { | |
return const ImageGeneratingOpenApi(); | |
// TextCompletion | |
}, | |
)); | |
}, | |
context: context, | |
), | |
const SizedBox(height: 20), | |
customButton( | |
tittletext: "Text Completions", | |
subtitletext: "Generate and edit Text easily", | |
customicon: FontAwesomeIcons.penToSquare, | |
colorone: const Color.fromARGB(133, 14, 224, 182), | |
colortwo: const Color.fromARGB(129, 19, 187, 142), | |
// textColor: Colors.black, is this sufficient enough for the text | |
onPressed: () { | |
Navigator.push(context, MaterialPageRoute( | |
builder: (context) { | |
return const TextCompletion(); | |
}, | |
)); | |
}, | |
context: context, | |
), | |
const SizedBox(height: 20), | |
customButton( | |
//is this recording sufficetn | |
tittletext: "Code Completions", | |
subtitletext: "Generate edit and explain code ", | |
customicon: FontAwesomeIcons.code, | |
colorone: const Color.fromARGB(133, 218, 15, 221), | |
colortwo: const Color.fromARGB(129, 145, 16, 100), | |
// textColor: Theme.of(context)., | |
onPressed: () { | |
// print("code generation button pressed"); | |
}, | |
context: context, | |
), | |
SizedBox(height: 20), | |
//logout button | |
ElevatedButton( | |
onPressed: () async { | |
SharedPreferences sp = await SharedPreferences.getInstance(); | |
sp.setBool("islogin", false); | |
sp.remove('name'); | |
//go to runapp | |
Navigator.pushReplacement( | |
context, | |
MaterialPageRoute( | |
builder: (context) => | |
MyApp(darkmode: widget.darkmode!, islogin: false), | |
)); | |
}, | |
child: const Text("Logout"), | |
), | |
])), | |
); | |
} | |
} | |
Widget customButton( | |
{required String tittletext, | |
required String subtitletext, | |
required IconData customicon, | |
required Color colorone, | |
required Color colortwo, | |
// required Color textColor, | |
required onPressed, | |
context}) { | |
return GestureDetector( | |
onTap: onPressed, | |
child: Container( | |
// width: MediaQuery.of(context).size.width, | |
height: 80, | |
child: Row( | |
mainAxisAlignment: MainAxisAlignment.center, | |
children: [ | |
Container( | |
width: 65, | |
height: 80, | |
decoration: BoxDecoration( | |
gradient: LinearGradient( | |
begin: Alignment.centerLeft, | |
end: Alignment.centerRight, | |
colors: [ | |
colorone, | |
colortwo, | |
], | |
), | |
borderRadius: const BorderRadius.only( | |
topLeft: Radius.circular(10), | |
bottomLeft: Radius.circular(10), | |
), | |
), | |
child: Icon( | |
customicon, | |
), | |
), | |
Container( | |
padding: EdgeInsets.only(left: 20), | |
width: 250, | |
height: 80, | |
decoration: BoxDecoration( | |
borderRadius: BorderRadius.only( | |
topRight: Radius.circular(10), | |
bottomRight: Radius.circular(10), | |
), | |
), | |
child: Column( | |
mainAxisAlignment: MainAxisAlignment.spaceEvenly, | |
crossAxisAlignment: CrossAxisAlignment.start, | |
children: [ | |
Text( | |
tittletext, | |
style: TextStyle( | |
// color: textColor, | |
fontSize: 20, | |
fontWeight: FontWeight.bold, | |
), | |
), | |
Text( | |
subtitletext, | |
style: TextStyle( | |
// color: textColor, | |
fontSize: 15, | |
), | |
), | |
], | |
)), | |
], | |
), | |
), | |
); | |
} |
The Code for the Imagegeneration is as follow:
import 'package:flutter/material.dart'; | |
import 'package:o2openai/apikey/apikey.dart'; | |
import 'package:http/http.dart' as http; | |
import 'dart:convert'; | |
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; | |
class ImageGeneratingOpenApi extends StatefulWidget { | |
const ImageGeneratingOpenApi({super.key}); | |
@override | |
State<ImageGeneratingOpenApi> createState() => _ImageGeneratingOpenApiState(); | |
} | |
class _ImageGeneratingOpenApiState extends State<ImageGeneratingOpenApi> { | |
var finalimages = | |
'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTjB8TzJsR7ENk2PpjL9pem0W27QYdxT-qgCg&usqp=CAU'; | |
final apikeys = api_keys; | |
TextEditingController TextToImage = TextEditingController(); | |
Future<void> imagegenerate() async { | |
final url = Uri.parse("https://api.openai.com/v1/images/generations"); | |
final headers = { | |
"Content-Type": "application/json", | |
"Authorization": "Bearer $apikeys" | |
}; | |
var response = await http.post(url, | |
headers: headers, | |
body: jsonEncode({ | |
"prompt": TextToImage.text, | |
"n": 3, | |
"size": "256x256", | |
})); | |
// print(response.statusCode); | |
if (response.statusCode == 200) { | |
var responseJson = jsonDecode(response.body); | |
finalimages = responseJson['data'][0]['url']; | |
} else { | |
// print("failed to generate image"); | |
} | |
// print(finalimages); | |
} | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
body: CustomScrollView(slivers: [ | |
SliverAppBar( | |
toolbarHeight: MediaQuery.of(context).size.height * 0.07, | |
automaticallyImplyLeading: false, | |
leading: IconButton( | |
onPressed: () { | |
Navigator.pop(context); | |
}, | |
icon: const Icon(Icons.arrow_back), | |
color: Colors.black, | |
), | |
centerTitle: false, | |
actions: [ | |
IconButton( | |
onPressed: () {}, | |
icon: const Icon( | |
FontAwesomeIcons.sun, | |
color: Color.fromARGB(255, 62, 63, 75), | |
), | |
), | |
], | |
title: const Text("Image Generation Beta", | |
style: TextStyle( | |
color: Color.fromARGB(255, 62, 63, 75), | |
fontSize: 20, | |
fontWeight: FontWeight.bold, | |
)), | |
backgroundColor: Theme.of(context).appBarTheme.backgroundColor, | |
), | |
SliverList( | |
delegate: SliverChildListDelegate([ | |
SizedBox(height: MediaQuery.of(context).size.height * 0.03), | |
Container( | |
height: MediaQuery.of(context).size.height * 0.1, | |
padding: const EdgeInsets.all(10), | |
child: TextField( | |
controller: TextToImage, | |
onSubmitted: (value) async { | |
setState(() { | |
finalimages = ""; | |
}); | |
}, | |
decoration: InputDecoration( | |
hintText: "Search for an image", | |
hintStyle: const TextStyle( | |
color: Color.fromARGB(255, 62, 63, 75), | |
fontSize: 20, | |
// fontWeight: FontWeight.bold, | |
), | |
suffixIcon: IconButton( | |
onPressed: () { | |
//clear text in textfield | |
TextToImage.clear(); | |
}, | |
icon: const Icon( | |
Icons.clear, | |
color: Color.fromARGB(255, 62, 63, 75), | |
), | |
), | |
prefixIcon: const Icon( | |
Icons.search, | |
color: Color.fromARGB(255, 62, 63, 75), | |
), | |
filled: true, | |
fillColor: const Color.fromARGB(255, 240, 240, 240), | |
enabledBorder: OutlineInputBorder( | |
borderRadius: BorderRadius.circular(15), | |
borderSide: const BorderSide( | |
color: Color.fromARGB(255, 240, 240, 240), | |
), | |
), | |
focusedBorder: OutlineInputBorder( | |
borderRadius: BorderRadius.circular(15), | |
borderSide: BorderSide( | |
// color: Color.fromARGB(255, 240, 240, 240), | |
color: Colors.green.shade200), | |
), | |
), | |
), | |
), | |
Container( | |
padding: const EdgeInsets.all(10), | |
height: MediaQuery.of(context).size.height * 0.8, | |
child: FutureBuilder( | |
future: imagegenerate(), | |
builder: (context, snapshot) { | |
if (snapshot.connectionState == ConnectionState.done) { | |
// print(finalimages.length); | |
return Column( | |
children: [ | |
Container( | |
margin: const EdgeInsets.only(top: 5), | |
height: 256, | |
width: 256, | |
decoration: BoxDecoration( | |
image: DecorationImage( | |
image: NetworkImage(finalimages.toString()), | |
fit: BoxFit.cover, | |
), | |
), | |
), | |
const SizedBox(height: 10), | |
//download button here | |
Row( | |
mainAxisAlignment: MainAxisAlignment.spaceEvenly, | |
children: [ | |
SizedBox( | |
height: 50, | |
child: ElevatedButton( | |
onPressed: () {}, | |
style: ElevatedButton.styleFrom( | |
backgroundColor: | |
const Color.fromARGB(255, 245, 245, 246), | |
shape: RoundedRectangleBorder( | |
borderRadius: BorderRadius.circular(15), | |
), | |
), | |
child: const Text( | |
"Download", | |
style: TextStyle( | |
color: Colors.black, | |
fontSize: 17, | |
), | |
), | |
), | |
), | |
SizedBox( | |
height: 50, | |
child: ElevatedButton( | |
onPressed: () {}, | |
style: ElevatedButton.styleFrom( | |
backgroundColor: | |
const Color.fromARGB(255, 245, 245, 246), | |
shape: RoundedRectangleBorder( | |
borderRadius: BorderRadius.circular(15), | |
), | |
), | |
child: const Text( | |
"Share", | |
style: TextStyle( | |
color: Colors.black, | |
fontSize: 17, | |
), | |
), | |
), | |
), | |
], | |
) | |
], | |
); | |
} else { | |
return Center( | |
child: CircularProgressIndicator( | |
color: Colors.grey[300], | |
), | |
); | |
} | |
}, | |
), | |
) | |
])) | |
]), | |
); | |
} | |
} |
The code for the Text Completion is as follow .Text completion is done by using the DaVinci 03 model which detail is available in open ai developer page so make sure to check it out.
import 'package:flutter/material.dart'; | |
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; | |
import 'package:o2openai/apikey/apikey.dart'; | |
import 'dart:convert'; | |
import 'package:http/http.dart' as http; | |
class TextCompletion extends StatefulWidget { | |
const TextCompletion({super.key}); | |
@override | |
State<TextCompletion> createState() => _TextCompletionState(); | |
} | |
class _TextCompletionState extends State<TextCompletion> { | |
TextEditingController textsolution = TextEditingController(); | |
// List<dynamic> textanswer = []; | |
String finaltextanswer = ""; | |
Future<void> generateAnswer() async { | |
final url = Uri.parse( | |
"https://api.openai.com/v1/engines/davinci-codex/completions"); | |
final headers = { | |
"Content-Type": "application/json", | |
"Authorization": "Bearer $api_keys" | |
}; | |
final body = jsonEncode( | |
{"prompt": textsolution.text, "temperature": 0.5, "max_tokens": 100}); | |
final response = await http.post(url, headers: headers, body: body); | |
if (response.statusCode == 200) { | |
final json = jsonDecode(response.body); | |
finaltextanswer = json['choices'][0]['text']; | |
print(finaltextanswer); | |
} else { | |
throw Exception("Failed to generate answer: ${response.statusCode}"); | |
} | |
print(finaltextanswer); | |
} | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar( | |
elevation: 0, | |
toolbarHeight: MediaQuery.of(context).size.height * 0.07, | |
automaticallyImplyLeading: false, | |
leading: IconButton( | |
onPressed: () { | |
Navigator.pop(context); | |
}, | |
icon: const Icon(Icons.arrow_back), | |
color: Colors.black, | |
), | |
centerTitle: false, | |
actions: [ | |
IconButton( | |
onPressed: () {}, | |
icon: const Icon(Icons.more_vert), | |
color: Colors.black, | |
), | |
], | |
title: const Text( | |
"Text Completion", | |
style: TextStyle( | |
color: Colors.black, | |
fontSize: 20, | |
fontWeight: FontWeight.bold, | |
), | |
), | |
backgroundColor: Theme.of(context).appBarTheme.backgroundColor, | |
), | |
body: Padding( | |
padding: const EdgeInsets.all(8.0), | |
child: SingleChildScrollView( | |
child: Column( | |
children: [ | |
//textformfield with validation | |
TextField( | |
controller: textsolution, | |
decoration: InputDecoration( | |
hintText: "Ask Anything", | |
hintStyle: const TextStyle( | |
color: Color.fromARGB(255, 62, 63, 75), | |
fontSize: 20, | |
// fontWeight: FontWeight.bold, | |
), | |
suffixIcon: IconButton( | |
onPressed: () { | |
textsolution.clear(); | |
}, | |
icon: const Icon( | |
Icons.clear, | |
color: Color.fromARGB(255, 62, 63, 75), | |
), | |
), | |
prefixIcon: const Icon( | |
Icons.search, | |
color: Color.fromARGB(255, 62, 63, 75), | |
), | |
filled: true, | |
fillColor: const Color.fromARGB(255, 240, 240, 240), | |
enabledBorder: OutlineInputBorder( | |
borderRadius: BorderRadius.circular(15), | |
borderSide: const BorderSide( | |
color: Color.fromARGB(255, 240, 240, 240), | |
), | |
), | |
focusedBorder: OutlineInputBorder( | |
borderRadius: BorderRadius.circular(15), | |
borderSide: BorderSide( | |
// color: Color.fromARGB(255, 240, 240, 240), | |
color: Colors.green.shade200), | |
), | |
), | |
onSubmitted: (value) async { | |
setState(() {}); | |
}, | |
), | |
SizedBox(height: 20), | |
//show the finaltextanswer calling the futurebuilder function | |
FutureBuilder( | |
future: generateAnswer(), | |
builder: (context, snapshot) { | |
if (snapshot.connectionState == ConnectionState.done) { | |
return Text( | |
finaltextanswer, | |
style: const TextStyle( | |
color: Color.fromARGB(255, 62, 63, 75), | |
fontSize: 20, | |
// fontWeight: FontWeight.bold, | |
), | |
); | |
} else { | |
return const Center( | |
child: CircularProgressIndicator( | |
color: Colors.white, | |
)); | |
} | |
}), | |
], | |
)), | |
), | |
); | |
} | |
} |
The Code Completion file isn't completed till now if completed then I will post it below. The main reason is there isn't good api for the code completion which is freely available .
Also make to subscribe to the Chanell on YouTube For more flutter tips and tricks