Skip to content
This repository was archived by the owner on May 18, 2022. It is now read-only.

Commit e995699

Browse files
committed
Implemented recorder and player. Callback not supported yet.
- Tested on ios.
1 parent 1bf90e1 commit e995699

26 files changed

+269
-34
lines changed

example/README.md

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,28 @@
11
# flutter_sound_example
2-
3-
Demonstrates how to use the flutter_sound plugin.
2+
This is not a playlist audio module and this library provides simple recorder and player functionalities for both `android` and `ios` platforms. This only supports default file extension for each platform. This module can also handle file from url.
43

54
## Getting Started
65

76
For help getting started with Flutter, view our online
87
[documentation](https://flutter.io/).
8+
9+
## Install
10+
Add ```flutter_sound``` as a dependency in pubspec.yaml
11+
For help on adding as a dependency, view the [documentation](https://flutter.io/using-packages/).
12+
13+
## Post Installation
14+
On *iOS* you need to add a usage description to `info.plist`:
15+
16+
```xml
17+
<key>NSMicrophoneUsageDescription</key>
18+
<string>This sample uses the microphone to record your speech and convert it to text.</string>
19+
<key>UIBackgroundModes</key>
20+
<array>
21+
<string>audio</string>
22+
</array>
23+
```
24+
25+
On *Android* you need to add a permission to `AndroidManifest.xml`:
26+
```xml
27+
<uses-permission android:name="android.permission.RECORD_AUDIO" />
28+
```

example/android/app/src/main/AndroidManifest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
to allow setting breakpoints, to provide hot reload, etc.
77
-->
88
<uses-permission android:name="android.permission.INTERNET"/>
9+
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
910

1011
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
1112
calls FlutterMain.startInitialization(this); in its onCreate method.

example/ios/Runner.xcodeproj/project.pbxproj

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,11 @@
182182
TargetAttributes = {
183183
97C146ED1CF9000F007C117D = {
184184
CreatedOnToolsVersion = 7.3.1;
185+
SystemCapabilities = {
186+
com.apple.BackgroundModes = {
187+
enabled = 1;
188+
};
189+
};
185190
};
186191
};
187192
};

example/ios/Runner/Info.plist

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,14 @@
2222
<string>$(FLUTTER_BUILD_NUMBER)</string>
2323
<key>LSRequiresIPhoneOS</key>
2424
<true/>
25+
<key>NSMicrophoneUsageDescription</key>
26+
<string>This sample uses the microphone to record your speech and convert it to text.</string>
27+
<key>UIBackgroundModes</key>
28+
<array>
29+
<string>audio</string>
30+
<string>fetch</string>
31+
<string>remote-notification</string>
32+
</array>
2533
<key>UILaunchStoryboardName</key>
2634
<string>LaunchScreen</string>
2735
<key>UIMainStoryboardFile</key>

example/lib/main.dart

Lines changed: 164 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,43 +12,185 @@ class MyApp extends StatefulWidget {
1212
}
1313

1414
class _MyAppState extends State<MyApp> {
15-
String _platformVersion = 'Unknown';
15+
bool _isRecording = false;
16+
bool _isPlaying = false;
1617

1718
@override
1819
void initState() {
1920
super.initState();
20-
initPlatformState();
2121
}
2222

23-
// Platform messages are asynchronous, so we initialize in an async method.
24-
Future<void> initPlatformState() async {
25-
String platformVersion;
26-
// Platform messages may fail, so we use a try/catch PlatformException.
23+
void startRecorder() async{
2724
try {
28-
platformVersion = await FlutterSound.platformVersion;
29-
} on PlatformException {
30-
platformVersion = 'Failed to get platform version.';
25+
String path = await FlutterSound.startRecorder(null);
26+
print('startRecorder: $path');
27+
28+
this.setState(() {
29+
this._isRecording = true;
30+
});
31+
} catch (err) {
32+
print('startRecorder error: $err');
3133
}
34+
}
3235

33-
// If the widget was removed from the tree while the asynchronous platform
34-
// message was in flight, we want to discard the reply rather than calling
35-
// setState to update our non-existent appearance.
36-
if (!mounted) return;
36+
void stopRecorder() async{
37+
try {
38+
String result = await FlutterSound.stopRecorder();
39+
print('stopRecorder: $result');
40+
this.setState(() {
41+
this._isRecording = false;
42+
});
43+
} catch (err) {
44+
print('stopRecorder error: $err');
45+
}
46+
}
47+
48+
void startPlayer() async{
49+
String path = await FlutterSound.startPlayer(null);
50+
print('startPlayer: $path');
51+
}
52+
53+
void stopPlayer() async{
54+
String result = await FlutterSound.stopPlayer();
55+
print('stopPlayer: $result');
56+
}
57+
58+
void pausePlayer() async{
59+
String result = await FlutterSound.pausePlayer();
60+
print('pausePlayer: $result');
61+
}
62+
63+
void resumePlayer() async{
64+
String result = await FlutterSound.resumePlayer();
65+
print('resumePlayer: $result');
66+
}
3767

38-
setState(() {
39-
_platformVersion = platformVersion;
40-
});
68+
void seekToPlayer(int sec) async{
69+
String result = await FlutterSound.seekToPlayer(sec);
70+
print('seekToPlayer: $result');
4171
}
4272

4373
@override
4474
Widget build(BuildContext context) {
45-
return new MaterialApp(
46-
home: new Scaffold(
47-
appBar: new AppBar(
48-
title: const Text('Plugin example app'),
75+
return MaterialApp(
76+
home: Scaffold(
77+
appBar: AppBar(
78+
title: const Text('Flutter Sound'),
4979
),
50-
body: new Center(
51-
child: new Text('Running on: $_platformVersion\n'),
80+
body: ListView(
81+
children: <Widget>[
82+
Column(
83+
crossAxisAlignment: CrossAxisAlignment.center,
84+
mainAxisAlignment: MainAxisAlignment.center,
85+
children: <Widget>[
86+
Container(
87+
margin: EdgeInsets.only(top: 24.0, bottom:16.0),
88+
child: Text(
89+
'00:00',
90+
style: TextStyle(
91+
fontSize: 48.0,
92+
color: Colors.black,
93+
),
94+
),
95+
),
96+
],
97+
),
98+
Row(
99+
children: <Widget>[
100+
Container(
101+
width: 56.0,
102+
height: 56.0,
103+
child: ClipOval(
104+
child: FlatButton(
105+
onPressed: () {
106+
if (!this._isRecording) {
107+
return this.startRecorder();
108+
}
109+
this.stopRecorder();
110+
},
111+
padding: EdgeInsets.all(8.0),
112+
child: Image(
113+
image: this._isRecording ? AssetImage('res/icons/ic_stop.png') : AssetImage('res/icons/ic_mic.png'),
114+
),
115+
),
116+
),
117+
),
118+
],
119+
mainAxisAlignment: MainAxisAlignment.center,
120+
crossAxisAlignment: CrossAxisAlignment.center,
121+
),
122+
Column(
123+
crossAxisAlignment: CrossAxisAlignment.center,
124+
mainAxisAlignment: MainAxisAlignment.center,
125+
children: <Widget>[
126+
Container(
127+
margin: EdgeInsets.only(top: 60.0, bottom:16.0),
128+
child: Text(
129+
'00:00',
130+
style: TextStyle(
131+
fontSize: 48.0,
132+
color: Colors.black,
133+
),
134+
),
135+
),
136+
],
137+
),
138+
Row(
139+
children: <Widget>[
140+
Container(
141+
width: 56.0,
142+
height: 56.0,
143+
child: ClipOval(
144+
child: FlatButton(
145+
onPressed: () {
146+
startPlayer();
147+
},
148+
padding: EdgeInsets.all(8.0),
149+
child: Image(
150+
image: AssetImage('res/icons/ic_play.png'),
151+
),
152+
),
153+
),
154+
),
155+
Container(
156+
width: 56.0,
157+
height: 56.0,
158+
child: ClipOval(
159+
child: FlatButton(
160+
onPressed: () {
161+
pausePlayer();
162+
},
163+
padding: EdgeInsets.all(8.0),
164+
child: Image(
165+
width: 36.0,
166+
height: 36.0,
167+
image: AssetImage('res/icons/ic_pause.png'),
168+
),
169+
),
170+
),
171+
),
172+
Container(
173+
width: 56.0,
174+
height: 56.0,
175+
child: ClipOval(
176+
child: FlatButton(
177+
onPressed: () {
178+
stopPlayer();
179+
},
180+
padding: EdgeInsets.all(8.0),
181+
child: Image(
182+
width: 28.0,
183+
height: 28.0,
184+
image: AssetImage('res/icons/ic_stop.png'),
185+
),
186+
),
187+
),
188+
),
189+
],
190+
mainAxisAlignment: MainAxisAlignment.center,
191+
crossAxisAlignment: CrossAxisAlignment.center,
192+
),
193+
],
52194
),
53195
),
54196
);

example/pubspec.yaml

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,25 @@ flutter:
3636
uses-material-design: true
3737

3838
# To add assets to your application, add an assets section, like this:
39-
# assets:
40-
# - images/a_dot_burr.jpeg
41-
# - images/a_dot_ham.jpeg
39+
assets:
40+
- res/icons/ic_play.png
41+
- res/icons/2.0x/ic_play.png
42+
- res/icons/3.0x/ic_play.png
43+
- res/icons/ic_stop.png
44+
- res/icons/2.0x/ic_stop.png
45+
- res/icons/3.0x/ic_stop.png
46+
- res/icons/ic_pause.png
47+
- res/icons/2.0x/ic_pause.png
48+
- res/icons/3.0x/ic_pause.png
49+
- res/icons/ic_mic.png
50+
- res/icons/2.0x/ic_mic.png
51+
- res/icons/3.0x/ic_mic.png
52+
- res/icons/ic_volume_down.png
53+
- res/icons/2.0x/ic_volume_down.png
54+
- res/icons/3.0x/ic_volume_down.png
55+
- res/icons/ic_volume_up.png
56+
- res/icons/2.0x/ic_volume_up.png
57+
- res/icons/3.0x/ic_volume_up.png
4258

4359
# An image asset can refer to one or more resolution-specific "variants", see
4460
# https://flutter.io/assets-and-images/#resolution-aware.

example/res/icons/2.0x/ic_mic.png

1.59 KB
Loading

example/res/icons/2.0x/ic_pause.png

383 Bytes
Loading

example/res/icons/2.0x/ic_play.png

970 Bytes
Loading

example/res/icons/2.0x/ic_stop.png

427 Bytes
Loading
1.09 KB
Loading
2.17 KB
Loading

example/res/icons/3.0x/ic_mic.png

3.35 KB
Loading

example/res/icons/3.0x/ic_pause.png

779 Bytes
Loading

example/res/icons/3.0x/ic_play.png

1.62 KB
Loading

example/res/icons/3.0x/ic_stop.png

891 Bytes
Loading
2.31 KB
Loading
4.58 KB
Loading

example/res/icons/ic_mic.png

824 Bytes
Loading

example/res/icons/ic_pause.png

235 Bytes
Loading

example/res/icons/ic_play.png

539 Bytes
Loading

example/res/icons/ic_stop.png

244 Bytes
Loading

example/res/icons/ic_volume_down.png

553 Bytes
Loading

example/res/icons/ic_volume_up.png

1.11 KB
Loading

ios/Classes/FlutterSoundPlugin.m

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
8585
}
8686

8787
- (void)startRecorder:(NSString*)path result: (FlutterResult)result {
88-
if ([path isEqualToString:@"DEFAULT"]) {
88+
if ([path class] == [NSNull class]) {
8989
audioFileURL = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingString:@"sound.m4a"]];
9090
} else {
9191
audioFileURL = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingString:path]];
@@ -127,6 +127,10 @@ - (void)stopRecorder:(FlutterResult)result {
127127
}
128128

129129
- (void)startPlayer:(NSString*)path result: (FlutterResult)result {
130+
if ([path class] == [NSNull class]) {
131+
path = @"sound.m4a";
132+
}
133+
130134
if ([[path substringToIndex:4] isEqualToString:@"http"]) {
131135
audioFileURL = [NSURL URLWithString:path];
132136

@@ -154,11 +158,7 @@ - (void)startPlayer:(NSString*)path result: (FlutterResult)result {
154158

155159
[downloadTask resume];
156160
} else {
157-
if ([path isEqualToString:@"DEFAULT"]) {
158-
audioFileURL = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingString:@"sound.m4a"]];
159-
} else {
160-
audioFileURL = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingString:path]];
161-
}
161+
audioFileURL = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingString:path]];
162162

163163
if (!audioPlayer) {
164164
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:audioFileURL error:nil];

lib/flutter_sound.dart

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,51 @@ class FlutterSound {
66
static const MethodChannel _channel =
77
const MethodChannel('flutter_sound');
88

9-
static Future<String> get platformVersion async {
9+
static Future<dynamic> get platformVersion async {
1010
final String version = await _channel.invokeMethod('getPlatformVersion');
1111
return version;
1212
}
13+
14+
static Future<String> startRecorder(String uri) async {
15+
String result = await _channel.invokeMethod('startRecorder', <String, dynamic> {
16+
'path': uri,
17+
});
18+
19+
return result;
20+
}
21+
22+
static Future<String> stopRecorder() async {
23+
String result = await _channel.invokeMethod('stopRecorder');
24+
return result;
25+
}
26+
27+
static Future<String> startPlayer(String uri) async {
28+
String result = await _channel.invokeMethod('startPlayer', <String, dynamic> {
29+
'path': uri,
30+
});
31+
32+
return result;
33+
}
34+
35+
static Future<String> stopPlayer() async {
36+
String result = await _channel.invokeMethod('stopPlayer');
37+
return result;
38+
}
39+
40+
static Future<String> pausePlayer() async {
41+
String result = await _channel.invokeMethod('pausePlayer');
42+
return result;
43+
}
44+
45+
static Future<String> resumePlayer() async {
46+
String result= await _channel.invokeMethod('resumePlayer');
47+
return result;
48+
}
49+
50+
static Future<String> seekToPlayer(int sec) async {
51+
String result = await _channel.invokeMethod('startPlayer', <String, dynamic> {
52+
'sec': sec,
53+
});
54+
return result;
55+
}
1356
}

0 commit comments

Comments
 (0)