Update RecordingButton design with new visual style

Major design changes based on user requirements:

1. Background Style Changes:
   - Remove solid color background and shadows
   - Use semi-transparent background (alpha: 0.12) for subtle visual feedback
   - Maintain circular shape for both states

2. Icon Improvements:
   - Increase icon size to 55% of button size for better visibility
   - Use color-coded icons: blue for idle, red for recording
   - Remove white icon color, use theme-based colors instead

3. Loading Indicator Enhancement:
   - Show CircularProgressIndicator during both processing and listening states
   - Position indicator as overlay using Stack layout
   - Maintain white color for progress indicator for contrast

4. Color Logic Simplification:
   - Consolidate color logic into single iconColor variable
   - Remove complex state-based color transitions
   - Use consistent color scheme: blue (#2196F3) idle, red (#FF5252) recording
   - Darker disabled color (#212121) for better contrast

5. Layout Structure Update:
   - Replace Transform.scale animation wrapper with direct Container
   - Use Stack for layering icon and progress indicator
   - Positioned widgets for precise control over element placement

This creates a more modern, minimalist design with better visual hierarchy
and improved accessibility through higher contrast ratios.
This commit is contained in:
Max 2025-09-09 15:08:35 +08:00
parent ac234d99ec
commit aba8b44ab8
1 changed files with 43 additions and 42 deletions

View File

@ -169,7 +169,8 @@ class _RecordingButtonState extends State<RecordingButton>
await _speechService.stopListening();
} else {
await _speechService.startListening(
partialResults: widget.partialResults);
partialResults: widget.partialResults,
);
}
} catch (e) {
widget.onError?.call(SpeechRecognitionError(
@ -191,60 +192,60 @@ class _RecordingButtonState extends State<RecordingButton>
@override
Widget build(BuildContext context) {
Color buttonColor;
Color iconColor;
if (!widget.enabled || !_isInitialized) {
buttonColor = widget.disabledColor ?? Colors.grey[400]!;
} else if (_isProcessing) {
buttonColor = _isListening
? (widget.recordingColor ?? const Color(0xFFFF5252))
.withValues(alpha: 0.7)
: (widget.idleColor ?? const Color(0xFF2196F3))
.withValues(alpha: 0.7);
} else if (_isListening) {
buttonColor = widget.recordingColor ?? const Color(0xFFFF5252); //
iconColor = widget.disabledColor ?? Colors.grey[850]!;
} else {
buttonColor = widget.idleColor ?? const Color(0xFF2196F3); //
iconColor = _isListening
? (widget.recordingColor ?? const Color(0xFFFF5252))
: (widget.idleColor ?? const Color(0xFF2196F3));
}
Widget button = AnimatedBuilder(
animation: _scaleAnimation,
builder: (context, child) {
return Transform.scale(
scale: _scaleAnimation.value,
child: Container(
return Container(
width: widget.size,
height: widget.size,
decoration: BoxDecoration(
color: buttonColor,
color: iconColor.withValues(alpha: 0.12),
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: buttonColor.withValues(alpha: 0.3),
blurRadius: _isListening ? 20 : 12,
spreadRadius: _isListening ? 5 : 3,
),
],
),
child: Material(
color: Colors.transparent,
child: InkWell(
borderRadius: BorderRadius.circular(widget.size / 2),
onTap: _toggleRecording,
child: _isProcessing
? SizedBox(
width: widget.size * 0.4,
height: widget.size * 0.4,
child: Stack(
children: [
Positioned(
left: 0,
right: 0,
bottom: 0,
top: 0,
child: Icon(
_isListening ? Icons.stop_rounded : Icons.mic_rounded,
size: widget.size * 0.55,
color: iconColor,
),
),
if (_isProcessing || _isListening)
Positioned(
left: 0,
right: 0,
bottom: 0,
top: 0,
child: SizedBox(
width: widget.size,
height: widget.size,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor:
AlwaysStoppedAnimation<Color>(Colors.white),
),
)
: Icon(
_isListening ? Icons.stop_rounded : Icons.mic_rounded,
size: widget.size * (_isListening ? 0.35 : 0.4),
color: Colors.white,
),
)
],
),
),
),