diff --git a/.metadata b/.metadata index 0edb585..764012c 100644 --- a/.metadata +++ b/.metadata @@ -4,7 +4,7 @@ # This file should be version controlled and should not be manually edited. version: - revision: "8495dee1fd4aacbe9de707e7581203232f591b2f" + revision: "67323de285b00232883f53b84095eb72be97d35c" channel: "stable" project_type: app @@ -13,26 +13,11 @@ project_type: app migration: platforms: - platform: root - create_revision: 8495dee1fd4aacbe9de707e7581203232f591b2f - base_revision: 8495dee1fd4aacbe9de707e7581203232f591b2f - - platform: android - create_revision: 8495dee1fd4aacbe9de707e7581203232f591b2f - base_revision: 8495dee1fd4aacbe9de707e7581203232f591b2f - - platform: ios - create_revision: 8495dee1fd4aacbe9de707e7581203232f591b2f - base_revision: 8495dee1fd4aacbe9de707e7581203232f591b2f - - platform: linux - create_revision: 8495dee1fd4aacbe9de707e7581203232f591b2f - base_revision: 8495dee1fd4aacbe9de707e7581203232f591b2f - - platform: macos - create_revision: 8495dee1fd4aacbe9de707e7581203232f591b2f - base_revision: 8495dee1fd4aacbe9de707e7581203232f591b2f + create_revision: 67323de285b00232883f53b84095eb72be97d35c + base_revision: 67323de285b00232883f53b84095eb72be97d35c - platform: web - create_revision: 8495dee1fd4aacbe9de707e7581203232f591b2f - base_revision: 8495dee1fd4aacbe9de707e7581203232f591b2f - - platform: windows - create_revision: 8495dee1fd4aacbe9de707e7581203232f591b2f - base_revision: 8495dee1fd4aacbe9de707e7581203232f591b2f + create_revision: 67323de285b00232883f53b84095eb72be97d35c + base_revision: 67323de285b00232883f53b84095eb72be97d35c # User provided section diff --git a/lib/main.dart b/lib/main.dart index 1013f29..512e359 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -46,13 +46,23 @@ class TranscriptionScreen extends StatefulWidget { State createState() => _TranscriptionScreenState(); } -class _TranscriptionScreenState extends State with SingleTickerProviderStateMixin { +class _TranscriptionScreenState extends State + with SingleTickerProviderStateMixin { final _audioRecorder = AudioRecorder(); bool _isRecording = false; String _transcription = ''; String _recordingPath = ''; bool _isTranscribing = false; bool _isProcessing = false; + String selectedValue = 'gemma-3-27b-it'; + + final List items = [ + 'gemma-3-27b-it', + 'gemini-2.5-flash', + 'gemma-3-12b-it', + 'gemini-3-flash', + 'gemini-2.0-flash' + ]; // Data for screens String _formattedTranscription = ''; @@ -60,7 +70,8 @@ class _TranscriptionScreenState extends State with SingleTi String _prescriptionContent = ''; // Chatbot service - final ChatbotService _chatbotService = ChatbotService(); + ChatbotService get _chatbotService => + ChatbotService(model: selectedValue); //Implemented a getter function // For waveform animation late AnimationController _animationController; @@ -120,7 +131,8 @@ class _TranscriptionScreenState extends State with SingleTi try { if (await _audioRecorder.hasPermission()) { final directory = await getTemporaryDirectory(); - _recordingPath = '${directory.path}/recording_${DateTime.now().millisecondsSinceEpoch}.m4a'; + _recordingPath = + '${directory.path}/recording_${DateTime.now().millisecondsSinceEpoch}.m4a'; await _audioRecorder.start( RecordConfig( @@ -210,12 +222,14 @@ class _TranscriptionScreenState extends State with SingleTi if (response.statusCode == 200) { final decodedResponse = json.decode(response.body); - final result = decodedResponse['results']['channels'][0]['alternatives'][0]['transcript']; + final result = decodedResponse['results']['channels'][0]['alternatives'] + [0]['transcript']; setState(() { _isTranscribing = false; _transcription = result.isNotEmpty ? result : 'No speech detected'; - _formattedTranscription = _transcription; // Store raw transcription directly + _formattedTranscription = + _transcription; // Store raw transcription directly _isProcessing = true; }); @@ -225,7 +239,8 @@ class _TranscriptionScreenState extends State with SingleTi print('============================================='); // Send to Gemini for processing if we have a valid transcription - if (_transcription.isNotEmpty && _transcription != 'No speech detected') { + if (_transcription.isNotEmpty && + _transcription != 'No speech detected') { await _processWithGemini(_transcription); } else { setState(() { @@ -261,13 +276,11 @@ class _TranscriptionScreenState extends State with SingleTi // Prompt 2: Generate summary final summary = await _chatbotService.getGeminiResponse( - "Generate a summary of the conversation based on this transcription: $transcription" - ); + "Generate a summary of the conversation based on this transcription: $transcription"); // Prompt 3: Generate prescription final prescription = await _chatbotService.getGeminiResponse( - "Generate a prescription based on the conversation in this transcription: $transcription" - ); + "Generate a prescription based on the conversation in this transcription: $transcription"); setState(() { // _formattedTranscription = formattedTranscription; @@ -277,7 +290,6 @@ class _TranscriptionScreenState extends State with SingleTi }); print('\n============ GEMINI PROCESSING COMPLETE ============'); - } catch (e) { setState(() { _isProcessing = false; @@ -310,7 +322,8 @@ class _TranscriptionScreenState extends State with SingleTi ), child: SafeArea( child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 16.0), + padding: + const EdgeInsets.symmetric(horizontal: 24.0, vertical: 16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -324,18 +337,58 @@ class _TranscriptionScreenState extends State with SingleTi ), ), const SizedBox(height: 8), - Text( - _isRecording - ? 'Recording your voice...' - : _isTranscribing - ? 'Transcribing your voice...' - : _isProcessing - ? 'Processing with Gemini...' - : 'Tap the mic to begin', - style: const TextStyle( - fontSize: 16, - color: Colors.white70, - ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + _isRecording + ? 'Recording your voice...' + : _isTranscribing + ? 'Transcribing your voice...' + : _isProcessing + ? 'Processing with Gemini...' + : 'Tap the mic to begin', + style: const TextStyle( + fontSize: 16, + color: Colors.white70, + ), + ), + Row( + children: [ + Text( + "Choose a model: ", + style: TextStyle(color: Colors.white70), + ), + SizedBox( + width: 20, + ), + DropdownButton( + padding: EdgeInsets.only(left: 5.0), + value: selectedValue, + icon: const Icon(Icons.arrow_drop_down), + elevation: 16, + onChanged: (String? newValue) { + if (newValue != null) { + setState(() { + selectedValue = newValue; + }); + } + }, + items: items.map((String item) { + return DropdownMenuItem( + value: item, + child: Text( + item, + style: TextStyle( + color: const Color.fromARGB(255, 127, 127, + 127)), // Currently no theming support, hence hardcoded color + ), + ); + }).toList(), + ), + ], + ), + ], ), const SizedBox(height: 30), @@ -351,7 +404,7 @@ class _TranscriptionScreenState extends State with SingleTi crossAxisAlignment: CrossAxisAlignment.end, children: List.generate( _waveformValues.length, - (index) { + (index) { final value = _waveformValues[index]; return AnimatedContainer( duration: const Duration(milliseconds: 100), @@ -360,11 +413,11 @@ class _TranscriptionScreenState extends State with SingleTi decoration: BoxDecoration( color: _isRecording ? HSLColor.fromAHSL( - 1.0, - (280 + index * 2) % 360, - 0.8, - 0.7 + value * 0.2 - ).toColor() + 1.0, + (280 + index * 2) % 360, + 0.8, + 0.7 + value * 0.2) + .toColor() : Colors.white.withOpacity(0.5), borderRadius: BorderRadius.circular(5), ), @@ -380,7 +433,9 @@ class _TranscriptionScreenState extends State with SingleTi // Microphone button Center( child: GestureDetector( - onTap: (_isTranscribing || _isProcessing) ? null : _toggleRecording, + onTap: (_isTranscribing || _isProcessing) + ? null + : _toggleRecording, child: Container( width: 100, height: 100, @@ -389,7 +444,8 @@ class _TranscriptionScreenState extends State with SingleTi color: _isRecording ? Colors.red : Colors.white, boxShadow: [ BoxShadow( - color: (_isRecording ? Colors.red : Colors.white).withOpacity(0.3), + color: (_isRecording ? Colors.red : Colors.white) + .withOpacity(0.3), spreadRadius: 8, blurRadius: 20, ), @@ -400,7 +456,9 @@ class _TranscriptionScreenState extends State with SingleTi child: Icon( _isRecording ? Icons.stop : Icons.mic, size: 50, - color: _isRecording ? Colors.white : Colors.deepPurple.shade800, + color: _isRecording + ? Colors.white + : Colors.deepPurple.shade800, ), ), ), @@ -423,20 +481,20 @@ class _TranscriptionScreenState extends State with SingleTi color: _isRecording ? Colors.red : _isProcessing - ? Colors.blue - : Colors.amber, + ? Colors.blue + : Colors.amber, ), ), Text( _isRecording ? 'Recording in progress' : _isTranscribing - ? 'Processing audio...' - : _isProcessing - ? 'Generating content with Gemini...' - : _transcription.isEmpty - ? 'Press the microphone button to start' - : 'Ready to view results', + ? 'Processing audio...' + : _isProcessing + ? 'Generating content with Gemini...' + : _transcription.isEmpty + ? 'Press the microphone button to start' + : 'Ready to view results', style: const TextStyle( fontSize: 16, fontWeight: FontWeight.w500, @@ -458,10 +516,11 @@ class _TranscriptionScreenState extends State with SingleTi 'Transcription', Icons.record_voice_over, _formattedTranscription.isNotEmpty, - () => Navigator.push( + () => Navigator.push( context, MaterialPageRoute( - builder: (context) => TranscriptionDetailScreen(transcription: _formattedTranscription), + builder: (context) => TranscriptionDetailScreen( + transcription: _formattedTranscription), ), ), ), @@ -471,10 +530,11 @@ class _TranscriptionScreenState extends State with SingleTi 'Summary', Icons.summarize, _summaryContent.isNotEmpty, - () => Navigator.push( + () => Navigator.push( context, MaterialPageRoute( - builder: (context) => SummaryScreen(summary: _summaryContent), + builder: (context) => + SummaryScreen(summary: _summaryContent), ), ), ), @@ -484,10 +544,11 @@ class _TranscriptionScreenState extends State with SingleTi 'Prescription', Icons.medication, _prescriptionContent.isNotEmpty, - () => Navigator.push( + () => Navigator.push( context, MaterialPageRoute( - builder: (context) => PrescriptionScreen(prescription: _prescriptionContent), + builder: (context) => PrescriptionScreen( + prescription: _prescriptionContent), ), ), ), @@ -504,12 +565,12 @@ class _TranscriptionScreenState extends State with SingleTi // Helper method to build navigation buttons Widget _buildNavigationButton( - BuildContext context, - String title, - IconData icon, - bool isEnabled, - VoidCallback onPressed, - ) { + BuildContext context, + String title, + IconData icon, + bool isEnabled, + VoidCallback onPressed, + ) { return SizedBox( width: double.infinity, child: ElevatedButton( @@ -542,4 +603,4 @@ class _TranscriptionScreenState extends State with SingleTi ), ); } -} \ No newline at end of file +} diff --git a/lib/services/chatbot_service.dart b/lib/services/chatbot_service.dart index 22d00b5..88a3a14 100644 --- a/lib/services/chatbot_service.dart +++ b/lib/services/chatbot_service.dart @@ -6,24 +6,30 @@ import 'package:flutter_dotenv/flutter_dotenv.dart'; class ChatbotService { // Get API key from .env file final String apiKey = dotenv.env['GEMINI_API_KEY'] ?? ''; + final String model; + ChatbotService({required this.model}); // Get a response from Gemini based on a prompt Future getGeminiResponse(String prompt) async { print('\n=== GEMINI PROMPT ==='); print(prompt); - final url = Uri.parse('https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=$apiKey'); + final url = Uri.parse( + 'https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=$apiKey'); //changing the model as Gemini-2.0-flash is discontinued try { final response = await http.post( url, headers: {'Content-Type': 'application/json'}, body: jsonEncode({ - "contents": [{"parts": [{"text": prompt}]}], - "generationConfig": { - "temperature": 0.7, - "maxOutputTokens": 1024 - } + "contents": [ + { + "parts": [ + {"text": prompt} + ] + } + ], + "generationConfig": {"temperature": 0.7, "maxOutputTokens": 1024} }), ); @@ -44,4 +50,4 @@ class ChatbotService { return "Error: Could not connect to API: $e"; } } -} \ No newline at end of file +} diff --git a/pubspec.lock b/pubspec.lock index 2702b93..db5a6b5 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -29,26 +29,26 @@ packages: dependency: transitive description: name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.0" clock: dependency: transitive description: name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.2" collection: dependency: transitive description: name: collection - sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" url: "https://pub.dev" source: hosted - version: "1.19.0" + version: "1.19.1" cross_file: dependency: transitive description: @@ -77,10 +77,10 @@ packages: dependency: transitive description: name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.3" ffi: dependency: transitive description: @@ -164,26 +164,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06" + sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" url: "https://pub.dev" source: hosted - version: "10.0.7" + version: "11.0.2" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379" + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" url: "https://pub.dev" source: hosted - version: "3.0.8" + version: "3.0.10" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.2" lints: dependency: transitive description: @@ -204,10 +204,10 @@ packages: dependency: transitive description: name: matcher - sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 url: "https://pub.dev" source: hosted - version: "0.12.16+1" + version: "0.12.17" material_color_utilities: dependency: transitive description: @@ -220,10 +220,10 @@ packages: dependency: transitive description: name: meta - sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 + sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" url: "https://pub.dev" source: hosted - version: "1.15.0" + version: "1.17.0" mime: dependency: transitive description: @@ -236,10 +236,10 @@ packages: dependency: transitive description: name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.9.1" path_provider: dependency: "direct main" description: @@ -449,18 +449,18 @@ packages: dependency: transitive description: name: stack_trace - sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.12.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.4" string_scanner: dependency: transitive description: @@ -481,10 +481,10 @@ packages: dependency: transitive description: name: test_api - sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" + sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55 url: "https://pub.dev" source: hosted - version: "0.7.3" + version: "0.7.7" typed_data: dependency: transitive description: @@ -537,10 +537,10 @@ packages: dependency: transitive description: name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.2.0" vm_service: dependency: transitive description: @@ -574,5 +574,5 @@ packages: source: hosted version: "1.1.0" sdks: - dart: ">=3.6.0 <4.0.0" + dart: ">=3.8.0-0 <4.0.0" flutter: ">=3.27.0"