RecorderPlaybackController

Overview

The RecorderPlaybackController widget provides a means to co-ordinate a SoundRecorderUI and SoundPlayerUI so that a user can record and playback via a single UI.
The RecorderPlaybackController is an InheritedWidget which allows the SoundRecorderUI and SoundPlayerUI to find and attach to it.
​SoundRecorderUI and SoundPlayerUI widgets will automatically search for and attach to the nearest RecorderPlaybackController above them in the widget tree.
Due to the automated nature of the linkage you need to be careful where you place a RecorderPlaybackController in your widget tree to avoid unintentional connections. Keep the RecorderPlaybackController as close to the other two widgets as possible.
The RecorderPlaybackController MUST be above SoundPlayerUI and the SoundRecorderUI widgets in the Widget tree.

Example:

This example is from the example app. It demonstrates how to create a Recorder SoundRecorderUI linked to a SoundPlayerUI.
The example demonstrates how to build a UI which allows a user to record audio and then immediately play it back.
The example also uses requestPermissions to display an explanatory dialog to the user before the OS displays its standard permission dialog.
The RecorderPlaybackController is responsible for coordinating the recording and playback so that only one can occur at a time.
1
import 'dart:async';
2
import 'dart:core';
3
​
4
import 'package:flutter/material.dart';
5
import 'package:sounds/sounds.dart';
6
import 'package:permission_handler/permission_handler.dart';
7
​
8
void main() {
9
var recordingPath = Track.tempFile(MediaFormat.aacADTS);
10
runApp(SoundExampleApp._internal(recordingPath));
11
}
12
​
13
class SoundExampleApp extends StatelessWidget {
14
final Track _track;
15
​
16
//
17
SoundExampleApp._internal(String recordingPath)
18
: _track = Track.fromFile(recordingPath, mediaFormat: MediaFormat.aacADTS);
19
​
20
@override
21
Widget build(BuildContext context) {
22
return MaterialApp(
23
title: 'Welcome to Flutter',
24
home: Scaffold(
25
appBar: AppBar(
26
title: Text('Welcome to Flutter'),
27
),
28
body: buildBody(),
29
),
30
);
31
}
32
​
33
Widget buildBody() {
34
// link the recorder and player so you can record
35
// and then playback the message.
36
// Note: the recorder and player MUST share the same track.
37
return RecorderPlaybackController(
38
child: Column(
39
children: [
40
/// Add the recorder
41
SoundRecorderUI(
42
/// the track to record into.
43
_track,
44
​
45
/// callback for when recording needs permissions
46
requestPermissions: requestPermissions,
47
),
48
Padding(
49
padding: const EdgeInsets.all(10),
50
// add the player
51
child: SoundPlayerUI.fromTrack(_track),
52
)
53
],
54
));
55
}
56
​
57
/// Callback for when the recorder needs permissions to record
58
/// to the [track].
59
Future<bool> requestPermissions(BuildContext context, Track track) async {
60
var granted = false;
61
​
62
/// change this to true if the track doesn't use
63
/// external storage on android.
64
var usingExternalStorage = false;
65
​
66
// Request Microphone permission if needed
67
print('storage: ${await Permission.microphone.status}');
68
var microphoneRequired = !await Permission.microphone.isGranted;
69
​
70
var storageRequired = false;
71
​
72
if (usingExternalStorage) {
73
/// only required if track is on external storage
74
if (Permission.storage.status == PermissionStatus.undetermined) {
75
print('You are probably missing the storage permission '
76
'in your manifest.');
77
}
78
​
79
storageRequired =
80
usingExternalStorage && !await Permission.storage.isGranted;
81
}
82
​
83
/// build the 'reason' why and what we are asking permissions for.
84
if (microphoneRequired || storageRequired) {
85
var both = false;
86
​
87
if (microphoneRequired && storageRequired) {
88
both = true;
89
}
90
​
91
var reason = "To record a message we need permission ";
92
​
93
if (microphoneRequired) {
94
reason += "to access your microphone";
95
}
96
​
97
if (both) {
98
reason += " and ";
99
}
100
​
101
if (storageRequired) {
102
reason += "to store a file on your phone";
103
}
104
​
105
reason += ".";
106
​
107
if (both) {
108
reason += " \n\nWhen prompted click the 'Allow' button on "
109
"each of the following prompts.";
110
} else {
111
reason += " \n\nWhen prompted click the 'Allow' button.";
112
}
113
​
114
/// tell the user we are about to ask for permissions.
115
if (await showAlertDialog(context, reason)) {
116
var permissions = <Permission>[];
117
if (microphoneRequired) permissions.add(Permission.microphone);
118
if (storageRequired) permissions.add(Permission.storage);
119
​
120
/// ask for the permissions.
121
await permissions.request();
122
​
123
/// check the user gave us the permissions.
124
granted = await Permission.microphone.isGranted &&
125
await Permission.storage.isGranted;
126
if (!granted) grantFailed(context);
127
} else {
128
granted = false;
129
grantFailed(context);
130
}
131
} else {
132
granted = true;
133
}
134
​
135
// we already have the required permissions.
136
return granted;
137
}
138
​
139
/// Display a snackbar saying that we can't record due to lack of permissions.
140
void grantFailed(BuildContext context) {
141
var snackBar = SnackBar(
142
content: Text('Recording cannot start as you did not allow '
143
'the required permissions'));
144
​
145
// Find the Scaffold in the widget tree and use it to show a SnackBar.
146
Scaffold.of(context).showSnackBar(snackBar);
147
}
148
​
149
///
150
Future<bool> showAlertDialog(BuildContext context, String prompt) {
151
// set up the buttons
152
Widget cancelButton = TextButton(
153
child: Text("Cancel"),
154
onPressed: () => Navigator.of(context).pop(false),
155
);
156
Widget continueButton = TextButton(
157
child: Text("Continue"),
158
onPressed: () => Navigator.of(context).pop(true),
159
);
160
​
161
// set up the AlertDialog
162
var alert = AlertDialog(
163
title: Text("Recording Permissions"),
164
content: Text(prompt),
165
actions: [
166
cancelButton,
167
continueButton,
168
],
169
);
170
​
171
// show the dialog
172
return showDialog<bool>(
173
context: context,
174
builder: (context) {
175
return alert;
176
},
177
);
178
}
179
}
Copied!
Last modified 7mo ago
Copy link