| # 🚨 EMERGENCY Flutter Fix - 404 & Microphone Issues | |
| ## ⚡ **QUICK FIX 1: API URL (404 Error)** | |
| Your Hugging Face Space URL format is incorrect. Try these URLs in order: | |
| ### **Option A: Try this URL format:** | |
| ```dart | |
| static const String baseUrl = 'https://carsaai-carsa-api.hf.space'; | |
| ``` | |
| ### **Option B: If Option A fails, try:** | |
| ```dart | |
| static const String baseUrl = 'https://carsaai-carsa-api.hf.space:7860'; | |
| ``` | |
| ### **Option C: If both fail, use direct Space URL:** | |
| ```dart | |
| static const String baseUrl = 'https://huggingface.co/spaces/CarsaAI/carsa_api/proxy'; | |
| ``` | |
| ## ⚡ **QUICK FIX 2: Microphone Crash** | |
| ### **Step 1: Update pubspec.yaml** | |
| ```yaml | |
| dependencies: | |
| flutter: | |
| sdk: flutter | |
| http: ^1.1.0 | |
| # ADD THESE IMMEDIATELY: | |
| permission_handler: ^11.0.1 | |
| flutter_sound: ^9.2.13 | |
| path_provider: ^2.1.1 | |
| ``` | |
| ### **Step 2: Run this command:** | |
| ```bash | |
| flutter pub get | |
| ``` | |
| ### **Step 3: Update AndroidManifest.xml** | |
| File: `android/app/src/main/AndroidManifest.xml` | |
| ```xml | |
| <manifest xmlns:android="http://schemas.android.com/apk/res/android"> | |
| <!-- ADD THESE PERMISSIONS --> | |
| <uses-permission android:name="android.permission.INTERNET" /> | |
| <uses-permission android:name="android.permission.RECORD_AUDIO" /> | |
| <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> | |
| <application | |
| android:label="carsa_ai" | |
| android:name="${applicationName}" | |
| android:icon="@mipmap/ic_launcher"> | |
| <!-- Your existing app configuration --> | |
| </application> | |
| </manifest> | |
| ``` | |
| ### **Step 4: Create Simple Audio Service** | |
| Create `lib/services/simple_audio_service.dart`: | |
| ```dart | |
| import 'dart:io'; | |
| import 'package:flutter_sound/flutter_sound.dart'; | |
| import 'package:permission_handler.dart'; | |
| import 'package:path_provider/path_provider.dart'; | |
| class SimpleAudioService { | |
| static FlutterSoundRecorder? _recorder; | |
| static bool _isInitialized = false; | |
| static Future<void> init() async { | |
| if (_isInitialized) return; | |
| _recorder = FlutterSoundRecorder(); | |
| await _recorder!.openRecorder(); | |
| _isInitialized = true; | |
| print('✅ Audio service initialized'); | |
| } | |
| static Future<bool> checkPermissions() async { | |
| var status = await Permission.microphone.status; | |
| if (!status.isGranted) { | |
| status = await Permission.microphone.request(); | |
| } | |
| print('🎤 Microphone permission: ${status.isGranted}'); | |
| return status.isGranted; | |
| } | |
| static Future<String?> startRecording() async { | |
| try { | |
| await init(); | |
| if (!await checkPermissions()) { | |
| throw Exception('Microphone permission denied'); | |
| } | |
| final directory = await getTemporaryDirectory(); | |
| final filePath = '${directory.path}/recording.wav'; | |
| await _recorder!.startRecorder( | |
| toFile: filePath, | |
| codec: Codec.pcm16WAV, | |
| ); | |
| print('🔴 Recording started: $filePath'); | |
| return filePath; | |
| } catch (e) { | |
| print('❌ Recording error: $e'); | |
| return null; | |
| } | |
| } | |
| static Future<String?> stopRecording() async { | |
| try { | |
| final path = await _recorder!.stopRecorder(); | |
| print('⏹️ Recording stopped: $path'); | |
| return path; | |
| } catch (e) { | |
| print('❌ Stop recording error: $e'); | |
| return null; | |
| } | |
| } | |
| } | |
| ``` | |
| ### **Step 5: Update Your UI (Quick Version)** | |
| ```dart | |
| import 'package:flutter/material.dart'; | |
| import 'services/api_service.dart'; | |
| import 'services/simple_audio_service.dart'; | |
| class QuickFixScreen extends StatefulWidget { | |
| @override | |
| _QuickFixScreenState createState() => _QuickFixScreenState(); | |
| } | |
| class _QuickFixScreenState extends State<QuickFixScreen> { | |
| bool isRecording = false; | |
| bool isLoading = false; | |
| String result = ''; | |
| @override | |
| Widget build(BuildContext context) { | |
| return Scaffold( | |
| appBar: AppBar(title: Text('Carsa AI - Emergency Fix')), | |
| body: Padding( | |
| padding: EdgeInsets.all(20), | |
| child: Column( | |
| mainAxisAlignment: MainAxisAlignment.center, | |
| children: [ | |
| // Test Translation Button | |
| ElevatedButton( | |
| onPressed: isLoading ? null : _testTranslation, | |
| child: Text('Test Translation'), | |
| ), | |
| SizedBox(height: 20), | |
| // Recording Button | |
| GestureDetector( | |
| onTap: isLoading ? null : (isRecording ? _stopRecording : _startRecording), | |
| child: Container( | |
| width: 100, | |
| height: 100, | |
| decoration: BoxDecoration( | |
| color: isRecording ? Colors.red : Colors.blue, | |
| shape: BoxShape.circle, | |
| ), | |
| child: Icon( | |
| isRecording ? Icons.stop : Icons.mic, | |
| color: Colors.white, | |
| size: 50, | |
| ), | |
| ), | |
| ), | |
| SizedBox(height: 20), | |
| if (isLoading) | |
| CircularProgressIndicator(), | |
| SizedBox(height: 20), | |
| Text( | |
| result, | |
| style: TextStyle(fontSize: 16), | |
| textAlign: TextAlign.center, | |
| ), | |
| ], | |
| ), | |
| ), | |
| ); | |
| } | |
| Future<void> _testTranslation() async { | |
| setState(() { | |
| isLoading = true; | |
| result = 'Testing translation...'; | |
| }); | |
| try { | |
| final response = await ApiService.translateText('Hello', 'twi'); | |
| setState(() { | |
| result = 'Translation SUCCESS: ${response['translated_text']}'; | |
| }); | |
| } catch (e) { | |
| setState(() { | |
| result = 'Translation ERROR: $e'; | |
| }); | |
| } finally { | |
| setState(() => isLoading = false); | |
| } | |
| } | |
| Future<void> _startRecording() async { | |
| setState(() { | |
| isLoading = true; | |
| result = 'Starting recording...'; | |
| }); | |
| try { | |
| final path = await SimpleAudioService.startRecording(); | |
| if (path != null) { | |
| setState(() { | |
| isRecording = true; | |
| result = 'Recording... Tap to stop'; | |
| }); | |
| } else { | |
| throw Exception('Failed to start recording'); | |
| } | |
| } catch (e) { | |
| setState(() { | |
| result = 'Recording ERROR: $e'; | |
| }); | |
| } finally { | |
| setState(() => isLoading = false); | |
| } | |
| } | |
| Future<void> _stopRecording() async { | |
| setState(() { | |
| isLoading = true; | |
| result = 'Stopping recording...'; | |
| }); | |
| try { | |
| final path = await SimpleAudioService.stopRecording(); | |
| setState(() => isRecording = false); | |
| if (path != null) { | |
| // Try speech to text | |
| final response = await ApiService.speechToText(path); | |
| setState(() { | |
| result = 'Speech-to-Text SUCCESS: ${response['transcribed_text']}'; | |
| }); | |
| } | |
| } catch (e) { | |
| setState(() { | |
| isRecording = false; | |
| result = 'Speech-to-Text ERROR: $e'; | |
| }); | |
| } finally { | |
| setState(() => isLoading = false); | |
| } | |
| } | |
| } | |
| ``` | |
| ## 🎯 **IMMEDIATE ACTION:** | |
| 1. **Try URL Option A first** in your API service | |
| 2. **Add the dependencies** to pubspec.yaml | |
| 3. **Run** `flutter pub get` | |
| 4. **Add permissions** to AndroidManifest.xml | |
| 5. **Test the quick fix screen** | |
| If Option A doesn't work, try Option B, then Option C. | |
| **This should fix both your 404 and microphone crashes immediately!** 🚀 | |