-
Notifications
You must be signed in to change notification settings - Fork 371
feat(ui): add haptic feedback support for audio recorder #2465
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
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.
WalkthroughAdds 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
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
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~30 minutes
Suggested reviewers
Poem
Pre-merge checks and finishing touches✅ Passed checks (5 passed)
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 |
Codecov Report❌ Patch coverage is 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. 🚀 New features to boost your workflow:
|
There was a problem hiding this 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
HapticFeedbackandSystemSoundbut doesn't show the necessary import statement. While developers can infer this, explicitly showingimport '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
_FeedbackCallbacktypedef is private (leading underscore) but is used as parameter types in the publicAudioRecorderFeedbackWrapperconstructor. 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.
📒 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
AudioRecorderFeedbackWrapperimplementation 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
FeedbackAPI (haptic feedback). System sounds are provided as customization options through class extension or theAudioRecorderFeedbackWrapperpattern, 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)."
There was a problem hiding this 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 duplicationThe new tests exercise all feedback hooks across idle/hold/locked/stopped states (including
enableFeedback: false), which should catch regressions in howAudioRecorderFeedbackWrapperis wired. The patterns are solid, but there’s a lot of repeated setup (localfeedbackCalledflag, 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 (
forLongPressfor start/lock,forTapfor the other events) are reasonable, but they don’t uniformly produce both sound and haptics on all platforms:
- On Android,
Feedback.forTapplays the click system sound, whileFeedback.forLongPressvibrates the device. (api.flutter.dev)- On iOS,
Feedback.forTapis a no-op, andFeedback.forLongPresstriggers 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
forLongPressandforTap(at least for key transitions like start/lock/cancel) or explicitly invokeHapticFeedbackandSystemSoundtogether 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
AudioRecorderFeedbackWrappercorrectly respectsenableFeedback, 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.
📒 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 surfaceThe Upcoming note clearly describes the new voice recording feedback behavior and points users at
AudioRecorderFeedback/AudioRecorderFeedbackWrapperfor customization; no changes needed here.
3bb7ed6 to
6b6de2e
Compare
Submit a pull request
Fixes: #2463
Description of the pull request
This PR introduces a new
AudioRecorderFeedbackclass to provide haptic and sound feedback for audio recording lifecycle events.The
StreamAudioRecorderButtonandStreamMessageInputwidgets now accept afeedbackparameter to customize or disable these interactions. Feedback is enabled by default.Summary by CodeRabbit
New Features
Tests
✏️ Tip: You can customize this high-level summary in your review settings.