Add compact voice progress grid
All checks were successful
Build and deploy Flutter Web / build (push) Successful in 2m6s

This commit is contained in:
Ruslan Bakiev
2026-05-13 17:41:28 +07:00
parent fcc2c26752
commit 729dd21b78

View File

@@ -1090,7 +1090,7 @@ class _VoiceStep extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final showNext = canContinue && !isRecording; final canFinish = canContinue;
return Column( return Column(
children: [ children: [
@@ -1106,8 +1106,11 @@ class _VoiceStep extends StatelessWidget {
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
], ],
_VoiceProgressGrid(progress: informationProgress),
const SizedBox(height: 18),
Expanded( Expanded(
child: Center( child: Align(
alignment: Alignment.bottomCenter,
child: _LibraryWaveSurface( child: _LibraryWaveSurface(
controller: waveController, controller: waveController,
active: isRecording, active: isRecording,
@@ -1125,40 +1128,76 @@ class _VoiceStep extends StatelessWidget {
size: 22, size: 22,
), ),
), ),
AnimatedSwitcher(
duration: const Duration(milliseconds: 180),
child: showNext
? Padding(
key: const ValueKey('next'),
padding: const EdgeInsets.only(bottom: 14),
child: FilledButton(
style: FilledButton.styleFrom(
backgroundColor: Colors.white,
foregroundColor: const Color(0xFF090613),
),
onPressed: isSubmitting ? null : onNext,
child: Text(isSubmitting ? 'Отправляем' : 'Далее'),
),
)
: const SizedBox.shrink(key: ValueKey('empty-next')),
),
_VoiceRecordButton( _VoiceRecordButton(
progress: informationProgress, progress: informationProgress,
liveLevel: liveLevel, liveLevel: liveLevel,
isRecording: isRecording, isRecording: isRecording,
canFinish: canFinish,
enabled: hasTelegramAuth && !isSubmitting, enabled: hasTelegramAuth && !isSubmitting,
onPressed: onToggleRecording, onPressed: canFinish ? onNext : onToggleRecording,
), ),
], ],
); );
} }
} }
class _VoiceProgressGrid extends StatelessWidget {
const _VoiceProgressGrid({required this.progress});
final double progress;
@override
Widget build(BuildContext context) {
const columns = 14;
const rows = 3;
const total = columns * rows;
final filled = (progress.clamp(0.0, 1.0) * total).round();
return SizedBox(
width: 230,
child: Wrap(
spacing: 5,
runSpacing: 5,
alignment: WrapAlignment.center,
children: [
for (var index = 0; index < total; index++)
AnimatedContainer(
duration: const Duration(milliseconds: 180),
curve: Curves.easeOutCubic,
width: 10,
height: 10,
decoration: BoxDecoration(
color: _gridOrder(index, total) < filled
? const Color(0xFFFF2D75)
: Colors.white.withValues(alpha: 0.11),
borderRadius: BorderRadius.circular(3),
boxShadow: _gridOrder(index, total) < filled
? [
BoxShadow(
color: const Color(
0xFFFF2D75,
).withValues(alpha: 0.34),
blurRadius: 12,
spreadRadius: 1,
),
]
: null,
),
),
],
),
);
}
static int _gridOrder(int index, int total) => ((index + 7) * 29) % total;
}
class _VoiceRecordButton extends StatefulWidget { class _VoiceRecordButton extends StatefulWidget {
const _VoiceRecordButton({ const _VoiceRecordButton({
required this.progress, required this.progress,
required this.liveLevel, required this.liveLevel,
required this.isRecording, required this.isRecording,
required this.canFinish,
required this.enabled, required this.enabled,
required this.onPressed, required this.onPressed,
}); });
@@ -1166,8 +1205,9 @@ class _VoiceRecordButton extends StatefulWidget {
final double progress; final double progress;
final double liveLevel; final double liveLevel;
final bool isRecording; final bool isRecording;
final bool canFinish;
final bool enabled; final bool enabled;
final Future<void> Function() onPressed; final VoidCallback onPressed;
@override @override
State<_VoiceRecordButton> createState() => _VoiceRecordButtonState(); State<_VoiceRecordButton> createState() => _VoiceRecordButtonState();
@@ -1264,7 +1304,7 @@ class _VoiceRecordButtonState extends State<_VoiceRecordButton>
width: 120, width: 120,
height: 120, height: 120,
child: FilledButton( child: FilledButton(
onPressed: widget.enabled ? () => widget.onPressed() : null, onPressed: widget.enabled ? widget.onPressed : null,
style: FilledButton.styleFrom( style: FilledButton.styleFrom(
backgroundColor: Colors.white, backgroundColor: Colors.white,
foregroundColor: const Color(0xFF090613), foregroundColor: const Color(0xFF090613),
@@ -1275,7 +1315,11 @@ class _VoiceRecordButtonState extends State<_VoiceRecordButton>
elevation: 0, elevation: 0,
), ),
child: Icon( child: Icon(
widget.isRecording ? Icons.pause_rounded : Icons.mic_rounded, widget.canFinish
? Icons.check_rounded
: widget.isRecording
? Icons.pause_rounded
: Icons.mic_rounded,
size: 48, size: 48,
), ),
), ),