reason

华为手机保持音频失败

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.mofunsky.one_poem">
<application
android:requestLegacyExternalStorage="true"
tools:replace="android:label"
android:label="一言"
android:icon="@mipmap/ic_launcher">
<activity
......
......@@ -41,5 +41,12 @@
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>NSMicrophoneUsageDescription</key>
<string>打开话筒</string>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
</dict>
</plist>
......
......@@ -73,7 +73,7 @@ class CategoryItem extends StatelessWidget {
],
),
const Text(
"",
"",
style: TextStyle(
fontSize: 30, fontFamily: "ZhiMangXing"),
),
......
import 'shared/size_fit.dart';
extension DoubleFit on double {
double get px {
return HYSizeFit.setPx(this);
}
double get rpx {
return HYSizeFit.setRpx(this);
}
}
import 'shared/size_fit.dart';
extension IntFit on int {
double get px {
return HYSizeFit.setPx(toDouble());
}
double get rpx {
return HYSizeFit.setRpx(toDouble());
}
}
import 'package:flutter/material.dart';
class HYSizeFit {
static late MediaQueryData _mediaQueryData;
static late double screenWidth;
static late double screenHeight;
static late double rpx;
static late double px;
static void initialize(BuildContext context, {double standardWidth = 750}) {
_mediaQueryData = MediaQuery.of(context);
screenWidth = _mediaQueryData.size.width;
screenHeight = _mediaQueryData.size.height;
rpx = screenWidth / standardWidth;
px = screenWidth / standardWidth * 2;
}
// 按照像素来设置
static double setPx(double size) {
return HYSizeFit.rpx * size * 2;
}
// 按照rxp来设置
static double setRpx(double size) {
return HYSizeFit.rpx * size;
}
}
......@@ -2,6 +2,7 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_swiper_null_safety/flutter_swiper_null_safety.dart';
import 'package:one_poem/extension/shared/size_fit.dart';
import 'package:one_poem/login/login_router.dart';
import 'package:one_poem/res/constant.dart';
import 'package:one_poem/routers/fluro_navigator.dart';
......@@ -83,6 +84,7 @@ class _SplashPageState extends State<SplashPage> {
@override
Widget build(BuildContext context) {
HYSizeFit.initialize(context);
return Material(
color: context.backgroundColor,
child: _status == 0 ?
......
......@@ -84,6 +84,7 @@ class _LoginPageState extends State<LoginPage> with ChangeNotifierMixin<LoginPag
return Scaffold(
appBar: MyAppBar(
isBack: false,
isTransparent: true,
onPressed: () {
NavigatorUtils.push(context, LoginRouter.smsLoginPage);
},
......
......@@ -11,6 +11,8 @@ import 'package:one_poem/widgets/bars/home_action_bar.dart';
import 'package:one_poem/widgets/bars/home_menu_bar.dart';
import 'package:one_poem/widgets/my_app_bar.dart';
import 'package:one_poem/extension/int_extension.dart';
import '../poem_router.dart';
enum PoemContentSwitch {
......@@ -52,8 +54,9 @@ class _PoemDetailPageState extends State<PoemDetailPage> {
contentSwitch = PoemContentSwitch.comment;
setState(() {});
},
funcRight: (){
NavigatorUtils.push(context, '${PoemRouter.poemRecordAudioPage}?id=100');
funcRight: () {
NavigatorUtils.push(
context, '${PoemRouter.poemRecordAudioPage}?id=100');
},
),
homeActionWidgets: HomeActionWidgets(
......@@ -75,10 +78,10 @@ class _PoemDetailPageState extends State<PoemDetailPage> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
margin: const EdgeInsets.symmetric(
vertical: 30.0, horizontal: 20.0),
margin:
EdgeInsets.symmetric(vertical: 30.px, horizontal: 20.px),
height: MediaQuery.of(context).size.height -
140 -
140.px -
widget.poemPanelHeight,
width: double.infinity,
decoration: BoxDecoration(
......@@ -97,7 +100,7 @@ class _PoemDetailPageState extends State<PoemDetailPage> {
color: Colors.grey.shade200.withOpacity(0.1),
),
child: Padding(
padding: const EdgeInsets.all(10.0),
padding: EdgeInsets.all(10.px),
child: Flex(
direction: Axis.vertical,
children: [
......@@ -109,7 +112,9 @@ class _PoemDetailPageState extends State<PoemDetailPage> {
Expanded(
flex: 1,
child: contentSwitch == PoemContentSwitch.audio
? const PoemUserAudio()
? PoemUserAudio(
poemPanelHeight: widget.poemPanelHeight,
)
: const PoemUserComments(
author: "老魔取西经",
comments:
......@@ -124,17 +129,17 @@ class _PoemDetailPageState extends State<PoemDetailPage> {
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: const Icon(
icon: Icon(
Icons.mic_none,
size: 36.0,
size: 36.px,
),
onPressed: () {},
),
Gaps.hGap16,
IconButton(
icon: const Icon(
icon: Icon(
Icons.camera_alt_outlined,
size: 36.0,
size: 36.px,
),
onPressed: () {},
)
......
......@@ -3,11 +3,13 @@ import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:one_poem/poem/widgets/poem_content.dart';
import 'package:one_poem/res/resources.dart';
import 'package:one_poem/recorder/widgets/poem_voice_widget.dart';
import 'package:one_poem/widgets/bars/home_action_bar.dart';
import 'package:one_poem/widgets/bars/home_menu_bar.dart';
import 'package:one_poem/widgets/my_app_bar.dart';
import 'package:one_poem/extension/int_extension.dart';
class PoemRecordAudioPage extends StatefulWidget {
@override
State<StatefulWidget> createState() => _PoemRecordAudioPageState();
......@@ -23,6 +25,16 @@ class PoemRecordAudioPage extends StatefulWidget {
}
class _PoemRecordAudioPageState extends State<PoemRecordAudioPage> {
startRecord() {
print("开始录制");
}
stopRecord(String path, double audioTimeLength) {
print("结束束录制");
print("音频文件位置" + path);
print("音频录制时长" + audioTimeLength.toString());
}
@override
Widget build(BuildContext context) {
const poemStr =
......@@ -59,10 +71,10 @@ class _PoemRecordAudioPageState extends State<PoemRecordAudioPage> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
margin: const EdgeInsets.symmetric(
vertical: 30.0, horizontal: 20.0),
margin:
EdgeInsets.symmetric(vertical: 20.px, horizontal: 20.px),
height: MediaQuery.of(context).size.height -
140 -
110.px -
widget.poemPanelHeight,
width: double.infinity,
decoration: BoxDecoration(
......@@ -81,39 +93,37 @@ class _PoemRecordAudioPageState extends State<PoemRecordAudioPage> {
color: Colors.grey.shade200.withOpacity(0.1),
),
child: Padding(
padding: const EdgeInsets.all(10.0),
padding: EdgeInsets.all(10.px),
child: Flex(
direction: Axis.vertical,
children: [
const PoemContent(
PoemContent(
title: "题破山寺后禅院",
author: "常建",
poemStr: poemStr,
fontSize: 22.0,
fontSize: 20.px,
),
Stack(
alignment: Alignment.center,
children: [
Positioned(
left: 10.0,
left: 10.px,
child: IconButton(
icon: const Icon(
icon: Icon(
Icons.camera_alt_outlined,
size: 28.0,
size: 28.px,
),
onPressed: () {},
),
),
SizedBox(
width: double.infinity,
height: 90.0,
child: IconButton(
icon: const Icon(
Icons.mic_none,
size: 70.0,
color: Colors.green,
),
onPressed: () {},
height: 80.px,
child: PoemVoiceWidget(
startRecord: startRecord,
stopRecord: stopRecord,
// 加入定制化Container的相关属性
height: 40.px,
),
),
],
......
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:one_poem/extension/int_extension.dart';
class PoemUserAudio extends StatelessWidget {
const PoemUserAudio({
Key? key,
this.audio, //TODO 传入数据
this.desc,
this.poemPanelHeight = 0,
}) : super(key: key);
final List<Map<String, String>>? audio;
final String? desc;
final int poemPanelHeight;
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(vertical: 5.0, horizontal: 10.0),
padding: EdgeInsets.symmetric(vertical: 5.px, horizontal: 10.px),
width: double.infinity,
child: Column(
children: <Widget>[
ListTile(
title: Text(
desc ?? "一大波用户朗读录制提交了“临境”",
style: const TextStyle(color: Colors.black54, fontSize: 15.0),
style: TextStyle(color: Colors.black54, fontSize: 15.px),
),
),
SizedBox(
width: double.infinity,
height: 200.0,
height: 200.px - poemPanelHeight,
child: ListView.builder(
itemBuilder: (BuildContext context, int index) {
return Wrap(
spacing: 5.0,
spacing: 5.px,
crossAxisAlignment: WrapCrossAlignment.center,
children: const [
children: [
Icon(
Icons.play_circle_outline,
size: 16.0,
size: 16.px,
color: Colors.black45,
),
Text(
const Text(
"普通话",
style: TextStyle(color: Colors.black45, fontSize: 16.0),
)
......
import 'package:flutter/material.dart';
class CustomOverlay extends StatelessWidget {
final Widget? icon;
final BoxDecoration decoration;
final double width;
final double height;
const CustomOverlay({
Key? key,
this.icon,
this.decoration = const BoxDecoration(
color: Color(0xff77797A),
borderRadius: BorderRadius.all(Radius.circular(20.0)),
),
this.width = 160,
this.height = 160,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Positioned(
top: MediaQuery.of(context).size.height * 0.5 - width / 2,
left: MediaQuery.of(context).size.width * 0.5 - height / 2,
child: Material(
type: MaterialType.transparency,
child: Center(
child: Opacity(
opacity: 0.8,
child: Container(
width: width,
height: height,
decoration: decoration,
child: icon,
),
),
),
),
);
}
}
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_plugin_record/flutter_plugin_record.dart';
import 'package:flutter_plugin_record/utils/common_toast.dart';
import 'custom_overlay.dart';
typedef StartRecord = Future Function();
typedef StopRecord = Future Function();
class PoemVoiceWidget extends StatefulWidget {
final Function? startRecord;
final Function? stopRecord;
final double? height;
final EdgeInsets? margin;
final Decoration? decoration;
/// startRecord 开始录制回调 stopRecord回调
const PoemVoiceWidget(
{Key? key,
this.startRecord,
this.stopRecord,
this.height,
this.decoration,
this.margin})
: super(key: key);
@override
_PoemVoiceWidgetState createState() => _PoemVoiceWidgetState();
}
class _PoemVoiceWidgetState extends State<PoemVoiceWidget> {
// 倒计时总时长
final int _countTotal = 12;
double starty = 0.0;
double offset = 0.0;
bool isUp = false;
String textShow = "按住说话";
String toastShow = "手指上滑,取消发送";
String voiceIco = "images/voice_volume_1.png";
///默认隐藏状态
bool voiceState = true;
FlutterPluginRecord? recordPlugin;
Timer? _timer;
int _count = 0;
OverlayEntry? overlayEntry;
String audioFilePath = "";
@override
void initState() {
super.initState();
recordPlugin = FlutterPluginRecord();
_init();
///初始化方法的监听
recordPlugin?.responseFromInit.listen((data) {
// if (data) {
// print("初始化成功");
// } else {
// print("初始化失败");
// }
});
/// 开始录制或结束录制的监听
recordPlugin?.response.listen((data) {
if (data.msg == "onStop") {
///结束录制时会返回录制文件的地址方便上传服务器
// print("onStop " + data.path!);
if (widget.stopRecord != null) {
audioFilePath = data.path!;
widget.stopRecord!(data.path, data.audioTimeLength);
}
} else if (data.msg == "onStart") {
if (widget.startRecord != null) widget.startRecord!();
}
});
///录制过程监听录制的声音的大小 方便做语音动画显示图片的样式
recordPlugin!.responseFromAmplitude.listen((data) {
var voiceData = double.parse(data.msg ?? '');
setState(() {
if (voiceData > 0 && voiceData < 0.1) {
voiceIco = "images/voice_volume_2.png";
} else if (voiceData > 0.2 && voiceData < 0.3) {
voiceIco = "images/voice_volume_3.png";
} else if (voiceData > 0.3 && voiceData < 0.4) {
voiceIco = "images/voice_volume_4.png";
} else if (voiceData > 0.4 && voiceData < 0.5) {
voiceIco = "images/voice_volume_5.png";
} else if (voiceData > 0.5 && voiceData < 0.6) {
voiceIco = "images/voice_volume_6.png";
} else if (voiceData > 0.6 && voiceData < 0.7) {
voiceIco = "images/voice_volume_7.png";
} else if (voiceData > 0.7 && voiceData < 1) {
voiceIco = "images/voice_volume_7.png";
} else {
voiceIco = "images/voice_volume_1.png";
}
if (overlayEntry != null) {
overlayEntry!.markNeedsBuild();
}
});
// print("振幅大小 " + voiceData.toString() + " " + voiceIco);
});
}
///显示录音悬浮布局
buildOverLayView(BuildContext context) {
if (overlayEntry == null) {
overlayEntry = OverlayEntry(builder: (content) {
return CustomOverlay(
icon: Column(
children: <Widget>[
Container(
margin: const EdgeInsets.only(top: 10),
child: _countTotal - _count < 11
? Center(
child: Padding(
padding: const EdgeInsets.only(bottom: 15.0),
child: Text(
(_countTotal - _count).toString(),
style: const TextStyle(
fontSize: 70.0,
color: Colors.white,
),
),
),
)
: Image.asset(
voiceIco,
width: 100,
height: 100,
package: 'flutter_plugin_record',
),
),
Text(
toastShow,
style: const TextStyle(
fontStyle: FontStyle.normal,
color: Colors.white,
fontSize: 14,
),
)
],
),
);
});
Overlay.of(context)!.insert(overlayEntry!);
}
}
showVoiceView() {
setState(() {
textShow = "松开结束";
voiceState = false;
});
///显示录音悬浮布局
buildOverLayView(context);
start();
}
hideVoiceView() {
if (_timer!.isActive) {
if (_count < 1) {
CommonToast.showView(
context: context,
msg: '说话时间太短',
icon: const Text(
'!',
style: TextStyle(fontSize: 80, color: Colors.white),
));
isUp = true;
}
_timer?.cancel();
_count = 0;
}
setState(() {
textShow = "按住说话";
voiceState = true;
});
stop();
if (overlayEntry != null) {
overlayEntry?.remove();
overlayEntry = null;
}
// if (isUp) {
// print("取消发送");
// } else {
// print("进行发送");
// }
}
moveVoiceView() {
setState(() {
isUp = starty - offset > 100 ? true : false;
if (isUp) {
textShow = "松开手指,取消发送";
toastShow = textShow;
} else {
textShow = "松开结束";
toastShow = "手指上滑,取消发送";
}
});
}
///初始化语音录制的方法
void _init() async {
recordPlugin?.initRecordMp3();
}
///开始语音录制的方法
void start() async {
recordPlugin?.start();
}
///停止语音录制的方法
void stop() {
recordPlugin?.stop();
}
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(right: 10.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.end,
children: [
GestureDetector(
onLongPressStart: (details) {
starty = details.globalPosition.dy;
_timer = Timer.periodic(const Duration(milliseconds: 1000), (t) {
_count++;
if (_count == _countTotal) {
hideVoiceView();
}
});
showVoiceView();
},
onLongPressEnd: (details) {
hideVoiceView();
},
onLongPressMoveUpdate: (details) {
offset = details.globalPosition.dy;
moveVoiceView();
},
child: Container(
height: widget.height ?? 60,
margin: widget.margin ?? const EdgeInsets.fromLTRB(50, 0, 50, 20),
child: const Icon(
Icons.mic_none,
size: 70.0,
color: Colors.green,
),
),
),
IconButton(
icon: const Icon(
Icons.play_circle_outline,
size: 28.0,
),
onPressed: () {
print("######:" + audioFilePath);
recordPlugin!.playByPath(audioFilePath, "file");
},
),
],
),
);
}
@override
void dispose() {
recordPlugin?.dispose();
_timer?.cancel();
super.dispose();
}
}
......@@ -366,6 +366,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.5"
flutter_plugin_record:
dependency: "direct main"
description:
name: flutter_plugin_record
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
flutter_slidable:
dependency: "direct main"
description:
......
......@@ -91,6 +91,7 @@ dependencies:
flutter_spinkit: ^5.0.0
json_annotation: ^4.4.0
flutter_plugin_record: ^1.0.1
dependency_overrides:
decimal: 1.5.0
......