Skip to content

fix: prevent RenderFlex overflow on small screens and improve API key validation#13

Open
SISIR-REDDY wants to merge 2 commits intoAOSSIE-Org:mainfrom
SISIR-REDDY:fix/mobile-overflow-api-setup
Open

fix: prevent RenderFlex overflow on small screens and improve API key validation#13
SISIR-REDDY wants to merge 2 commits intoAOSSIE-Org:mainfrom
SISIR-REDDY:fix/mobile-overflow-api-setup

Conversation

@SISIR-REDDY
Copy link

@SISIR-REDDY SISIR-REDDY commented Mar 14, 2026

Problem

On small mobile screens (e.g. tested on device R5CXA1EQ6LM), the main screen's Column was not scrollable, causing a RenderFlex overflowed by 56 pixels error 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:

Mobile overflow issue

Changes

lib/main.dart

  • Wrapped the Column body inside a SingleChildScrollView to eliminate the RenderFlex overflow on smaller screens
  • Added _isValidApiKey(value, provider) that rejects empty strings and known placeholder patterns (your_deepgram_api_key_here, replace_with, example, dummy)
  • Shows an inline warning banner when either key is missing or still a placeholder
  • Surfaces the Deepgram HTTP error text in the UI instead of a generic message
  • Snackbar prompt when mic button is tapped without a valid Deepgram key

lib/services/chatbot_service.dart

  • Added _isValidApiKey() guard that rejects empty/placeholder Gemini keys
  • Extracts and returns the Gemini API error.message field for actionable error messages

pubspec.yaml

  • Bundle .env.example as a Flutter asset instead of .env to avoid accidentally shipping secrets

test/widget_test.dart

  • Replaced the auto-generated counter smoke test with real widget tests for SummaryScreen, TranscriptionDetailScreen, and PrescriptionScreen

docs/screenshots/mobile-overflow-issue.png

  • Added screenshot of the overflow bug for reference

Testing

flutter run -d R5CXA1EQ6LM   # RenderFlex overflow no longer appears
flutter analyze lib/          # No issues found

Summary by CodeRabbit

Release Notes

  • New Features

    • Added API key configuration validation with visual indicators for missing or placeholder keys.
    • Setup state guidance displays when keys are not properly configured.
  • Bug Fixes

    • Improved error handling for transcription and API processing with more informative messages.
    • Added validation guards preventing operations without valid API keys.
    • Enhanced API response parsing for better error surfacing.
  • Tests

    • Added focused widget tests for key application screens.

@coderabbitai
Copy link

coderabbitai bot commented Mar 14, 2026

📝 Walkthrough

Walkthrough

The 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

Cohort / File(s) Summary
API Key Validation & Error Handling
lib/main.dart, lib/services/chatbot_service.dart
Introduces validation methods and setup state checks. Adds pre-operation guards for transcription and Gemini processing. Improves error messages and HTTP timeouts. UI reflects setup completeness with conditional prompts and warning panels for missing keys.
Configuration
pubspec.yaml
Updates Flutter assets list to bundle .env.example instead of .env, with fallback logic in app startup.
Testing
test/widget_test.dart
Replaces single smoke test with three focused widget tests for SummaryScreen, PrescriptionScreen, and TranscriptionDetailScreen, each validating specific content rendering.

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
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A rabbit hops through keys so fine,
Validates each secret sign,
When API's missing, warnings gleam,
Setup's checked—a developer's dream!
Error messages now truly speak,
Tests focus sharp, no longer bleak.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately describes the two primary changes: fixing RenderFlex overflow on small screens (via ScrollView wrapper) and improving API key validation (guarding against placeholders and empty strings).
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
📝 Coding Plan
  • Generate coding plan for human review comments

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

You can disable sequence diagrams in the walkthrough.

Disable the reviews.sequence_diagrams setting to disable sequence diagrams in the walkthrough.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (4)
lib/main.dart (2)

254-256: Use developer.log instead of print for consistency.

The codebase uses developer.log elsewhere (e.g., lines 169, 198, 205). Consider replacing these print statements with developer.log for 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 .env and .env.example fail to load (e.g., file corruption or missing asset), the app proceeds silently with an empty environment. Consider logging when falling back to .env.example and 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 _isValidApiKey implementation is nearly identical to the one in main.dart (lines 73-90). The only difference is the provider-specific placeholder check (your_gemini_api_key_here vs your_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 TimeoutException is caught by the generic catch (e) block, which returns a generic "Could not connect" message. Consider catching TimeoutException separately 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

📥 Commits

Reviewing files that changed from the base of the PR and between ece30ed and c15912c.

⛔ Files ignored due to path filters (2)
  • docs/screenshots/mobile-overflow-issue.png is excluded by !**/*.png
  • pubspec.lock is excluded by !**/*.lock
📒 Files selected for processing (4)
  • lib/main.dart
  • lib/services/chatbot_service.dart
  • pubspec.yaml
  • test/widget_test.dart

width: double.infinity,
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.14),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 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:


🏁 Script executed:

cat pubspec.yaml | head -30

Repository: AOSSIE-Org/DocPilot

Length of output: 1817


🏁 Script executed:

rg "withOpacity" lib/main.dart -n

Repository: 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.

SISIR-REDDY added a commit to SISIR-REDDY/DocPilot that referenced this pull request Mar 14, 2026
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
@github-actions
Copy link

⚠️ This PR has merge conflicts.

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:
https://www.youtube.com/watch?v=Sqsz1-o7nXk

@dhruvi-16-me
Copy link

If you want your PR to be reviewed, raise issue for it and solve merge conflicts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants