zego_zim_audio 1.0.0+2 zego_zim_audio: ^1.0.0+2 copied to clipboard
ZEGO ZIM Audio SDK is a flutter plugin wrapper based on ZIM native Android / iOS SDK.
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:zego_zim_audio/zego_zim_audio.dart';
import 'package:path_provider/path_provider.dart';
import 'dart:io' show Directory, File;
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String _version = "";
List<String> _recordings = [];
Map<String,int> recordDurationMap = {};
bool _isRecording = false;
int _currentRecordDuration = 0;
String _currentPlaying = '';
int _currentPlayingDuration = 0;
String _waitingPlaying = '';
bool _isPlaying = false;
bool _isSpeaker = true;
bool isCancelRecord = false;
ScrollController _scrollController = ScrollController();
final GlobalKey<_MyAppState> key = GlobalKey();
Future<void> clearApplicationDocumentsDirectory() async {
Directory appDocDir = await getApplicationDocumentsDirectory();
if (appDocDir.existsSync()) {
await for (var entity in appDocDir.list()) {
if (entity is File) {
await entity.delete();
}
}
}
}
@override
void initState(){
super.initState();
clearApplicationDocumentsDirectory();
ZIMAudio.getVersion().then((value) => {
_version = value,
setState((){})
});
ZIMAudio.getInstance().init("");
ZIMAudioEventHandler.onError = (ZIMAudioError errorInfo){
ErrorDiaLog.showFailedDialog(key.currentContext!,
errorInfo.code.toString(), 'onError.message:${errorInfo.message}');
};
ZIMAudioEventHandler.onRecorderCompleted = (int totalDuration) async{
setState(() {
_isRecording = false;
recordDurationMap['${_recordings.length + 1}'] = totalDuration;
_recordings.add('${_recordings.length + 1}');
});
Future.delayed(Duration(milliseconds: 500), () {
_scrollToTop();
});
};
ZIMAudioEventHandler.onRecorderFailed = (int errorCode)async{
setState((){
_isRecording = false;
ErrorDiaLog.showFailedDialog(key.currentContext!, errorCode.toString(), 'onRecorderFailed');
});
};
ZIMAudioEventHandler.onRecorderStarted =()async{
_isRecording = true;
_currentRecordDuration = 0;
setState(() {});
};
ZIMAudioEventHandler.onRecorderCancelled = () {
_isRecording = false;
setState(() {});
};
ZIMAudioEventHandler.onRecorderProgress = (int currentDuration) {
if (kDebugMode) {
print('[Flutter] onRecorderProgress:$currentDuration');
}
_currentRecordDuration = currentDuration;
setState(() {
});
};
ZIMAudioEventHandler.onPlayerStarted = (int duration) {
setState(() {
_isPlaying = true;
_currentPlaying = _waitingPlaying;
_currentPlayingDuration = duration;
});
};
ZIMAudioEventHandler.onPlayerEnded = () {
setState(() {
_currentPlaying = '';
_isPlaying = false;
});
};
ZIMAudioEventHandler.onPlayerStopped = () {
setState(() {
_currentPlaying = '';
_isPlaying = false;
});
};
ZIMAudioEventHandler.onPlayerInterrupted = () {
setState(() {
_currentPlaying = '';
_isPlaying = false;
});
};
ZIMAudioEventHandler.onPlayerProgress = (int currentDuration) {
setState(() {
_currentPlayingDuration = currentDuration;
});
};
ZIMAudioEventHandler.onPlayerFailed = (int errorCode) {
setState(() {
_isPlaying = false;
_currentPlaying = '';
ErrorDiaLog.showFailedDialog(key.currentContext!, errorCode.toString(), 'onPlayerFailed');
});
};
}
void _startRecording() async {
try {
Directory? dic = await getApplicationDocumentsDirectory();
String path = '${dic.path}/${_recordings.length + 1}.mp3';
await ZIMAudio.getInstance().startRecord(ZIMAudioRecordConfig(path,maxDuration: 120000));
} catch (e) {
print("Error starting recording: $e");
}
}
void _CancelRecording() async {
try {
await ZIMAudio.getInstance().cancelRecord();
}catch (e) {
print("Error cancel recording: $e");
}
}
void _CompleteRecording() async {
try {
await ZIMAudio.getInstance().completeRecord();
} catch (e) {
print("Error complete recording: $e");
}
}
void _startPlaying(String recording) async {
if(_isRecording){
return;
}
try {
Directory? dic = await getApplicationDocumentsDirectory();
String path = '${dic.path}/$recording.mp3';
await ZIMAudio.getInstance().startPlay(ZIMAudioPlayConfig(path,routeType: _isSpeaker?ZIMAudioRouteType.speaker:ZIMAudioRouteType.receiver));
_waitingPlaying = recording;
} catch (e) {
print("Error starting playback: $e");
}
}
Future<void> _stopPlaying() async {
try {
await ZIMAudio.getInstance().stopPlay();
} catch (e) {
print("Error stopping playback: $e");
}
}
void _scrollToTop() {
_scrollController.animateTo(
_scrollController.position.maxScrollExtent,
duration: Duration(milliseconds: 500),
curve: Curves.easeInOut,
);
}
String formatMillisecondsTime(int milliseconds) {
int seconds = (milliseconds / 1000).floor();
int remainingMilliseconds = milliseconds % 1000;
int minutes = (seconds / 60).floor();
int remainingSeconds = seconds % 60;
String minutesStr = minutes.toString();
String secondsStr = remainingSeconds.toString().padLeft(2, '0');
String millisecondsStr = (remainingMilliseconds ~/ 10).toString().padLeft(2, '0');
return '$minutesStr:$secondsStr.$millisecondsStr';
}
String formatSecondTime(int milliseconds) {
int seconds = (milliseconds / 1000).floor();
int remainingSeconds = seconds % 60;
int minutes = (seconds / 60).floor();
String minutesStr = minutes.toString().padLeft(1, '0');
String secondsStr = remainingSeconds.toString().padLeft(2, '0');
return '$minutesStr:$secondsStr';
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('ZIM Audio:$_version'),
),
key: key,
body: Column(
children: [
Flexible(
flex: 2,
child: ListView(
controller: _scrollController,
reverse: false,
children: _recordings
.map(
(recording) => ListTile(
title: Text('Recording $recording'),
subtitle: Text('${formatSecondTime(recordDurationMap[recording]!)}'),
trailing: ElevatedButton(
onPressed: () {
if (recording == _currentPlaying) {
_stopPlaying();
} else {
_startPlaying(recording);
}
HapticFeedback.mediumImpact();
},
style: ButtonStyle(
backgroundColor: recording == _currentPlaying
? MaterialStateProperty.all(Colors.red)
: null,
),
child: Text(
recording == _currentPlaying && _isPlaying
? '${formatSecondTime(recordDurationMap[recording]! - _currentPlayingDuration)}'
: 'Play',
),
),
),
)
.toList(),
),
),
SizedBox(height: 20),
IconButton(
onPressed: () {
_isSpeaker = !_isSpeaker;
HapticFeedback.mediumImpact();
if(_isSpeaker == true){
ZIMAudio.getInstance().setAudioRouteType(ZIMAudioRouteType.speaker);
}else{
ZIMAudio.getInstance().setAudioRouteType(ZIMAudioRouteType.receiver);
}
setState(() {
});
},
icon: Icon(
_isSpeaker ? Icons.volume_up : Icons.hearing,
color: Colors.grey,
),
iconSize: 50,
splashRadius: 30,
padding: EdgeInsets.all(16),
color: Colors.blue,
),
Align(
alignment: Alignment.bottomCenter,
child: Padding(
padding: const EdgeInsets.only(bottom: 20.0),
child: GestureDetector(
onLongPressStart: (details) async{
_startRecording();
HapticFeedback.mediumImpact();
},
onLongPressEnd: (_) {
if(isCancelRecord){
_CancelRecording();
}else{
HapticFeedback.mediumImpact();
_CompleteRecording();
}
isCancelRecord = false;
},
onLongPressMoveUpdate: (LongPressMoveUpdateDetails details) {
if (details.localPosition.dy < -50) {
isCancelRecord = true;
}
},
child: Container(
padding: EdgeInsets.all(20),
decoration: BoxDecoration(
color: _isRecording ? Colors.red : Colors.blue,
borderRadius: BorderRadius.circular(10),
),
child: Text(
_isRecording ? 'Swipe up to cancel ${formatSecondTime(_currentRecordDuration)}' : 'Press and hold to record',
style: TextStyle(
color: Colors.white,
fontSize: 18,
),
),
),
),
),
),
],
),
),
);
}
}
class ErrorDiaLog {
static Future<bool?> showFailedDialog(BuildContext context,
String code, String message) {
return showDialog<bool>(
context: context,
builder: (context) {
return AlertDialog(
title: const Text(
"Error",
),
content: Text('code:' + code + '\n\n' + 'message:' + message),
actions: <Widget>[
TextButton(
onPressed: (() {
Navigator.of(context).pop();
}),
child: const Text('OK'))
],
);
});
}
}