Blogging my journey of learning Full stack devlopment and creating an App to get a hands on knowledge.

Search This Blog

Wednesday, 4 March 2026

Building ArtFlow: Vibe Coding an AI Drawing Tutor for the Kaggle Hackathon

Building ArtFlow: Vibe Coding an AI Drawing Tutor for the Kaggle Hackathon

In December 2025, a teammate and I decided to jump into a Kaggle hackathon to see what we could build using Google AI Studio and the new Gemini models. We wanted to build something highly interactive, so we created ArtFlow—a personal, AI-powered art teacher that breaks down any image into a simple, step-by-step drawing tutorial.

Here is a look at what the app does, the tech stack we used, and how "vibe coding" and AI Studio brought the whole thing to life.

The ArtFlow dashboard where users can upload an image to start their tutorial.


🎨 What ArtFlow Does

At its core, ArtFlow is an interactive tutorial generator. Here is the user experience:

  • Upload an Image: You upload a picture of a cartoon, animal, or anything you want to draw.
  • AI Analysis: The app analyzes the target image, identifying the art style, the exact colors you will need, and creating a custom color-mixing guide.
  • Step-by-Step Breakdown: It generates a sequential plan, figuring out exactly how to draw the image from scratch.
  • Visual Generation: The app actually draws each step for you, showing progressive images of the artwork coming together.
  • Auto-Save History: All your generated masterpieces are saved to a personal history drawer so you can revisit them anytime.

💻 The Tech Stack: React, TypeScript, and Tailwind

To make the app fast, responsive, and visually appealing, we relied on a modern frontend stack:

    • Language: TypeScript. With complex data structures mapping out tutorial steps and AI image analysis, TypeScript's strict typing kept our code bug-free and manageable.
    • Framework: React. The entire UI is built with functional React components and hooks to manage the complex generation states and UI rendering.
    • Styling: Tailwind CSS. All the styling—from the clean backgrounds to the responsive grid layouts and sticky headers—was done using utility classes.
    • Backend/Storage: Firebase. We used Firebase services to handle saving the generated tutorials and fetching them for the user's history drawer.

    ArtFlow generating the step-by-step visual guides in real-time.


    🧠 The AI Magic: Gemini + Imagen

    Since this was for a Kaggle hackathon, Google AI Studio was our playground. It allowed my teammate and me to rapidly prototype and test our prompts for planning and image generation before writing the actual application logic.

    The app operates in two distinct AI stages:

    Stage 1: Reasoning & Planning (The Brain - Gemini 3 Pro)

    When an image is uploaded, the app uses Gemini 3 Pro to act as the "brain." It looks at the image, writes a comprehensive plan, breaks the drawing down into logical steps, and extracts metadata like the art style and color palette.

    Stage 2: Image Generation & The "AI Supervisor" (The Artist - Imagen)

    Once the plan is ready, we use the Imagen model to render each specific step. By feeding the model specific prompts generated by Gemini, Imagen creates the visual guides that show the user exactly what their canvas should look like at each stage of the drawing.

    The Midpoint Checkpoint: Halfway through generating the tutorial, an "AI Supervisor" agent steps in. It takes the most recently generated image and compares it to your original upload.

    If it looks good, it continues. If it notices a detail error, it issues a PATCH decision, refining the remaining steps. If it detects a foundational mistake, it issues a RESTART decision, completely rewriting the plan and starting over to ensure the final tutorial is actually helpful.

    🌊 The Development Process: Vibe Coding

    Building this wasn't your traditional software engineering process. My teammate and I heavily utilized "vibe coding" to bring ArtFlow to life.

    Instead of manually typing every line of code, we acted as directors. We used AI coding assistants, feeding them natural language prompts (or "vibes") about what we wanted the UI to look like or how the React state should flow. The AI generated the boilerplate, handled the complex Tailwind styling, and wired up the basic logic. This allowed us to iterate incredibly fast. Instead of getting bogged down in syntax errors, we could focus our energy on the high-level architecture, the user experience, and perfecting our Gemini prompts in AI Studio.

    🏆 Check Out Our Hackathon Submission

    It was an amazing experience building this as a team and seeing what the latest AI models are capable of when paired with modern web frameworks.

    If you want to read the full details of our submission, see our architecture diagrams, and dive deeper into the code, check out our official Kaggle writeup here:

    Read our Kaggle Competition Writeup

Read More

Wednesday, 7 June 2023

Building a Recipe Creator App with Flutter and OpenAI ChatGPT API

The app will allow users to enter ingredients in the textbox and they will get the personalized recipe recommendation based on their inputs through the capabilities of OpenAI .

Step 1: Create a new Flutter project

Step 2: Add Dependencies

dependencies:

  http: ^0.13.0

Step 3: Design the App UI and  Integrate OpenAI API with Flutter UI





Preview of the App:



Check out the Git Code here

Read More

Friday, 2 June 2023

Building a Travel Itinerary App with Flutter and OpenAI ChatGPT

In this technical blog post, we will walk through the process of creating a travel itinerary app using Flutter and integrating the OpenAI ChatGPT API. By leveraging the cross-platform capabilities of Flutter and the conversational intelligence of ChatGPT, we can develop an app that assists users in planning their trips and generates personalized itineraries based on their preferences.

Step 1: Setting Up the Development Environment

Install Flutter: Follow the official Flutter installation guide for your operating system.

Set Up an Editor: Choose an IDE or text editor for Flutter development, such as Visual Studio Code or Android Studio, and install the necessary plugins and extensions.

Step 2: Creating a New Flutter Project

Open your terminal or command prompt and run the command flutter create your_app_name to create a new Flutter project.

Step 3: Adding Dependencies

  chat_gpt_sdk: ^2.1.7

Step 4: Designing the User Interface and Integrating OpenAI ChatGPT API

I developed a basic user interface using the Dart language to gather user input. The input will be used as a variable in the prompt for Chat GPT. Upon submission, the UI will generate a travel itinerary based on the provided user data.

OpenAI ChatGPT API:

 Create an OpenAI account and obtain an API key for authentication.You can find the API key at here.

Note: No free version of Chat GPT API is available now. However, you can start for free with $5 in free credit that can be used during your first 3 months. Check details here

Chat GPT SDK

There is a SDK wrapper for OpenAI's APIs in Flutter, which assists in seamlessly integrating ChatGPT into your Flutter applications.

Add Chat GPT SDK and import it in your file and use following code to request and get response from Open AI's Chat GPT.


Preview of the App





Check out the Git Code here

Read More

Wednesday, 10 May 2023

Thursday, 4 May 2023

Implementing BLOC design pattern for state management of Search Field in Flutter

 Business logic components (BLoC) allow you to separate the business logic from the UI. Using Bloc, we can easily separate the application into multiple layers, first we would have the presentation layer which would contain the UI/Views/widgets, then the business logic layer (Bloc) which will take care about the state changes and will have a dependency on the data access layer which contains a repository class which will act as an abstract class above the data access object classes.

Dependencies:

  • flutter_bloc: ^8.1.2
  • equatable: ^2.0.5
  • http: ^0.13.5

Code Implementation :

Let's build a simple app to implement this Bloc pattern. We will create a simple search field that will search recipe and give the the list of recipe based on your search. I have used Forkify API to search, view the recipe.

data/model/receipe.dart

class Recipe {
Recipe({
required this.publisher,
required this.title,
required this.sourceUrl,
required this.recipeId,
required this.imageUrl,
required this.socialRank,
required this.publisherUrl,
});

String publisher;
String title;
String sourceUrl;
String recipeId;
String imageUrl;
double socialRank;
String publisherUrl;

factory Recipe.fromJson(Map<String, dynamic> json) => Recipe(
publisher: json["publisher"],
title: json["title"],
sourceUrl: json["source_url"],
recipeId: json["recipe_id"],
imageUrl: json["image_url"],
socialRank: json["social_rank"]?.toDouble(),
publisherUrl: json["publisher_url"],
);

Map<String, dynamic> toJson() => {
"publisher": publisher,
"title": title,
"source_url": sourceUrl,
"recipe_id": recipeId,
"image_url": imageUrl,
"social_rank": socialRank,
"publisher_url": publisherUrl,
};
}
data/repositories/search_repositories.dart
import 'package:searchscreen/data/model/pizza.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;
abstract class SearchRepository{
Future<List<Recipe>> searchPizza(String query);
}

class SearchRepositoryImpl implements SearchRepository{
List<Recipe> recipes=[];
@override
Future<List<Recipe>> searchPizza(query) async{
try{
var response= await http.get( Uri.parse('https://forkify-api.herokuapp.com/api/search?q=$query' ));
if (response.statusCode == 200) {
var data = json.decode(response.body);
recipes = Pizza
.fromJson(data)
.recipes;
return recipes;
} else
{
return recipes;
}
}
catch(error){
throw (error.toString());

}
}
}
bloc/search_bloc.dart
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:searchscreen/bloc/search/search_event.dart';
import 'package:searchscreen/bloc/search/search_state.dart';
import 'package:searchscreen/data/model/pizza.dart';
import 'package:searchscreen/data/repositories/search_repository.dart';
class SearchBloc extends Bloc<SearchEvent,SearchState> {
final SearchRepository repository;

SearchBloc(this.repository) : super(SearchUninitialized()) {
on<Search>(
(event, emit) async {
try {
emit(SearchLoadingState());
final List<Recipe> recipes = await repository.searchPizza(event.query);

emit(SearchLoaded(recipes: recipes));
} catch (e,stackTrace) {
emit(SearchErrorState(message: e.toString()));
}
});
}
}

bloc/search_event.dart

import 'package:equatable/equatable.dart';
abstract class SearchEvent extends Equatable{
const SearchEvent();
}
class Search extends SearchEvent{
late String query;
Search({required this.query});

@override
List<Object> get props => [];
}

bloc/search_state.dart


import 'package:equatable/equatable.dart';
import 'package:searchscreen/data/model/pizza.dart';
abstract class SearchState extends Equatable{

}
class SearchUninitialized extends SearchState{
@override
List<Object?> get props =>[];
}
class SearchLoadingState extends SearchState{

@override
List<Object?> get props => [];

}
class SearchLoaded extends SearchState{
final List<Recipe> recipes;
SearchLoaded({required this.recipes});
@override
List<Object?> get props => [];

}

class SearchErrorState extends SearchState{
final String message;
SearchErrorState({required this.message});
@override
List<Object?> get props => [];

}

main.dart

import 'package:flutter/material.dart';
import 'package:searchscreen/bloc/search/search_bloc.dart';
import 'package:searchscreen/data/repositories/search_repository.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:searchscreen/ui/search_view.dart';
void main() {
runApp(const MyApp());
}

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

@override
Widget build(BuildContext context) {
return

RepositoryProvider(
create: (context) =>PizzaRepositoryImpl() ,
child: MultiBlocProvider(
providers: [
BlocProvider<PizzaBloc>(
create: (context) => PizzaBloc(PizzaRepositoryImpl()
)..add(FetchPizzaEvent()),lazy: false,),
BlocProvider<SearchBloc>(
create: (context) => SearchBloc(SearchRepositoryImpl()
))
],
child: MaterialApp(
title: 'Recipe Search App',
home: SearchView(),
)),
);

}
}

ui/search_view.dart

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:searchscreen/bloc/search/search_bloc.dart';
import 'package:searchscreen/bloc/search/search_event.dart';
import 'package:searchscreen/bloc/search/search_state.dart';
import 'package:searchscreen/ui/list.dart';
import 'package:searchscreen/widgets/search_text_field.dart';

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

@override
SearchViewState createState() => SearchViewState();
}

class SearchViewState extends State<SearchView> {


final TextEditingController _controller = TextEditingController();
@override
void initState() {
super.initState();
_controller.addListener(
() => context
.read<SearchBloc>()
.add(Search(query: _controller.text))
);

}
@override
void dispose() {
_controller.dispose();
super.dispose();
}


@override
Widget build(BuildContext context) {

return BlocConsumer<SearchBloc, SearchState>(

listener: (context, state) {
if (state is SearchErrorState) {
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(
const SnackBar(content: Text("Failed to Load")),
);

}


},
builder: (context, state) {
return Scaffold(appBar: AppBar(
title: const Text(
'Recipes'
)
),body:ListView(
shrinkWrap: true,
children: [
Card (child: SearchTextField(
key: const Key('searchPage_searchTextField'),
controller: _controller,
),),
const Divider(),
if(state is SearchLoaded)
buildHintsList(state.recipes)
],
));

},
);
}

}


Output:









Git code : https://github.com/anjutus/searchscreendemo
Read More

Friday, 31 March 2023

Add CORS configuration for your Firebase Storage

Firebase is using the same storage infrastructure as google cloud .

1. Go to https://console.cloud.google.com and select the dashboard of the project that contains the firebase storage.

2. On the top right, there will be a button named "Activate Cloud Shell", click on it, a terminal will open on the bottom of the page

Click on Open Editor

Click on File, and New file. 

3.Create new file with name cors.json, paste the following save.

[
  {
    "origin": ["*"],
    "method": ["GET"],
    "maxAgeSeconds": 3600
  }
]

4.On your Firebase Console and select "Storage" and  get your storage bucket URL (e.g gs://)

5.Then open the terminal again and paste following

       gsutil cors set cors.json gs://<your-cloud-storage-bucket>         

The CORS error should be gone for Storage for which we added the configuration. It wont be applied to other storage bucket

Read More

Firebase Firestore Database and Storage implementation for Flutter Project

  1. From your Flutter project directory, run the following command to install the core plugin:

    flutter pub add firebase_core

  2. Add following code to the root of your Flutter project,  

    import'package:firebase_core/firebase_core.dart'; import'firebase_options.dart'; Future main()async{ WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(  options:DefaultFirebaseOptions.currentPlatform); runApp(constMyApp()); }

  3. Run the following command to install the core plugin:

    flutter pub add cloud_firestore
    flutter pub add firebase_storage


  4. Once complete, rebuild your Flutter application:

    flutter run

  5. Add the following code for Adding details in your Database(Image in Firebase Storage retrieve the URL to save it along with the details in Database )
    Code for Firestore database:
    import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_storage/firebase_storage.dart'; final incidentDetails =FirebaseFirestore.instance.collection('IncidentDetails'); // Call the incident's CollectionReference to add a new incident details incidentDetails.add({ 'incident_number': _formKey.currentState!.value['incidentnumber'], 'date_of_incident':formattedDateofIncident, 'incident_location':_formKey.currentState!.value['incidentlocation'], 'incident_priority': _formKey.currentState!.value['incidentpriority'], 'incident_type':_formKey.currentState!.value['incidenttype'], 'incident_description':_formKey.currentState!.value['incidentdescription'], 'incident_image':imageUrl, }).then((DocumentReference) { setState(() { submitMessage =true; ScaffoldMessenger.of(context).showSnackBar(const SnackBar( content:Text("Submitted Succesfully!"), backgroundColor:Colors.teal, behavior:SnackBarBehavior.floating, duration:Duration(seconds: 5), )); _formKey.currentState?.reset(); }); }).catchError((error) { ScaffoldMessenger.of(context).showSnackBar(SnackBar( content:Text('Error $error'), backgroundColor:Colors.teal, behavior:SnackBarBehavior.floating, duration:const Duration(seconds: 5), )); });

    Code for FilePicker for selecting Image from my local drive and adding it in Storage:


    //File object PlatformFile? pickedFile; //File from the local drive stored in a byte format Uint8List? file; //Variable for storing URL of image fetched from firebase storage String? imageUrl; // Variable for file List<File?> files = []; final result = await FilePicker.platform .pickFiles(allowMultiple: false, type: FileType.image); if(result==null) { return; } setState(() { pickedFile= result.files.first; file= result.files.single.bytes; }); final filename='files/${pickedFile!.name}'; //Create reference for Firebase Storage Reference ref=FirebaseStorage.instance.ref().child(filename); //Add file UploadTask uploadTask=ref.putData(file!); //Wait for the Complete Response final TaskSnapshot taskSnapshot = await uploadTask.whenComplete(() {}); //get the image url imageUrl = await taskSnapshot.ref.getDownloadURL(); //file.clear(); setState(() { });


  6. Run your project and check for file creation in Firebase.(I encountered blocked by CORS error for Firebase Storage. So I had to Update my CORS policy for Storage of my project in Google Cloud Console ) . Here is link for it Click here
Read More

Featured Post

Invoice Portal Project Links

Web App link for Invoice Portal  Invoice Portal App lets an user view and create an Invoice. Features: View the list of Number of Invoices b...

Powered by Blogger.

Popular Posts