Make voice grid visibly animate
All checks were successful
Build and deploy Flutter Web / build (push) Successful in 2m6s

This commit is contained in:
Ruslan Bakiev
2026-05-13 16:58:42 +07:00
parent 2366587693
commit 73ed4c2614

View File

@@ -737,6 +737,7 @@ class _AddExperienceFlowState extends ConsumerState<AddExperienceFlow> {
Future<List<PlaceRecommendation>>? _nearbyPlacesFuture;
StreamSubscription<Amplitude>? _amplitudeSub;
Timer? _visualTimer;
var _step = 0;
var _informationUnits = 0.0;
var _recording = false;
@@ -745,7 +746,9 @@ class _AddExperienceFlowState extends ConsumerState<AddExperienceFlow> {
var _noiseDb = -72.0;
var _voicePeakDb = -34.0;
var _liveLevel = 0.0;
var _visualPhase = 0.0;
DateTime? _lastInformationAt;
DateTime? _lastAmplitudeAt;
@override
void initState() {
@@ -755,6 +758,7 @@ class _AddExperienceFlowState extends ConsumerState<AddExperienceFlow> {
@override
void dispose() {
_amplitudeSub?.cancel();
_visualTimer?.cancel();
_waveController.dispose();
super.dispose();
}
@@ -784,7 +788,9 @@ class _AddExperienceFlowState extends ConsumerState<AddExperienceFlow> {
await _waveController.startRecording();
await _amplitudeSub?.cancel();
_lastInformationAt = DateTime.now();
_lastAmplitudeAt = null;
_amplitudeSub = _waveController.amplitudeStream.listen(_handleAmplitude);
_startVisualTick();
setState(() {
_micAllowed = true;
@@ -798,8 +804,11 @@ class _AddExperienceFlowState extends ConsumerState<AddExperienceFlow> {
Future<void> _stopRecording() async {
await _amplitudeSub?.cancel();
_amplitudeSub = null;
_visualTimer?.cancel();
_visualTimer = null;
await _waveController.stopRecording();
_lastInformationAt = null;
_lastAmplitudeAt = null;
if (!mounted) {
return;
}
@@ -813,12 +822,16 @@ class _AddExperienceFlowState extends ConsumerState<AddExperienceFlow> {
final currentDb = amplitude.current;
final now = DateTime.now();
final level = _normalizeDbLevel(currentDb);
final informationDelta = _consumeInformationDelta(level, now);
final informationLevel = currentDb > -64
? math.max(0.22, level)
: level * 0.35;
final informationDelta = _consumeInformationDelta(informationLevel, now);
_lastAmplitudeAt = now;
setState(() {
_voiceSamples
..removeAt(0)
..add(level);
..add(math.max(0.04, level));
_liveLevel = _smoothLevel(_liveLevel, level);
_informationUnits = math.min(
_minimumInformationUnits,
@@ -830,6 +843,30 @@ class _AddExperienceFlowState extends ConsumerState<AddExperienceFlow> {
.setReviewDuration(_waveController.timeElapsed);
}
void _startVisualTick() {
_visualTimer?.cancel();
_visualTimer = Timer.periodic(const Duration(milliseconds: 70), (_) {
if (!mounted || !_recording) {
return;
}
_visualPhase += 0.34;
final hasFreshAmplitude =
_lastAmplitudeAt != null &&
DateTime.now().difference(_lastAmplitudeAt!).inMilliseconds < 160;
final baseLevel = hasFreshAmplitude ? _liveLevel : 0.10;
final wave =
(math.sin(_visualPhase) * 0.5 + 0.5) * (0.16 + baseLevel * 0.34);
final visualLevel = (0.06 + baseLevel * 0.70 + wave).clamp(0.0, 1.0);
setState(() {
_voiceSamples
..removeAt(0)
..add(visualLevel);
});
});
}
double _normalizeDbLevel(double currentDb) {
final db = currentDb.clamp(-160.0, 0.0);
if (db < _noiseDb) {
@@ -1095,7 +1132,7 @@ class _VoiceStep extends StatelessWidget {
@override
Widget build(BuildContext context) {
final canFinish = canContinue && !isRecording;
final canFinish = canContinue;
return Column(
children: [
@@ -1392,7 +1429,8 @@ class _VoiceInformationPainter extends CustomPainter {
final signal = samples.isEmpty
? 0.0
: samples[sampleIndex].clamp(0.0, 1.0);
final filled = _cellOrder(cellIndex, totalCells) < activeCells;
final fillOrder = (rows - 1 - row) * columns + column;
final filled = fillOrder < activeCells;
final x = startX + column * (cellSize + gap);
final y = startY + row * (cellSize + gap);
final rect = RRect.fromRectAndRadius(
@@ -1442,8 +1480,6 @@ class _VoiceInformationPainter extends CustomPainter {
}
}
int _cellOrder(int index, int total) => ((index + 11) * 73) % total;
double _hashUnit(int index) {
final value = math.sin(index * 12.9898 + 78.233) * 43758.5453;
return value - value.floorToDouble();