Skip to content

Conversation

@xsahil03x
Copy link
Member

@xsahil03x xsahil03x commented Dec 5, 2025

Submit a pull request

Fixes: #2463

Description of the pull request

This PR introduces a new AudioRecorderFeedback class to provide haptic and sound feedback for audio recording lifecycle events.

The StreamAudioRecorderButton and StreamMessageInput widgets now accept a feedback parameter to customize or disable these interactions. Feedback is enabled by default.

Summary by CodeRabbit

  • New Features

    • Added customizable voice recording feedback (haptics/system sounds) for recording lifecycle events (start, pause, finish, lock, cancel, stop). Message input exposes a configurable voice recording feedback option with sensible defaults and ability to disable or supply custom behavior.
  • Tests

    • Added widget tests validating feedback callbacks across recording states and interactions.

✏️ Tip: You can customize this high-level summary in your review settings.

This introduces a new `AudioRecorderFeedback` class to provide haptic and sound feedback for audio recording lifecycle events.

The `StreamAudioRecorderButton` and `StreamMessageInput` widgets now accept a `feedback` parameter to customize or disable these interactions. Feedback is enabled by default.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 5, 2025

Caution

Review failed

The head commit changed during the review from 3bb7ed6 to 6b6de2e.

Walkthrough

Adds a new AudioRecorderFeedback API that emits haptic/sound feedback during audio recording lifecycle, wires it into StreamAudioRecorderButton and StreamMessageInput (exposed as voiceRecordingFeedback), updates package exports, adjusts a couple imports, and adds widget tests covering feedback callbacks.

Changes

Cohort / File(s) Summary
Audio Recorder Feedback
packages/stream_chat_flutter/lib/src/message_input/audio_recorder/audio_recorder_feedback.dart
New public AudioRecorderFeedback class with enableFeedback, disabled() constructor, lifecycle hook methods (onRecordStart, onRecordPause, onRecordFinish, onRecordLock, onRecordCancel, onRecordStartCancel, onRecordStop) and AudioRecorderFeedbackWrapper for per-event callbacks.
Recorder Button Integration
packages/stream_chat_flutter/lib/src/message_input/audio_recorder/stream_audio_recorder.dart
StreamAudioRecorderButton gains a feedback constructor parameter/field (default AudioRecorderFeedback()); feedback hooks are invoked at recording lifecycle points before existing handlers.
Message Input Integration
packages/stream_chat_flutter/lib/src/message_input/stream_message_input.dart
StreamMessageInput adds voiceRecordingFeedback parameter/field (default const AudioRecorderFeedback()); passes it into StreamAudioRecorderButton.
Public Exports
packages/stream_chat_flutter/lib/stream_chat_flutter.dart
Exports src/message_input/audio_recorder/audio_recorder_feedback.dart from package barrel.
Controller / State Imports
packages/stream_chat_flutter/lib/src/message_input/audio_recorder/audio_recorder_controller.dart, packages/stream_chat_flutter/lib/src/message_input/audio_recorder/audio_recorder_state.dart
Replaced package:flutter/gestures.dart imports with dart:ui; no public API changes.
Widget Tests
packages/stream_chat_flutter/test/src/message_input/audio_recorder/stream_audio_recorder_test.dart
Adds widget tests verifying feedback callbacks (onStart, onFinish, onLock, onCancel, onStop, onStartCancel) across interactions and enable/disable behavior.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant StreamMessageInput
    participant StreamAudioRecorderButton
    participant AudioRecorderFeedback
    participant FlutterFeedback as "Flutter Feedback API"

    Note over User,FlutterFeedback: Recording lifecycle with feedback hooks

    User->>StreamMessageInput: interacts with mic control
    StreamMessageInput->>StreamAudioRecorderButton: forward voiceRecordingFeedback
    User->>StreamAudioRecorderButton: long-press (start)
    StreamAudioRecorderButton->>AudioRecorderFeedback: onRecordStart(context)
    AudioRecorderFeedback->>FlutterFeedback: emit haptic/sound (if enabled)
    AudioRecorderFeedback-->>StreamAudioRecorderButton: return
    StreamAudioRecorderButton->>StreamAudioRecorderButton: begin recording

    alt Drag up (lock)
        User->>StreamAudioRecorderButton: drag up -> lock
        StreamAudioRecorderButton->>AudioRecorderFeedback: onRecordLock(context)
        AudioRecorderFeedback->>FlutterFeedback: emit haptic/sound
        AudioRecorderFeedback-->>StreamAudioRecorderButton: return
        StreamAudioRecorderButton->>StreamAudioRecorderButton: enter locked state
    else Drag left (cancel)
        User->>StreamAudioRecorderButton: drag left -> cancel
        StreamAudioRecorderButton->>AudioRecorderFeedback: onRecordCancel(context)
        AudioRecorderFeedback->>FlutterFeedback: emit haptic/sound
        AudioRecorderFeedback-->>StreamAudioRecorderButton: return
        StreamAudioRecorderButton->>StreamAudioRecorderButton: cancel recording
    end

    alt Release to finish
        User->>StreamAudioRecorderButton: release to finish
        StreamAudioRecorderButton->>AudioRecorderFeedback: onRecordFinish(context)
        AudioRecorderFeedback->>FlutterFeedback: emit haptic/sound
        AudioRecorderFeedback-->>StreamAudioRecorderButton: return
        StreamAudioRecorderButton->>StreamMessageInput: send audio message
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

  • Verify feedback hooks are invoked at correct lifecycle points and called before existing handlers in stream_audio_recorder.dart.
  • Confirm voiceRecordingFeedback defaulting and propagation from StreamMessageInput.
  • Review new tests for coverage across hold/lock/cancel/stop flows.
  • Check import changes to dart:ui for any analyzer/runtime implications.

Suggested reviewers

  • renefloor
  • Brazol

Poem

🐰 A tiny tap, a cheerful little sound,
Haptics and chirps when the mic goes round.
Start, lock, or cancel — each moment's a cheer,
A rabbit's drum for feedback we hear! 🎶

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main feature: adding haptic feedback support for the audio recorder.
Linked Issues check ✅ Passed The PR fully implements the requirements from issue #2463: haptic/sound feedback for recording lifecycle (start, lock, cancel, finish), customizable via AudioRecorderFeedback class with enable/disable options, integrated into StreamMessageInput for out-of-the-box support, and provides extension points via AudioRecorderFeedbackWrapper.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing audio recorder feedback functionality requested in issue #2463; import adjustments and test additions support the primary objective without introducing unrelated features.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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.

@codecov
Copy link

codecov bot commented Dec 5, 2025

Codecov Report

❌ Patch coverage is 73.49398% with 22 lines in your changes missing coverage. Please review.
✅ Project coverage is 64.78%. Comparing base (2eb3158) to head (d16d662).
⚠️ Report is 2 commits behind head on master.

Files with missing lines Patch % Lines
..._input/audio_recorder/audio_recorder_feedback.dart 69.49% 18 Missing ⚠️
...ge_input/audio_recorder/stream_audio_recorder.dart 86.95% 3 Missing ⚠️
...er/lib/src/message_input/stream_message_input.dart 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #2465      +/-   ##
==========================================
+ Coverage   64.66%   64.78%   +0.11%     
==========================================
  Files         420      422       +2     
  Lines       26233    26403     +170     
==========================================
+ Hits        16964    17105     +141     
- Misses       9269     9298      +29     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Contributor

@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: 0

🧹 Nitpick comments (2)
packages/stream_chat_flutter/lib/src/message_input/audio_recorder/audio_recorder_feedback.dart (2)

3-33: Documentation example should show required imports.

The example code references HapticFeedback and SystemSound but doesn't show the necessary import statement. While developers can infer this, explicitly showing import 'package:flutter/services.dart'; would improve clarity.

Apply this diff:

 /// Custom feedback (haptic or system sounds):
 /// ```dart
+/// import 'package:flutter/services.dart';
+///
 /// class CustomFeedback extends AudioRecorderFeedback {
 ///   @override
 ///   Future<void> onRecordStart(BuildContext context) async {

114-115: Consider making the typedef public for API clarity.

The _FeedbackCallback typedef is private (leading underscore) but is used as parameter types in the public AudioRecorderFeedbackWrapper constructor. While Dart allows this within the same library, making the typedef public would improve API clarity and be more conventional.

Apply this diff:

-// A callback function for providing feedback during audio recorder interaction.
-typedef _FeedbackCallback = Future<void> Function(BuildContext context);
+/// A callback function for providing feedback during audio recorder interaction.
+typedef FeedbackCallback = Future<void> Function(BuildContext context);

Then update the references in AudioRecorderFeedbackWrapper:

-    _FeedbackCallback? onStart,
-    _FeedbackCallback? onPause,
+    FeedbackCallback? onStart,
+    FeedbackCallback? onPause,
     // ... etc for other parameters and fields
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 1227c68 and 173bd67.

📒 Files selected for processing (1)
  • packages/stream_chat_flutter/lib/src/message_input/audio_recorder/audio_recorder_feedback.dart (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: test
  • GitHub Check: analyze_legacy_versions
  • GitHub Check: build (ios)
  • GitHub Check: build (android)
  • GitHub Check: stream_chat_persistence
  • GitHub Check: stream_chat_flutter
  • GitHub Check: stream_chat_localizations
  • GitHub Check: stream_chat
  • GitHub Check: stream_chat_flutter_core
🔇 Additional comments (2)
packages/stream_chat_flutter/lib/src/message_input/audio_recorder/audio_recorder_feedback.dart (2)

143-225: LGTM! Well-designed callback-based customization pattern.

The AudioRecorderFeedbackWrapper implementation is clean and correct:

  • Properly delegates to provided callbacks when available
  • Falls back to base class implementation when callbacks are not provided
  • Uses idiomatic Dart patterns (null-aware calls with null-coalescing)
  • Provides excellent developer ergonomics as an alternative to inheritance

This gives developers flexibility to customize feedback without extending the base class.


52-112: The implementation's design intentionally separates haptic (default) from sound (customization) feedback.

The documentation at line 5 is misleading—it states "Provides default feedback behavior (haptic feedback, system sounds, etc.)" but the actual default implementation uses only the Feedback API (haptic feedback). System sounds are provided as customization options through class extension or the AudioRecorderFeedbackWrapper pattern, shown in commented examples.

This appears to be the intended design: default behavior provides simple, platform-standard haptic feedback, while developers can add sound feedback by extending the class or using the wrapper with custom callbacks (as demonstrated in the examples at lines 19–32 and in stream_message_input.dart).

Update the documentation at line 5 to clarify that only haptic feedback is provided by default. For example:

"Provides default haptic feedback and can be extended to add custom feedback behavior (such as system sounds)."

Copy link
Contributor

@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: 0

🧹 Nitpick comments (3)
packages/stream_chat_flutter/test/src/message_input/audio_recorder/stream_audio_recorder_test.dart (1)

324-623: Feedback tests are thorough; consider small helpers to reduce duplication

The new tests exercise all feedback hooks across idle/hold/locked/stopped states (including enableFeedback: false), which should catch regressions in how AudioRecorderFeedbackWrapper is wired. The patterns are solid, but there’s a lot of repeated setup (local feedbackCalled flag, wrapper construction, pumpWidget, gesture/tap). If you want to keep this file leaner, consider extracting tiny helpers (e.g. a _buildRecorderButtonWithFeedback(...) or a helper that drives the common gesture patterns) to DRY things up; behavior can stay exactly as-is.

packages/stream_chat_flutter/lib/src/message_input/audio_recorder/audio_recorder_feedback.dart (2)

34-112: Confirm default Feedback. behavior matches desired sound + haptic patterns*

The default mappings (forLongPress for start/lock, forTap for the other events) are reasonable, but they don’t uniformly produce both sound and haptics on all platforms:

  • On Android, Feedback.forTap plays the click system sound, while Feedback.forLongPress vibrates the device. (api.flutter.dev)
  • On iOS, Feedback.forTap is a no-op, and Feedback.forLongPress triggers both the click sound and a heavy-impact haptic. (api.flutter.dev)

That means:

  • Android: start/lock → haptic only; finish/cancel/stop/etc. → sound only.
  • iOS: start/lock → sound + haptic; finish/cancel/stop/etc. → no feedback.

Given the linked issue calls out “short tone + light haptic” (and distinct cancel/lock/send feedback), it might be worth double-checking whether the default implementation should combine forLongPress and forTap (at least for key transitions like start/lock/cancel) or explicitly invoke HapticFeedback and SystemSound together per event, leaving this class as the high-level default.

If the current mapping is intentional, consider tightening the docs to explicitly state that defaults are platform-dependent and that not every event produces both sound and haptic feedback, to avoid surprises for integrators.


143-259: Wrapper override pattern is clean; a tiny helper could remove repetition

AudioRecorderFeedbackWrapper correctly respects enableFeedback, prefers the per-event callback when present, and otherwise falls back to the base implementation. The seven overrides all share the same pattern (if (_onX case final callback?) { ... } else { super.onRecordX(...) }), so if you ever want to trim boilerplate you could introduce a small private helper (e.g. _invokeOrFallback(context, _onStart, super.onRecordStart)) and reuse it across methods. Behavior can remain unchanged; this would just centralize the enablement/check logic.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 173bd67 and 6b6de2e.

📒 Files selected for processing (3)
  • packages/stream_chat_flutter/CHANGELOG.md (1 hunks)
  • packages/stream_chat_flutter/lib/src/message_input/audio_recorder/audio_recorder_feedback.dart (1 hunks)
  • packages/stream_chat_flutter/test/src/message_input/audio_recorder/stream_audio_recorder_test.dart (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: analyze_legacy_versions
  • GitHub Check: build (ios)
  • GitHub Check: build (android)
  • GitHub Check: test
  • GitHub Check: analyze
  • GitHub Check: stream_chat_flutter
  • GitHub Check: stream_chat_localizations
  • GitHub Check: stream_chat_flutter_core
  • GitHub Check: stream_chat_persistence
  • GitHub Check: stream_chat
🔇 Additional comments (1)
packages/stream_chat_flutter/CHANGELOG.md (1)

1-7: Changelog entry matches the new feedback API surface

The Upcoming note clearly describes the new voice recording feedback behavior and points users at AudioRecorderFeedback / AudioRecorderFeedbackWrapper for customization; no changes needed here.

@xsahil03x xsahil03x force-pushed the feat/audio-recorder-haptic branch from 3bb7ed6 to 6b6de2e Compare December 8, 2025 14:27
@xsahil03x xsahil03x merged commit 151fa40 into master Dec 9, 2025
13 checks passed
@xsahil03x xsahil03x deleted the feat/audio-recorder-haptic branch December 9, 2025 09:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Voice Recording with Sound & Haptic Feedback

3 participants