Add Empty-State UI and Retry Actions for Summary and Prescription Screens#6
Add Empty-State UI and Retry Actions for Summary and Prescription Screens#6Ishaan400 wants to merge 2 commits intoAOSSIE-Org:mainfrom
Conversation
📝 WalkthroughWalkthroughAdded resilient env key retrieval utilities and switched API key access to getters. SummaryScreen and PrescriptionScreen gained optional onRetry callbacks and richer empty-state UI with conditional Retry buttons. main.dart wires onRetry to pop the route and re-trigger Gemini processing using the existing transcription. Changes
Sequence Diagram(s)sequenceDiagram
%% Styling note: primary flows in blue, retries in orange
participant User as User
participant Screen as Summary/Prescription Screen
participant Nav as Navigator
participant Main as main.dart (controller)
participant Gemini as GeminiService
User->>Screen: tap "Retry" (onRetry)
Screen->>Nav: pop()
Nav->>Main: route popped (resume)
Main->>Gemini: process transcription (existing data)
alt success
Gemini-->>Main: summary/prescription result
Main->>Screen: navigate to Summary/Prescription (new content)
else error
Gemini-->>Main: error
Main->>Screen: navigate to Summary/Prescription (empty/error state)
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
lib/main.dart (1)
498-500: Apply the same validation and concurrency guard as SummaryScreen retry.This retry callback has identical issues to the SummaryScreen retry at lines 479-481: no validation of
_transcriptioncontent and no concurrency guard.🔎 Proposed fix
onRetry: () { + if (_isProcessing) return; + if (_transcription.isEmpty || _transcription == 'No speech detected') { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('No valid transcription to retry')), + ); + return; + } Navigator.pop(context); _processWithGemini(_transcription); },
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
lib/main.dartlib/screens/prescription_screen.dartlib/screens/summary_screen.dart
🔇 Additional comments (5)
lib/screens/summary_screen.dart (2)
6-8: LGTM! Clean API extension with optional retry callback.The optional
onRetryparameter is well-designed, maintaining backward compatibility while enabling retry functionality.
59-128: Well-structured empty state UI with proper null safety.The empty state implementation is clean and defensive:
- Conditional Retry button properly guarded by
onRetry != nullcheck- Friendly messaging with icon and instructions
- Markdown rendering preserved for non-empty content
Note: With the current navigation logic in
main.dart(line 473 enables the button only when_summaryContent.isNotEmpty), this empty state is unreachable through normal user flow. However, this defensive UI is good future-proofing if navigation logic changes or direct navigation is added later.lib/screens/prescription_screen.dart (3)
10-12: LGTM! Consistent retry API extension.The
onRetryparameter follows the same clean pattern asSummaryScreen, maintaining consistency across screens.
207-207: Good defensive check on FAB.Disabling the save button when
prescription.isEmptyprevents users from attempting to save empty content, improving UX consistency.
128-197: Well-implemented empty state matching SummaryScreen pattern.The empty state UI is properly structured with:
- Icon and descriptive messaging
- Conditional Retry button with null safety
- Preserved Markdown rendering for non-empty content
As with
SummaryScreen, this empty state is currently unreachable through normal navigation (button enabled only when_prescriptionContent.isNotEmptyat line 492 inmain.dart), but serves as good defensive UI for future changes.
| onRetry: () { | ||
| Navigator.pop(context); | ||
| _processWithGemini(_transcription); |
There was a problem hiding this comment.
Add validation and concurrency guard before retrying Gemini processing.
The retry callback directly passes _transcription to _processWithGemini without validating its content or checking if processing is already in progress. This can lead to:
- Invalid input:
_transcriptionmight contain error messages like'No speech detected','Transcription failed', or'Error during transcription'. Line 228 shows the proper validation that should be replicated here. - Concurrent processing: Users can trigger multiple retries before the previous one completes, causing race conditions and wasted API calls.
🔎 Proposed fix with validation and concurrency guard
onRetry: () {
+ if (_isProcessing) return;
+ if (_transcription.isEmpty || _transcription == 'No speech detected') {
+ ScaffoldMessenger.of(context).showSnackBar(
+ const SnackBar(content: Text('No valid transcription to retry')),
+ );
+ return;
+ }
Navigator.pop(context);
_processWithGemini(_transcription);
},📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| onRetry: () { | |
| Navigator.pop(context); | |
| _processWithGemini(_transcription); | |
| onRetry: () { | |
| if (_isProcessing) return; | |
| if (_transcription.isEmpty || _transcription == 'No speech detected') { | |
| ScaffoldMessenger.of(context).showSnackBar( | |
| const SnackBar(content: Text('No valid transcription to retry')), | |
| ); | |
| return; | |
| } | |
| Navigator.pop(context); | |
| _processWithGemini(_transcription); | |
| }, |
🤖 Prompt for AI Agents
In lib/main.dart around lines 479-481, the onRetry callback pops the dialog and
calls _processWithGemini(_transcription) without validating the transcription or
checking for ongoing work; replicate the validation used at line 228 (reject
values like 'No speech detected', 'Transcription failed', 'Error during
transcription', empty or null) and add a concurrency guard (e.g., check an
_isProcessing boolean, return early if true, set it true before calling
_processWithGemini and false in finally/whenComplete). Also only call
Navigator.pop and start processing if validation passes and processing is not
already in progress.
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (2)
lib/main.dart (2)
486-489: Validation and concurrency guard still missing in retry callback.This issue was already flagged in a previous review. The
onRetrycallback should validate_transcriptioncontent (rejecting error messages like'No speech detected','Transcription failed', etc.) and check_isProcessingto prevent concurrent API calls.
505-508: Same validation and concurrency concern applies here.The
PrescriptionScreenretry callback has the same issues as theSummaryScreencallback—missing input validation and concurrency guard.
🧹 Nitpick comments (1)
lib/services/config.dart (1)
4-15: Consider logging a warning when API keys are missing.The fallback logic is correct and the implementation is clean. However, returning an empty string when both sources fail could lead to silent failures downstream (e.g., 401 errors from Deepgram/Gemini that may be harder to debug).
Consider adding a debug log when falling back to an empty string:
🔎 Optional: Add debug logging for missing keys
+import 'dart:developer' as developer; import 'package:flutter_dotenv/flutter_dotenv.dart'; /// Return GEMINI API key from --dart-define or .env as fallback. String getGeminiApiKey() { const envValue = String.fromEnvironment('GEMINI_API_KEY'); if (envValue.isNotEmpty) return envValue; - return dotenv.env['GEMINI_API_KEY'] ?? ''; + final dotenvValue = dotenv.env['GEMINI_API_KEY'] ?? ''; + if (dotenvValue.isEmpty) { + developer.log('GEMINI_API_KEY not found in dart-define or .env'); + } + return dotenvValue; }
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
lib/main.dartlib/services/chatbot_service.dartlib/services/config.dart
🔇 Additional comments (1)
lib/main.dart (1)
20-26: Good resilient .env loading pattern.The try-catch approach gracefully handles missing
.envfiles in production builds while logging for debugging purposes.
| Future<void> _transcribeAudio() async { | ||
| try { | ||
| final apiKey = dotenv.env['DEEPGRAM_API_KEY'] ?? ''; | ||
| final apiKey = getDeepgramApiKey(); |
There was a problem hiding this comment.
Missing import for getDeepgramApiKey will cause a compile error.
The function getDeepgramApiKey() is called but not imported from config.dart. This will fail at compile time.
🔎 Proposed fix
Add the import at the top of the file with the other service imports:
import 'services/chatbot_service.dart';
import 'screens/transcription_detail_screen.dart';
import 'screens/summary_screen.dart';
import 'screens/prescription_screen.dart';
+import 'services/config.dart';Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In lib/main.dart around line 196, the call to getDeepgramApiKey() is missing its
import which will cause a compile error; add the import for the symbol from its
defining file (config.dart) alongside the other service imports at the top of
lib/main.dart so the function is recognized by the compiler.
| // Get API key from .env file | ||
| final String apiKey = dotenv.env['GEMINI_API_KEY'] ?? ''; | ||
| // Get API key using config fallback (dart-define > .env) | ||
| String get apiKey => getGeminiApiKey(); |
There was a problem hiding this comment.
Missing import for getGeminiApiKey will cause a compile error.
The getter references getGeminiApiKey() but the function is not imported from config.dart. This will fail at compile time.
Also, the flutter_dotenv import on line 4 is now unused and the comment is stale.
🔎 Proposed fix
import 'dart:convert';
import 'dart:developer' as developer;
import 'package:http/http.dart' as http;
-import 'package:flutter_dotenv/flutter_dotenv.dart';
+import 'config.dart';
class ChatbotService {
- // Get API key from .env file
// Get API key using config fallback (dart-define > .env)
String get apiKey => getGeminiApiKey();📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Get API key from .env file | |
| final String apiKey = dotenv.env['GEMINI_API_KEY'] ?? ''; | |
| // Get API key using config fallback (dart-define > .env) | |
| String get apiKey => getGeminiApiKey(); | |
| import 'dart:convert'; | |
| import 'dart:developer' as developer; | |
| import 'package:http/http.dart' as http; | |
| import 'config.dart'; | |
| class ChatbotService { | |
| // Get API key using config fallback (dart-define > .env) | |
| String get apiKey => getGeminiApiKey(); |
🤖 Prompt for AI Agents
In lib/services/chatbot_service.dart around lines 7 to 9, the getter references
getGeminiApiKey() but the function isn't imported which causes a compile error;
add the proper import for the file that exports getGeminiApiKey (e.g., import
'package:your_package/config.dart'; or the correct relative path) at the top of
the file, remove the now-unused flutter_dotenv import, and update or remove the
stale comment so imports and comments match the code.
Add Empty-State UI, Retry Actions, and API Key Fallback Config
This PR improves the user experience and resilience of the app when transcription or Gemini data is missing or fails to load, and also adds robust API key handling for different environments.
UI Enhancements: Friendly Empty-State and Retry Support
Adds friendly empty-state UIs with icon, title, and helpful message to:
summary_screen.dart
prescription_screen.dart
Each screen includes a Retry button when onRetry is available
Updates main.dart to wire:
onRetry → _processWithGemini(_transcription)
Allows users to regenerate summaries/prescriptions without restarting the app
Config Upgrade: Safer API Key Loading
Loads GEMINI_API_KEY, DEEPGRAM_API_KEY from --dart-define
Falls back to .env values for local dev
Prevents failures in CI/production where .env isn’t bundled
Wraps dotenv.load() in safe try-catch to avoid startup crashes
Why This Matters
Shows clear UI feedback instead of blank screens on failure
Makes app more resilient and user-friendly with Retry flows
Enables safe, flexible API key access in local + production environments
Improves polish, developer experience, and maintainability
Testing
Verified success/failure flows on local emulator
Confirmed Retry actions re-trigger expected logic
Tested fallback API key logic in debug mode with and without .env
Files Changed
lib/screens/summary_screen.dart
lib/screens/prescription_screen.dart
lib/main.dart
lib/services/chatbot_service.dart
lib/services/config.dart