Showing
15 changed files
with
456 additions
and
37 deletions
| 1 | <manifest xmlns:android="http://schemas.android.com/apk/res/android" | 1 | <manifest xmlns:android="http://schemas.android.com/apk/res/android" |
| 2 | + xmlns:tools="http://schemas.android.com/tools" | ||
| 2 | package="com.mofunsky.one_poem"> | 3 | package="com.mofunsky.one_poem"> |
| 3 | <application | 4 | <application |
| 5 | + android:requestLegacyExternalStorage="true" | ||
| 6 | + tools:replace="android:label" | ||
| 4 | android:label="一言" | 7 | android:label="一言" |
| 5 | android:icon="@mipmap/ic_launcher"> | 8 | android:icon="@mipmap/ic_launcher"> |
| 6 | <activity | 9 | <activity | ... | ... |
| ... | @@ -41,5 +41,12 @@ | ... | @@ -41,5 +41,12 @@ |
| 41 | </array> | 41 | </array> |
| 42 | <key>UIViewControllerBasedStatusBarAppearance</key> | 42 | <key>UIViewControllerBasedStatusBarAppearance</key> |
| 43 | <false/> | 43 | <false/> |
| 44 | + <key>NSMicrophoneUsageDescription</key> | ||
| 45 | + <string>打开话筒</string> | ||
| 46 | + <key>NSAppTransportSecurity</key> | ||
| 47 | + <dict> | ||
| 48 | + <key>NSAllowsArbitraryLoads</key> | ||
| 49 | + <true/> | ||
| 50 | + </dict> | ||
| 44 | </dict> | 51 | </dict> |
| 45 | </plist> | 52 | </plist> | ... | ... |
| ... | @@ -73,7 +73,7 @@ class CategoryItem extends StatelessWidget { | ... | @@ -73,7 +73,7 @@ class CategoryItem extends StatelessWidget { |
| 73 | ], | 73 | ], |
| 74 | ), | 74 | ), |
| 75 | const Text( | 75 | const Text( |
| 76 | - "京", | 76 | + "冬", |
| 77 | style: TextStyle( | 77 | style: TextStyle( |
| 78 | fontSize: 30, fontFamily: "ZhiMangXing"), | 78 | fontSize: 30, fontFamily: "ZhiMangXing"), |
| 79 | ), | 79 | ), | ... | ... |
lib/extension/double_extension.dart
0 → 100644
lib/extension/int_extension.dart
0 → 100644
lib/extension/shared/size_fit.dart
0 → 100644
| 1 | +import 'package:flutter/material.dart'; | ||
| 2 | + | ||
| 3 | +class HYSizeFit { | ||
| 4 | + static late MediaQueryData _mediaQueryData; | ||
| 5 | + static late double screenWidth; | ||
| 6 | + static late double screenHeight; | ||
| 7 | + static late double rpx; | ||
| 8 | + static late double px; | ||
| 9 | + | ||
| 10 | + static void initialize(BuildContext context, {double standardWidth = 750}) { | ||
| 11 | + _mediaQueryData = MediaQuery.of(context); | ||
| 12 | + screenWidth = _mediaQueryData.size.width; | ||
| 13 | + screenHeight = _mediaQueryData.size.height; | ||
| 14 | + rpx = screenWidth / standardWidth; | ||
| 15 | + px = screenWidth / standardWidth * 2; | ||
| 16 | + } | ||
| 17 | + | ||
| 18 | + // 按照像素来设置 | ||
| 19 | + static double setPx(double size) { | ||
| 20 | + return HYSizeFit.rpx * size * 2; | ||
| 21 | + } | ||
| 22 | + | ||
| 23 | + // 按照rxp来设置 | ||
| 24 | + static double setRpx(double size) { | ||
| 25 | + return HYSizeFit.rpx * size; | ||
| 26 | + } | ||
| 27 | +} |
| ... | @@ -2,6 +2,7 @@ import 'dart:async'; | ... | @@ -2,6 +2,7 @@ import 'dart:async'; |
| 2 | 2 | ||
| 3 | import 'package:flutter/material.dart'; | 3 | import 'package:flutter/material.dart'; |
| 4 | import 'package:flutter_swiper_null_safety/flutter_swiper_null_safety.dart'; | 4 | import 'package:flutter_swiper_null_safety/flutter_swiper_null_safety.dart'; |
| 5 | +import 'package:one_poem/extension/shared/size_fit.dart'; | ||
| 5 | import 'package:one_poem/login/login_router.dart'; | 6 | import 'package:one_poem/login/login_router.dart'; |
| 6 | import 'package:one_poem/res/constant.dart'; | 7 | import 'package:one_poem/res/constant.dart'; |
| 7 | import 'package:one_poem/routers/fluro_navigator.dart'; | 8 | import 'package:one_poem/routers/fluro_navigator.dart'; |
| ... | @@ -83,6 +84,7 @@ class _SplashPageState extends State<SplashPage> { | ... | @@ -83,6 +84,7 @@ class _SplashPageState extends State<SplashPage> { |
| 83 | 84 | ||
| 84 | @override | 85 | @override |
| 85 | Widget build(BuildContext context) { | 86 | Widget build(BuildContext context) { |
| 87 | + HYSizeFit.initialize(context); | ||
| 86 | return Material( | 88 | return Material( |
| 87 | color: context.backgroundColor, | 89 | color: context.backgroundColor, |
| 88 | child: _status == 0 ? | 90 | child: _status == 0 ? | ... | ... |
| ... | @@ -84,6 +84,7 @@ class _LoginPageState extends State<LoginPage> with ChangeNotifierMixin<LoginPag | ... | @@ -84,6 +84,7 @@ class _LoginPageState extends State<LoginPage> with ChangeNotifierMixin<LoginPag |
| 84 | return Scaffold( | 84 | return Scaffold( |
| 85 | appBar: MyAppBar( | 85 | appBar: MyAppBar( |
| 86 | isBack: false, | 86 | isBack: false, |
| 87 | + isTransparent: true, | ||
| 87 | onPressed: () { | 88 | onPressed: () { |
| 88 | NavigatorUtils.push(context, LoginRouter.smsLoginPage); | 89 | NavigatorUtils.push(context, LoginRouter.smsLoginPage); |
| 89 | }, | 90 | }, | ... | ... |
| ... | @@ -11,6 +11,8 @@ import 'package:one_poem/widgets/bars/home_action_bar.dart'; | ... | @@ -11,6 +11,8 @@ import 'package:one_poem/widgets/bars/home_action_bar.dart'; |
| 11 | import 'package:one_poem/widgets/bars/home_menu_bar.dart'; | 11 | import 'package:one_poem/widgets/bars/home_menu_bar.dart'; |
| 12 | import 'package:one_poem/widgets/my_app_bar.dart'; | 12 | import 'package:one_poem/widgets/my_app_bar.dart'; |
| 13 | 13 | ||
| 14 | +import 'package:one_poem/extension/int_extension.dart'; | ||
| 15 | + | ||
| 14 | import '../poem_router.dart'; | 16 | import '../poem_router.dart'; |
| 15 | 17 | ||
| 16 | enum PoemContentSwitch { | 18 | enum PoemContentSwitch { |
| ... | @@ -52,8 +54,9 @@ class _PoemDetailPageState extends State<PoemDetailPage> { | ... | @@ -52,8 +54,9 @@ class _PoemDetailPageState extends State<PoemDetailPage> { |
| 52 | contentSwitch = PoemContentSwitch.comment; | 54 | contentSwitch = PoemContentSwitch.comment; |
| 53 | setState(() {}); | 55 | setState(() {}); |
| 54 | }, | 56 | }, |
| 55 | - funcRight: (){ | 57 | + funcRight: () { |
| 56 | - NavigatorUtils.push(context, '${PoemRouter.poemRecordAudioPage}?id=100'); | 58 | + NavigatorUtils.push( |
| 59 | + context, '${PoemRouter.poemRecordAudioPage}?id=100'); | ||
| 57 | }, | 60 | }, |
| 58 | ), | 61 | ), |
| 59 | homeActionWidgets: HomeActionWidgets( | 62 | homeActionWidgets: HomeActionWidgets( |
| ... | @@ -75,10 +78,10 @@ class _PoemDetailPageState extends State<PoemDetailPage> { | ... | @@ -75,10 +78,10 @@ class _PoemDetailPageState extends State<PoemDetailPage> { |
| 75 | crossAxisAlignment: CrossAxisAlignment.start, | 78 | crossAxisAlignment: CrossAxisAlignment.start, |
| 76 | children: [ | 79 | children: [ |
| 77 | Container( | 80 | Container( |
| 78 | - margin: const EdgeInsets.symmetric( | 81 | + margin: |
| 79 | - vertical: 30.0, horizontal: 20.0), | 82 | + EdgeInsets.symmetric(vertical: 30.px, horizontal: 20.px), |
| 80 | height: MediaQuery.of(context).size.height - | 83 | height: MediaQuery.of(context).size.height - |
| 81 | - 140 - | 84 | + 140.px - |
| 82 | widget.poemPanelHeight, | 85 | widget.poemPanelHeight, |
| 83 | width: double.infinity, | 86 | width: double.infinity, |
| 84 | decoration: BoxDecoration( | 87 | decoration: BoxDecoration( |
| ... | @@ -97,7 +100,7 @@ class _PoemDetailPageState extends State<PoemDetailPage> { | ... | @@ -97,7 +100,7 @@ class _PoemDetailPageState extends State<PoemDetailPage> { |
| 97 | color: Colors.grey.shade200.withOpacity(0.1), | 100 | color: Colors.grey.shade200.withOpacity(0.1), |
| 98 | ), | 101 | ), |
| 99 | child: Padding( | 102 | child: Padding( |
| 100 | - padding: const EdgeInsets.all(10.0), | 103 | + padding: EdgeInsets.all(10.px), |
| 101 | child: Flex( | 104 | child: Flex( |
| 102 | direction: Axis.vertical, | 105 | direction: Axis.vertical, |
| 103 | children: [ | 106 | children: [ |
| ... | @@ -109,7 +112,9 @@ class _PoemDetailPageState extends State<PoemDetailPage> { | ... | @@ -109,7 +112,9 @@ class _PoemDetailPageState extends State<PoemDetailPage> { |
| 109 | Expanded( | 112 | Expanded( |
| 110 | flex: 1, | 113 | flex: 1, |
| 111 | child: contentSwitch == PoemContentSwitch.audio | 114 | child: contentSwitch == PoemContentSwitch.audio |
| 112 | - ? const PoemUserAudio() | 115 | + ? PoemUserAudio( |
| 116 | + poemPanelHeight: widget.poemPanelHeight, | ||
| 117 | + ) | ||
| 113 | : const PoemUserComments( | 118 | : const PoemUserComments( |
| 114 | author: "老魔取西经", | 119 | author: "老魔取西经", |
| 115 | comments: | 120 | comments: |
| ... | @@ -124,17 +129,17 @@ class _PoemDetailPageState extends State<PoemDetailPage> { | ... | @@ -124,17 +129,17 @@ class _PoemDetailPageState extends State<PoemDetailPage> { |
| 124 | mainAxisSize: MainAxisSize.min, | 129 | mainAxisSize: MainAxisSize.min, |
| 125 | children: [ | 130 | children: [ |
| 126 | IconButton( | 131 | IconButton( |
| 127 | - icon: const Icon( | 132 | + icon: Icon( |
| 128 | Icons.mic_none, | 133 | Icons.mic_none, |
| 129 | - size: 36.0, | 134 | + size: 36.px, |
| 130 | ), | 135 | ), |
| 131 | onPressed: () {}, | 136 | onPressed: () {}, |
| 132 | ), | 137 | ), |
| 133 | Gaps.hGap16, | 138 | Gaps.hGap16, |
| 134 | IconButton( | 139 | IconButton( |
| 135 | - icon: const Icon( | 140 | + icon: Icon( |
| 136 | Icons.camera_alt_outlined, | 141 | Icons.camera_alt_outlined, |
| 137 | - size: 36.0, | 142 | + size: 36.px, |
| 138 | ), | 143 | ), |
| 139 | onPressed: () {}, | 144 | onPressed: () {}, |
| 140 | ) | 145 | ) | ... | ... |
| ... | @@ -3,11 +3,13 @@ import 'dart:ui'; | ... | @@ -3,11 +3,13 @@ import 'dart:ui'; |
| 3 | import 'package:flutter/cupertino.dart'; | 3 | import 'package:flutter/cupertino.dart'; |
| 4 | import 'package:flutter/material.dart'; | 4 | import 'package:flutter/material.dart'; |
| 5 | import 'package:one_poem/poem/widgets/poem_content.dart'; | 5 | import 'package:one_poem/poem/widgets/poem_content.dart'; |
| 6 | -import 'package:one_poem/res/resources.dart'; | 6 | +import 'package:one_poem/recorder/widgets/poem_voice_widget.dart'; |
| 7 | import 'package:one_poem/widgets/bars/home_action_bar.dart'; | 7 | import 'package:one_poem/widgets/bars/home_action_bar.dart'; |
| 8 | import 'package:one_poem/widgets/bars/home_menu_bar.dart'; | 8 | import 'package:one_poem/widgets/bars/home_menu_bar.dart'; |
| 9 | import 'package:one_poem/widgets/my_app_bar.dart'; | 9 | import 'package:one_poem/widgets/my_app_bar.dart'; |
| 10 | 10 | ||
| 11 | +import 'package:one_poem/extension/int_extension.dart'; | ||
| 12 | + | ||
| 11 | class PoemRecordAudioPage extends StatefulWidget { | 13 | class PoemRecordAudioPage extends StatefulWidget { |
| 12 | @override | 14 | @override |
| 13 | State<StatefulWidget> createState() => _PoemRecordAudioPageState(); | 15 | State<StatefulWidget> createState() => _PoemRecordAudioPageState(); |
| ... | @@ -23,6 +25,16 @@ class PoemRecordAudioPage extends StatefulWidget { | ... | @@ -23,6 +25,16 @@ class PoemRecordAudioPage extends StatefulWidget { |
| 23 | } | 25 | } |
| 24 | 26 | ||
| 25 | class _PoemRecordAudioPageState extends State<PoemRecordAudioPage> { | 27 | class _PoemRecordAudioPageState extends State<PoemRecordAudioPage> { |
| 28 | + startRecord() { | ||
| 29 | + print("开始录制"); | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | + stopRecord(String path, double audioTimeLength) { | ||
| 33 | + print("结束束录制"); | ||
| 34 | + print("音频文件位置" + path); | ||
| 35 | + print("音频录制时长" + audioTimeLength.toString()); | ||
| 36 | + } | ||
| 37 | + | ||
| 26 | @override | 38 | @override |
| 27 | Widget build(BuildContext context) { | 39 | Widget build(BuildContext context) { |
| 28 | const poemStr = | 40 | const poemStr = |
| ... | @@ -59,10 +71,10 @@ class _PoemRecordAudioPageState extends State<PoemRecordAudioPage> { | ... | @@ -59,10 +71,10 @@ class _PoemRecordAudioPageState extends State<PoemRecordAudioPage> { |
| 59 | crossAxisAlignment: CrossAxisAlignment.start, | 71 | crossAxisAlignment: CrossAxisAlignment.start, |
| 60 | children: [ | 72 | children: [ |
| 61 | Container( | 73 | Container( |
| 62 | - margin: const EdgeInsets.symmetric( | 74 | + margin: |
| 63 | - vertical: 30.0, horizontal: 20.0), | 75 | + EdgeInsets.symmetric(vertical: 20.px, horizontal: 20.px), |
| 64 | height: MediaQuery.of(context).size.height - | 76 | height: MediaQuery.of(context).size.height - |
| 65 | - 140 - | 77 | + 110.px - |
| 66 | widget.poemPanelHeight, | 78 | widget.poemPanelHeight, |
| 67 | width: double.infinity, | 79 | width: double.infinity, |
| 68 | decoration: BoxDecoration( | 80 | decoration: BoxDecoration( |
| ... | @@ -81,39 +93,37 @@ class _PoemRecordAudioPageState extends State<PoemRecordAudioPage> { | ... | @@ -81,39 +93,37 @@ class _PoemRecordAudioPageState extends State<PoemRecordAudioPage> { |
| 81 | color: Colors.grey.shade200.withOpacity(0.1), | 93 | color: Colors.grey.shade200.withOpacity(0.1), |
| 82 | ), | 94 | ), |
| 83 | child: Padding( | 95 | child: Padding( |
| 84 | - padding: const EdgeInsets.all(10.0), | 96 | + padding: EdgeInsets.all(10.px), |
| 85 | child: Flex( | 97 | child: Flex( |
| 86 | direction: Axis.vertical, | 98 | direction: Axis.vertical, |
| 87 | children: [ | 99 | children: [ |
| 88 | - const PoemContent( | 100 | + PoemContent( |
| 89 | title: "题破山寺后禅院", | 101 | title: "题破山寺后禅院", |
| 90 | author: "常建", | 102 | author: "常建", |
| 91 | poemStr: poemStr, | 103 | poemStr: poemStr, |
| 92 | - fontSize: 22.0, | 104 | + fontSize: 20.px, |
| 93 | ), | 105 | ), |
| 94 | Stack( | 106 | Stack( |
| 95 | alignment: Alignment.center, | 107 | alignment: Alignment.center, |
| 96 | children: [ | 108 | children: [ |
| 97 | Positioned( | 109 | Positioned( |
| 98 | - left: 10.0, | 110 | + left: 10.px, |
| 99 | child: IconButton( | 111 | child: IconButton( |
| 100 | - icon: const Icon( | 112 | + icon: Icon( |
| 101 | Icons.camera_alt_outlined, | 113 | Icons.camera_alt_outlined, |
| 102 | - size: 28.0, | 114 | + size: 28.px, |
| 103 | ), | 115 | ), |
| 104 | onPressed: () {}, | 116 | onPressed: () {}, |
| 105 | ), | 117 | ), |
| 106 | ), | 118 | ), |
| 107 | SizedBox( | 119 | SizedBox( |
| 108 | width: double.infinity, | 120 | width: double.infinity, |
| 109 | - height: 90.0, | 121 | + height: 80.px, |
| 110 | - child: IconButton( | 122 | + child: PoemVoiceWidget( |
| 111 | - icon: const Icon( | 123 | + startRecord: startRecord, |
| 112 | - Icons.mic_none, | 124 | + stopRecord: stopRecord, |
| 113 | - size: 70.0, | 125 | + // 加入定制化Container的相关属性 |
| 114 | - color: Colors.green, | 126 | + height: 40.px, |
| 115 | - ), | ||
| 116 | - onPressed: () {}, | ||
| 117 | ), | 127 | ), |
| 118 | ), | 128 | ), |
| 119 | ], | 129 | ], | ... | ... |
| 1 | import 'package:flutter/cupertino.dart'; | 1 | import 'package:flutter/cupertino.dart'; |
| 2 | import 'package:flutter/material.dart'; | 2 | import 'package:flutter/material.dart'; |
| 3 | 3 | ||
| 4 | +import 'package:one_poem/extension/int_extension.dart'; | ||
| 5 | + | ||
| 4 | class PoemUserAudio extends StatelessWidget { | 6 | class PoemUserAudio extends StatelessWidget { |
| 5 | const PoemUserAudio({ | 7 | const PoemUserAudio({ |
| 6 | Key? key, | 8 | Key? key, |
| 7 | this.audio, //TODO 传入数据 | 9 | this.audio, //TODO 传入数据 |
| 8 | this.desc, | 10 | this.desc, |
| 11 | + this.poemPanelHeight = 0, | ||
| 9 | }) : super(key: key); | 12 | }) : super(key: key); |
| 10 | 13 | ||
| 11 | final List<Map<String, String>>? audio; | 14 | final List<Map<String, String>>? audio; |
| 12 | final String? desc; | 15 | final String? desc; |
| 16 | + final int poemPanelHeight; | ||
| 13 | @override | 17 | @override |
| 14 | Widget build(BuildContext context) { | 18 | Widget build(BuildContext context) { |
| 15 | return Container( | 19 | return Container( |
| 16 | - padding: const EdgeInsets.symmetric(vertical: 5.0, horizontal: 10.0), | 20 | + padding: EdgeInsets.symmetric(vertical: 5.px, horizontal: 10.px), |
| 17 | width: double.infinity, | 21 | width: double.infinity, |
| 18 | child: Column( | 22 | child: Column( |
| 19 | children: <Widget>[ | 23 | children: <Widget>[ |
| 20 | ListTile( | 24 | ListTile( |
| 21 | title: Text( | 25 | title: Text( |
| 22 | desc ?? "一大波用户朗读录制提交了“临境”", | 26 | desc ?? "一大波用户朗读录制提交了“临境”", |
| 23 | - style: const TextStyle(color: Colors.black54, fontSize: 15.0), | 27 | + style: TextStyle(color: Colors.black54, fontSize: 15.px), |
| 24 | ), | 28 | ), |
| 25 | ), | 29 | ), |
| 26 | SizedBox( | 30 | SizedBox( |
| 27 | width: double.infinity, | 31 | width: double.infinity, |
| 28 | - height: 200.0, | 32 | + height: 200.px - poemPanelHeight, |
| 29 | child: ListView.builder( | 33 | child: ListView.builder( |
| 30 | itemBuilder: (BuildContext context, int index) { | 34 | itemBuilder: (BuildContext context, int index) { |
| 31 | return Wrap( | 35 | return Wrap( |
| 32 | - spacing: 5.0, | 36 | + spacing: 5.px, |
| 33 | crossAxisAlignment: WrapCrossAlignment.center, | 37 | crossAxisAlignment: WrapCrossAlignment.center, |
| 34 | - children: const [ | 38 | + children: [ |
| 35 | Icon( | 39 | Icon( |
| 36 | Icons.play_circle_outline, | 40 | Icons.play_circle_outline, |
| 37 | - size: 16.0, | 41 | + size: 16.px, |
| 38 | color: Colors.black45, | 42 | color: Colors.black45, |
| 39 | ), | 43 | ), |
| 40 | - Text( | 44 | + const Text( |
| 41 | "普通话", | 45 | "普通话", |
| 42 | style: TextStyle(color: Colors.black45, fontSize: 16.0), | 46 | style: TextStyle(color: Colors.black45, fontSize: 16.0), |
| 43 | ) | 47 | ) | ... | ... |
lib/recorder/widgets/custom_overlay.dart
0 → 100644
| 1 | +import 'package:flutter/material.dart'; | ||
| 2 | + | ||
| 3 | +class CustomOverlay extends StatelessWidget { | ||
| 4 | + final Widget? icon; | ||
| 5 | + final BoxDecoration decoration; | ||
| 6 | + final double width; | ||
| 7 | + final double height; | ||
| 8 | + const CustomOverlay({ | ||
| 9 | + Key? key, | ||
| 10 | + this.icon, | ||
| 11 | + this.decoration = const BoxDecoration( | ||
| 12 | + color: Color(0xff77797A), | ||
| 13 | + borderRadius: BorderRadius.all(Radius.circular(20.0)), | ||
| 14 | + ), | ||
| 15 | + this.width = 160, | ||
| 16 | + this.height = 160, | ||
| 17 | + }) : super(key: key); | ||
| 18 | + | ||
| 19 | + @override | ||
| 20 | + Widget build(BuildContext context) { | ||
| 21 | + return Positioned( | ||
| 22 | + top: MediaQuery.of(context).size.height * 0.5 - width / 2, | ||
| 23 | + left: MediaQuery.of(context).size.width * 0.5 - height / 2, | ||
| 24 | + child: Material( | ||
| 25 | + type: MaterialType.transparency, | ||
| 26 | + child: Center( | ||
| 27 | + child: Opacity( | ||
| 28 | + opacity: 0.8, | ||
| 29 | + child: Container( | ||
| 30 | + width: width, | ||
| 31 | + height: height, | ||
| 32 | + decoration: decoration, | ||
| 33 | + child: icon, | ||
| 34 | + ), | ||
| 35 | + ), | ||
| 36 | + ), | ||
| 37 | + ), | ||
| 38 | + ); | ||
| 39 | + } | ||
| 40 | +} |
lib/recorder/widgets/poem_voice_widget.dart
0 → 100644
| 1 | +import 'dart:async'; | ||
| 2 | + | ||
| 3 | +import 'package:flutter/material.dart'; | ||
| 4 | +import 'package:flutter_plugin_record/flutter_plugin_record.dart'; | ||
| 5 | +import 'package:flutter_plugin_record/utils/common_toast.dart'; | ||
| 6 | + | ||
| 7 | +import 'custom_overlay.dart'; | ||
| 8 | + | ||
| 9 | +typedef StartRecord = Future Function(); | ||
| 10 | +typedef StopRecord = Future Function(); | ||
| 11 | + | ||
| 12 | +class PoemVoiceWidget extends StatefulWidget { | ||
| 13 | + final Function? startRecord; | ||
| 14 | + final Function? stopRecord; | ||
| 15 | + final double? height; | ||
| 16 | + final EdgeInsets? margin; | ||
| 17 | + final Decoration? decoration; | ||
| 18 | + | ||
| 19 | + /// startRecord 开始录制回调 stopRecord回调 | ||
| 20 | + const PoemVoiceWidget( | ||
| 21 | + {Key? key, | ||
| 22 | + this.startRecord, | ||
| 23 | + this.stopRecord, | ||
| 24 | + this.height, | ||
| 25 | + this.decoration, | ||
| 26 | + this.margin}) | ||
| 27 | + : super(key: key); | ||
| 28 | + | ||
| 29 | + @override | ||
| 30 | + _PoemVoiceWidgetState createState() => _PoemVoiceWidgetState(); | ||
| 31 | +} | ||
| 32 | + | ||
| 33 | +class _PoemVoiceWidgetState extends State<PoemVoiceWidget> { | ||
| 34 | + // 倒计时总时长 | ||
| 35 | + final int _countTotal = 12; | ||
| 36 | + double starty = 0.0; | ||
| 37 | + double offset = 0.0; | ||
| 38 | + bool isUp = false; | ||
| 39 | + String textShow = "按住说话"; | ||
| 40 | + String toastShow = "手指上滑,取消发送"; | ||
| 41 | + String voiceIco = "images/voice_volume_1.png"; | ||
| 42 | + | ||
| 43 | + ///默认隐藏状态 | ||
| 44 | + bool voiceState = true; | ||
| 45 | + FlutterPluginRecord? recordPlugin; | ||
| 46 | + Timer? _timer; | ||
| 47 | + int _count = 0; | ||
| 48 | + OverlayEntry? overlayEntry; | ||
| 49 | + | ||
| 50 | + String audioFilePath = ""; | ||
| 51 | + | ||
| 52 | + @override | ||
| 53 | + void initState() { | ||
| 54 | + super.initState(); | ||
| 55 | + recordPlugin = FlutterPluginRecord(); | ||
| 56 | + | ||
| 57 | + _init(); | ||
| 58 | + | ||
| 59 | + ///初始化方法的监听 | ||
| 60 | + recordPlugin?.responseFromInit.listen((data) { | ||
| 61 | + // if (data) { | ||
| 62 | + // print("初始化成功"); | ||
| 63 | + // } else { | ||
| 64 | + // print("初始化失败"); | ||
| 65 | + // } | ||
| 66 | + }); | ||
| 67 | + | ||
| 68 | + /// 开始录制或结束录制的监听 | ||
| 69 | + recordPlugin?.response.listen((data) { | ||
| 70 | + if (data.msg == "onStop") { | ||
| 71 | + ///结束录制时会返回录制文件的地址方便上传服务器 | ||
| 72 | + // print("onStop " + data.path!); | ||
| 73 | + if (widget.stopRecord != null) { | ||
| 74 | + audioFilePath = data.path!; | ||
| 75 | + widget.stopRecord!(data.path, data.audioTimeLength); | ||
| 76 | + } | ||
| 77 | + } else if (data.msg == "onStart") { | ||
| 78 | + if (widget.startRecord != null) widget.startRecord!(); | ||
| 79 | + } | ||
| 80 | + }); | ||
| 81 | + | ||
| 82 | + ///录制过程监听录制的声音的大小 方便做语音动画显示图片的样式 | ||
| 83 | + recordPlugin!.responseFromAmplitude.listen((data) { | ||
| 84 | + var voiceData = double.parse(data.msg ?? ''); | ||
| 85 | + setState(() { | ||
| 86 | + if (voiceData > 0 && voiceData < 0.1) { | ||
| 87 | + voiceIco = "images/voice_volume_2.png"; | ||
| 88 | + } else if (voiceData > 0.2 && voiceData < 0.3) { | ||
| 89 | + voiceIco = "images/voice_volume_3.png"; | ||
| 90 | + } else if (voiceData > 0.3 && voiceData < 0.4) { | ||
| 91 | + voiceIco = "images/voice_volume_4.png"; | ||
| 92 | + } else if (voiceData > 0.4 && voiceData < 0.5) { | ||
| 93 | + voiceIco = "images/voice_volume_5.png"; | ||
| 94 | + } else if (voiceData > 0.5 && voiceData < 0.6) { | ||
| 95 | + voiceIco = "images/voice_volume_6.png"; | ||
| 96 | + } else if (voiceData > 0.6 && voiceData < 0.7) { | ||
| 97 | + voiceIco = "images/voice_volume_7.png"; | ||
| 98 | + } else if (voiceData > 0.7 && voiceData < 1) { | ||
| 99 | + voiceIco = "images/voice_volume_7.png"; | ||
| 100 | + } else { | ||
| 101 | + voiceIco = "images/voice_volume_1.png"; | ||
| 102 | + } | ||
| 103 | + if (overlayEntry != null) { | ||
| 104 | + overlayEntry!.markNeedsBuild(); | ||
| 105 | + } | ||
| 106 | + }); | ||
| 107 | + | ||
| 108 | + // print("振幅大小 " + voiceData.toString() + " " + voiceIco); | ||
| 109 | + }); | ||
| 110 | + } | ||
| 111 | + | ||
| 112 | + ///显示录音悬浮布局 | ||
| 113 | + buildOverLayView(BuildContext context) { | ||
| 114 | + if (overlayEntry == null) { | ||
| 115 | + overlayEntry = OverlayEntry(builder: (content) { | ||
| 116 | + return CustomOverlay( | ||
| 117 | + icon: Column( | ||
| 118 | + children: <Widget>[ | ||
| 119 | + Container( | ||
| 120 | + margin: const EdgeInsets.only(top: 10), | ||
| 121 | + child: _countTotal - _count < 11 | ||
| 122 | + ? Center( | ||
| 123 | + child: Padding( | ||
| 124 | + padding: const EdgeInsets.only(bottom: 15.0), | ||
| 125 | + child: Text( | ||
| 126 | + (_countTotal - _count).toString(), | ||
| 127 | + style: const TextStyle( | ||
| 128 | + fontSize: 70.0, | ||
| 129 | + color: Colors.white, | ||
| 130 | + ), | ||
| 131 | + ), | ||
| 132 | + ), | ||
| 133 | + ) | ||
| 134 | + : Image.asset( | ||
| 135 | + voiceIco, | ||
| 136 | + width: 100, | ||
| 137 | + height: 100, | ||
| 138 | + package: 'flutter_plugin_record', | ||
| 139 | + ), | ||
| 140 | + ), | ||
| 141 | + Text( | ||
| 142 | + toastShow, | ||
| 143 | + style: const TextStyle( | ||
| 144 | + fontStyle: FontStyle.normal, | ||
| 145 | + color: Colors.white, | ||
| 146 | + fontSize: 14, | ||
| 147 | + ), | ||
| 148 | + ) | ||
| 149 | + ], | ||
| 150 | + ), | ||
| 151 | + ); | ||
| 152 | + }); | ||
| 153 | + Overlay.of(context)!.insert(overlayEntry!); | ||
| 154 | + } | ||
| 155 | + } | ||
| 156 | + | ||
| 157 | + showVoiceView() { | ||
| 158 | + setState(() { | ||
| 159 | + textShow = "松开结束"; | ||
| 160 | + voiceState = false; | ||
| 161 | + }); | ||
| 162 | + | ||
| 163 | + ///显示录音悬浮布局 | ||
| 164 | + buildOverLayView(context); | ||
| 165 | + | ||
| 166 | + start(); | ||
| 167 | + } | ||
| 168 | + | ||
| 169 | + hideVoiceView() { | ||
| 170 | + if (_timer!.isActive) { | ||
| 171 | + if (_count < 1) { | ||
| 172 | + CommonToast.showView( | ||
| 173 | + context: context, | ||
| 174 | + msg: '说话时间太短', | ||
| 175 | + icon: const Text( | ||
| 176 | + '!', | ||
| 177 | + style: TextStyle(fontSize: 80, color: Colors.white), | ||
| 178 | + )); | ||
| 179 | + isUp = true; | ||
| 180 | + } | ||
| 181 | + _timer?.cancel(); | ||
| 182 | + _count = 0; | ||
| 183 | + } | ||
| 184 | + | ||
| 185 | + setState(() { | ||
| 186 | + textShow = "按住说话"; | ||
| 187 | + voiceState = true; | ||
| 188 | + }); | ||
| 189 | + | ||
| 190 | + stop(); | ||
| 191 | + if (overlayEntry != null) { | ||
| 192 | + overlayEntry?.remove(); | ||
| 193 | + overlayEntry = null; | ||
| 194 | + } | ||
| 195 | + | ||
| 196 | + // if (isUp) { | ||
| 197 | + // print("取消发送"); | ||
| 198 | + // } else { | ||
| 199 | + // print("进行发送"); | ||
| 200 | + // } | ||
| 201 | + } | ||
| 202 | + | ||
| 203 | + moveVoiceView() { | ||
| 204 | + setState(() { | ||
| 205 | + isUp = starty - offset > 100 ? true : false; | ||
| 206 | + if (isUp) { | ||
| 207 | + textShow = "松开手指,取消发送"; | ||
| 208 | + toastShow = textShow; | ||
| 209 | + } else { | ||
| 210 | + textShow = "松开结束"; | ||
| 211 | + toastShow = "手指上滑,取消发送"; | ||
| 212 | + } | ||
| 213 | + }); | ||
| 214 | + } | ||
| 215 | + | ||
| 216 | + ///初始化语音录制的方法 | ||
| 217 | + void _init() async { | ||
| 218 | + recordPlugin?.initRecordMp3(); | ||
| 219 | + } | ||
| 220 | + | ||
| 221 | + ///开始语音录制的方法 | ||
| 222 | + void start() async { | ||
| 223 | + recordPlugin?.start(); | ||
| 224 | + } | ||
| 225 | + | ||
| 226 | + ///停止语音录制的方法 | ||
| 227 | + void stop() { | ||
| 228 | + recordPlugin?.stop(); | ||
| 229 | + } | ||
| 230 | + | ||
| 231 | + @override | ||
| 232 | + Widget build(BuildContext context) { | ||
| 233 | + return Padding( | ||
| 234 | + padding: const EdgeInsets.only(right: 10.0), | ||
| 235 | + child: Row( | ||
| 236 | + crossAxisAlignment: CrossAxisAlignment.center, | ||
| 237 | + mainAxisAlignment: MainAxisAlignment.end, | ||
| 238 | + children: [ | ||
| 239 | + GestureDetector( | ||
| 240 | + onLongPressStart: (details) { | ||
| 241 | + starty = details.globalPosition.dy; | ||
| 242 | + _timer = Timer.periodic(const Duration(milliseconds: 1000), (t) { | ||
| 243 | + _count++; | ||
| 244 | + if (_count == _countTotal) { | ||
| 245 | + hideVoiceView(); | ||
| 246 | + } | ||
| 247 | + }); | ||
| 248 | + showVoiceView(); | ||
| 249 | + }, | ||
| 250 | + onLongPressEnd: (details) { | ||
| 251 | + hideVoiceView(); | ||
| 252 | + }, | ||
| 253 | + onLongPressMoveUpdate: (details) { | ||
| 254 | + offset = details.globalPosition.dy; | ||
| 255 | + moveVoiceView(); | ||
| 256 | + }, | ||
| 257 | + child: Container( | ||
| 258 | + height: widget.height ?? 60, | ||
| 259 | + margin: widget.margin ?? const EdgeInsets.fromLTRB(50, 0, 50, 20), | ||
| 260 | + child: const Icon( | ||
| 261 | + Icons.mic_none, | ||
| 262 | + size: 70.0, | ||
| 263 | + color: Colors.green, | ||
| 264 | + ), | ||
| 265 | + ), | ||
| 266 | + ), | ||
| 267 | + IconButton( | ||
| 268 | + icon: const Icon( | ||
| 269 | + Icons.play_circle_outline, | ||
| 270 | + size: 28.0, | ||
| 271 | + ), | ||
| 272 | + onPressed: () { | ||
| 273 | + print("######:" + audioFilePath); | ||
| 274 | + recordPlugin!.playByPath(audioFilePath, "file"); | ||
| 275 | + }, | ||
| 276 | + ), | ||
| 277 | + ], | ||
| 278 | + ), | ||
| 279 | + ); | ||
| 280 | + } | ||
| 281 | + | ||
| 282 | + @override | ||
| 283 | + void dispose() { | ||
| 284 | + recordPlugin?.dispose(); | ||
| 285 | + _timer?.cancel(); | ||
| 286 | + super.dispose(); | ||
| 287 | + } | ||
| 288 | +} |
| ... | @@ -366,6 +366,13 @@ packages: | ... | @@ -366,6 +366,13 @@ packages: |
| 366 | url: "https://pub.dartlang.org" | 366 | url: "https://pub.dartlang.org" |
| 367 | source: hosted | 367 | source: hosted |
| 368 | version: "2.0.5" | 368 | version: "2.0.5" |
| 369 | + flutter_plugin_record: | ||
| 370 | + dependency: "direct main" | ||
| 371 | + description: | ||
| 372 | + name: flutter_plugin_record | ||
| 373 | + url: "https://pub.dartlang.org" | ||
| 374 | + source: hosted | ||
| 375 | + version: "1.0.1" | ||
| 369 | flutter_slidable: | 376 | flutter_slidable: |
| 370 | dependency: "direct main" | 377 | dependency: "direct main" |
| 371 | description: | 378 | description: | ... | ... |
| ... | @@ -91,6 +91,7 @@ dependencies: | ... | @@ -91,6 +91,7 @@ dependencies: |
| 91 | flutter_spinkit: ^5.0.0 | 91 | flutter_spinkit: ^5.0.0 |
| 92 | 92 | ||
| 93 | json_annotation: ^4.4.0 | 93 | json_annotation: ^4.4.0 |
| 94 | + flutter_plugin_record: ^1.0.1 | ||
| 94 | 95 | ||
| 95 | dependency_overrides: | 96 | dependency_overrides: |
| 96 | decimal: 1.5.0 | 97 | decimal: 1.5.0 | ... | ... |
-
Please register or login to post a comment