| # Flutter Finance App - Python API Integration Guide | |
| This guide explains how to connect the Flutter Finance App front-end with a Python API backend for the chat interface and database interactions. | |
| ## Architecture Overview | |
|  | |
| ## 1. Setting up the Python API Server | |
| ### Requirements | |
| - Python 3.8+ | |
| - Flask/FastAPI | |
| - SQL.js connector or alternative database library | |
| ### Step 1: Create a Flask API | |
| ```python | |
| from flask import Flask, request, jsonify | |
| from flask_cors import CORS | |
| import sqlite3 | |
| import json | |
| app = Flask(__name__) | |
| CORS(app) # Enable Cross-Origin Resource Sharing | |
| # Database connection | |
| def get_db_connection(): | |
| conn = sqlite3.connect('finance_app.db') | |
| conn.row_factory = sqlite3.Row | |
| return conn | |
| # Create tables if they don't exist | |
| def init_db(): | |
| conn = get_db_connection() | |
| conn.execute(''' | |
| CREATE TABLE IF NOT EXISTS transactions ( | |
| id TEXT PRIMARY KEY, | |
| title TEXT NOT NULL, | |
| amount REAL NOT NULL, | |
| type TEXT NOT NULL, | |
| category TEXT NOT NULL, | |
| date TEXT NOT NULL, | |
| note TEXT, | |
| message TEXT | |
| ) | |
| ''') | |
| conn.execute(''' | |
| CREATE TABLE IF NOT EXISTS products ( | |
| id TEXT PRIMARY KEY, | |
| name TEXT NOT NULL, | |
| sku TEXT NOT NULL, | |
| quantity INTEGER NOT NULL, | |
| price REAL NOT NULL, | |
| category TEXT NOT NULL, | |
| last_updated TEXT NOT NULL, | |
| reorder_point INTEGER DEFAULT 5, | |
| trend TEXT DEFAULT 'stable', | |
| demand TEXT DEFAULT 'medium' | |
| ) | |
| ''') | |
| conn.commit() | |
| conn.close() | |
| # Initialize database on startup | |
| init_db() | |
| # Chat endpoints | |
| @app.route('/api/messages', methods=['GET']) | |
| def get_messages(): | |
| conn = get_db_connection() | |
| messages = conn.execute('SELECT * FROM messages ORDER BY timestamp').fetchall() | |
| conn.close() | |
| return jsonify([dict(message) for message in messages]) | |
| @app.route('/api/messages', methods=['POST']) | |
| def send_message(): | |
| data = request.json | |
| conn = get_db_connection() | |
| conn.execute( | |
| 'INSERT INTO messages (user_id, content, timestamp) VALUES (?, ?, ?)', | |
| (data['user_id'], data['content'], data['timestamp']) | |
| ) | |
| conn.commit() | |
| conn.close() | |
| # Here you can implement AI response logic | |
| return jsonify({"status": "success", "message": "Message sent"}) | |
| # Transaction endpoints | |
| @app.route('/api/transactions', methods=['GET']) | |
| def get_transactions(): | |
| conn = get_db_connection() | |
| transactions = conn.execute('SELECT * FROM transactions').fetchall() | |
| conn.close() | |
| return jsonify([dict(tx) for tx in transactions]) | |
| @app.route('/api/transactions', methods=['POST']) | |
| def add_transaction(): | |
| data = request.json | |
| conn = get_db_connection() | |
| conn.execute( | |
| 'INSERT INTO transactions (id, title, amount, type, category, date, note, message) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', | |
| (data['id'], data['title'], data['amount'], data['type'], data['category'], data['date'], data.get('note'), data.get('message')) | |
| ) | |
| conn.commit() | |
| conn.close() | |
| return jsonify({"status": "success"}) | |
| # Product/Stock endpoints | |
| @app.route('/api/products', methods=['GET']) | |
| def get_products(): | |
| conn = get_db_connection() | |
| products = conn.execute('SELECT * FROM products ORDER BY name').fetchall() | |
| conn.close() | |
| return jsonify([dict(product) for product in products]) | |
| @app.route('/api/products/low', methods=['GET']) | |
| def get_low_stock_products(): | |
| conn = get_db_connection() | |
| products = conn.execute('SELECT * FROM products WHERE quantity <= reorder_point ORDER BY quantity').fetchall() | |
| conn.close() | |
| return jsonify([dict(product) for product in products]) | |
| @app.route('/api/products/<product_id>', methods=['GET']) | |
| def get_product(product_id): | |
| conn = get_db_connection() | |
| product = conn.execute('SELECT * FROM products WHERE id = ?', (product_id,)).fetchone() | |
| conn.close() | |
| if not product: | |
| return jsonify({"error": "Product not found"}), 404 | |
| return jsonify(dict(product)) | |
| @app.route('/api/products', methods=['POST']) | |
| def add_product(): | |
| data = request.json | |
| conn = get_db_connection() | |
| try: | |
| conn.execute( | |
| 'INSERT INTO products (id, name, sku, quantity, price, category, last_updated, reorder_point) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', | |
| ( | |
| data['id'], | |
| data['name'], | |
| data['sku'], | |
| data['quantity'], | |
| data['price'], | |
| data['category'], | |
| data['last_updated'], | |
| data.get('reorder_point', 5) | |
| ) | |
| ) | |
| conn.commit() | |
| return jsonify({"status": "success", "message": "Product added successfully"}) | |
| except Exception as e: | |
| conn.rollback() | |
| return jsonify({"status": "error", "message": str(e)}), 400 | |
| finally: | |
| conn.close() | |
| @app.route('/api/products/<product_id>', methods=['PUT']) | |
| def update_product(product_id): | |
| data = request.json | |
| conn = get_db_connection() | |
| try: | |
| # Check if product exists | |
| product = conn.execute('SELECT * FROM products WHERE id = ?', (product_id,)).fetchone() | |
| if not product: | |
| return jsonify({"error": "Product not found"}), 404 | |
| # Update product | |
| conn.execute( | |
| ''' | |
| UPDATE products | |
| SET name = ?, sku = ?, quantity = ?, price = ?, | |
| category = ?, last_updated = ?, reorder_point = ? | |
| WHERE id = ? | |
| ''', | |
| ( | |
| data['name'], | |
| data['sku'], | |
| data['quantity'], | |
| data['price'], | |
| data['category'], | |
| data['last_updated'], | |
| data.get('reorder_point', 5), | |
| product_id | |
| ) | |
| ) | |
| conn.commit() | |
| return jsonify({"status": "success", "message": "Product updated successfully"}) | |
| except Exception as e: | |
| conn.rollback() | |
| return jsonify({"error": str(e)}), 400 | |
| finally: | |
| conn.close() | |
| @app.route('/api/products/<product_id>', methods=['DELETE']) | |
| def delete_product(product_id): | |
| conn = get_db_connection() | |
| try: | |
| # Check if product exists | |
| product = conn.execute('SELECT * FROM products WHERE id = ?', (product_id,)).fetchone() | |
| if not product: | |
| return jsonify({"error": "Product not found"}), 404 | |
| # Delete product | |
| conn.execute('DELETE FROM products WHERE id = ?', (product_id,)) | |
| conn.commit() | |
| return jsonify({"status": "success", "message": "Product deleted successfully"}) | |
| except Exception as e: | |
| conn.rollback() | |
| return jsonify({"error": str(e)}), 400 | |
| finally: | |
| conn.close() | |
| @app.route('/api/stock/analytics', methods=['GET']) | |
| def get_stock_analytics(): | |
| conn = get_db_connection() | |
| try: | |
| # Get total product count | |
| total_count = conn.execute('SELECT COUNT(*) as count FROM products').fetchone()['count'] | |
| # Get total inventory value | |
| total_value = conn.execute('SELECT SUM(quantity * price) as value FROM products').fetchone()['value'] | |
| # Get low stock count | |
| low_stock = conn.execute('SELECT COUNT(*) as count FROM products WHERE quantity <= reorder_point').fetchone()['count'] | |
| # Get top categories | |
| categories = conn.execute(''' | |
| SELECT category, COUNT(*) as count, SUM(quantity * price) as value | |
| FROM products | |
| GROUP BY category | |
| ORDER BY count DESC | |
| LIMIT 5 | |
| ''').fetchall() | |
| return jsonify({ | |
| "total_count": total_count, | |
| "total_value": total_value or 0, | |
| "low_stock_count": low_stock, | |
| "categories": [dict(category) for category in categories] | |
| }) | |
| except Exception as e: | |
| return jsonify({"error": str(e)}), 400 | |
| finally: | |
| conn.close() | |
| if __name__ == '__main__': | |
| app.run(debug=True, port=5000) | |
| ``` | |
| ### Step 2: Set up a virtual environment | |
| ```bash | |
| # Create a virtual environment | |
| python -m venv venv | |
| # Activate it (Windows) | |
| venv\Scripts\activate | |
| # Activate it (macOS/Linux) | |
| source venv/bin/activate | |
| # Install dependencies | |
| pip install flask flask-cors sqlite3 | |
| ``` | |
| ## 2. Connecting the React App to the Python API | |
| ### Step 1: Create an API Service in the React App | |
| Create a new file `src/services/api.ts`: | |
| ```typescript | |
| const API_URL = 'http://localhost:5000/api'; | |
| // Messages API | |
| export const fetchMessages = async () => { | |
| const response = await fetch(`${API_URL}/messages`); | |
| if (!response.ok) { | |
| throw new Error('Failed to fetch messages'); | |
| } | |
| return response.json(); | |
| }; | |
| export const sendMessage = async (message: { | |
| user_id: string; | |
| content: string; | |
| timestamp: string; | |
| }) => { | |
| const response = await fetch(`${API_URL}/messages`, { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify(message), | |
| }); | |
| if (!response.ok) { | |
| throw new Error('Failed to send message'); | |
| } | |
| return response.json(); | |
| }; | |
| // Transactions API | |
| export const fetchTransactions = async () => { | |
| const response = await fetch(`${API_URL}/transactions`); | |
| if (!response.ok) { | |
| throw new Error('Failed to fetch transactions'); | |
| } | |
| return response.json(); | |
| }; | |
| export const addTransaction = async (transaction: any) => { | |
| const response = await fetch(`${API_URL}/transactions`, { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify(transaction), | |
| }); | |
| if (!response.ok) { | |
| throw new Error('Failed to add transaction'); | |
| } | |
| return response.json(); | |
| }; | |
| // Products/Stock API | |
| export const fetchProducts = async () => { | |
| const response = await fetch(`${API_URL}/products`); | |
| if (!response.ok) { | |
| throw new Error('Failed to fetch products'); | |
| } | |
| return response.json(); | |
| }; | |
| export const fetchLowStockProducts = async () => { | |
| const response = await fetch(`${API_URL}/products/low`); | |
| if (!response.ok) { | |
| throw new Error('Failed to fetch low stock products'); | |
| } | |
| return response.json(); | |
| }; | |
| export const fetchProductById = async (id: string) => { | |
| const response = await fetch(`${API_URL}/products/${id}`); | |
| if (!response.ok) { | |
| throw new Error('Failed to fetch product'); | |
| } | |
| return response.json(); | |
| }; | |
| export const addProduct = async (product: any) => { | |
| const response = await fetch(`${API_URL}/products`, { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify(product), | |
| }); | |
| if (!response.ok) { | |
| throw new Error('Failed to add product'); | |
| } | |
| return response.json(); | |
| }; | |
| export const updateProduct = async (id: string, product: any) => { | |
| const response = await fetch(`${API_URL}/products/${id}`, { | |
| method: 'PUT', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify(product), | |
| }); | |
| if (!response.ok) { | |
| throw new Error('Failed to update product'); | |
| } | |
| return response.json(); | |
| }; | |
| export const deleteProduct = async (id: string) => { | |
| const response = await fetch(`${API_URL}/products/${id}`, { | |
| method: 'DELETE', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| }); | |
| if (!response.ok) { | |
| throw new Error('Failed to delete product'); | |
| } | |
| return response.json(); | |
| }; | |
| export const fetchStockAnalytics = async () => { | |
| const response = await fetch(`${API_URL}/stock/analytics`); | |
| if (!response.ok) { | |
| throw new Error('Failed to fetch stock analytics'); | |
| } | |
| return response.json(); | |
| }; | |
| ``` | |
| ### Step 2: Update Messages Component to use the API | |
| Update the Messages component to fetch and send messages via the API: | |
| ```typescript | |
| import { useState, useEffect } from "react"; | |
| import { fetchMessages, sendMessage } from "../services/api"; | |
| import { Send, Search } from "lucide-react"; | |
| import AppLayout from "@/components/layout/AppLayout"; | |
| import { Input } from "@/components/ui/input"; | |
| import { Button } from "@/components/ui/button"; | |
| import { Avatar } from "@/components/ui/avatar"; | |
| import { ScrollArea } from "@/components/ui/scroll-area"; | |
| import { cn } from "@/lib/utils"; | |
| import { motion } from "framer-motion"; | |
| // Sample data for messages | |
| const initialContacts = [ | |
| { id: 1, name: "AI Assistant", avatar: "A", lastMessage: "How can I help with your finances?", time: "10:30 AM", unread: 2 }, | |
| { id: 2, name: "Budget Bot", avatar: "B", lastMessage: "Your weekly spending report is ready", time: "Yesterday", unread: 0 }, | |
| { id: 3, name: "Investment Advisor", avatar: "I", lastMessage: "Consider these stocks for your portfolio", time: "Yesterday", unread: 1 }, | |
| { id: 4, name: "Expense Tracker", avatar: "E", lastMessage: "You've exceeded your dining budget", time: "Monday", unread: 0 }, | |
| { id: 5, name: "Financial Coach", avatar: "F", lastMessage: "Let's review your saving goals", time: "08/12/23", unread: 0 }, | |
| ]; | |
| const initialMessages = [ | |
| { id: 1, sender: "client", text: "Hello! I need some help understanding my recent transactions.", time: "10:30 AM" }, | |
| { id: 2, sender: "me", text: "Hi there! I'd be happy to help you analyze your spending patterns. What specifically would you like to know?", time: "10:32 AM" }, | |
| { id: 3, sender: "client", text: "I noticed some unusual activity in my account", time: "10:33 AM" }, | |
| { id: 4, sender: "me", text: "I can check that for you. Could you tell me which transactions look suspicious?", time: "10:35 AM" }, | |
| { id: 5, sender: "me", text: "Based on your spending history, the transaction at 'Tech Store' for $349.99 is unusual for you.", time: "10:36 AM" }, | |
| { id: 6, sender: "client", text: "Yes, that's the one I was concerned about. I don't remember making that purchase.", time: "10:38 AM" }, | |
| ]; | |
| const Messages = () => { | |
| const [contacts, setContacts] = useState(initialContacts); | |
| const [selectedContact, setSelectedContact] = useState(initialContacts[0]); | |
| const [messages, setMessages] = useState([]); | |
| const [loading, setLoading] = useState(true); | |
| const [newMessage, setNewMessage] = useState(""); | |
| const [searchTerm, setSearchTerm] = useState(""); | |
| useEffect(() => { | |
| // Fetch messages when component mounts | |
| const getMessages = async () => { | |
| try { | |
| setLoading(true); | |
| const data = await fetchMessages(); | |
| setMessages(data); | |
| } catch (error) { | |
| console.error('Failed to fetch messages:', error); | |
| } finally { | |
| setLoading(false); | |
| } | |
| }; | |
| getMessages(); | |
| }, []); | |
| const handleSendMessage = async () => { | |
| if (newMessage.trim() === "") return; | |
| const messageData = { | |
| user_id: "user123", // Replace with actual user id | |
| content: newMessage, | |
| timestamp: new Date().toISOString() | |
| }; | |
| try { | |
| await sendMessage(messageData); | |
| // Refetch messages or add the new one to state | |
| setMessages([...messages, { | |
| id: Date.now(), | |
| sender: "me", | |
| text: newMessage, | |
| time: new Date().toLocaleTimeString([], {hour: '2-digit', minute: '2-digit'}) | |
| }]); | |
| setNewMessage(""); | |
| } catch (error) { | |
| console.error('Failed to send message:', error); | |
| } | |
| }; | |
| const filteredContacts = contacts.filter(contact => | |
| contact.name.toLowerCase().includes(searchTerm.toLowerCase()) | |
| ); | |
| return ( | |
| <AppLayout> | |
| <div className="max-w-6xl mx-auto h-full p-4"> | |
| <h1 className="text-xl font-bold mb-4 text-slate-800">Messages</h1> | |
| <div className="flex h-[calc(100vh-180px)] rounded-2xl overflow-hidden bg-white/80 backdrop-blur-lg border border-slate-200 shadow-lg"> | |
| {/* Contacts sidebar */} | |
| <div className="w-full max-w-xs border-r border-slate-200 hidden md:flex flex-col"> | |
| <div className="p-3 border-b border-slate-200"> | |
| <div className="relative"> | |
| <Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-slate-400 h-4 w-4" /> | |
| <Input | |
| placeholder="Search contacts..." | |
| className="pl-10 bg-slate-50 border-slate-200 text-slate-800" | |
| value={searchTerm} | |
| onChange={(e) => setSearchTerm(e.target.value)} | |
| /> | |
| </div> | |
| </div> | |
| <ScrollArea className="flex-1"> | |
| {filteredContacts.map(contact => ( | |
| <div | |
| key={contact.id} | |
| className={cn( | |
| "p-3 flex items-center gap-3 cursor-pointer hover:bg-slate-50 transition-colors", | |
| selectedContact.id === contact.id ? "bg-slate-50" : "" | |
| )} | |
| onClick={() => setSelectedContact(contact)} | |
| > | |
| <Avatar className="h-10 w-10 bg-blue-600 text-white"> | |
| <div>{contact.avatar}</div> | |
| </Avatar> | |
| <div className="flex-1 min-w-0"> | |
| <div className="flex justify-between items-center"> | |
| <span className="font-medium truncate text-slate-800">{contact.name}</span> | |
| <span className="text-xs text-slate-500">{contact.time}</span> | |
| </div> | |
| <p className="text-sm text-slate-500 truncate">{contact.lastMessage}</p> | |
| </div> | |
| {contact.unread > 0 && ( | |
| <div className="bg-blue-600 text-white text-xs rounded-full h-5 w-5 flex items-center justify-center"> | |
| {contact.unread} | |
| </div> | |
| )} | |
| </div> | |
| ))} | |
| </ScrollArea> | |
| </div> | |
| {/* Chat area */} | |
| <div className="flex-1 flex flex-col"> | |
| <div className="p-3 border-b border-slate-200 flex items-center gap-3"> | |
| <Avatar className="h-8 w-8 bg-blue-600 text-white md:hidden"> | |
| <div>{selectedContact.avatar}</div> | |
| </Avatar> | |
| <div> | |
| <h3 className="font-medium text-slate-800">{selectedContact.name}</h3> | |
| </div> | |
| </div> | |
| <ScrollArea className="flex-1 p-4"> | |
| <div className="space-y-4"> | |
| {messages.map((message, index) => ( | |
| <motion.div | |
| key={message.id} | |
| initial={{ opacity: 0, y: 10 }} | |
| animate={{ opacity: 1, y: 0 }} | |
| transition={{ duration: 0.3, delay: index * 0.1 }} | |
| className={cn( | |
| "flex", | |
| message.sender === "me" ? "justify-end" : "justify-start" | |
| )} | |
| > | |
| <div | |
| className={cn( | |
| "max-w-[80%] p-3 rounded-2xl shadow-sm", | |
| message.sender === "me" | |
| ? "bg-blue-600 text-white rounded-tr-none" | |
| : "bg-slate-100 text-slate-800 rounded-tl-none" | |
| )} | |
| > | |
| <p>{message.text}</p> | |
| <span className={cn( | |
| "text-xs block mt-1", | |
| message.sender === "me" | |
| ? "text-white/70" | |
| : "text-slate-500" | |
| )}> | |
| {message.time} | |
| </span> | |
| </div> | |
| </motion.div> | |
| ))} | |
| </div> | |
| </ScrollArea> | |
| <div className="p-3 border-t border-slate-200 flex items-center gap-2"> | |
| <Input | |
| placeholder="Type a message..." | |
| value={newMessage} | |
| onChange={(e) => setNewMessage(e.target.value)} | |
| onKeyDown={(e) => { | |
| if (e.key === "Enter") { | |
| handleSendMessage(); | |
| } | |
| }} | |
| className="flex-1 bg-slate-50 border-slate-200 text-slate-800" | |
| /> | |
| <Button size="icon" className="bg-blue-600 hover:bg-blue-700" onClick={handleSendMessage}> | |
| <Send size={18} /> | |
| </Button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </AppLayout> | |
| ); | |
| }; | |
| ``` | |
| ## 3. Deploying the Python API | |
| ### Option 1: Docker Deployment | |
| Create a `Dockerfile` for the Python API: | |
| ```dockerfile | |
| FROM python:3.9-slim | |
| WORKDIR /app | |
| COPY requirements.txt . | |
| RUN pip install -r requirements.txt | |
| COPY . . | |
| EXPOSE 5000 | |
| CMD ["python", "app.py"] | |
| ``` | |
| Create a `requirements.txt` file: | |
| ``` | |
| flask>=2.0.0 | |
| flask-cors>=3.0.10 | |
| ``` | |
| Update the `docker-compose.yml` to include the Python API: | |
| ```yaml | |
| version: '3.8' | |
| services: | |
| app: | |
| build: . | |
| ports: | |
| - "8080:80" | |
| restart: unless-stopped | |
| depends_on: | |
| - api | |
| api: | |
| build: ./api | |
| ports: | |
| - "5000:5000" | |
| restart: unless-stopped | |
| volumes: | |
| - ./api/data:/app/data | |
| ``` | |
| ### Option 2: Cloud Deployment | |
| Deploy the Python API to a cloud service like: | |
| - Heroku | |
| - AWS Lambda + API Gateway | |
| - Google Cloud Functions | |
| - Azure Functions | |
| ## 4. Advanced Integration: AI Chat Assistant | |
| To enhance the chat interface with AI capabilities, you can integrate with OpenAI's API: | |
| ```python | |
| import openai | |
| # Set your OpenAI API key | |
| openai.api_key = "your-api-key" | |
| @app.route('/api/chat', methods=['POST']) | |
| def chat(): | |
| data = request.json | |
| user_message = data['message'] | |
| # Save user message to database | |
| conn = get_db_connection() | |
| conn.execute( | |
| 'INSERT INTO messages (user_id, content, timestamp, is_ai) VALUES (?, ?, ?, ?)', | |
| (data['user_id'], user_message, data['timestamp'], 0) | |
| ) | |
| conn.commit() | |
| # Get AI response | |
| response = openai.ChatCompletion.create( | |
| model="gpt-3.5-turbo", | |
| messages=[ | |
| {"role": "system", "content": "You are a helpful financial assistant."}, | |
| {"role": "user", "content": user_message} | |
| ] | |
| ) | |
| ai_message = response.choices[0].message.content | |
| # Save AI response to database | |
| conn.execute( | |
| 'INSERT INTO messages (user_id, content, timestamp, is_ai) VALUES (?, ?, ?, ?)', | |
| ('assistant', ai_message, data['timestamp'], 1) | |
| ) | |
| conn.commit() | |
| conn.close() | |
| return jsonify({ | |
| "message": ai_message, | |
| "timestamp": data['timestamp'] | |
| }) | |
| ``` | |
| ## 5. Running Both Applications Together | |
| 1. Start the Python API: | |
| ```bash | |
| cd api | |
| python app.py | |
| ``` | |
| 2. Start the React app: | |
| ```bash | |
| npm run dev | |
| ``` | |
| ## 6. Security Considerations | |
| 1. Implement proper authentication for API endpoints | |
| 2. Use HTTPS for production | |
| 3. Validate and sanitize all user inputs | |
| 4. Use environment variables for sensitive information | |
| 5. Implement rate limiting to prevent abuse | |
| ## 7. Troubleshooting | |
| ### Common Issues: | |
| - **CORS errors**: Ensure CORS is properly configured in both the React app and the Python API | |
| - **Database connection errors**: Check file paths and permissions | |
| - **API connection issues**: Verify the API URL and port are correct | |
| ## 8. Resources | |
| - [Flask Documentation](https://flask.palletsprojects.com/) | |
| - [React Query Documentation](https://react-query.tanstack.com/) | |
| - [SQLite Documentation](https://www.sqlite.org/docs.html) | |
| ## 9. License | |
| This integration guide is provided under the same license as the Flutter Finance App. | |