完成录音功能

pull/1/head
张萌 3 years ago
parent 68b1a6c1b3
commit f623ea1f65

@ -56,4 +56,6 @@
<uses-permission-sdk-23 android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission-sdk-23 android:name="android.permission.ACCESS_FINE_LOCATION"/>
<!-- 读取手机IMEI的设备权限 --> <!-- 读取手机IMEI的设备权限 -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" />
<!-- 麦克风权限 -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
</manifest> </manifest>

@ -44,6 +44,7 @@ class _PublishTaskPageState extends State<PublishTaskPage> {
TextEditingController _telController = TextEditingController(); TextEditingController _telController = TextEditingController();
String? _content; String? _content;
List<File> _photos = []; List<File> _photos = [];
String? _voiceUri;
@override @override
void dispose() { void dispose() {
@ -818,7 +819,11 @@ class _PublishTaskPageState extends State<PublishTaskPage> {
24.w.heightBox, 24.w.heightBox,
GestureDetector( GestureDetector(
onTap: () async { onTap: () async {
await Get.bottomSheet(BeeRecordVoiceWidget()); var re = await Get.bottomSheet(BeeRecordVoiceWidget());
if (re != null) {
_voiceUri = re;
}
setState(() {});
}, },
child: Material( child: Material(
color: Colors.transparent, color: Colors.transparent,
@ -832,11 +837,15 @@ class _PublishTaskPageState extends State<PublishTaskPage> {
.color(Colors.black.withOpacity(0.45)) .color(Colors.black.withOpacity(0.45))
.make(), .make(),
), ),
VoicePlayer( if (_voiceUri != null)
url: '5d143e1b735b0.mp3', VoicePlayer(
showXmark: true, path: _voiceUri,
onDelete: () {}, showXmark: true,
), onDelete: () {
_voiceUri = null;
setState(() {});
},
),
Spacer(), Spacer(),
Icon( Icon(
CupertinoIcons.chevron_right, CupertinoIcons.chevron_right,

@ -1,10 +1,12 @@
import 'dart:async';
import 'package:aku_new_community/base/base_style.dart'; import 'package:aku_new_community/base/base_style.dart';
import 'package:aku_new_community/extensions/num_ext.dart'; import 'package:aku_new_community/extensions/num_ext.dart';
import 'package:aku_new_community/widget/bee_divider.dart'; import 'package:aku_new_community/widget/bee_divider.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_sound/public/flutter_sound_recorder.dart'; import 'package:flutter_sound/flutter_sound.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
import 'package:velocity_x/src/extensions/string_ext.dart'; import 'package:velocity_x/src/extensions/string_ext.dart';
@ -18,17 +20,78 @@ class BeeRecordVoiceWidget extends StatefulWidget {
class _BeeRecordVoiceWidgetState extends State<BeeRecordVoiceWidget> { class _BeeRecordVoiceWidgetState extends State<BeeRecordVoiceWidget> {
bool _inRecordVoice = false; bool _inRecordVoice = false;
var myPlayer = FlutterSoundRecorder(); var myRecorder = FlutterSoundRecorder();
var myPlayer = FlutterSoundPlayer();
String? filePath;
String fileName = 'record.aac';
bool get reclaim => !_inRecordVoice && filePath != null;
bool get play => !_inRecordVoice && filePath != null;
Timer? _timer;
int _recordSeconds = 0;
int _maxSeconds = 0;
String get _recordTime {
var min = (_recordSeconds ~/ 60).toString();
if (min.length < 2) {
min = '0$min';
}
var sec = (_recordSeconds % 60).toString();
if (sec.length < 2) {
sec = '0$sec';
}
return '$min:$sec';
}
@override @override
void initState() { void initState() {
myPlayer.openRecorder(); myRecorder.openRecorder();
myPlayer.openPlayer();
Future.delayed(Duration(seconds: 0), () async {
var permission = await Permission.microphone.isGranted;
print(permission);
if (!permission) {
await Permission.microphone.request();
}
});
super.initState(); super.initState();
} }
void cancelTimer() {
_timer?.cancel();
_timer = null;
}
void startRecord() {
cancelTimer();
_timer = Timer.periodic(Duration(seconds: 1), (timer) {
_recordSeconds++;
setState(() {});
});
}
void startPlay() {
cancelTimer();
_maxSeconds = _recordSeconds;
_timer = Timer.periodic(Duration(seconds: 1), (timer) {
if (_recordSeconds == 0) {
cancelTimer();
_recordSeconds = _maxSeconds;
setState(() {});
} else {
_recordSeconds--;
setState(() {});
}
});
}
@override @override
void dispose() { void dispose() {
myPlayer.closeRecorder(); myRecorder.closeRecorder();
myPlayer.closePlayer();
cancelTimer();
super.dispose(); super.dispose();
} }
@ -50,8 +113,7 @@ class _BeeRecordVoiceWidgetState extends State<BeeRecordVoiceWidget> {
.color(Colors.black.withOpacity(0.85)) .color(Colors.black.withOpacity(0.85))
.make(), .make(),
20.hb, 20.hb,
'00:00' _recordTime.text
.text
.size(24.sp) .size(24.sp)
.isIntrinsic .isIntrinsic
.bold .bold
@ -65,27 +127,38 @@ class _BeeRecordVoiceWidgetState extends State<BeeRecordVoiceWidget> {
.text .text
.size(24.sp) .size(24.sp)
.isIntrinsic .isIntrinsic
.color(Colors.black.withOpacity(0.45)) .color(
reclaim ? kPrimaryColor : Colors.black.withOpacity(0.45))
.make(), .make(),
12.wb, 12.wb,
Icon( GestureDetector(
CupertinoIcons.mic_circle, onTap: reclaim
size: 60.w, ? () async {
color: Colors.black.withOpacity(0.45), myRecorder.deleteRecord(fileName: fileName);
filePath = null;
_recordSeconds = 0;
setState(() {});
}
: null,
child: Icon(
CupertinoIcons.mic_circle,
size: 60.w,
color:
reclaim ? kPrimaryColor : Colors.black.withOpacity(0.45),
),
), ),
40.wb, 40.wb,
GestureDetector( GestureDetector(
onTap: () async { onTap: () async {
var permission = await Permission.microphone.isGranted; if (myRecorder.isRecording) {
if (!permission) { filePath = await myRecorder.stopRecorder();
await Permission.microphone.request(); cancelTimer();
}
if (myPlayer.isRecording) {
await myPlayer.pauseRecorder();
} else if (myPlayer.isPaused) {
await myPlayer.resumeRecorder();
} else { } else {
await myPlayer.startRecorder(); if (filePath != null) {
myRecorder.deleteRecord(fileName: fileName);
}
await myRecorder.startRecorder(toFile: fileName);
startRecord();
} }
_inRecordVoice = !_inRecordVoice; _inRecordVoice = !_inRecordVoice;
setState(() {}); setState(() {});
@ -113,16 +186,31 @@ class _BeeRecordVoiceWidgetState extends State<BeeRecordVoiceWidget> {
), ),
), ),
40.wb, 40.wb,
Icon( GestureDetector(
CupertinoIcons.play_circle, onTap: play
size: 60.w, ? () async {
color: Colors.black.withOpacity(0.45), if (myPlayer.isPlaying) {
myPlayer.stopPlayer();
cancelTimer();
_recordSeconds = _maxSeconds;
setState(() {});
} else {
await myPlayer.startPlayer(fromURI: filePath);
startPlay();
}
}
: null,
child: Icon(
CupertinoIcons.play_circle,
size: 60.w,
color: play ? kPrimaryColor : Colors.black.withOpacity(0.45),
),
), ),
12.wb, 12.wb,
'试听' '试听'
.text .text
.size(24.sp) .size(24.sp)
.color(Colors.black.withOpacity(0.45)) .color(play ? kPrimaryColor : Colors.black.withOpacity(0.45))
.isIntrinsic .isIntrinsic
.make() .make()
], ],
@ -136,15 +224,18 @@ class _BeeRecordVoiceWidgetState extends State<BeeRecordVoiceWidget> {
onTap: () { onTap: () {
Get.back(); Get.back();
}, },
child: Container( child: Material(
height: 80.w, color: Colors.transparent,
alignment: Alignment.center, child: Container(
child: '取消' height: 80.w,
.text alignment: Alignment.center,
.size(28.sp) child: '取消'
.color(Colors.black.withOpacity(0.85)) .text
.isIntrinsic .size(28.sp)
.make(), .color(Colors.black.withOpacity(0.85))
.isIntrinsic
.make(),
),
), ),
), ),
), ),
@ -156,17 +247,20 @@ class _BeeRecordVoiceWidgetState extends State<BeeRecordVoiceWidget> {
Expanded( Expanded(
child: GestureDetector( child: GestureDetector(
onTap: () { onTap: () {
Get.back(); Get.back(result: filePath);
}, },
child: Container( child: Material(
height: 80.w, color: Colors.transparent,
alignment: Alignment.center, child: Container(
child: '确定' height: 80.w,
.text alignment: Alignment.center,
.size(28.sp) child: '确定'
.color(kPrimaryColor) .text
.isIntrinsic .size(28.sp)
.make(), .color(kPrimaryColor)
.isIntrinsic
.make(),
),
), ),
), ),
) )

@ -9,13 +9,15 @@ import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:just_audio/just_audio.dart'; import 'package:just_audio/just_audio.dart';
class VoicePlayer extends StatefulWidget { class VoicePlayer extends StatefulWidget {
final String url; final String? url;
final VoidCallback? onDelete; final VoidCallback? onDelete;
final bool showXmark; final bool showXmark;
final String? path;
const VoicePlayer( const VoicePlayer(
{Key? key, required this.url, this.onDelete, this.showXmark = false}) {Key? key, this.url, this.onDelete, this.showXmark = false, this.path})
: super(key: key); : assert(url != null || path != null),
super(key: key);
@override @override
_VoicePlayerState createState() => _VoicePlayerState(); _VoicePlayerState createState() => _VoicePlayerState();
@ -62,7 +64,11 @@ class _VoicePlayerState extends State<VoicePlayer>
} }
Future initVoice() async { Future initVoice() async {
await player.setUrl(SARSAPI.image(widget.url)); if (widget.url != null) {
await player.setUrl(SARSAPI.image(widget.url));
} else {
await player.setFilePath(widget.path!);
}
_voiceLength = await player.load(); _voiceLength = await player.load();
_currentLength = _voiceLength?.inSeconds ?? 0; _currentLength = _voiceLength?.inSeconds ?? 0;
await player.setClip(start: Duration(seconds: 0), end: _voiceLength); await player.setClip(start: Duration(seconds: 0), end: _voiceLength);

Loading…
Cancel
Save