Integrating OpenAI API for File Handling and Vision in Flutter App

Hello OpenAI Community,

I’m working on a Flutter app that allows users to upload and process files (text, images, PDFs) using the OpenAI API. I need help with integrating the file handling and vision capabilities into my Flutter app. Below are the specifics of my current setup and the issues I’m facing:

Current Configuration:

AndroidManifest.xml:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.my_jadlu_app">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.CAMERA" />

    <application
        android:label="jadlu"
        android:icon="@mipmap/ic_launcher"
        android:requestLegacyExternalStorage="true">
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:launchMode="singleTop"
           Here are the prepared messages for posting on the forums:

### Stack Overflow Post

#### Title: **Issue with File Permissions and File Handling in Flutter App**

#### Body:
Hello Flutter Community,

I'm working on a Flutter app where users can upload files, and the app then processes these files using the OpenAI API. Despite configuring permissions and implementing the necessary code, I keep encountering permission and file handling issues. Here are the details:

**Current Configuration:**
- **AndroidManifest.xml:**
```xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.my_jadlu_app">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.CAMERA" />

    <application
        android:label="jadlu"
        android:icon="@mipmap/ic_launcher"
        android:requestLegacyExternalStorage="true">
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:launchMode="singleTop"
            android:theme="@style/LaunchTheme"
            android:configChanges="orientation|keyboardHidden|screenSize"
            android:windowSoftInputMode="adjustResize">
            <meta-data
                android:name="io.flutter.embedding.android.NormalTheme"
                android:resource="@style/NormalTheme" />
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <meta-data
            android:name="flutterEmbedding"
            android:value="2" />
    </application>

    <queries>
        <intent>
            <action android:name="android.intent.action.PROCESS_TEXT" />
            <data android:mimeType="text/plain" />
        </intent>
    </queries>
</manifest>

Dart Code for Permission Handling and File Upload:

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:file_picker/file_picker.dart';
import 'package:permission_handler/permission_handler.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Chat Jadlu',
      theme: ThemeData(
        primarySwatch: Colors.green,
        brightness: Brightness.dark,
        scaffoldBackgroundColor: Colors.black,
      ),
      home: const ChatScreen(),
    );
  }
}

class ChatScreen extends StatefulWidget {
  const ChatScreen({Key? key}) : super(key: key);

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

class ChatScreenState extends State<ChatScreen> {
  final TextEditingController _controller = TextEditingController();
  final List<String> _messages = [];
  final ScrollController _scrollController = ScrollController();
  final String _prompt = "Bonjour ! Je suis Jadlu, votre guide dévoué dans le fascinant voyage de la parentalité...";
  final String _apiKey = 'your-api-key-here';

  @override
  void initState() {
    super.initState();
    _requestPermissions();
  }

  Future<void> _requestPermissions() async {
    var status = await Permission.storage.status;
    if (!status.isGranted) {
      await Permission.storage.request();
    }
  }

  Future<void> _sendMessage(String message) async {
    setState(() {
      _messages.add("You: $message");
    });

    try {
      final response = await http.post(
        Uri.parse('https://api.openai.com/v1/chat/completions'),
        headers: {
          'Content-Type': 'application/json; charset=UTF-8',
          'Authorization': 'Bearer $_apiKey',
        },
        body: jsonEncode({
          'model': 'gpt-4o',
          'messages': [
            {
              'role': 'system',
              'content': _prompt,
            },
            {
              'role': 'user',
              'content': message,
            }
          ],
          'max_tokens': 2048,
        }),
      );

      if (response.statusCode == 200) {
        final data = jsonDecode(utf8.decode(response.bodyBytes));
        final reply = data['choices'][0]['message']['content'].trim();

        setState(() {
          _messages.add("Jadlu: $reply");
        });
      } else {
        setState(() {
          _messages.add("Jadlu: Failed to get response");
        });
      }
    } catch (error) {
      setState(() {
        _messages.add("Jadlu: Failed to get response due to an error");
      });
    }

    _controller.clear();
    _scrollToBottom();
  }

  Future<void> _sendFile() async {
    try {
      FilePickerResult? result = await FilePicker.platform.pickFiles();
      if (result != null) {
        PlatformFile file = result.files.first;
        setState(() {
          _messages.add("You sent a file: ${file.name}");
        });

        final response = await http.post(
          Uri.parse('https://api.openai.com/v1/embeddings'),
          headers: {
            'Authorization': 'Bearer $_apiKey',
            'Content-Type': 'application/json',
          },
          body: jsonEncode({
            'model': 'text-embedding-ada-002',
            'input': utf8.decode(file.bytes!),
          }),
        );

        if (response.statusCode == 200) {
          final data = jsonDecode(utf8.decode(response.bodyBytes));
          final embeddings = data['data'][0]['embedding'];

          setState(() {
            _messages.add("Jadlu: Les embeddings sont ${embeddings.toString()}");
          });
        } else {
          setState(() {
            _messages.add("Jadlu: Échec du traitement du fichier.");
          });
        }
      }
    } catch (error) {
      setState(() {
        _messages.add("Jadlu: Échec du traitement du fichier en raison d'une erreur.");
      });
    }

    _scrollToBottom();
  }

  void _scrollToBottom() {
    WidgetsBinding.instance.addPostFrameCallback((_) {
      if (_scrollController.hasClients) {
        _scrollController.animateTo(
          _scrollController.position.maxScrollExtent,
          duration: const Duration(milliseconds: 300),
          curve: Curves.easeOut,
        );
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Chat Jadlu.fr'),
      ),
      body: SafeArea(
        child: Column(
          children: <Widget>[
            Expanded(
              child: ListView.builder(
                controller: _scrollController,
                itemCount: _messages.length,
                itemBuilder: (context, index) {
                  return ListTile(
                    title: Text(
                      _messages[index],
                      style: const TextStyle(color: Colors.white),
                    ),
                  );
                },
              ),
            ),
            Padding(
              padding: const EdgeInsets.all(8.0),
              child: Row(
                children: <Widget>[
                  Expanded(
                    child: TextField(
                      controller: _controller,
                      decoration: const InputDecoration(
                        hintText: 'Enter your message',
                        hintStyle: TextStyle(color: Colors.white70),
                        filled: true,
                        fillColor: Colors.white24,
                      ),
                      style: const TextStyle(color: Colors.white),
                      onSubmitted: (value) {
                        _sendMessage(value);
                      },
                    ),
                  ),
                  IconButton(
                    icon: const Icon(Icons.send, color: Colors.green),
                    onPressed: () {
                      _sendMessage(_controller.text);
                    },
                  ),
                  IconButton(
                    icon: const Icon(Icons.attach_file, color: Colors.green),
                    onPressed: () {
                      _sendFile();
                    },
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

Receiving PlatformException(read_external_storage_denied) error despite requesting permissions.
File handling process often results in “Échec du traitement du fichier en raison d’une erreur.”
Any guidance on resolving these issues would be greatly appreciated!

Thank you!