Expand voice recording layout
All checks were successful
Build and deploy Flutter Web / build (push) Successful in 2m7s
All checks were successful
Build and deploy Flutter Web / build (push) Successful in 2m7s
This commit is contained in:
@@ -1106,11 +1106,15 @@ class _VoiceStep extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
],
|
||||
_VoiceProgressGrid(progress: informationProgress),
|
||||
const SizedBox(height: 18),
|
||||
Expanded(
|
||||
child: Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
flex: 5,
|
||||
child: _VoiceProgressGrid(progress: informationProgress),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Expanded(
|
||||
flex: 3,
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: _LibraryWaveSurface(
|
||||
controller: waveController,
|
||||
active: isRecording,
|
||||
@@ -1148,50 +1152,80 @@ class _VoiceProgressGrid extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
const columns = 14;
|
||||
const rows = 3;
|
||||
const columns = 20;
|
||||
const rows = 10;
|
||||
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,
|
||||
),
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
const gap = 5.0;
|
||||
final maxCellWidth =
|
||||
(constraints.maxWidth - gap * (columns - 1)) / columns;
|
||||
final maxCellHeight = (constraints.maxHeight - gap * (rows - 1)) / rows;
|
||||
final cellSize = math.max(
|
||||
6.0,
|
||||
math.min(maxCellWidth, maxCellHeight).clamp(6.0, 18.0),
|
||||
);
|
||||
final gridWidth = columns * cellSize + gap * (columns - 1);
|
||||
final gridHeight = rows * cellSize + gap * (rows - 1);
|
||||
|
||||
return Center(
|
||||
child: SizedBox(
|
||||
width: gridWidth,
|
||||
height: gridHeight,
|
||||
child: Wrap(
|
||||
spacing: gap,
|
||||
runSpacing: gap,
|
||||
children: [
|
||||
for (var index = 0; index < total; index++)
|
||||
_VoiceProgressCell(
|
||||
filled: _gridOrder(index, total) < filled,
|
||||
size: cellSize,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
static int _gridOrder(int index, int total) => ((index + 7) * 29) % total;
|
||||
}
|
||||
|
||||
class _VoiceProgressCell extends StatelessWidget {
|
||||
const _VoiceProgressCell({required this.filled, required this.size});
|
||||
|
||||
final bool filled;
|
||||
final double size;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 180),
|
||||
curve: Curves.easeOutCubic,
|
||||
width: size,
|
||||
height: size,
|
||||
decoration: BoxDecoration(
|
||||
color: filled
|
||||
? const Color(0xFFFF2D75)
|
||||
: Colors.white.withValues(alpha: 0.11),
|
||||
borderRadius: BorderRadius.circular(3),
|
||||
boxShadow: filled
|
||||
? [
|
||||
BoxShadow(
|
||||
color: const Color(0xFFFF2D75).withValues(alpha: 0.34),
|
||||
blurRadius: 12,
|
||||
spreadRadius: 1,
|
||||
),
|
||||
]
|
||||
: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _VoiceRecordButton extends StatefulWidget {
|
||||
const _VoiceRecordButton({
|
||||
required this.progress,
|
||||
@@ -1252,8 +1286,8 @@ class _VoiceRecordButtonState extends State<_VoiceRecordButton>
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: 164,
|
||||
height: 164,
|
||||
width: 220,
|
||||
height: 220,
|
||||
child: AnimatedBuilder(
|
||||
animation: _pulseController,
|
||||
builder: (context, child) {
|
||||
@@ -1266,11 +1300,11 @@ class _VoiceRecordButtonState extends State<_VoiceRecordButton>
|
||||
Transform.scale(
|
||||
scale:
|
||||
1 +
|
||||
((pulse + offset) % 1) * (0.10 + level * 0.32) +
|
||||
level * 0.10,
|
||||
((pulse + offset) % 1) * (0.20 + level * 0.44) +
|
||||
level * 0.16,
|
||||
child: Container(
|
||||
width: 130,
|
||||
height: 130,
|
||||
width: 150,
|
||||
height: 150,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
@@ -1286,11 +1320,11 @@ class _VoiceRecordButtonState extends State<_VoiceRecordButton>
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 154,
|
||||
height: 154,
|
||||
width: 174,
|
||||
height: 174,
|
||||
child: CircularProgressIndicator(
|
||||
value: widget.progress,
|
||||
strokeWidth: 7,
|
||||
strokeWidth: 8,
|
||||
strokeCap: StrokeCap.round,
|
||||
color: const Color(0xFFFF2D75),
|
||||
backgroundColor: Colors.white.withValues(alpha: 0.12),
|
||||
@@ -1301,8 +1335,8 @@ class _VoiceRecordButtonState extends State<_VoiceRecordButton>
|
||||
);
|
||||
},
|
||||
child: SizedBox(
|
||||
width: 120,
|
||||
height: 120,
|
||||
width: 132,
|
||||
height: 132,
|
||||
child: FilledButton(
|
||||
onPressed: widget.enabled ? widget.onPressed : null,
|
||||
style: FilledButton.styleFrom(
|
||||
@@ -1347,31 +1381,31 @@ class _LibraryWaveSurface extends StatelessWidget {
|
||||
return Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: const Color(
|
||||
0xFFFF2D75,
|
||||
).withValues(alpha: active ? 0.20 + liveLevel * 0.18 : 0.06),
|
||||
blurRadius: 110,
|
||||
spreadRadius: 24,
|
||||
),
|
||||
BoxShadow(
|
||||
color: const Color(
|
||||
0xFF38F5D3,
|
||||
).withValues(alpha: active ? 0.10 + liveLevel * 0.12 : 0.04),
|
||||
blurRadius: 130,
|
||||
spreadRadius: 14,
|
||||
),
|
||||
],
|
||||
Positioned.fill(
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: const Color(
|
||||
0xFFFF2D75,
|
||||
).withValues(alpha: active ? 0.16 + liveLevel * 0.16 : 0.04),
|
||||
blurRadius: 96,
|
||||
spreadRadius: 10,
|
||||
),
|
||||
BoxShadow(
|
||||
color: const Color(
|
||||
0xFF38F5D3,
|
||||
).withValues(alpha: active ? 0.08 + liveLevel * 0.10 : 0.03),
|
||||
blurRadius: 120,
|
||||
spreadRadius: 8,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
child: const SizedBox.square(dimension: 210),
|
||||
),
|
||||
Positioned.fill(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 0),
|
||||
child: active
|
||||
? AnimatedWaveList(
|
||||
key: ValueKey(controller.startTime),
|
||||
@@ -1404,7 +1438,7 @@ class _VoiceWaveBar extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final level = _amplitudeLevel(amplitude.current);
|
||||
final height = 14 + level * 210;
|
||||
final height = 18 + level * 180;
|
||||
final color = Color.lerp(
|
||||
Colors.white.withValues(alpha: 0.28),
|
||||
const Color(0xFFFF2D75),
|
||||
@@ -1417,9 +1451,9 @@ class _VoiceWaveBar extends StatelessWidget {
|
||||
child: Align(
|
||||
alignment: Alignment.center,
|
||||
child: Container(
|
||||
width: 5,
|
||||
width: 6,
|
||||
height: height,
|
||||
margin: const EdgeInsets.symmetric(horizontal: 2.5),
|
||||
margin: const EdgeInsets.symmetric(horizontal: 3),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
gradient: LinearGradient(
|
||||
@@ -1465,15 +1499,15 @@ class _IdleWaveBars extends StatelessWidget {
|
||||
|
||||
return Center(
|
||||
child: SizedBox(
|
||||
height: 220,
|
||||
height: 210,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: List.generate(26, (index) {
|
||||
final distance = (index - 12.5).abs();
|
||||
final height = 18 + math.max(0.0, 1 - distance / 13) * 56;
|
||||
final height = 22 + math.max(0.0, 1 - distance / 13) * 94;
|
||||
return Container(
|
||||
width: 4,
|
||||
width: 6,
|
||||
height: height,
|
||||
margin: const EdgeInsets.symmetric(horizontal: 3),
|
||||
decoration: BoxDecoration(
|
||||
|
||||
Reference in New Issue
Block a user