fix: prevent RenderFlex overflow on small screens and improve API key validation#13
fix: prevent RenderFlex overflow on small screens and improve API key validation#13SISIR-REDDY wants to merge 2 commits intoAOSSIE-Org:mainfrom
Conversation
📝 WalkthroughWalkthroughThe pull request adds API key validation and setup state management across the app. When Deepgram or Gemini API keys are missing or invalid, the UI displays warnings and prevents operations. Error handling improves with timeouts and informative error messages. Widget tests are refactored to target individual screens rather than the entire app. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant Main as Main Screen
participant ValidatorMain as Validation<br/>(Main)
participant Deepgram as Deepgram API
participant TranscriptProcessor as Transcript<br/>Processor
participant ChatService as ChatbotService
participant ValidatorChat as Validation<br/>(Chat)
participant Gemini as Gemini API
User->>Main: Tap mic button
Main->>ValidatorMain: Check DEEPGRAM_API_KEY validity
alt Invalid or Missing
ValidatorMain-->>Main: Error: API key invalid
Main-->>User: Show snackbar with setup message
else Valid
Main->>Deepgram: Request transcription
Deepgram-->>Main: Transcription response
Main->>TranscriptProcessor: Process transcription
TranscriptProcessor->>ChatService: Send for Gemini processing
ChatService->>ValidatorChat: Check GEMINI_API_KEY validity
alt Invalid or Missing
ValidatorChat-->>ChatService: Error: API key invalid
ChatService-->>TranscriptProcessor: Return error message
TranscriptProcessor-->>Main: Display error
else Valid
ChatService->>Gemini: POST request (30s timeout)
alt 200 Response
Gemini-->>ChatService: candidates/content/parts/text
ChatService-->>TranscriptProcessor: Return parsed text
else Non-200 Response
Gemini-->>ChatService: error.message
ChatService-->>TranscriptProcessor: Return error with details
else Timeout/Exception
ChatService-->>TranscriptProcessor: Return connection error
end
TranscriptProcessor-->>Main: Display result/error
Main-->>User: Show summary/prescription
end
end
Estimated Code Review Effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
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 Tip You can disable sequence diagrams in the walkthrough.Disable the |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (4)
lib/main.dart (2)
254-256: Usedeveloper.loginstead ofThe codebase uses
developer.logelsewhere (e.g., lines 169, 198, 205). Consider replacing thesedeveloper.logfor consistent, structured logging.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/main.dart` around lines 254 - 256, Replace the three print calls that output the transcription (the lines printing the header, _transcription, and footer) with developer.log calls for consistent structured logging; ensure dart:developer is imported if missing and use developer.log with a clear message and optional name/level (e.g., developer.log('TRANSCRIPTION: $_transcription', name: 'transcription', level: 0)) so the header/footer are folded into a single descriptive log entry referencing the _transcription variable.
18-24: Silent fallback may mask configuration issues.If both
.envand.env.examplefail to load (e.g., file corruption or missing asset), the app proceeds silently with an empty environment. Consider logging when falling back to.env.exampleand showing an error if both fail.♻️ Suggested improvement
Future<void> main() async { try { await dotenv.load(fileName: '.env'); } catch (_) { - await dotenv.load(fileName: '.env.example'); + try { + await dotenv.load(fileName: '.env.example'); + developer.log('Loaded .env.example (no .env found)', name: 'main'); + } catch (e) { + developer.log('Failed to load any .env file: $e', name: 'main', error: e); + } } runApp(const MyApp()); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/main.dart` around lines 18 - 24, The current main() silently falls back from dotenv.load('.env') to '.env.example' and proceeds without reporting errors; update main() to catch and log the first load error (capture the exception in the catch), log a warning when falling back to dotenv.load('.env.example') (include the original error), and if loading the example also fails, log the final error and stop startup (e.g., rethrow or call exit) before runApp(const MyApp()); reference the main() function and the dotenv.load calls so the try/catch blocks explicitly log exceptions and prevent silent continuation.lib/services/chatbot_service.dart (2)
12-23: Consider extracting shared API key validation logic.This
_isValidApiKeyimplementation is nearly identical to the one inmain.dart(lines 73-90). The only difference is the provider-specific placeholder check (your_gemini_api_key_herevsyour_deepgram_api_key_here). Consider extracting a shared utility to avoid drift between the two implementations.♻️ Suggested refactor: shared validation utility
Create a shared helper (e.g., in a new
lib/utils/api_key_validator.dart):bool isValidApiKey(String value, {String? providerPlaceholder}) { final trimmed = value.trim(); if (trimmed.isEmpty) return false; final normalized = trimmed.toLowerCase(); if (providerPlaceholder != null && normalized.contains(providerPlaceholder)) { return false; } return !normalized.contains('replace_with') && !normalized.contains('example') && !normalized.contains('dummy'); }Then use it in both files with the appropriate provider placeholder.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/services/chatbot_service.dart` around lines 12 - 23, Extract the duplicated API key logic into a shared utility function named isValidApiKey(String value, {String? providerPlaceholder}) that implements the common trimming, emptiness check and normalized substring checks (replace_with, example, dummy) and conditionally checks providerPlaceholder; then replace the existing _isValidApiKey in this file and the near-duplicate implementation in main.dart to call isValidApiKey(value, providerPlaceholder: 'your_gemini_api_key_here') (or the appropriate provider placeholder) so both callers reuse the single implementation and avoid drift.
46-46: Timeout exception could provide a more specific error message.The 30-second timeout is good, but
TimeoutExceptionis caught by the genericcatch (e)block, which returns a generic "Could not connect" message. Consider catchingTimeoutExceptionseparately to provide clearer feedback.♻️ Proposed refinement
+import 'dart:async'; ... - } catch (e) { + } on TimeoutException { + developer.log('Gemini request timed out', name: 'ChatbotService'); + return 'Error: Request timed out. Please try again.'; + } catch (e) { developer.log('Gemini request failed: $e', name: 'ChatbotService', error: e); return 'Error: Could not connect to Gemini API. Please try again.'; }Also applies to: 84-86
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/services/chatbot_service.dart` at line 46, The generic catch block swallowing the 30s .timeout(...) call should be split to handle TimeoutException explicitly: add an on TimeoutException catch to the same function where the .timeout(const Duration(seconds: 30)) call occurs (the occurrence at the shown .timeout(...) and the similar call around lines 84-86) and return or throw a clearer "Request timed out" (or similar) message; keep the existing generic catch for other errors. Ensure dart:async is imported if TimeoutException is not yet available in the file and reference the specific .timeout(...) call sites so you change the error handling in the function(s) that invoke .timeout(...) rather than altering unrelated code.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@lib/main.dart`:
- Line 403: Replace deprecated Color.withOpacity usages with the new
withValues(alpha: ...) API: change the occurrences of
Colors.white.withOpacity(0.14) to Colors.white.withValues(alpha: 0.14),
Colors.white.withOpacity(0.5) to Colors.white.withValues(alpha: 0.5), any
instance like (<expression>).withOpacity(0.3) to
(<expression>).withValues(alpha: 0.3), and the other two
Colors.white.withOpacity(0.3) and Colors.white.withOpacity(0.5) similarly;
locate these by searching for "withOpacity(" in the file (e.g., the color:
Colors.white.withOpacity(0.14) property and the other three withOpacity calls)
and replace each call signature to use withValues(alpha: <same float>)
preserving the original alpha values.
---
Nitpick comments:
In `@lib/main.dart`:
- Around line 254-256: Replace the three print calls that output the
transcription (the lines printing the header, _transcription, and footer) with
developer.log calls for consistent structured logging; ensure dart:developer is
imported if missing and use developer.log with a clear message and optional
name/level (e.g., developer.log('TRANSCRIPTION: $_transcription', name:
'transcription', level: 0)) so the header/footer are folded into a single
descriptive log entry referencing the _transcription variable.
- Around line 18-24: The current main() silently falls back from
dotenv.load('.env') to '.env.example' and proceeds without reporting errors;
update main() to catch and log the first load error (capture the exception in
the catch), log a warning when falling back to dotenv.load('.env.example')
(include the original error), and if loading the example also fails, log the
final error and stop startup (e.g., rethrow or call exit) before runApp(const
MyApp()); reference the main() function and the dotenv.load calls so the
try/catch blocks explicitly log exceptions and prevent silent continuation.
In `@lib/services/chatbot_service.dart`:
- Around line 12-23: Extract the duplicated API key logic into a shared utility
function named isValidApiKey(String value, {String? providerPlaceholder}) that
implements the common trimming, emptiness check and normalized substring checks
(replace_with, example, dummy) and conditionally checks providerPlaceholder;
then replace the existing _isValidApiKey in this file and the near-duplicate
implementation in main.dart to call isValidApiKey(value, providerPlaceholder:
'your_gemini_api_key_here') (or the appropriate provider placeholder) so both
callers reuse the single implementation and avoid drift.
- Line 46: The generic catch block swallowing the 30s .timeout(...) call should
be split to handle TimeoutException explicitly: add an on TimeoutException catch
to the same function where the .timeout(const Duration(seconds: 30)) call occurs
(the occurrence at the shown .timeout(...) and the similar call around lines
84-86) and return or throw a clearer "Request timed out" (or similar) message;
keep the existing generic catch for other errors. Ensure dart:async is imported
if TimeoutException is not yet available in the file and reference the specific
.timeout(...) call sites so you change the error handling in the function(s)
that invoke .timeout(...) rather than altering unrelated code.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 01bebbce-4067-41fb-94ad-14ced424501a
⛔ Files ignored due to path filters (2)
docs/screenshots/mobile-overflow-issue.pngis excluded by!**/*.pngpubspec.lockis excluded by!**/*.lock
📒 Files selected for processing (4)
lib/main.dartlib/services/chatbot_service.dartpubspec.yamltest/widget_test.dart
| width: double.infinity, | ||
| padding: const EdgeInsets.all(12), | ||
| decoration: BoxDecoration( | ||
| color: Colors.white.withOpacity(0.14), |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Flutter Color withOpacity deprecated 2025 2026
💡 Result:
Color.withOpacity(double) was deprecated starting in Flutter 3.27 as part of the wide-gamut color work. The old API converts a 0.0–1.0 opacity into an 8-bit alpha (0–255), which quantizes the value and can cause precision loss. Flutter now stores alpha as a floating-point value, so you should use withValues(alpha: ...) instead. [1]
Replace this
final c = Colors.blue.withOpacity(0.5);With this
final c = Colors.blue.withValues(alpha: 0.5);[1]
If you intentionally want the old 8-bit/quantized behavior
Use withAlpha (0–255):
final c = Colors.blue.withAlpha((0.5 * 255).round());[1][2]
Sources: Flutter wide-gamut migration guide (opacity section) [1], Flutter API docs for Color.withOpacity (shows it rounds to 8-bit alpha) [2], Flutter GitHub deprecation message (“Use .withValues() to avoid precision loss.”) [3]
[1] (docs.flutter.dev)
[2] (api.flutter.dev)
[3] (github.com)
Citations:
- 1: https://docs.flutter.dev/release/breaking-changes/wide-gamut-framework?utm_source=openai
- 2: https://api.flutter.dev/flutter/dart-ui/Color/withOpacity.html?utm_source=openai
- 3: Change implementation of
withOpacityand remove deprecated message flutter/flutter#164991
🏁 Script executed:
cat pubspec.yaml | head -30Repository: AOSSIE-Org/DocPilot
Length of output: 1817
🏁 Script executed:
rg "withOpacity" lib/main.dart -nRepository: AOSSIE-Org/DocPilot
Length of output: 440
Replace deprecated Color.withOpacity with Color.withValues(alpha: ...)
Color.withOpacity was deprecated in Flutter 3.27 due to precision loss from 8-bit alpha quantization. The new withValues(alpha: ...) API uses floating-point alpha values. Replace all instances:
- Line 403:
Colors.white.withOpacity(0.14)→Colors.white.withValues(alpha: 0.14) - Line 447:
Colors.white.withOpacity(0.5)→Colors.white.withValues(alpha: 0.5) - Line 484:
(...).withOpacity(0.3)→(...).withValues(alpha: 0.3) - Line 612:
Colors.white.withOpacity(0.3)→Colors.white.withValues(alpha: 0.3) - Line 613:
Colors.white.withOpacity(0.5)→Colors.white.withValues(alpha: 0.5)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@lib/main.dart` at line 403, Replace deprecated Color.withOpacity usages with
the new withValues(alpha: ...) API: change the occurrences of
Colors.white.withOpacity(0.14) to Colors.white.withValues(alpha: 0.14),
Colors.white.withOpacity(0.5) to Colors.white.withValues(alpha: 0.5), any
instance like (<expression>).withOpacity(0.3) to
(<expression>).withValues(alpha: 0.3), and the other two
Colors.white.withOpacity(0.3) and Colors.white.withOpacity(0.5) similarly;
locate these by searching for "withOpacity(" in the file (e.g., the color:
Colors.white.withOpacity(0.14) property and the other three withOpacity calls)
and replace each call signature to use withValues(alpha: <same float>)
preserving the original alpha values.
Closes AOSSIE-Org#8 - Add GeminiModel enum with three options: Gemini 2.0 Flash (default), Gemini 1.5 Pro, and Gemma 3 27B; each carries a modelId, displayName, and a short description shown in the picker - Update ChatbotService to hold a selectedModel field and build the API URL from it at call time; model can be changed at any point between recordings - Add hasValidApiKey guard and structured error extraction (mirrors the approach in AOSSIE-Org#13) while keeping this PR independently reviewable - Add a tune icon button in the header row of TranscriptionScreen that opens a PopupMenuButton listing all three models with their descriptions - Status text updates to show the active model name during processing
|
Please resolve the merge conflicts before review. Your PR will only be reviewed by a maintainer after all conflicts have been resolved. 📺 Watch this video to understand why conflicts occur and how to resolve them: |
|
If you want your PR to be reviewed, raise issue for it and solve merge conflicts. |
Problem
On small mobile screens (e.g. tested on device
R5CXA1EQ6LM), the main screen'sColumnwas not scrollable, causing aRenderFlex overflowed by 56 pixelserror at the bottom of the screen.Additionally, placeholder API key values (e.g.
your_deepgram_api_key_here) were accepted as valid, causing silent failures when users forgot to set real keys.Screenshot
The overflow bug before the fix:
Changes
lib/main.dartColumnbody inside aSingleChildScrollViewto eliminate theRenderFlexoverflow on smaller screens_isValidApiKey(value, provider)that rejects empty strings and known placeholder patterns (your_deepgram_api_key_here,replace_with,example,dummy)lib/services/chatbot_service.dart_isValidApiKey()guard that rejects empty/placeholder Gemini keyserror.messagefield for actionable error messagespubspec.yaml.env.exampleas a Flutter asset instead of.envto avoid accidentally shipping secretstest/widget_test.dartSummaryScreen,TranscriptionDetailScreen, andPrescriptionScreendocs/screenshots/mobile-overflow-issue.pngTesting
Summary by CodeRabbit
Release Notes
New Features
Bug Fixes
Tests