Reason Pun

重构了录音页面功能

1 import 'package:flutter/material.dart'; 1 import 'package:flutter/material.dart';
2 -import 'package:flutter/cupertino.dart';
3 -
4 import 'package:one_poem/extension/int_extension.dart'; 2 import 'package:one_poem/extension/int_extension.dart';
5 import 'package:one_poem/widgets/my_app_bar.dart'; 3 import 'package:one_poem/widgets/my_app_bar.dart';
6 import 'package:flutter_gen/gen_l10n/one_poem_localizations.dart'; 4 import 'package:flutter_gen/gen_l10n/one_poem_localizations.dart';
......
1 import 'dart:ui'; 1 import 'dart:ui';
2 2
3 -import 'package:flutter/cupertino.dart';
4 import 'package:flutter/material.dart'; 3 import 'package:flutter/material.dart';
5 import 'package:one_poem/category/models/category_item_entity.dart'; 4 import 'package:one_poem/category/models/category_item_entity.dart';
6 import 'package:one_poem/poem/poem_router.dart'; 5 import 'package:one_poem/poem/poem_router.dart';
......
...@@ -4,11 +4,8 @@ ...@@ -4,11 +4,8 @@
4 4
5 // This file is automatically generated. DO NOT EDIT, all your changes would be lost. 5 // This file is automatically generated. DO NOT EDIT, all your changes would be lost.
6 import 'package:one_poem/account/models/user_entity.dart'; 6 import 'package:one_poem/account/models/user_entity.dart';
7 -import 'package:one_poem/generated/json/user_entity.g.dart';
8 import 'package:one_poem/category/models/category_item_entity.dart'; 7 import 'package:one_poem/category/models/category_item_entity.dart';
9 -import 'package:one_poem/generated/json/category_item_entity.g.dart';
10 import 'package:one_poem/timeline/models/friend_entity.dart'; 8 import 'package:one_poem/timeline/models/friend_entity.dart';
11 -import 'package:one_poem/generated/json/friend_entity.g.dart';
12 9
13 JsonConvert jsonConvert = JsonConvert(); 10 JsonConvert jsonConvert = JsonConvert();
14 11
......
...@@ -71,7 +71,9 @@ class _LoginPageState extends State<LoginPage> ...@@ -71,7 +71,9 @@ class _LoginPageState extends State<LoginPage>
71 "其他错误" 71 "其他错误"
72 ]; 72 ];
73 73
74 - Future.delayed(Duration.zero, () { 74 + Future.delayed(
75 + Duration.zero,
76 + () {
75 NavigatorUtils.pushPageByFade( 77 NavigatorUtils.pushPageByFade(
76 context: context, 78 context: context,
77 //目标页面 79 //目标页面
...@@ -86,8 +88,10 @@ class _LoginPageState extends State<LoginPage> ...@@ -86,8 +88,10 @@ class _LoginPageState extends State<LoginPage>
86 //权限申请结果 88 //权限申请结果
87 dismissCallBack: (value) { 89 dismissCallBack: (value) {
88 showPrivacyPage(); 90 showPrivacyPage();
89 - }); 91 + },
90 - }); 92 + );
93 + },
94 + );
91 } 95 }
92 96
93 void showPrivacyPage() async { 97 void showPrivacyPage() async {
......
...@@ -2,18 +2,24 @@ import 'dart:ui'; ...@@ -2,18 +2,24 @@ import 'dart:ui';
2 2
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:flutter_sound/flutter_sound.dart';
5 import 'package:one_poem/poem/widgets/poem_content.dart'; 6 import 'package:one_poem/poem/widgets/poem_content.dart';
6 -import 'package:one_poem/recorder/audio/widgets/poem_voice_widget.dart';
7 import 'package:one_poem/routers/fluro_navigator.dart'; 7 import 'package:one_poem/routers/fluro_navigator.dart';
8 -import 'package:one_poem/util/toast_utils.dart';
9 import 'package:one_poem/widgets/bars/home_action_bar.dart'; 8 import 'package:one_poem/widgets/bars/home_action_bar.dart';
10 import 'package:one_poem/widgets/bars/home_menu_bar.dart'; 9 import 'package:one_poem/widgets/bars/home_menu_bar.dart';
11 import 'package:one_poem/widgets/my_app_bar.dart'; 10 import 'package:one_poem/widgets/my_app_bar.dart';
12 11
13 import 'package:one_poem/extension/int_extension.dart'; 12 import 'package:one_poem/extension/int_extension.dart';
13 +import 'package:path_provider/path_provider.dart';
14 +import 'package:pausable_timer/pausable_timer.dart';
15 +import 'package:flutter_sound_platform_interface/flutter_sound_recorder_platform_interface.dart';
16 +import 'package:flutter/foundation.dart' show kIsWeb;
17 +import 'package:permission_handler/permission_handler.dart';
14 18
15 import '../poem_router.dart'; 19 import '../poem_router.dart';
16 20
21 +const theSource = AudioSource.microphone;
22 +
17 class PoemRecordAudioPage extends StatefulWidget { 23 class PoemRecordAudioPage extends StatefulWidget {
18 @override 24 @override
19 State<StatefulWidget> createState() => _PoemRecordAudioPageState(); 25 State<StatefulWidget> createState() => _PoemRecordAudioPageState();
...@@ -29,20 +35,33 @@ class PoemRecordAudioPage extends StatefulWidget { ...@@ -29,20 +35,33 @@ class PoemRecordAudioPage extends StatefulWidget {
29 } 35 }
30 36
31 class _PoemRecordAudioPageState extends State<PoemRecordAudioPage> { 37 class _PoemRecordAudioPageState extends State<PoemRecordAudioPage> {
32 - startRecord() { 38 + bool _isGetPoemInProgress = false;
33 - print("开始录制"); 39 + String poemStr = '';
40 +
41 + @override
42 + void initState() {
43 + super.initState();
44 + getPoem();
34 } 45 }
35 46
36 - stopRecord(String path, double audioTimeLength) { 47 + Future<void> getPoem() async {
37 - print("结束束录制"); 48 + // TODO 等待套入正式接口发布临境
38 - print("音频文件位置" + path); 49 + _isGetPoemInProgress = true;
39 - print("音频录制时长" + audioTimeLength.toString()); 50 + await Future.delayed(const Duration(seconds: 2), () {
51 + poemStr =
52 + "qīng chén rù gǔ sì\n清晨入古寺,\nchū rì zhào gāo lín\n初日照高林。\nzhú jìng tōng yōu chù\n竹径通幽处,\nchán fáng huā mù shēn\n禅房花木深。\nshān guāng yuè niǎo xìng\n山光悦鸟性,\ntán yǐng kōng rén xīn\n潭影空人心。\nwàn lài cǐ dōu jì\n万籁此都寂,\ndàn yú zhōng qìng yīn\n但余钟磬音。";
53 + _isGetPoemInProgress = false;
54 + setState(() {});
55 + });
56 + }
57 +
58 + @override
59 + void dispose() {
60 + super.dispose();
40 } 61 }
41 62
42 @override 63 @override
43 Widget build(BuildContext context) { 64 Widget build(BuildContext context) {
44 - const poemStr =
45 - "qīng chén rù gǔ sì\n清晨入古寺,\nchū rì zhào gāo lín\n初日照高林。\nzhú jìng tōng yōu chù\n竹径通幽处,\nchán fáng huā mù shēn\n禅房花木深。\nshān guāng yuè niǎo xìng\n山光悦鸟性,\ntán yǐng kōng rén xīn\n潭影空人心。\nwàn lài cǐ dōu jì\n万籁此都寂,\ndàn yú zhōng qìng yīn\n但余钟磬音。";
46 return Scaffold( 65 return Scaffold(
47 appBar: MyAppBar( 66 appBar: MyAppBar(
48 isBack: true, 67 isBack: true,
...@@ -71,20 +90,28 @@ class _PoemRecordAudioPageState extends State<PoemRecordAudioPage> { ...@@ -71,20 +90,28 @@ class _PoemRecordAudioPageState extends State<PoemRecordAudioPage> {
71 ), 90 ),
72 ), 91 ),
73 child: SafeArea( 92 child: SafeArea(
74 - child: Column( 93 + child: _isGetPoemInProgress
94 + ? const Center(
95 + child: CupertinoActivityIndicator(
96 + radius: 16.0,
97 + ),
98 + )
99 + : Column(
75 crossAxisAlignment: CrossAxisAlignment.start, 100 crossAxisAlignment: CrossAxisAlignment.start,
76 children: [ 101 children: [
77 Container( 102 Container(
78 - margin: 103 + margin: EdgeInsets.symmetric(
79 - EdgeInsets.symmetric(vertical: 20.px, horizontal: 20.px), 104 + vertical: 20.px, horizontal: 20.px),
80 height: MediaQuery.of(context).size.height - 105 height: MediaQuery.of(context).size.height -
81 - 125.px - 106 + 100.px -
82 widget.poemPanelHeight, 107 widget.poemPanelHeight,
83 width: double.infinity, 108 width: double.infinity,
84 decoration: BoxDecoration( 109 decoration: BoxDecoration(
85 color: Colors.grey.shade200.withOpacity(0.1), 110 color: Colors.grey.shade200.withOpacity(0.1),
86 border: Border.all( 111 border: Border.all(
87 - color: Colors.grey.shade50, width: 0.5), // 边色与边宽度 112 + color: Colors.grey.shade50,
113 + width: 0.5,
114 + ), // 边色与边宽度
88 ), 115 ),
89 child: ClipRect( 116 child: ClipRect(
90 child: BackdropFilter( 117 child: BackdropFilter(
...@@ -108,68 +135,303 @@ class _PoemRecordAudioPageState extends State<PoemRecordAudioPage> { ...@@ -108,68 +135,303 @@ class _PoemRecordAudioPageState extends State<PoemRecordAudioPage> {
108 poemStr: poemStr, 135 poemStr: poemStr,
109 fontSize: 22.px, 136 fontSize: 22.px,
110 ), 137 ),
111 - Stack( 138 + const AudioToolBar(),
112 - alignment: Alignment.center, 139 + ],
113 - children: [ 140 + ),
114 - Positioned( 141 + ),
115 - left: 10.px,
116 - child: IconButton(
117 - icon: Icon(
118 - Icons.camera_alt_outlined,
119 - size: 28.px,
120 ), 142 ),
121 - onPressed: () {
122 - Toast.show("不要着急吖,正在开发ing....");
123 - },
124 ), 143 ),
125 ), 144 ),
126 - SizedBox(
127 - width: double.infinity,
128 - height: 80.px,
129 - child: PoemVoiceWidget(
130 - startRecord: startRecord,
131 - stopRecord: stopRecord,
132 - // 加入定制化Container的相关属性
133 - height: 40.px,
134 ), 145 ),
135 ), 146 ),
136 ], 147 ],
137 ), 148 ),
138 - Container(
139 - padding: const EdgeInsets.all(10.0),
140 - alignment: Alignment.centerRight,
141 - height: 54.0,
142 - width: double.infinity,
143 - child: TextButton(
144 - style: ButtonStyle(
145 - side: MaterialStateProperty.all(
146 - const BorderSide(
147 - color: Colors.black54, width: 1),
148 ), 149 ),
149 ), 150 ),
150 - onPressed: () { 151 + );
152 + }
153 +}
154 +
155 +class AudioToolBar extends StatefulWidget {
156 + const AudioToolBar({
157 + Key? key,
158 + }) : super(key: key);
159 +
160 + @override
161 + _AudioToolBarState createState() => _AudioToolBarState();
162 +}
163 +
164 +class _AudioToolBarState extends State<AudioToolBar> {
165 + late final PausableTimer _timer;
166 + int currentTimer = 0;
167 + int duration = 10 * 1000; //TODO 60 * 1000;
168 +
169 + Codec _codec = Codec.aacMP4;
170 + String _mPath = 'tau_file.mp4';
171 + FlutterSoundPlayer? _mPlayer = FlutterSoundPlayer();
172 + FlutterSoundRecorder? _mRecorder = FlutterSoundRecorder();
173 + bool _mPlayerIsInited = false;
174 + bool _mRecorderIsInited = false;
175 + bool _mPlaybackReady = false;
176 + bool _mRecorderIsRecording = false;
177 + bool _mRecorderIsPaused = false;
178 +
179 + @override
180 + void initState() {
181 + super.initState();
182 +
183 + _mPlayer!.openAudioSession().then((value) {
184 + setState(() {
185 + _mPlayerIsInited = true;
186 + });
187 + });
188 +
189 + openTheRecorder().then((value) {
190 + setState(() {
191 + _mRecorderIsInited = true;
192 + });
193 + });
194 +
195 + _timer = PausableTimer(
196 + const Duration(milliseconds: 100),
197 + () {
198 + currentTimer += 100;
199 + _timer
200 + ..reset()
201 + ..start();
202 + if (currentTimer >= duration) {
203 + _mRecorderIsRecording = false;
204 + stopRecorder();
205 + }
206 + setState(() {});
207 + },
208 + );
209 + }
210 +
211 + @override
212 + void dispose() {
213 + _mPlayer!.closeAudioSession();
214 + _mPlayer = null;
215 +
216 + _mRecorder!.closeAudioSession();
217 + _mRecorder = null;
218 + super.dispose();
219 + }
220 +
221 + Future<void> openTheRecorder() async {
222 + if (!kIsWeb) {
223 + var status = await Permission.microphone.request();
224 + if (status != PermissionStatus.granted) {
225 + //TODO 弹出授权提示框
226 + throw RecordingPermissionException('Microphone permission not granted');
227 + }
228 + }
229 + await _mRecorder!.openAudioSession();
230 + if (!await _mRecorder!.isEncoderSupported(_codec) && kIsWeb) {
231 + final directory = await getApplicationDocumentsDirectory();
232 + int currentUnix = DateTime.now().millisecondsSinceEpoch;
233 + _codec = Codec.opusWebM; //TODO 音频保存格式,mp3?
234 + _mPath = '${directory.path}/$currentUnix.webm';
235 + if (!await _mRecorder!.isEncoderSupported(_codec) && kIsWeb) {
236 + _mRecorderIsInited = true;
237 + return;
238 + }
239 + }
240 + _mRecorderIsInited = true;
241 + }
242 +
243 + void record() {
244 + if (_mRecorderIsInited && _mPlayer!.isStopped) {
245 + currentTimer = 0;
246 + _timer
247 + ..reset()
248 + ..start();
249 + _mRecorderIsRecording = true;
250 + _mRecorder!
251 + .startRecorder(
252 + toFile: _mPath,
253 + codec: _codec,
254 + audioSource: theSource,
255 + )
256 + .then((value) {
257 + setState(() {});
258 + });
259 + }
260 + }
261 +
262 + void pauseRecorder() async {
263 + if (_mRecorderIsInited && _mPlayer!.isStopped) {
264 + _timer.pause();
265 + _mRecorderIsPaused = true;
266 + await _mRecorder!.pauseRecorder().then((value) {
267 + setState(() {});
268 + });
269 + }
270 + }
271 +
272 + void resumeRecorder() async {
273 + if (_mRecorderIsInited && _mPlayer!.isStopped) {
274 + _timer.start();
275 + await _mRecorder!.resumeRecorder().then((value) {
276 + _mRecorderIsPaused = false;
277 + setState(() {});
278 + });
279 + }
280 + }
281 +
282 + void stopRecorder() async {
283 + if (_mRecorderIsInited && _mPlayer!.isStopped) {
284 + print("### stop record");
285 +
286 + _timer.pause();
287 + await _mRecorder!.stopRecorder().then((value) {
288 + _mRecorderIsRecording = false;
289 + setState(() {
290 + _mPlaybackReady = true;
291 + });
292 + });
293 + }
294 + }
295 +
296 + void play() {
297 + if (_mPlayerIsInited && _mPlaybackReady && _mRecorder!.isStopped) {
298 + _mPlayer!
299 + .startPlayer(
300 + fromURI: _mPath,
301 + whenFinished: () {
302 + setState(() {});
303 + })
304 + .then((value) {
305 + setState(() {});
306 + });
307 + }
308 + }
309 +
310 + void stopPlayer() {
311 + if (_mPlayerIsInited && _mPlaybackReady && _mRecorder!.isStopped) {
312 + _mPlayer!.stopPlayer().then((value) {
313 + setState(() {});
314 + });
315 + }
316 + }
317 +
318 + @override
319 + Widget build(BuildContext context) {
320 + return Padding(
321 + padding: EdgeInsets.fromLTRB(
322 + 16.px,
323 + 8.px,
324 + 16.px,
325 + 8.px,
326 + ),
327 + child: Row(
328 + mainAxisAlignment: MainAxisAlignment.spaceBetween,
329 + crossAxisAlignment: CrossAxisAlignment.end,
330 + children: [
331 + InkWell(
332 + onTap: () {
333 + if (_mRecorderIsRecording) {
334 + _mRecorderIsPaused ? resumeRecorder() : pauseRecorder();
335 + } else {
151 NavigatorUtils.push( 336 NavigatorUtils.push(
152 context, 337 context,
153 - '${PoemRouter.poemPublish}?data=100', 338 + '${PoemRouter.poemRecordVideoPage}?id=100',
154 ); 339 );
340 + }
155 }, 341 },
156 - child: const Text( 342 + child: Stack(
157 - "下一步", 343 + alignment: Alignment.center,
158 - style: TextStyle(color: Colors.white), 344 + children: [
159 - ), 345 + Icon(
346 + Icons.circle,
347 + color: Colors.black38,
348 + size: 60.px,
160 ), 349 ),
350 + _mRecorderIsRecording
351 + ? _mRecorderIsPaused
352 + ? Icon(
353 + Icons.play_arrow,
354 + color: Colors.white,
355 + size: 30.px,
356 + )
357 + : Icon(
358 + Icons.pause,
359 + color: Colors.white,
360 + size: 30.px,
361 + )
362 + : Icon(
363 + Icons.camera_alt_outlined,
364 + color: Colors.white,
365 + size: 30.px,
161 ), 366 ),
162 ], 367 ],
163 ), 368 ),
164 ), 369 ),
370 + InkWell(
371 + onTap: () {
372 + _mRecorderIsRecording ? stopRecorder() : record();
373 + },
374 + child: Stack(
375 + alignment: Alignment.center,
376 + children: [
377 + Icon(
378 + Icons.circle,
379 + color: Colors.white,
380 + size: 80.px,
381 + ),
382 + _mRecorderIsRecording
383 + ? SizedBox(
384 + width: 60.px,
385 + height: 60.px,
386 + child: CircularProgressIndicator(
387 + strokeWidth: 5.px,
388 + value: currentTimer / duration,
389 + ),
390 + )
391 + : Container(),
392 + Icon(
393 + Icons.circle,
394 + color: Colors.red,
395 + size: 65.px,
165 ), 396 ),
397 + _mRecorderIsRecording
398 + ? Container()
399 + : Text(
400 + "60s",
401 + style: TextStyle(
402 + fontSize: 12.px,
403 + color: Colors.white,
166 ), 404 ),
167 ), 405 ),
406 + _mRecorderIsRecording
407 + ? Icon(
408 + Icons.stop_rounded,
409 + color: Colors.white,
410 + size: 32.px,
411 + )
412 + : Container(),
413 + ],
414 + ),
415 + ),
416 + InkWell(
417 + onTap: () {},
418 + child: Stack(
419 + alignment: Alignment.center,
420 + children: [
421 + Icon(
422 + Icons.circle,
423 + color: Colors.black38,
424 + size: 60.px,
168 ), 425 ),
426 + Icon(
427 + Icons.arrow_right_alt,
428 + color: Colors.white,
429 + size: 30.px,
169 ), 430 ),
170 ], 431 ],
171 ), 432 ),
172 ), 433 ),
434 + ],
173 ), 435 ),
174 ); 436 );
175 } 437 }
......
...@@ -42,10 +42,7 @@ class _PoemRecordVideoPageState extends State<PoemRecordVideoPage> ...@@ -42,10 +42,7 @@ class _PoemRecordVideoPageState extends State<PoemRecordVideoPage>
42 42
43 List<CameraDescription>? cameras = []; 43 List<CameraDescription>? cameras = [];
44 44
45 - ///声明变量
46 late final PausableTimer _timer; 45 late final PausableTimer _timer;
47 -
48 - ///记录当前的时间
49 int currentTimer = 0; 46 int currentTimer = 0;
50 int duration = 10 * 1000; //TODO 60 * 1000; 47 int duration = 10 * 1000; //TODO 60 * 1000;
51 48
......
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 -}
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 -import 'package:one_poem/extension/int_extension.dart';
10 -
11 -typedef StartRecord = Future Function();
12 -typedef StopRecord = Future Function();
13 -
14 -class PoemVoiceWidget extends StatefulWidget {
15 - final Function? startRecord;
16 - final Function? stopRecord;
17 - final double? height;
18 - final EdgeInsets? margin;
19 - final Decoration? decoration;
20 -
21 - /// startRecord 开始录制回调 stopRecord回调
22 - const PoemVoiceWidget(
23 - {Key? key,
24 - this.startRecord,
25 - this.stopRecord,
26 - this.height,
27 - this.decoration,
28 - this.margin})
29 - : super(key: key);
30 -
31 - @override
32 - _PoemVoiceWidgetState createState() => _PoemVoiceWidgetState();
33 -}
34 -
35 -class _PoemVoiceWidgetState extends State<PoemVoiceWidget> {
36 - // 倒计时总时长
37 - final int _countTotal = 12;
38 - double startY = 0.0;
39 - double offset = 0.0;
40 - bool isUp = false;
41 - String textShow = "按住说话";
42 - String toastShow = "手指上滑,取消发送";
43 - String voiceIco = "images/voice_volume_1.png";
44 -
45 - ///默认隐藏状态
46 - bool voiceState = true;
47 - FlutterPluginRecord? recordPlugin;
48 - Timer? _timer;
49 - int _count = 0;
50 - OverlayEntry? overlayEntry;
51 -
52 - String audioFilePath = "";
53 -
54 - @override
55 - void initState() {
56 - super.initState();
57 - recordPlugin = FlutterPluginRecord();
58 -
59 - _init();
60 -
61 - ///初始化方法的监听
62 - recordPlugin?.responseFromInit.listen((data) {
63 - // if (data) {
64 - // print("初始化成功");
65 - // } else {
66 - // print("初始化失败");
67 - // }
68 - });
69 -
70 - /// 开始录制或结束录制的监听
71 - recordPlugin?.response.listen((data) {
72 - if (data.msg == "onStop") {
73 - ///结束录制时会返回录制文件的地址方便上传服务器
74 - if (widget.stopRecord != null) {
75 - audioFilePath = data.path!;
76 - widget.stopRecord!(data.path, data.audioTimeLength);
77 - }
78 - } else if (data.msg == "onStart") {
79 - if (widget.startRecord != null) widget.startRecord!();
80 - }
81 - });
82 -
83 - ///录制过程监听录制的声音的大小 方便做语音动画显示图片的样式
84 - recordPlugin!.responseFromAmplitude.listen((data) {
85 - var voiceData = double.parse(data.msg ?? '');
86 - setState(() {
87 - if (voiceData > 0 && voiceData < 0.1) {
88 - voiceIco = "images/voice_volume_2.png";
89 - } else if (voiceData > 0.2 && voiceData < 0.3) {
90 - voiceIco = "images/voice_volume_3.png";
91 - } else if (voiceData > 0.3 && voiceData < 0.4) {
92 - voiceIco = "images/voice_volume_4.png";
93 - } else if (voiceData > 0.4 && voiceData < 0.5) {
94 - voiceIco = "images/voice_volume_5.png";
95 - } else if (voiceData > 0.5 && voiceData < 0.6) {
96 - voiceIco = "images/voice_volume_6.png";
97 - } else if (voiceData > 0.6 && voiceData < 0.7) {
98 - voiceIco = "images/voice_volume_7.png";
99 - } else if (voiceData > 0.7 && voiceData < 1) {
100 - voiceIco = "images/voice_volume_7.png";
101 - } else {
102 - voiceIco = "images/voice_volume_1.png";
103 - }
104 - if (overlayEntry != null) {
105 - overlayEntry!.markNeedsBuild();
106 - }
107 - });
108 - });
109 - }
110 -
111 - ///显示录音悬浮布局
112 - buildOverLayView(BuildContext context) {
113 - if (overlayEntry == null) {
114 - overlayEntry = OverlayEntry(builder: (content) {
115 - return CustomOverlay(
116 - icon: Column(
117 - children: <Widget>[
118 - Container(
119 - margin: EdgeInsets.only(top: 10.px),
120 - child: _countTotal - _count < 11
121 - ? Center(
122 - child: Padding(
123 - padding: EdgeInsets.only(bottom: 15.px),
124 - child: Text(
125 - (_countTotal - _count).toString(),
126 - style: TextStyle(
127 - fontSize: 70.px,
128 - color: Colors.white,
129 - ),
130 - ),
131 - ),
132 - )
133 - : Image.asset(
134 - voiceIco,
135 - width: 100.px,
136 - height: 100.px,
137 - package: 'flutter_plugin_record',
138 - ),
139 - ),
140 - Text(
141 - toastShow,
142 - style: TextStyle(
143 - fontStyle: FontStyle.normal,
144 - color: Colors.white,
145 - fontSize: 14.px,
146 - ),
147 - )
148 - ],
149 - ),
150 - );
151 - });
152 - Overlay.of(context)!.insert(overlayEntry!);
153 - }
154 - }
155 -
156 - showVoiceView() {
157 - setState(() {
158 - textShow = "松开结束";
159 - voiceState = false;
160 - });
161 -
162 - ///显示录音悬浮布局
163 - buildOverLayView(context);
164 -
165 - start();
166 - }
167 -
168 - hideVoiceView() {
169 - if (_timer!.isActive) {
170 - if (_count < 1) {
171 - CommonToast.showView(
172 - context: context,
173 - msg: '说话时间太短',
174 - icon: Text(
175 - '!',
176 - style: TextStyle(fontSize: 80.px, color: Colors.white),
177 - ));
178 - isUp = true;
179 - }
180 - _timer?.cancel();
181 - _count = 0;
182 - }
183 -
184 - setState(() {
185 - textShow = "按住说话";
186 - voiceState = true;
187 - });
188 -
189 - stop();
190 - if (overlayEntry != null) {
191 - overlayEntry?.remove();
192 - overlayEntry = null;
193 - }
194 -
195 - // if (isUp) {
196 - // print("取消发送");
197 - // } else {
198 - // print("进行发送");
199 - // }
200 - }
201 -
202 - moveVoiceView() {
203 - setState(() {
204 - isUp = startY - offset > 100 ? true : false;
205 - if (isUp) {
206 - textShow = "松开手指,取消发送";
207 - toastShow = textShow;
208 - } else {
209 - textShow = "松开结束";
210 - toastShow = "手指上滑,取消发送";
211 - }
212 - });
213 - }
214 -
215 - ///初始化语音录制的方法
216 - void _init() async {
217 - recordPlugin?.initRecordMp3();
218 - }
219 -
220 - ///开始语音录制的方法
221 - void start() async {
222 - recordPlugin?.start();
223 - }
224 -
225 - ///停止语音录制的方法
226 - void stop() {
227 - recordPlugin?.stop();
228 - }
229 -
230 - @override
231 - Widget build(BuildContext context) {
232 - return Padding(
233 - padding: EdgeInsets.only(right: 10.px),
234 - child: Row(
235 - crossAxisAlignment: CrossAxisAlignment.center,
236 - mainAxisAlignment: MainAxisAlignment.end,
237 - children: [
238 - GestureDetector(
239 - onLongPressStart: (details) {
240 - startY = details.globalPosition.dy;
241 - _timer = Timer.periodic(const Duration(milliseconds: 1000), (t) {
242 - _count++;
243 - if (_count == _countTotal) {
244 - hideVoiceView();
245 - }
246 - });
247 - showVoiceView();
248 - },
249 - onLongPressEnd: (details) {
250 - hideVoiceView();
251 - },
252 - onLongPressMoveUpdate: (details) {
253 - offset = details.globalPosition.dy;
254 - moveVoiceView();
255 - },
256 - child: Container(
257 - height: widget.height ?? 60.px,
258 - margin:
259 - widget.margin ?? EdgeInsets.fromLTRB(50.px, 0, 50.px, 20.px),
260 - child: Icon(
261 - Icons.mic_none,
262 - size: 70.px,
263 - color: Colors.black45.withOpacity(0.6),
264 - ),
265 - ),
266 - ),
267 - IconButton(
268 - icon: Icon(
269 - Icons.play_circle_outline,
270 - size: 28.px,
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 -}
1 -import 'dart:io';
2 -
3 -import 'package:flutter/material.dart';
4 -import 'preview_screen.dart';
5 -
6 -class CapturesScreen extends StatelessWidget {
7 - final List<File> imageFileList;
8 -
9 - const CapturesScreen({
10 - Key? key,
11 - required this.imageFileList,
12 - }) : super(key: key);
13 -
14 - @override
15 - Widget build(BuildContext context) {
16 - return Scaffold(
17 - backgroundColor: Colors.black,
18 - body: SingleChildScrollView(
19 - physics: const BouncingScrollPhysics(),
20 - child: Column(
21 - crossAxisAlignment: CrossAxisAlignment.start,
22 - children: [
23 - const Padding(
24 - padding: EdgeInsets.all(16.0),
25 - child: Text(
26 - 'Captures',
27 - style: TextStyle(
28 - fontSize: 32.0,
29 - color: Colors.white,
30 - ),
31 - ),
32 - ),
33 - GridView.count(
34 - shrinkWrap: true,
35 - physics: const NeverScrollableScrollPhysics(),
36 - crossAxisCount: 2,
37 - children: [
38 - for (File imageFile in imageFileList)
39 - Container(
40 - decoration: BoxDecoration(
41 - border: Border.all(
42 - color: Colors.black,
43 - width: 2,
44 - ),
45 - ),
46 - child: InkWell(
47 - onTap: () {
48 - Navigator.of(context).pushReplacement(
49 - MaterialPageRoute(
50 - builder: (context) => PreviewScreen(
51 - fileList: imageFileList,
52 - imageFile: imageFile,
53 - ),
54 - ),
55 - );
56 - },
57 - child: Image.file(
58 - imageFile,
59 - fit: BoxFit.cover,
60 - ),
61 - ),
62 - ),
63 - ],
64 - ),
65 - ],
66 - ),
67 - ),
68 - );
69 - }
70 -}
1 -import 'dart:io';
2 -
3 -import 'package:flutter/material.dart';
4 -
5 -import 'captures_screen.dart';
6 -
7 -class PreviewScreen extends StatelessWidget {
8 - final File imageFile;
9 - final List<File> fileList;
10 -
11 - const PreviewScreen({
12 - Key? key,
13 - required this.imageFile,
14 - required this.fileList,
15 - }) : super(key: key);
16 -
17 - @override
18 - Widget build(BuildContext context) {
19 - return Scaffold(
20 - backgroundColor: Colors.black,
21 - body: Column(
22 - crossAxisAlignment: CrossAxisAlignment.start,
23 - children: [
24 - Padding(
25 - padding: const EdgeInsets.all(8.0),
26 - child: TextButton(
27 - onPressed: () {
28 - Navigator.of(context).pushReplacement(
29 - MaterialPageRoute(
30 - builder: (context) => CapturesScreen(
31 - imageFileList: fileList,
32 - ),
33 - ),
34 - );
35 - },
36 - child: const Text('打开全部视频'),
37 - style: TextButton.styleFrom(
38 - primary: Colors.black,
39 - backgroundColor: Colors.white,
40 - ),
41 - ),
42 - ),
43 - Expanded(
44 - child: Image.file(imageFile),
45 - ),
46 - ],
47 - ),
48 - );
49 - }
50 -}
...@@ -21,7 +21,7 @@ packages: ...@@ -21,7 +21,7 @@ packages:
21 name: archive 21 name: archive
22 url: "https://pub.dartlang.org" 22 url: "https://pub.dartlang.org"
23 source: hosted 23 source: hosted
24 - version: "3.1.2" 24 + version: "3.1.6"
25 args: 25 args:
26 dependency: transitive 26 dependency: transitive
27 description: 27 description:
...@@ -35,7 +35,7 @@ packages: ...@@ -35,7 +35,7 @@ packages:
35 name: async 35 name: async
36 url: "https://pub.dartlang.org" 36 url: "https://pub.dartlang.org"
37 source: hosted 37 source: hosted
38 - version: "2.8.1" 38 + version: "2.8.2"
39 boolean_selector: 39 boolean_selector:
40 dependency: transitive 40 dependency: transitive
41 description: 41 description:
...@@ -126,14 +126,14 @@ packages: ...@@ -126,14 +126,14 @@ packages:
126 name: camera 126 name: camera
127 url: "https://pub.dartlang.org" 127 url: "https://pub.dartlang.org"
128 source: hosted 128 source: hosted
129 - version: "0.9.4+6" 129 + version: "0.9.4+7"
130 camera_platform_interface: 130 camera_platform_interface:
131 dependency: transitive 131 dependency: transitive
132 description: 132 description:
133 name: camera_platform_interface 133 name: camera_platform_interface
134 url: "https://pub.dartlang.org" 134 url: "https://pub.dartlang.org"
135 source: hosted 135 source: hosted
136 - version: "2.1.4" 136 + version: "2.1.5"
137 camera_web: 137 camera_web:
138 dependency: transitive 138 dependency: transitive
139 description: 139 description:
...@@ -147,7 +147,7 @@ packages: ...@@ -147,7 +147,7 @@ packages:
147 name: characters 147 name: characters
148 url: "https://pub.dartlang.org" 148 url: "https://pub.dartlang.org"
149 source: hosted 149 source: hosted
150 - version: "1.1.0" 150 + version: "1.2.0"
151 charcode: 151 charcode:
152 dependency: transitive 152 dependency: transitive
153 description: 153 description:
...@@ -400,7 +400,7 @@ packages: ...@@ -400,7 +400,7 @@ packages:
400 name: flutter_native_splash 400 name: flutter_native_splash
401 url: "https://pub.dartlang.org" 401 url: "https://pub.dartlang.org"
402 source: hosted 402 source: hosted
403 - version: "1.3.2" 403 + version: "1.3.3"
404 flutter_plugin_android_lifecycle: 404 flutter_plugin_android_lifecycle:
405 dependency: transitive 405 dependency: transitive
406 description: 406 description:
...@@ -408,20 +408,34 @@ packages: ...@@ -408,20 +408,34 @@ packages:
408 url: "https://pub.dartlang.org" 408 url: "https://pub.dartlang.org"
409 source: hosted 409 source: hosted
410 version: "2.0.5" 410 version: "2.0.5"
411 - flutter_plugin_record: 411 + flutter_slidable:
412 dependency: "direct main" 412 dependency: "direct main"
413 description: 413 description:
414 - name: flutter_plugin_record 414 + name: flutter_slidable
415 url: "https://pub.dartlang.org" 415 url: "https://pub.dartlang.org"
416 source: hosted 416 source: hosted
417 - version: "1.0.1" 417 + version: "1.2.0"
418 - flutter_slidable: 418 + flutter_sound:
419 dependency: "direct main" 419 dependency: "direct main"
420 description: 420 description:
421 - name: flutter_slidable 421 + name: flutter_sound
422 url: "https://pub.dartlang.org" 422 url: "https://pub.dartlang.org"
423 source: hosted 423 source: hosted
424 - version: "1.2.0" 424 + version: "8.5.0"
425 + flutter_sound_platform_interface:
426 + dependency: transitive
427 + description:
428 + name: flutter_sound_platform_interface
429 + url: "https://pub.dartlang.org"
430 + source: hosted
431 + version: "8.5.0"
432 + flutter_sound_web:
433 + dependency: transitive
434 + description:
435 + name: flutter_sound_web
436 + url: "https://pub.dartlang.org"
437 + source: hosted
438 + version: "8.5.0"
425 flutter_spinkit: 439 flutter_spinkit:
426 dependency: "direct main" 440 dependency: "direct main"
427 description: 441 description:
...@@ -513,7 +527,7 @@ packages: ...@@ -513,7 +527,7 @@ packages:
513 name: image_picker 527 name: image_picker
514 url: "https://pub.dartlang.org" 528 url: "https://pub.dartlang.org"
515 source: hosted 529 source: hosted
516 - version: "0.8.4+4" 530 + version: "0.8.4+5"
517 image_picker_for_web: 531 image_picker_for_web:
518 dependency: transitive 532 dependency: transitive
519 description: 533 description:
...@@ -582,6 +596,13 @@ packages: ...@@ -582,6 +596,13 @@ packages:
582 url: "https://pub.dartlang.org" 596 url: "https://pub.dartlang.org"
583 source: hosted 597 source: hosted
584 version: "1.0.1" 598 version: "1.0.1"
599 + logger:
600 + dependency: transitive
601 + description:
602 + name: logger
603 + url: "https://pub.dartlang.org"
604 + source: hosted
605 + version: "1.1.0"
585 logging: 606 logging:
586 dependency: transitive 607 dependency: transitive
587 description: 608 description:
...@@ -595,7 +616,7 @@ packages: ...@@ -595,7 +616,7 @@ packages:
595 name: matcher 616 name: matcher
596 url: "https://pub.dartlang.org" 617 url: "https://pub.dartlang.org"
597 source: hosted 618 source: hosted
598 - version: "0.12.10" 619 + version: "0.12.11"
599 meta: 620 meta:
600 dependency: transitive 621 dependency: transitive
601 description: 622 description:
...@@ -742,7 +763,7 @@ packages: ...@@ -742,7 +763,7 @@ packages:
742 name: platform 763 name: platform
743 url: "https://pub.dartlang.org" 764 url: "https://pub.dartlang.org"
744 source: hosted 765 source: hosted
745 - version: "3.0.0" 766 + version: "3.0.2"
746 plugin_platform_interface: 767 plugin_platform_interface:
747 dependency: transitive 768 dependency: transitive
748 description: 769 description:
...@@ -763,7 +784,7 @@ packages: ...@@ -763,7 +784,7 @@ packages:
763 name: process 784 name: process
764 url: "https://pub.dartlang.org" 785 url: "https://pub.dartlang.org"
765 source: hosted 786 source: hosted
766 - version: "4.2.3" 787 + version: "4.2.4"
767 provider: 788 provider:
768 dependency: "direct main" 789 dependency: "direct main"
769 description: 790 description:
...@@ -820,6 +841,13 @@ packages: ...@@ -820,6 +841,13 @@ packages:
820 url: "https://pub.dartlang.org" 841 url: "https://pub.dartlang.org"
821 source: hosted 842 source: hosted
822 version: "1.2.1" 843 version: "1.2.1"
844 + recase:
845 + dependency: transitive
846 + description:
847 + name: recase
848 + url: "https://pub.dartlang.org"
849 + source: hosted
850 + version: "4.0.0"
823 rxdart: 851 rxdart:
824 dependency: "direct main" 852 dependency: "direct main"
825 description: 853 description:
...@@ -985,7 +1013,7 @@ packages: ...@@ -985,7 +1013,7 @@ packages:
985 name: sqflite_common 1013 name: sqflite_common
986 url: "https://pub.dartlang.org" 1014 url: "https://pub.dartlang.org"
987 source: hosted 1015 source: hosted
988 - version: "2.0.1+1" 1016 + version: "2.2.0"
989 stack_trace: 1017 stack_trace:
990 dependency: transitive 1018 dependency: transitive
991 description: 1019 description:
...@@ -1055,21 +1083,21 @@ packages: ...@@ -1055,21 +1083,21 @@ packages:
1055 name: test 1083 name: test
1056 url: "https://pub.dartlang.org" 1084 url: "https://pub.dartlang.org"
1057 source: hosted 1085 source: hosted
1058 - version: "1.17.10" 1086 + version: "1.17.12"
1059 test_api: 1087 test_api:
1060 dependency: transitive 1088 dependency: transitive
1061 description: 1089 description:
1062 name: test_api 1090 name: test_api
1063 url: "https://pub.dartlang.org" 1091 url: "https://pub.dartlang.org"
1064 source: hosted 1092 source: hosted
1065 - version: "0.4.2" 1093 + version: "0.4.3"
1066 test_core: 1094 test_core:
1067 dependency: transitive 1095 dependency: transitive
1068 description: 1096 description:
1069 name: test_core 1097 name: test_core
1070 url: "https://pub.dartlang.org" 1098 url: "https://pub.dartlang.org"
1071 source: hosted 1099 source: hosted
1072 - version: "0.4.0" 1100 + version: "0.4.2"
1073 timing: 1101 timing:
1074 dependency: transitive 1102 dependency: transitive
1075 description: 1103 description:
...@@ -1097,7 +1125,7 @@ packages: ...@@ -1097,7 +1125,7 @@ packages:
1097 name: url_launcher 1125 name: url_launcher
1098 url: "https://pub.dartlang.org" 1126 url: "https://pub.dartlang.org"
1099 source: hosted 1127 source: hosted
1100 - version: "6.0.17" 1128 + version: "6.0.18"
1101 url_launcher_android: 1129 url_launcher_android:
1102 dependency: transitive 1130 dependency: transitive
1103 description: 1131 description:
...@@ -1167,7 +1195,7 @@ packages: ...@@ -1167,7 +1195,7 @@ packages:
1167 name: vector_math 1195 name: vector_math
1168 url: "https://pub.dartlang.org" 1196 url: "https://pub.dartlang.org"
1169 source: hosted 1197 source: hosted
1170 - version: "2.1.0" 1198 + version: "2.1.1"
1171 vibration: 1199 vibration:
1172 dependency: "direct main" 1200 dependency: "direct main"
1173 description: 1201 description:
...@@ -1209,7 +1237,7 @@ packages: ...@@ -1209,7 +1237,7 @@ packages:
1209 name: vm_service 1237 name: vm_service
1210 url: "https://pub.dartlang.org" 1238 url: "https://pub.dartlang.org"
1211 source: hosted 1239 source: hosted
1212 - version: "7.1.1" 1240 + version: "7.3.0"
1213 watcher: 1241 watcher:
1214 dependency: transitive 1242 dependency: transitive
1215 description: 1243 description:
...@@ -1272,7 +1300,7 @@ packages: ...@@ -1272,7 +1300,7 @@ packages:
1272 name: win32 1300 name: win32
1273 url: "https://pub.dartlang.org" 1301 url: "https://pub.dartlang.org"
1274 source: hosted 1302 source: hosted
1275 - version: "2.3.6" 1303 + version: "2.3.8"
1276 xdg_directories: 1304 xdg_directories:
1277 dependency: transitive 1305 dependency: transitive
1278 description: 1306 description:
...@@ -1295,5 +1323,5 @@ packages: ...@@ -1295,5 +1323,5 @@ packages:
1295 source: hosted 1323 source: hosted
1296 version: "3.1.0" 1324 version: "3.1.0"
1297 sdks: 1325 sdks:
1298 - dart: ">=2.14.0 <3.0.0" 1326 + dart: ">=2.15.1 <3.0.0"
1299 flutter: ">=2.5.0" 1327 flutter: ">=2.5.0"
......
...@@ -50,9 +50,9 @@ dependencies: ...@@ -50,9 +50,9 @@ dependencies:
50 # Flutter 轮播图 https://github.com/lianyagang/flutter_swiper_null_safety 50 # Flutter 轮播图 https://github.com/lianyagang/flutter_swiper_null_safety
51 flutter_swiper_null_safety: ^1.0.2 # flutter_swiper很久不维护,可以使用空安全版本:flutter_swiper_null_safety 51 flutter_swiper_null_safety: ^1.0.2 # flutter_swiper很久不维护,可以使用空安全版本:flutter_swiper_null_safety
52 # 启动URL的插件(支持Web) https://github.com/flutter/plugins/tree/master/packages/url_launcher 52 # 启动URL的插件(支持Web) https://github.com/flutter/plugins/tree/master/packages/url_launcher
53 - url_launcher: 6.0.17 53 + url_launcher: ^6.0.18
54 # 图片选择插件(支持Web) https://github.com/flutter/plugins/tree/master/packages/image_picker 54 # 图片选择插件(支持Web) https://github.com/flutter/plugins/tree/master/packages/image_picker
55 - image_picker: 0.8.4+4 55 + image_picker: ^0.8.4+5
56 # 侧滑删除 https://github.com/letsar/flutter_slidable 56 # 侧滑删除 https://github.com/letsar/flutter_slidable
57 flutter_slidable: ^1.1.0 57 flutter_slidable: ^1.1.0
58 # WebView插件 https://github.com/flutter/plugins/tree/master/packages/webview_flutter 58 # WebView插件 https://github.com/flutter/plugins/tree/master/packages/webview_flutter
...@@ -91,12 +91,12 @@ dependencies: ...@@ -91,12 +91,12 @@ 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 + flutter_sound: ^8.5.0
95 95
96 # fijkplayer (Video player plugin for Flutter) Flutter 媒体播放器 96 # fijkplayer (Video player plugin for Flutter) Flutter 媒体播放器
97 fijkplayer: ^0.10.1 97 fijkplayer: ^0.10.1
98 98
99 - camera: ^0.9.4+5 99 + camera: ^0.9.4+7
100 path_provider: ^2.0.8 100 path_provider: ^2.0.8
101 101
102 # A Dart timer that can be paused, resumed and reset. 102 # A Dart timer that can be paused, resumed and reset.
......