Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ class _HomeState extends State<Home> {
size: Size(MediaQuery.of(context).size.width, 200.0),
updateFrequency: updateFrequency,
waveController: waveController,
margin: const EdgeInsets.all(10.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(14.0), color: Colors.black),
),
const SizedBox(height: 40),
Row(
Expand Down
92 changes: 56 additions & 36 deletions lib/src/audio_wave.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,28 @@ import 'package:audio_wave/src/base/wave_style.dart';
import 'package:audio_wave/src/wave_painter.dart';
import 'package:flutter/material.dart';
import './base/wave_controller.dart';
import 'base/wave_clipper.dart';

class AudioWave extends StatefulWidget {
final Size size;
final Duration updateFrequency;
final WaveController waveController;
final WaveStyle waveStyle;
final EdgeInsets? padding;
final EdgeInsets? margin;
final BoxDecoration? decoration;
final Color? backgroundColor;

const AudioWave({
Key? key,
required this.size,
required this.updateFrequency,
required this.waveController,
this.waveStyle = const WaveStyle(),
this.padding,
this.margin,
this.decoration,
this.backgroundColor,
}) : super(key: key);

@override
Expand Down Expand Up @@ -47,42 +56,53 @@ class _AudioWaveState extends State<AudioWave> {

@override
Widget build(BuildContext context) {
return GestureDetector(
onHorizontalDragUpdate: _handleHorizontalDragUpdate,
onHorizontalDragStart: _handleHorizontalDragStart,
child: ColoredBox(
color: widget.waveStyle.backgroundColor,
child: RepaintBoundary(
child: CustomPaint(
size: widget.size,
isComplex: false,
willChange: true,
painter: WavePainter(
waveThickness: widget.waveStyle.waveThickness,
middleLineThickness: widget.waveStyle.middleLineThickness,
middleLineColor: widget.waveStyle.middleLineColor,
waveData: widget.waveController.waveData,
callPushback: widget.waveController.shouldRefresh,
bottomPadding:
widget.waveStyle.bottomPadding ?? widget.size.height / 2,
spacing: widget.waveStyle.spacing,
waveCap: widget.waveStyle.waveCap,
showBottom: widget.waveStyle.showBottom,
showTop: widget.waveStyle.showTop,
waveColor: widget.waveStyle.waveColor,
showMiddleLine: widget.waveStyle.showMiddleLine,
totalBackDistance: _totalBackDistance,
dragOffset: _dragOffset,
pushBack: _pushBackWave,
initialPosition: _initialPosition,
extendWaveform: widget.waveStyle.extendWaveform,
showHourInDuration: widget.waveStyle.showHourInDuration,
showDurationLine: widget.waveStyle.showDurationLine,
durationLinesColor: widget.waveStyle.durationLinesColor,
durationStyle: widget.waveStyle.durationStyle,
updateFrequecy: const Duration(seconds: 1).inMilliseconds /
widget.waveController.updateFrequency.inMilliseconds,
durationTextPadding: widget.waveStyle.durationTextPadding,
return Container(
padding: widget.padding,
margin: widget.margin,
color: widget.backgroundColor,
decoration: widget.decoration,
child: GestureDetector(
onHorizontalDragUpdate: _handleHorizontalDragUpdate,
onHorizontalDragStart: _handleHorizontalDragStart,
child: ClipPath(
clipper: WaveClipper(!widget.waveStyle.showDurationLine
? 0.0
: widget.waveStyle.extraClipperHeight ??
(widget.waveStyle.durationLinesHeight +
(widget.waveStyle.durationStyle.fontSize ??
widget.waveStyle.durationLinesHeight))),
child: RepaintBoundary(
child: CustomPaint(
size: widget.size,
painter: WavePainter(
waveThickness: widget.waveStyle.waveThickness,
middleLineThickness: widget.waveStyle.middleLineThickness,
middleLineColor: widget.waveStyle.middleLineColor,
waveData: widget.waveController.waveData,
callPushback: widget.waveController.shouldRefresh,
bottomPadding:
widget.waveStyle.bottomPadding ?? widget.size.height / 2,
spacing: widget.waveStyle.spacing,
waveCap: widget.waveStyle.waveCap,
showBottom: widget.waveStyle.showBottom,
showTop: widget.waveStyle.showTop,
waveColor: widget.waveStyle.waveColor,
showMiddleLine: widget.waveStyle.showMiddleLine,
totalBackDistance: _totalBackDistance,
dragOffset: _dragOffset,
pushBack: _pushBackWave,
initialPosition: _initialPosition,
extendWaveform: widget.waveStyle.extendWaveform,
showHourInDuration: widget.waveStyle.showHourInDuration,
showDurationLine: widget.waveStyle.showDurationLine,
durationLinesColor: widget.waveStyle.durationLinesColor,
durationStyle: widget.waveStyle.durationStyle,
updateFrequecy: const Duration(seconds: 1).inMilliseconds /
widget.waveController.updateFrequency.inMilliseconds,
durationTextPadding: widget.waveStyle.durationTextPadding,
durationLinesHeight: widget.waveStyle.durationLinesHeight,
labelSpacing: widget.waveStyle.labelSpacing,
),
),
),
),
Expand Down
11 changes: 11 additions & 0 deletions lib/src/base/label.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import 'package:flutter/material.dart';

class Label {
final String content;
Offset offset;

Label({
required this.content,
required this.offset,
});
}
24 changes: 24 additions & 0 deletions lib/src/base/wave_clipper.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import 'package:flutter/material.dart';

///This clipper clips left and right side of waveform square so that waves
///doesn't get outside of the bounds
///
/// This clipper also allows extra clipping height to label so that they can be
/// visible.
class WaveClipper extends CustomClipper<Path> {
final double extraClipperHeight;

WaveClipper(this.extraClipperHeight);

@override
getClip(Size size) {
final path = Path()
..lineTo(0, size.height + extraClipperHeight)
..lineTo(size.width, size.height + extraClipperHeight)
..lineTo(size.width, 0);
return path;
}

@override
bool shouldReclip(WaveClipper oldClipper) => false;
}
65 changes: 34 additions & 31 deletions lib/src/base/wave_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class WaveController extends ChangeNotifier {
///and in IOS value added
late double normalizationFactor = Platform.isAndroid ? 60 : 40;

///Current list of decibels(different for platform)
///Current list of decibels(different values for each platform)
List<double> get waveData => _waveData;

RecorderState _recorderState = RecorderState.stopped;
Expand All @@ -40,39 +40,41 @@ class WaveController extends ChangeNotifier {
///Can be called after pausing.
///If called after stoping the recording, it will re-initialize
Future<void> record([String? path]) async {
await _checkPermission();
if (_hasPermission) {
if (Platform.isAndroid && _recorderState == RecorderState.stopped) {
await _initRecorder(path);
}
if (Platform.isIOS) {
_recorderState = RecorderState.initialized;
}
if (_recorderState == RecorderState.paused && Platform.isAndroid) {
_isRecording = await AudioWaveInterface.instance.resume();
if (_isRecording) {
_startTimer();
_recorderState = RecorderState.recording;
} else {
throw "Failed to resume recording";
if (_recorderState != RecorderState.recording) {
await _checkPermission();
if (_hasPermission) {
if (Platform.isAndroid && _recorderState == RecorderState.stopped) {
await _initRecorder(path);
}
if (Platform.isIOS) {
_recorderState = RecorderState.initialized;
}
if (_recorderState == RecorderState.paused && Platform.isAndroid) {
_isRecording = await AudioWaveInterface.instance.resume();
if (_isRecording) {
_startTimer();
_recorderState = RecorderState.recording;
} else {
throw "Failed to resume recording";
}
notifyListeners();
return;
}
notifyListeners();
return;
}

if (_recorderState == RecorderState.initialized) {
_isRecording = await AudioWaveInterface.instance.record(path);
if (_isRecording) {
_recorderState = RecorderState.recording;
_startTimer();
} else {
throw "Failed to start recording";
if (_recorderState == RecorderState.initialized) {
_isRecording = await AudioWaveInterface.instance.record(path);
if (_isRecording) {
_recorderState = RecorderState.recording;
_startTimer();
} else {
throw "Failed to start recording";
}
notifyListeners();
}
} else {
_recorderState = RecorderState.stopped;
notifyListeners();
}
} else {
_recorderState = RecorderState.stopped;
notifyListeners();
}
}

Expand All @@ -87,7 +89,7 @@ class WaveController extends ChangeNotifier {
notifyListeners();
}

///checks permission
///checks microphone permission
Future<void> _checkPermission() async {
final result = await AudioWaveInterface.instance.checkPermission();
if (result) {
Expand All @@ -113,7 +115,8 @@ class WaveController extends ChangeNotifier {
///Use this stop recording.
///Resouces are freed after calling this.
Future<void> stop() async {
if (_recorderState == RecorderState.initialized) {
if (_recorderState == RecorderState.recording ||
_recorderState == RecorderState.paused) {
_isRecording = await AudioWaveInterface.instance.stop();
}
if (!_isRecording) {
Expand Down
26 changes: 18 additions & 8 deletions lib/src/base/wave_style.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import 'package:flutter/material.dart';

class WaveStyle {

///Color of the [Wave]
final Color waveColor;

Expand Down Expand Up @@ -39,21 +38,29 @@ class WaveStyle {
///Extend the wave to the end of size.width, default is size.width/2
final bool extendWaveform;

///Has no effect
///[Reseaon] -> laying out text is expensive task so for now not included
///Show duration labels. Default is true
final bool showDurationLine;

///Has no effect
///Show duration label in HH:MM:SS format. Default is MM:SS
final bool showHourInDuration;

///Has no effect
///Text style for duration labels
final TextStyle durationStyle;

///Has no effect
///Color of duration lines
final Color durationLinesColor;

///Has no effect
///value > 0 will be padded right and value < 0 will be padded left
///Height of duration lines
final double durationLinesHeight;

///Space between duration labels and waveform square
final double labelSpacing;

///It might happen that label text gets cut or have extra clipping.
///So use this to add or remove clipping
final double? extraClipperHeight;

///Value > 0 will be padded right and value < 0 will be padded left
final double durationTextPadding;

const WaveStyle({
Expand All @@ -71,10 +78,13 @@ class WaveStyle {
this.extendWaveform = false,
this.backgroundColor = Colors.black,
this.showHourInDuration = false,
this.durationLinesHeight = 16.0,
this.durationStyle = const TextStyle(
color: Colors.red,
fontSize: 16.0,
),
this.extraClipperHeight,
this.labelSpacing = 16.0,
this.durationTextPadding = 20.0,
this.durationLinesColor = Colors.blueAccent,
}) : assert(waveThickness < spacing,
Expand Down
Loading