AudioReactive¶
Create visuals that respond to audio data
AudioReactive provides audio analysis data to descendant widgets, enabling beat-synced animations, frequency visualizations, and other audio-responsive effects.
Table of Contents¶
Overview¶
AudioReactive uses an InheritedWidget pattern to make audio analysis data available throughout the widget tree:
AudioReactive(
provider: myAudioProvider,
child: TimeConsumer(
builder: (context, frame, _) {
final audioData = AudioReactive.of(context);
final beatStrength = audioData?.provider.getBeatStrengthAt(frame) ?? 0;
return Transform.scale(
scale: 1.0 + beatStrength * 0.2,
child: Circle(),
);
},
),
)
Widgets¶
AudioReactive¶
The InheritedWidget that provides audio data:
| Property | Type | Description |
|---|---|---|
provider |
AudioDataProvider |
Audio analysis provider |
isReady |
bool |
Whether data is ready to use |
child |
Widget |
Child widget tree |
AudioReactiveBuilder¶
Handles async initialization of the audio provider:
AudioReactiveBuilder(
provider: MockAudioDataProvider(bpm: 120),
loadingBuilder: (context) => CircularProgressIndicator(),
errorBuilder: (context, error) => Text('Error: $error'),
builder: (context, provider) {
return MyVideoComposition();
},
)
| Property | Type | Description |
|---|---|---|
provider |
AudioDataProvider |
Provider to initialize |
builder |
Widget Function(context, provider) |
Builder when ready |
loadingBuilder |
Widget Function(context)? |
Loading state builder |
errorBuilder |
Widget Function(context, error)? |
Error state builder |
AudioDataProvider¶
The AudioDataProvider abstract class defines the interface for audio analysis:
Available Methods¶
| Method | Returns | Description |
|---|---|---|
getBpm() |
double |
Beats per minute |
getAmplitudeAt(frame, fps) |
double |
Volume level (0.0-1.0) |
getBeatStrengthAt(frame, fps) |
double |
Beat intensity (0.0-1.0) |
isBeatAt(frame, fps, threshold) |
bool |
Whether there's a beat |
getFrequencyBandsAt(frame, fps, bandCount) |
List<double> |
Frequency spectrum |
getBassAt(frame, fps) |
double |
Low frequency level |
getMidAt(frame, fps) |
double |
Mid frequency level |
getTrebleAt(frame, fps) |
double |
High frequency level |
MockAudioDataProvider¶
For testing and prototyping:
Examples¶
Beat-Synced Scale¶
AudioReactive(
provider: audioProvider,
child: TimeConsumer(
builder: (context, frame, _) {
final audioData = AudioReactive.of(context);
final beatStrength = audioData?.provider.getBeatStrengthAt(frame) ?? 0;
return Transform.scale(
scale: 1.0 + beatStrength * 0.3, // Scale up on beat
child: LogoWidget(),
);
},
),
)
Bass-Reactive Color¶
AudioReactive(
provider: audioProvider,
child: TimeConsumer(
builder: (context, frame, _) {
final audioData = AudioReactive.of(context);
final bass = audioData?.provider.getBassAt(frame) ?? 0;
return Container(
color: Color.lerp(Colors.blue, Colors.purple, bass),
child: Content(),
);
},
),
)
Beat Flash¶
AudioReactive(
provider: audioProvider,
child: TimeConsumer(
builder: (context, frame, _) {
final audioData = AudioReactive.of(context);
final isBeat = audioData?.provider.isBeatAt(frame, threshold: 0.7) ?? false;
return Container(
color: isBeat ? Colors.white : Colors.black,
child: Content(),
);
},
),
)
Using AudioReactiveMixin¶
For cleaner code in custom widgets:
class PulsingCircle extends StatelessWidget with AudioReactiveMixin {
final int frame;
const PulsingCircle({required this.frame});
@override
Widget build(BuildContext context) {
final beatStrength = getBeatStrength(context, frame);
final bass = getBass(context, frame);
return Container(
width: 100 + beatStrength * 50,
height: 100 + beatStrength * 50,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.red.withOpacity(0.5 + bass * 0.5),
),
);
}
}
Mixin Methods¶
The AudioReactiveMixin provides convenient access methods:
// Get provider
AudioDataProvider? getAudioProvider(context)
// Get metrics
double getBpm(context)
double getAmplitude(context, frame)
double getBeatStrength(context, frame)
bool isBeat(context, frame, threshold)
List<double> getFrequencyBands(context, frame, bandCount)
double getBass(context, frame)
double getMid(context, frame)
double getTreble(context, frame)
Built-in Visualizations¶
BeatPulse¶
Applies a pulsing scale effect:
TimeConsumer(
builder: (context, frame, _) {
return BeatPulse(
frame: frame,
minScale: 1.0,
maxScale: 1.2,
threshold: 0.3,
child: Text('Pulse!'),
);
},
)
| Property | Type | Default | Description |
|---|---|---|---|
child |
Widget |
required | Widget to pulse |
frame |
int? |
null |
Current frame |
minScale |
double |
1.0 |
Scale at no beat |
maxScale |
double |
1.15 |
Scale at full beat |
threshold |
double |
0.3 |
Beat detection threshold |
fps |
int |
30 |
Frames per second |
FrequencyBars¶
Visualizes frequency spectrum as bars:
TimeConsumer(
builder: (context, frame, _) {
return FrequencyBars(
frame: frame,
barCount: 8,
barWidth: 20,
maxHeight: 100,
spacing: 4,
color: Colors.green,
mirror: false,
);
},
)
| Property | Type | Default | Description |
|---|---|---|---|
frame |
int |
required | Current frame |
barCount |
int |
8 |
Number of bars |
barWidth |
double |
20 |
Width of each bar |
maxHeight |
double |
100 |
Maximum bar height |
spacing |
double |
4 |
Space between bars |
color |
Color |
green | Bar color |
gradient |
Gradient? |
null |
Optional gradient |
mirror |
bool |
false |
Show mirrored bars |
borderRadius |
BorderRadius? |
null |
Bar corners |
Complete Example¶
class AudioVisualizer extends StatelessWidget {
@override
Widget build(BuildContext context) {
return AudioReactiveBuilder(
provider: MockAudioDataProvider(bpm: 120),
loadingBuilder: (context) => const CircularProgressIndicator(),
builder: (context, provider) {
return VideoComposition(
fps: 30,
durationInFrames: 300,
width: 1920,
height: 1080,
child: LayerStack(
children: [
// Beat-reactive background
TimeConsumer(
builder: (context, frame, _) {
final audioData = AudioReactive.of(context);
final bass = audioData?.provider.getBassAt(frame) ?? 0;
return Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Color.lerp(Colors.purple, Colors.pink, bass)!,
Color.lerp(Colors.blue, Colors.cyan, bass)!,
],
),
),
);
},
),
// Centered frequency bars
VCenter(
child: TimeConsumer(
builder: (context, frame, _) {
return FrequencyBars(
frame: frame,
barCount: 12,
barWidth: 30,
maxHeight: 200,
spacing: 8,
color: Colors.white,
mirror: true,
);
},
),
),
// Pulsing logo
VPositioned(
right: 50,
bottom: 50,
child: TimeConsumer(
builder: (context, frame, _) {
return BeatPulse(
frame: frame,
minScale: 1.0,
maxScale: 1.3,
child: Icon(Icons.music_note, size: 48, color: Colors.white),
);
},
),
),
],
),
);
},
);
}
}
Related¶
- TimeConsumer - Frame-based animation
- AudioTrack - Adding audio
- Embedding Audio - Audio guide