D1APP-101 chat waveform changed to slider
This commit is contained in:
parent
abe153d1ff
commit
1c62f956c7
|
|
@ -4,10 +4,10 @@ class ChatRoom {
|
|||
final int id;
|
||||
final String type;
|
||||
final String updatedAt;
|
||||
final int unread;
|
||||
int unread;
|
||||
final LastMessage lastMessage;
|
||||
|
||||
const ChatRoom({
|
||||
ChatRoom({
|
||||
required this.id,
|
||||
required this.type,
|
||||
required this.updatedAt,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:just_waveform/just_waveform.dart';
|
||||
|
||||
import 'radar_attachment.dart';
|
||||
|
||||
|
|
@ -15,7 +12,6 @@ class MessageData {
|
|||
final String createdAt;
|
||||
final RadarAttachment? radar;
|
||||
final File? audioFile;
|
||||
final Waveform? waveform;
|
||||
final int? audioDuration;
|
||||
|
||||
const MessageData({
|
||||
|
|
@ -26,7 +22,6 @@ class MessageData {
|
|||
this.text,
|
||||
this.audio,
|
||||
this.radar,
|
||||
this.waveform,
|
||||
this.audioFile,
|
||||
this.audioDuration,
|
||||
});
|
||||
|
|
@ -41,20 +36,6 @@ class MessageData {
|
|||
audioDuration: json['waveform'] == null
|
||||
? null
|
||||
: jsonDecode(json['waveform'])['duration'] ?? 0,
|
||||
waveform: json['waveform'] != null
|
||||
? Waveform(
|
||||
version: 1,
|
||||
flags: 0,
|
||||
sampleRate: 44100,
|
||||
samplesPerPixel: 441,
|
||||
length: jsonDecode(json['waveform'])['length'],
|
||||
data: Int16List.fromList(
|
||||
List<int>.from(
|
||||
jsonDecode(json['waveform'])['data'],
|
||||
),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
radar: json['radar'] == null
|
||||
? null
|
||||
: RadarAttachment.fromJson(json['radar']),
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ class _DirectState extends State<Direct> {
|
|||
@override
|
||||
void initState() {
|
||||
final state = context.read<DirectState>();
|
||||
state.replyRadar = widget.pageData['radarAttachment'];
|
||||
final typeId = ServerDataProvider.labelToTypeId(widget.pageData['type']);
|
||||
state.typeId = typeId;
|
||||
Future.delayed(Duration.zero, () {
|
||||
|
|
@ -48,11 +49,15 @@ class _DirectState extends State<Direct> {
|
|||
appBarData: AppBarData(
|
||||
hasBack: true,
|
||||
subtitle: 'ارتباط با سردبیر',
|
||||
title: widget.pageData['type'].substring(7),
|
||||
title: widget.pageData['type'] ?? 'پشتیبانی اپلیکیشن',
|
||||
),
|
||||
slivers: [
|
||||
if (state.appState != AppState.busy)
|
||||
SliverStateHandler<DirectState>(
|
||||
SliverPadding(
|
||||
padding: state.replyRadar == null
|
||||
? EdgeInsets.zero
|
||||
: const EdgeInsets.only(bottom: 68),
|
||||
sliver: SliverStateHandler<DirectState>(
|
||||
itemPadding: const EdgeInsets.only(bottom: 12),
|
||||
state: state,
|
||||
builder: (context, state, index) => Message(
|
||||
|
|
@ -61,6 +66,7 @@ class _DirectState extends State<Direct> {
|
|||
childCount: state.messages.length,
|
||||
onRetry: state.getMessages,
|
||||
),
|
||||
),
|
||||
],
|
||||
children: [
|
||||
if (state.appState == AppState.busy)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:didvan/models/enums.dart';
|
||||
|
|
@ -9,7 +8,6 @@ import 'package:didvan/services/network/request.dart';
|
|||
import 'package:didvan/services/network/request_helper.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_vibrate/flutter_vibrate.dart';
|
||||
import 'package:just_waveform/just_waveform.dart';
|
||||
import 'package:record/record.dart';
|
||||
|
||||
class DirectState extends CoreProvier {
|
||||
|
|
@ -21,7 +19,7 @@ class DirectState extends CoreProvier {
|
|||
String? text;
|
||||
RadarAttachment? replyRadar;
|
||||
File? recordedFile;
|
||||
Waveform? waveform;
|
||||
int? audioDuration;
|
||||
|
||||
bool isRecording = false;
|
||||
|
||||
|
|
@ -90,9 +88,7 @@ class DirectState extends CoreProvier {
|
|||
|
||||
Future<void> sendMessage() async {
|
||||
if ((text == null || text!.isEmpty) && recordedFile == null) return;
|
||||
if (recordedFile != null) {
|
||||
while (waveform == null) {}
|
||||
}
|
||||
replyRadar = null;
|
||||
messages.insert(
|
||||
0,
|
||||
MessageData(
|
||||
|
|
@ -105,8 +101,7 @@ class DirectState extends CoreProvier {
|
|||
audio: null,
|
||||
audioFile: recordedFile,
|
||||
radar: replyRadar,
|
||||
waveform: waveform,
|
||||
audioDuration: waveform != null ? waveform!.duration.inSeconds : null,
|
||||
audioDuration: audioDuration,
|
||||
),
|
||||
);
|
||||
_addToDailyGrouped();
|
||||
|
|
@ -126,13 +121,6 @@ class DirectState extends CoreProvier {
|
|||
if (uploadFile == null) {
|
||||
service.post();
|
||||
} else {
|
||||
body.addAll({
|
||||
'waveform': jsonEncode({
|
||||
'data': waveform!.data,
|
||||
'length': waveform!.length,
|
||||
'duration': waveform!.duration,
|
||||
})
|
||||
});
|
||||
service.multipart(
|
||||
file: uploadFile,
|
||||
method: 'POST',
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:didvan/models/message_data/message_data.dart';
|
||||
import 'package:didvan/pages/home/widgets/audio_slider.dart';
|
||||
import 'package:didvan/pages/home/widgets/player_controller_button.dart';
|
||||
import 'package:didvan/services/media/media.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AudioWidget extends StatelessWidget {
|
||||
final String? audioUrl;
|
||||
final File? audioFile;
|
||||
const AudioWidget({Key? key, this.audioUrl, this.audioFile})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return StreamBuilder<bool>(
|
||||
stream: MediaService.audioPlayer.playingStream,
|
||||
builder: (context, snapshot) {
|
||||
return Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: AudioSlider(
|
||||
tag: audioUrl ?? audioFile!.path,
|
||||
),
|
||||
),
|
||||
AudioControllerButton(
|
||||
audioFile: audioFile,
|
||||
audioUrl: audioUrl,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,12 @@
|
|||
import 'package:didvan/config/design_config.dart';
|
||||
import 'package:didvan/config/theme_data.dart';
|
||||
import 'package:didvan/constants/app_icons.dart';
|
||||
import 'package:didvan/models/message_data/message_data.dart';
|
||||
import 'package:didvan/pages/home/direct/direct_state.dart';
|
||||
import 'package:didvan/pages/home/widgets/audio_visualizer.dart';
|
||||
import 'package:didvan/pages/home/direct/widgets/audio_widget.dart';
|
||||
import 'package:didvan/pages/home/widgets/audio_slider.dart';
|
||||
import 'package:didvan/pages/home/widgets/player_controller_button.dart';
|
||||
import 'package:didvan/services/media/media.dart';
|
||||
import 'package:didvan/utils/date_time.dart';
|
||||
import 'package:didvan/widgets/didvan/divider.dart';
|
||||
import 'package:didvan/widgets/didvan/text.dart';
|
||||
|
|
@ -60,13 +64,9 @@ class Message extends StatelessWidget {
|
|||
children: [
|
||||
if (message.text != null) DidvanText(message.text!),
|
||||
if (message.audio != null || message.audioFile != null)
|
||||
SizedBox(
|
||||
height: 50,
|
||||
child: AudioVisualizer(
|
||||
AudioWidget(
|
||||
audioFile: message.audioFile,
|
||||
audioUrl: message.audio,
|
||||
waveform: message.waveform,
|
||||
backgroundColor: Colors.transparent,
|
||||
),
|
||||
),
|
||||
if (message.radar != null) const DidvanDivider(),
|
||||
if (message.radar != null) const SizedBox(height: 4),
|
||||
|
|
@ -77,11 +77,23 @@ class Message extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
DidvanText(
|
||||
DateTimeUtils.timeWithAmPm(message.createdAt),
|
||||
style: Theme.of(context).textTheme.overline,
|
||||
color: Theme.of(context).colorScheme.caption,
|
||||
),
|
||||
if (!message.writedByAdmin)
|
||||
Icon(
|
||||
message.readed
|
||||
? DidvanIcons.check_double_light
|
||||
: DidvanIcons.check_light,
|
||||
size: 16,
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import 'package:didvan/config/design_config.dart';
|
|||
import 'package:didvan/config/theme_data.dart';
|
||||
import 'package:didvan/constants/app_icons.dart';
|
||||
import 'package:didvan/pages/home/direct/direct_state.dart';
|
||||
import 'package:didvan/pages/home/widgets/audio_visualizer.dart';
|
||||
import 'package:didvan/pages/home/direct/widgets/audio_widget.dart';
|
||||
import 'package:didvan/widgets/didvan/icon_button.dart';
|
||||
import 'package:didvan/widgets/didvan/text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
|
@ -13,16 +13,46 @@ class MessageBox extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
height: 56,
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
top: BorderSide(
|
||||
color: Theme.of(context).colorScheme.cardBorder,
|
||||
return Column(
|
||||
children: [
|
||||
Consumer<DirectState>(
|
||||
builder: (context, state, child) => state.replyRadar != null
|
||||
? _MessageBoxContainer(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const DidvanText(
|
||||
'لینک به مطلب:',
|
||||
),
|
||||
DidvanText(
|
||||
state.replyRadar!.title,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
DidvanIconButton(
|
||||
icon: DidvanIcons.close_regular,
|
||||
gestureSize: 24,
|
||||
onPressed: () {
|
||||
state.replyRadar = null;
|
||||
state.update();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
: const SizedBox(),
|
||||
),
|
||||
_MessageBoxContainer(
|
||||
child: Consumer<DirectState>(
|
||||
builder: (context, state, child) {
|
||||
if (state.isRecording) {
|
||||
|
|
@ -33,6 +63,29 @@ class MessageBox extends StatelessWidget {
|
|||
return const _Typing();
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _MessageBoxContainer extends StatelessWidget {
|
||||
final Widget child;
|
||||
const _MessageBoxContainer({Key? key, required this.child}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
height: 68,
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
top: BorderSide(
|
||||
color: Theme.of(context).colorScheme.cardBorder,
|
||||
),
|
||||
),
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -153,9 +206,7 @@ class _RecordChecking extends StatelessWidget {
|
|||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: AudioVisualizer(
|
||||
audioFile: state.recordedFile!,
|
||||
),
|
||||
child: AudioWidget(audioFile: state.recordedFile!),
|
||||
),
|
||||
),
|
||||
DidvanIconButton(
|
||||
|
|
|
|||
|
|
@ -64,10 +64,7 @@ class _NewsDetailsState extends State<NewsDetails> {
|
|||
hasUnmarkConfirmation:
|
||||
widget.pageData['hasUnmarkConfirmation'],
|
||||
scrollController: _scrollController,
|
||||
comments: state.currentNews.comments,
|
||||
id: state.currentNews.id,
|
||||
marked: state.currentNews.marked,
|
||||
title: state.currentNews.title,
|
||||
item: state.currentNews,
|
||||
onCommentsChanged: state.onCommentsChanged,
|
||||
onMarkChanged: (value) => widget.pageData['onMarkChanged'](
|
||||
state.currentNews.id,
|
||||
|
|
|
|||
|
|
@ -63,18 +63,14 @@ class _RadarDetailsState extends State<RadarDetails> {
|
|||
child: FloatingNavigationBar(
|
||||
hasUnmarkConfirmation:
|
||||
widget.pageData['hasUnmarkConfirmation'],
|
||||
comments: state.currentRadar.comments,
|
||||
id: state.currentRadar.id,
|
||||
isRadar: true,
|
||||
marked: state.currentRadar.marked,
|
||||
title: state.currentRadar.title,
|
||||
categories: state.currentRadar.categories,
|
||||
scrollController: _scrollController,
|
||||
onMarkChanged: (value) =>
|
||||
widget.pageData['onMarkChanged']?.call(
|
||||
state.currentRadar.id,
|
||||
value,
|
||||
),
|
||||
item: state.currentRadar,
|
||||
onCommentsChanged: (count) {
|
||||
state.onCommentsChanged(count);
|
||||
widget.pageData['onCommentsChanged']?.call(
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import 'package:didvan/constants/assets.dart';
|
||||
import 'package:didvan/models/view/app_bar_data.dart';
|
||||
import 'package:didvan/pages/home/settings/direct_list/direct_list_state.dart';
|
||||
import 'package:didvan/pages/home/settings/direct_list/widgets/chat_room_item.dart';
|
||||
import 'package:didvan/pages/home/settings/direct_list/widgets/direct_item.dart';
|
||||
import 'package:didvan/widgets/didvan/badge.dart';
|
||||
import 'package:didvan/widgets/didvan/divider.dart';
|
||||
import 'package:didvan/widgets/didvan/scaffold.dart';
|
||||
|
|
|
|||
|
|
@ -15,10 +15,13 @@ class ChatRoomItem extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () => Navigator.of(context).pushNamed(
|
||||
onTap: () {
|
||||
Navigator.of(context).pushNamed(
|
||||
Routes.direct,
|
||||
arguments: {'type': chatRoom.type},
|
||||
),
|
||||
);
|
||||
chatRoom.unread = 0;
|
||||
},
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
child: Column(
|
||||
|
|
@ -62,11 +65,13 @@ class ChatRoomItem extends StatelessWidget {
|
|||
DidvanIcons.mic_light,
|
||||
size: 18,
|
||||
),
|
||||
DidvanText(
|
||||
Expanded(
|
||||
child: DidvanText(
|
||||
chatRoom.lastMessage.text ?? 'پیام صوتی',
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
DidvanText(
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
import 'package:audio_video_progress_bar/audio_video_progress_bar.dart';
|
||||
import 'package:didvan/services/media/media.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AudioSlider extends StatelessWidget {
|
||||
final String tag;
|
||||
const AudioSlider({Key? key, required this.tag}) : super(key: key);
|
||||
|
||||
bool get _isPlaying => MediaService.audioPlayerTag == tag;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return IgnorePointer(
|
||||
ignoring: MediaService.audioPlayerTag != tag,
|
||||
child: Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: StreamBuilder<Duration>(
|
||||
stream: _isPlaying ? MediaService.audioPlayer.positionStream : null,
|
||||
builder: (context, snapshot) {
|
||||
return ProgressBar(
|
||||
total: MediaService.audioPlayer.duration ?? Duration.zero,
|
||||
progress: snapshot.data ?? Duration.zero,
|
||||
buffered:
|
||||
_isPlaying ? MediaService.audioPlayer.bufferedPosition : null,
|
||||
thumbRadius: 6,
|
||||
barHeight: 3,
|
||||
timeLabelTextStyle: const TextStyle(fontSize: 0),
|
||||
onSeek: (value) => _onSeek(value.inMilliseconds),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _onSeek(int value) {
|
||||
MediaService.audioPlayer.seek(Duration(milliseconds: value));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,314 +1,314 @@
|
|||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
// import 'dart:io';
|
||||
// import 'dart:math';
|
||||
|
||||
import 'package:didvan/config/design_config.dart';
|
||||
import 'package:didvan/config/theme_data.dart';
|
||||
import 'package:didvan/constants/app_icons.dart';
|
||||
import 'package:didvan/constants/assets.dart';
|
||||
import 'package:didvan/pages/home/direct/direct_state.dart';
|
||||
import 'package:didvan/services/media/media.dart';
|
||||
import 'package:didvan/services/storage/storage.dart';
|
||||
import 'package:didvan/utils/date_time.dart';
|
||||
import 'package:didvan/widgets/didvan/icon_button.dart';
|
||||
import 'package:didvan/widgets/didvan/text.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:just_waveform/just_waveform.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
// import 'package:didvan/config/design_config.dart';
|
||||
// import 'package:didvan/config/theme_data.dart';
|
||||
// import 'package:didvan/constants/app_icons.dart';
|
||||
// import 'package:didvan/constants/assets.dart';
|
||||
// import 'package:didvan/pages/home/direct/direct_state.dart';
|
||||
// import 'package:didvan/services/media/media.dart';
|
||||
// import 'package:didvan/services/storage/storage.dart';
|
||||
// import 'package:didvan/utils/date_time.dart';
|
||||
// import 'package:didvan/widgets/didvan/icon_button.dart';
|
||||
// import 'package:didvan/widgets/didvan/text.dart';
|
||||
// import 'package:flutter/foundation.dart';
|
||||
// import 'package:flutter/material.dart';
|
||||
// import 'package:flutter_svg/flutter_svg.dart';
|
||||
// import 'package:just_waveform/just_waveform.dart';
|
||||
// import 'package:provider/provider.dart';
|
||||
|
||||
class AudioVisualizer extends StatefulWidget {
|
||||
final File? audioFile;
|
||||
final Waveform? waveform;
|
||||
final String? audioUrl;
|
||||
final int? duration;
|
||||
final Color? backgroundColor;
|
||||
// class AudioVisualizer extends StatefulWidget {
|
||||
// final File? audioFile;
|
||||
// final Waveform? waveform;
|
||||
// final String? audioUrl;
|
||||
// final int? duration;
|
||||
// final Color? backgroundColor;
|
||||
|
||||
const AudioVisualizer({
|
||||
Key? key,
|
||||
this.audioFile,
|
||||
this.waveform,
|
||||
this.audioUrl,
|
||||
this.duration,
|
||||
this.backgroundColor,
|
||||
}) : super(key: key);
|
||||
// const AudioVisualizer({
|
||||
// Key? key,
|
||||
// this.audioFile,
|
||||
// this.waveform,
|
||||
// this.audioUrl,
|
||||
// this.duration,
|
||||
// this.backgroundColor,
|
||||
// }) : super(key: key);
|
||||
|
||||
@override
|
||||
State<AudioVisualizer> createState() => _AudioVisualizerState();
|
||||
}
|
||||
// @override
|
||||
// State<AudioVisualizer> createState() => _AudioVisualizerState();
|
||||
// }
|
||||
|
||||
class _AudioVisualizerState extends State<AudioVisualizer> {
|
||||
Stream<WaveformProgress>? waveDataStream;
|
||||
// class _AudioVisualizerState extends State<AudioVisualizer> {
|
||||
// Stream<WaveformProgress>? waveDataStream;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
if (!kIsWeb && widget.audioFile != null) {
|
||||
waveDataStream = JustWaveform.extract(
|
||||
audioInFile: widget.audioFile!,
|
||||
waveOutFile: File(StorageService.appTempsDir + '/rec-wave.wave'),
|
||||
zoom: const WaveformZoom.pixelsPerSecond(100),
|
||||
);
|
||||
}
|
||||
super.initState();
|
||||
}
|
||||
// @override
|
||||
// void initState() {
|
||||
// if (!kIsWeb && widget.audioFile != null) {
|
||||
// waveDataStream = JustWaveform.extract(
|
||||
// audioInFile: widget.audioFile!,
|
||||
// waveOutFile: File(StorageService.appTempsDir + '/rec-wave.wave'),
|
||||
// zoom: const WaveformZoom.pixelsPerSecond(100),
|
||||
// );
|
||||
// }
|
||||
// super.initState();
|
||||
// }
|
||||
|
||||
bool get _nowPlaying =>
|
||||
MediaService.lastAudioPath == widget.audioFile ||
|
||||
MediaService.lastAudioPath == widget.audioUrl;
|
||||
// bool get _nowPlaying =>
|
||||
// MediaService.lastAudioPath == widget.audioFile ||
|
||||
// MediaService.lastAudioPath == widget.audioUrl;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: widget.backgroundColor ??
|
||||
(DesignConfig.isDark
|
||||
? Theme.of(context).colorScheme.black
|
||||
: Theme.of(context).colorScheme.background),
|
||||
borderRadius: DesignConfig.mediumBorderRadius,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
const SizedBox(width: 12),
|
||||
StreamBuilder<Duration>(
|
||||
stream:
|
||||
_nowPlaying ? MediaService.audioPlayer.positionStream : null,
|
||||
builder: (context, snapshot) {
|
||||
String text = '';
|
||||
if (MediaService.audioPlayer.duration == null) {
|
||||
Future.delayed(Duration.zero, () {
|
||||
if (mounted) {
|
||||
setState(() {});
|
||||
}
|
||||
});
|
||||
}
|
||||
if (snapshot.data == null || snapshot.data == Duration.zero) {
|
||||
text = DateTimeUtils.normalizeTimeDuration(
|
||||
MediaService.audioPlayer.duration ??
|
||||
widget.waveform?.duration ??
|
||||
Duration.zero);
|
||||
} else {
|
||||
text = DateTimeUtils.normalizeTimeDuration(snapshot.data!);
|
||||
}
|
||||
return DidvanText(
|
||||
text,
|
||||
color: Theme.of(context).colorScheme.focusedBorder,
|
||||
isEnglishFont: true,
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
if (kIsWeb) {
|
||||
return SvgPicture.asset(Assets.record);
|
||||
}
|
||||
if (widget.audioFile != null) {
|
||||
return StreamBuilder<WaveformProgress>(
|
||||
stream: waveDataStream,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.data == null ||
|
||||
snapshot.data!.waveform == null) {
|
||||
return const SizedBox();
|
||||
}
|
||||
final waveform = snapshot.data!.waveform!;
|
||||
context.read<DirectState>().waveform = waveform;
|
||||
return _waveWidget(waveform);
|
||||
},
|
||||
);
|
||||
}
|
||||
if (widget.waveform == null && waveDataStream == null) {
|
||||
return SvgPicture.asset(Assets.record);
|
||||
}
|
||||
return _waveWidget(widget.waveform!);
|
||||
},
|
||||
),
|
||||
),
|
||||
StreamBuilder<bool>(
|
||||
stream: _nowPlaying ? MediaService.audioPlayer.playingStream : null,
|
||||
builder: (context, snapshot) {
|
||||
return DidvanIconButton(
|
||||
icon: snapshot.data == true
|
||||
? DidvanIcons.pause_circle_solid
|
||||
: DidvanIcons.play_circle_solid,
|
||||
color: Theme.of(context).colorScheme.focusedBorder,
|
||||
onPressed: () {
|
||||
MediaService.handleAudioPlayback(
|
||||
audioSource: widget.audioFile ?? widget.audioUrl,
|
||||
isNetworkAudio: widget.audioFile == null,
|
||||
);
|
||||
setState(() {});
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// return Container(
|
||||
// decoration: BoxDecoration(
|
||||
// color: widget.backgroundColor ??
|
||||
// (DesignConfig.isDark
|
||||
// ? Theme.of(context).colorScheme.black
|
||||
// : Theme.of(context).colorScheme.background),
|
||||
// borderRadius: DesignConfig.mediumBorderRadius,
|
||||
// ),
|
||||
// child: Row(
|
||||
// children: [
|
||||
// const SizedBox(width: 12),
|
||||
// StreamBuilder<Duration>(
|
||||
// stream:
|
||||
// _nowPlaying ? MediaService.audioPlayer.positionStream : null,
|
||||
// builder: (context, snapshot) {
|
||||
// String text = '';
|
||||
// if (MediaService.audioPlayer.duration == null) {
|
||||
// Future.delayed(Duration.zero, () {
|
||||
// if (mounted) {
|
||||
// setState(() {});
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// if (snapshot.data == null || snapshot.data == Duration.zero) {
|
||||
// text = DateTimeUtils.normalizeTimeDuration(
|
||||
// MediaService.audioPlayer.duration ??
|
||||
// widget.waveform?.duration ??
|
||||
// Duration.zero);
|
||||
// } else {
|
||||
// text = DateTimeUtils.normalizeTimeDuration(snapshot.data!);
|
||||
// }
|
||||
// return DidvanText(
|
||||
// text,
|
||||
// color: Theme.of(context).colorScheme.focusedBorder,
|
||||
// isEnglishFont: true,
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
// const SizedBox(width: 12),
|
||||
// Expanded(
|
||||
// child: Builder(
|
||||
// builder: (context) {
|
||||
// if (kIsWeb) {
|
||||
// return SvgPicture.asset(Assets.record);
|
||||
// }
|
||||
// if (widget.audioFile != null) {
|
||||
// return StreamBuilder<WaveformProgress>(
|
||||
// stream: waveDataStream,
|
||||
// builder: (context, snapshot) {
|
||||
// if (snapshot.data == null ||
|
||||
// snapshot.data!.waveform == null) {
|
||||
// return const SizedBox();
|
||||
// }
|
||||
// final waveform = snapshot.data!.waveform!;
|
||||
// context.read<DirectState>().waveform = waveform;
|
||||
// return _waveWidget(waveform);
|
||||
// },
|
||||
// );
|
||||
// }
|
||||
// if (widget.waveform == null && waveDataStream == null) {
|
||||
// return SvgPicture.asset(Assets.record);
|
||||
// }
|
||||
// return _waveWidget(widget.waveform!);
|
||||
// },
|
||||
// ),
|
||||
// ),
|
||||
// StreamBuilder<bool>(
|
||||
// stream: _nowPlaying ? MediaService.audioPlayer.playingStream : null,
|
||||
// builder: (context, snapshot) {
|
||||
// return DidvanIconButton(
|
||||
// icon: snapshot.data == true
|
||||
// ? DidvanIcons.pause_circle_solid
|
||||
// : DidvanIcons.play_circle_solid,
|
||||
// color: Theme.of(context).colorScheme.focusedBorder,
|
||||
// onPressed: () {
|
||||
// MediaService.handleAudioPlayback(
|
||||
// audioSource: widget.audioFile ?? widget.audioUrl,
|
||||
// isNetworkAudio: widget.audioFile == null,
|
||||
// );
|
||||
// setState(() {});
|
||||
// },
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
|
||||
Widget _waveWidget(Waveform waveform) => IgnorePointer(
|
||||
ignoring: !_nowPlaying,
|
||||
child: GestureDetector(
|
||||
onHorizontalDragUpdate: _changePosition,
|
||||
onTapDown: _changePosition,
|
||||
child: SizedBox(
|
||||
height: double.infinity,
|
||||
width: double.infinity,
|
||||
child: _AudioWaveformWidget(
|
||||
waveform: waveform,
|
||||
start: Duration.zero,
|
||||
scale: 2,
|
||||
strokeWidth: 3,
|
||||
nowPlaying: _nowPlaying,
|
||||
duration: waveform.duration,
|
||||
waveColor: Theme.of(context).colorScheme.focusedBorder,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
// Widget _waveWidget(Waveform waveform) => IgnorePointer(
|
||||
// ignoring: !_nowPlaying,
|
||||
// child: GestureDetector(
|
||||
// onHorizontalDragUpdate: _changePosition,
|
||||
// onTapDown: _changePosition,
|
||||
// child: SizedBox(
|
||||
// height: double.infinity,
|
||||
// width: double.infinity,
|
||||
// child: _AudioWaveformWidget(
|
||||
// waveform: waveform,
|
||||
// start: Duration.zero,
|
||||
// scale: 2,
|
||||
// strokeWidth: 3,
|
||||
// nowPlaying: _nowPlaying,
|
||||
// duration: waveform.duration,
|
||||
// waveColor: Theme.of(context).colorScheme.focusedBorder,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
|
||||
void _changePosition(details) {
|
||||
if (MediaService.audioPlayer.audioSource == null) return;
|
||||
double posper =
|
||||
details.localPosition.dx / (MediaQuery.of(context).size.width - 200);
|
||||
if (posper >= 1 || posper < 0) return;
|
||||
final position = MediaService.audioPlayer.duration!.inMilliseconds;
|
||||
MediaService.audioPlayer.seek(
|
||||
Duration(milliseconds: (posper * position).toInt()),
|
||||
);
|
||||
}
|
||||
}
|
||||
// void _changePosition(details) {
|
||||
// if (MediaService.audioPlayer.audioSource == null) return;
|
||||
// double posper =
|
||||
// details.localPosition.dx / (MediaQuery.of(context).size.width - 200);
|
||||
// if (posper >= 1 || posper < 0) return;
|
||||
// final position = MediaService.audioPlayer.duration!.inMilliseconds;
|
||||
// MediaService.audioPlayer.seek(
|
||||
// Duration(milliseconds: (posper * position).toInt()),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
class _AudioWaveformWidget extends StatelessWidget {
|
||||
final Color waveColor;
|
||||
final double scale;
|
||||
final double strokeWidth;
|
||||
final double pixelsPerStep;
|
||||
final Waveform waveform;
|
||||
final Duration start;
|
||||
final bool nowPlaying;
|
||||
final Duration duration;
|
||||
// class _AudioWaveformWidget extends StatelessWidget {
|
||||
// final Color waveColor;
|
||||
// final double scale;
|
||||
// final double strokeWidth;
|
||||
// final double pixelsPerStep;
|
||||
// final Waveform waveform;
|
||||
// final Duration start;
|
||||
// final bool nowPlaying;
|
||||
// final Duration duration;
|
||||
|
||||
const _AudioWaveformWidget({
|
||||
Key? key,
|
||||
required this.waveform,
|
||||
required this.start,
|
||||
required this.duration,
|
||||
required this.nowPlaying,
|
||||
this.waveColor = Colors.blue,
|
||||
this.scale = 1.0,
|
||||
this.strokeWidth = 5.0,
|
||||
this.pixelsPerStep = 8.0,
|
||||
}) : super(key: key);
|
||||
// const _AudioWaveformWidget({
|
||||
// Key? key,
|
||||
// required this.waveform,
|
||||
// required this.start,
|
||||
// required this.duration,
|
||||
// required this.nowPlaying,
|
||||
// this.waveColor = Colors.blue,
|
||||
// this.scale = 1.0,
|
||||
// this.strokeWidth = 5.0,
|
||||
// this.pixelsPerStep = 8.0,
|
||||
// }) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ClipRect(
|
||||
child: StreamBuilder<Duration?>(
|
||||
stream: nowPlaying ? MediaService.audioPlayer.positionStream : null,
|
||||
builder: (context, snapshot) {
|
||||
double progress = 0;
|
||||
if (snapshot.data == null ||
|
||||
MediaService.audioPlayer.duration == null) {
|
||||
progress = 0;
|
||||
} else {
|
||||
progress = snapshot.data!.inMilliseconds /
|
||||
MediaService.audioPlayer.duration!.inMilliseconds *
|
||||
100;
|
||||
}
|
||||
if (progress >= 100) {
|
||||
progress = 0;
|
||||
MediaService.audioPlayer.stop();
|
||||
MediaService.audioPlayer.seek(Duration.zero);
|
||||
}
|
||||
return CustomPaint(
|
||||
painter: _AudioWaveformPainter(
|
||||
waveColor: waveColor,
|
||||
waveform: waveform,
|
||||
start: start,
|
||||
duration: duration,
|
||||
scale: scale,
|
||||
strokeWidth: strokeWidth,
|
||||
pixelsPerStep: pixelsPerStep,
|
||||
progressPercentage: progress,
|
||||
progressColor: Theme.of(context).colorScheme.focusedBorder,
|
||||
color: Theme.of(context).colorScheme.border,
|
||||
),
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// return ClipRect(
|
||||
// child: StreamBuilder<Duration?>(
|
||||
// stream: nowPlaying ? MediaService.audioPlayer.positionStream : null,
|
||||
// builder: (context, snapshot) {
|
||||
// double progress = 0;
|
||||
// if (snapshot.data == null ||
|
||||
// MediaService.audioPlayer.duration == null) {
|
||||
// progress = 0;
|
||||
// } else {
|
||||
// progress = snapshot.data!.inMilliseconds /
|
||||
// MediaService.audioPlayer.duration!.inMilliseconds *
|
||||
// 100;
|
||||
// }
|
||||
// if (progress >= 100) {
|
||||
// progress = 0;
|
||||
// MediaService.audioPlayer.stop();
|
||||
// MediaService.audioPlayer.seek(Duration.zero);
|
||||
// }
|
||||
// return CustomPaint(
|
||||
// painter: _AudioWaveformPainter(
|
||||
// waveColor: waveColor,
|
||||
// waveform: waveform,
|
||||
// start: start,
|
||||
// duration: duration,
|
||||
// scale: scale,
|
||||
// strokeWidth: strokeWidth,
|
||||
// pixelsPerStep: pixelsPerStep,
|
||||
// progressPercentage: progress,
|
||||
// progressColor: Theme.of(context).colorScheme.focusedBorder,
|
||||
// color: Theme.of(context).colorScheme.border,
|
||||
// ),
|
||||
// );
|
||||
// }),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
class _AudioWaveformPainter extends CustomPainter {
|
||||
final double scale;
|
||||
final double strokeWidth;
|
||||
final double pixelsPerStep;
|
||||
final Waveform waveform;
|
||||
final Duration start;
|
||||
final Duration duration;
|
||||
final double progressPercentage;
|
||||
final Color progressColor;
|
||||
final Color color;
|
||||
// class _AudioWaveformPainter extends CustomPainter {
|
||||
// final double scale;
|
||||
// final double strokeWidth;
|
||||
// final double pixelsPerStep;
|
||||
// final Waveform waveform;
|
||||
// final Duration start;
|
||||
// final Duration duration;
|
||||
// final double progressPercentage;
|
||||
// final Color progressColor;
|
||||
// final Color color;
|
||||
|
||||
_AudioWaveformPainter({
|
||||
required this.waveform,
|
||||
required this.start,
|
||||
required this.duration,
|
||||
required this.progressPercentage,
|
||||
required this.color,
|
||||
required this.progressColor,
|
||||
Color waveColor = Colors.blue,
|
||||
this.scale = 1.0,
|
||||
this.strokeWidth = 5.0,
|
||||
this.pixelsPerStep = 8.0,
|
||||
});
|
||||
// _AudioWaveformPainter({
|
||||
// required this.waveform,
|
||||
// required this.start,
|
||||
// required this.duration,
|
||||
// required this.progressPercentage,
|
||||
// required this.color,
|
||||
// required this.progressColor,
|
||||
// Color waveColor = Colors.blue,
|
||||
// this.scale = 1.0,
|
||||
// this.strokeWidth = 5.0,
|
||||
// this.pixelsPerStep = 8.0,
|
||||
// });
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
if (duration == Duration.zero) return;
|
||||
double width = size.width;
|
||||
double height = size.height;
|
||||
// @override
|
||||
// void paint(Canvas canvas, Size size) {
|
||||
// if (duration == Duration.zero) return;
|
||||
// double width = size.width;
|
||||
// double height = size.height;
|
||||
|
||||
final waveformPixelsPerWindow = waveform.positionToPixel(duration).toInt();
|
||||
final waveformPixelsPerDevicePixel = waveformPixelsPerWindow / width;
|
||||
final waveformPixelsPerStep = waveformPixelsPerDevicePixel * pixelsPerStep;
|
||||
final sampleOffset = waveform.positionToPixel(start);
|
||||
final sampleStart = -sampleOffset % waveformPixelsPerStep;
|
||||
final totalLength = waveformPixelsPerWindow;
|
||||
final wavePaintB = Paint()
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = strokeWidth
|
||||
..strokeCap = StrokeCap.round
|
||||
..color = progressColor;
|
||||
final wavePaintA = Paint()
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = strokeWidth
|
||||
..strokeCap = StrokeCap.round
|
||||
..color = color;
|
||||
for (var i = sampleStart.toDouble();
|
||||
i <= waveformPixelsPerWindow + 1.0;
|
||||
i += waveformPixelsPerStep) {
|
||||
final sampleIdx = (sampleOffset + i).toInt();
|
||||
final x = i / waveformPixelsPerDevicePixel;
|
||||
final minY = normalise(waveform.getPixelMin(sampleIdx), height);
|
||||
final maxY = normalise(waveform.getPixelMax(sampleIdx), height);
|
||||
canvas.drawLine(
|
||||
Offset(x + strokeWidth / 2, max(strokeWidth * 0.75, minY)),
|
||||
Offset(x + strokeWidth / 2, min(height - strokeWidth * 0.75, maxY)),
|
||||
i / totalLength < progressPercentage / 100 ? wavePaintB : wavePaintA,
|
||||
);
|
||||
}
|
||||
}
|
||||
// final waveformPixelsPerWindow = waveform.positionToPixel(duration).toInt();
|
||||
// final waveformPixelsPerDevicePixel = waveformPixelsPerWindow / width;
|
||||
// final waveformPixelsPerStep = waveformPixelsPerDevicePixel * pixelsPerStep;
|
||||
// final sampleOffset = waveform.positionToPixel(start);
|
||||
// final sampleStart = -sampleOffset % waveformPixelsPerStep;
|
||||
// final totalLength = waveformPixelsPerWindow;
|
||||
// final wavePaintB = Paint()
|
||||
// ..style = PaintingStyle.stroke
|
||||
// ..strokeWidth = strokeWidth
|
||||
// ..strokeCap = StrokeCap.round
|
||||
// ..color = progressColor;
|
||||
// final wavePaintA = Paint()
|
||||
// ..style = PaintingStyle.stroke
|
||||
// ..strokeWidth = strokeWidth
|
||||
// ..strokeCap = StrokeCap.round
|
||||
// ..color = color;
|
||||
// for (var i = sampleStart.toDouble();
|
||||
// i <= waveformPixelsPerWindow + 1.0;
|
||||
// i += waveformPixelsPerStep) {
|
||||
// final sampleIdx = (sampleOffset + i).toInt();
|
||||
// final x = i / waveformPixelsPerDevicePixel;
|
||||
// final minY = normalise(waveform.getPixelMin(sampleIdx), height);
|
||||
// final maxY = normalise(waveform.getPixelMax(sampleIdx), height);
|
||||
// canvas.drawLine(
|
||||
// Offset(x + strokeWidth / 2, max(strokeWidth * 0.75, minY)),
|
||||
// Offset(x + strokeWidth / 2, min(height - strokeWidth * 0.75, maxY)),
|
||||
// i / totalLength < progressPercentage / 100 ? wavePaintB : wavePaintA,
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
@override
|
||||
bool shouldRepaint(covariant _AudioWaveformPainter oldDelegate) {
|
||||
return oldDelegate.progressPercentage != progressPercentage;
|
||||
}
|
||||
// @override
|
||||
// bool shouldRepaint(covariant _AudioWaveformPainter oldDelegate) {
|
||||
// return oldDelegate.progressPercentage != progressPercentage;
|
||||
// }
|
||||
|
||||
double normalise(int s, double height) {
|
||||
final y = 32768 + (scale * s).clamp(-32768.0, 32767.0).toDouble();
|
||||
return height - 1 - y * height / 65536;
|
||||
}
|
||||
}
|
||||
// double normalise(int s, double height) {
|
||||
// final y = 32768 + (scale * s).clamp(-32768.0, 32767.0).toDouble();
|
||||
// return height - 1 - y * height / 65536;
|
||||
// }
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import 'package:didvan/config/design_config.dart';
|
||||
import 'package:didvan/config/theme_data.dart';
|
||||
import 'package:didvan/constants/app_icons.dart';
|
||||
import 'package:didvan/models/category.dart';
|
||||
import 'package:didvan/models/message_data/radar_attachment.dart';
|
||||
import 'package:didvan/models/view/action_sheet_data.dart';
|
||||
import 'package:didvan/pages/home/widgets/menu_item.dart';
|
||||
import 'package:didvan/routes/routes.dart';
|
||||
|
|
@ -16,14 +16,10 @@ import 'package:flutter/material.dart';
|
|||
|
||||
class FloatingNavigationBar extends StatefulWidget {
|
||||
final ScrollController scrollController;
|
||||
final dynamic item;
|
||||
final bool hasUnmarkConfirmation;
|
||||
final void Function(int count) onCommentsChanged;
|
||||
final bool isRadar;
|
||||
final bool marked;
|
||||
final int comments;
|
||||
final int id;
|
||||
final String title;
|
||||
final List<CategoryData>? categories;
|
||||
final void Function(bool value) onMarkChanged;
|
||||
|
||||
const FloatingNavigationBar({
|
||||
|
|
@ -32,11 +28,7 @@ class FloatingNavigationBar extends StatefulWidget {
|
|||
required this.onCommentsChanged,
|
||||
required this.onMarkChanged,
|
||||
required this.isRadar,
|
||||
required this.marked,
|
||||
required this.comments,
|
||||
required this.id,
|
||||
required this.title,
|
||||
this.categories,
|
||||
required this.item,
|
||||
this.hasUnmarkConfirmation = false,
|
||||
}) : super(key: key);
|
||||
|
||||
|
|
@ -50,7 +42,7 @@ class _FloatingNavigationBarState extends State<FloatingNavigationBar> {
|
|||
|
||||
@override
|
||||
void didUpdateWidget(covariant FloatingNavigationBar oldWidget) {
|
||||
_comments = widget.comments;
|
||||
_comments = widget.item.comments;
|
||||
_isScrolled = false;
|
||||
_handleScroll();
|
||||
super.didUpdateWidget(oldWidget);
|
||||
|
|
@ -60,7 +52,7 @@ class _FloatingNavigationBarState extends State<FloatingNavigationBar> {
|
|||
void initState() {
|
||||
_handleScroll();
|
||||
_isScrolled = false;
|
||||
_comments = widget.comments;
|
||||
_comments = widget.item.comments;
|
||||
super.initState();
|
||||
}
|
||||
|
||||
|
|
@ -113,7 +105,7 @@ class _FloatingNavigationBarState extends State<FloatingNavigationBar> {
|
|||
if (widget.isRadar)
|
||||
BookmarkButton(
|
||||
askForConfirmation: widget.hasUnmarkConfirmation,
|
||||
value: widget.marked,
|
||||
value: widget.item.marked,
|
||||
onMarkChanged: (value) {
|
||||
widget.onMarkChanged(value);
|
||||
if (widget.hasUnmarkConfirmation && !value) {
|
||||
|
|
@ -137,9 +129,9 @@ class _FloatingNavigationBarState extends State<FloatingNavigationBar> {
|
|||
onPressed: () => Navigator.of(context).pushNamed(
|
||||
Routes.comments,
|
||||
arguments: {
|
||||
'id': widget.id,
|
||||
'id': widget.item.id,
|
||||
'isRadar': widget.isRadar,
|
||||
'title': widget.title,
|
||||
'title': widget.item.title,
|
||||
'onCommentsChanged': widget.onCommentsChanged,
|
||||
},
|
||||
),
|
||||
|
|
@ -152,7 +144,7 @@ class _FloatingNavigationBarState extends State<FloatingNavigationBar> {
|
|||
if (!widget.isRadar)
|
||||
BookmarkButton(
|
||||
askForConfirmation: widget.hasUnmarkConfirmation,
|
||||
value: widget.marked,
|
||||
value: widget.item.marked,
|
||||
onMarkChanged: (value) {
|
||||
widget.onMarkChanged(value);
|
||||
if (widget.hasUnmarkConfirmation && !value) {
|
||||
|
|
@ -191,7 +183,7 @@ class _FloatingNavigationBarState extends State<FloatingNavigationBar> {
|
|||
}
|
||||
|
||||
void _showMoreOptions() {
|
||||
final categories = widget.categories!;
|
||||
final categories = widget.item.categories!;
|
||||
ActionSheetUtils.showBottomSheet(
|
||||
data: ActionSheetData(
|
||||
content: Column(
|
||||
|
|
@ -211,7 +203,19 @@ class _FloatingNavigationBarState extends State<FloatingNavigationBar> {
|
|||
Navigator.of(context).pop();
|
||||
Navigator.of(context).pushNamed(
|
||||
Routes.direct,
|
||||
arguments: categories[i].id,
|
||||
arguments: {
|
||||
'radarAttachment': RadarAttachment(
|
||||
id: widget.item.id,
|
||||
title: widget.item.title,
|
||||
description: widget.item.contents.first.text,
|
||||
timeToRead: widget.item.timeToRead,
|
||||
image: widget.item.image,
|
||||
forManagers: widget.item.forManagers,
|
||||
categories: widget.item.categories,
|
||||
createdAt: widget.item.createdAt,
|
||||
),
|
||||
'type': categories[i].label,
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
|
|
@ -229,7 +233,7 @@ class _FloatingNavigationBarState extends State<FloatingNavigationBar> {
|
|||
Navigator.of(context).pop();
|
||||
Navigator.of(context).pushNamed(
|
||||
Routes.direct,
|
||||
arguments: 0,
|
||||
arguments: {},
|
||||
);
|
||||
},
|
||||
icon: DidvanIcons.description_regular,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:didvan/config/theme_data.dart';
|
||||
import 'package:didvan/constants/app_icons.dart';
|
||||
import 'package:didvan/services/media/media.dart';
|
||||
import 'package:didvan/widgets/didvan/icon_button.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AudioControllerButton extends StatelessWidget {
|
||||
final String? audioUrl;
|
||||
final File? audioFile;
|
||||
|
||||
const AudioControllerButton({Key? key, this.audioUrl, this.audioFile})
|
||||
: super(key: key);
|
||||
|
||||
bool get _nowPlaying =>
|
||||
MediaService.audioPlayerTag == audioUrl ||
|
||||
audioFile != null && MediaService.audioPlayerTag == audioFile!.path;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DidvanIconButton(
|
||||
icon: MediaService.audioPlayer.playing == true && _nowPlaying
|
||||
? DidvanIcons.pause_circle_solid
|
||||
: DidvanIcons.play_circle_solid,
|
||||
color: Theme.of(context).colorScheme.focusedBorder,
|
||||
onPressed: () {
|
||||
MediaService.handleAudioPlayback(
|
||||
audioSource: audioFile ?? audioUrl,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -8,8 +8,9 @@ class ServerDataProvider {
|
|||
await _getDirectTypes();
|
||||
}
|
||||
|
||||
static int labelToTypeId(String label) =>
|
||||
directTypes.firstWhere((element) => element.value == label).key;
|
||||
static int labelToTypeId(String? label) => label == null
|
||||
? 7
|
||||
: directTypes.firstWhere((element) => element.value.contains(label)).key;
|
||||
|
||||
static Future<void> _getDirectTypes() async {
|
||||
final service = RequestService(RequestHelper.directTypes);
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:didvan/services/media/media.dart';
|
||||
import 'package:didvan/services/storage/storage.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
|
@ -9,6 +10,7 @@ class AppInitializer {
|
|||
StorageService.appDocsDir =
|
||||
(await getApplicationDocumentsDirectory()).path;
|
||||
StorageService.appTempsDir = (await getTemporaryDirectory()).path;
|
||||
MediaService.init();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,18 +6,36 @@ import 'package:just_audio/just_audio.dart';
|
|||
|
||||
class MediaService {
|
||||
static final AudioPlayer audioPlayer = AudioPlayer();
|
||||
static dynamic lastAudioPath;
|
||||
static String? audioPlayerTag;
|
||||
|
||||
static Future<void> handleAudioPlayback(
|
||||
{required dynamic audioSource, required bool isNetworkAudio}) async {
|
||||
if (lastAudioPath == audioSource) {
|
||||
static void init() {
|
||||
audioPlayer.positionStream.listen((event) {
|
||||
if (audioPlayer.duration != null && audioPlayer.duration! < event) {
|
||||
audioPlayer.stop();
|
||||
audioPlayer.seek(const Duration(seconds: 0));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static Future<void> handleAudioPlayback({
|
||||
required dynamic audioSource,
|
||||
}) async {
|
||||
bool isNetworkAudio = audioSource.runtimeType == String;
|
||||
String tag;
|
||||
if (isNetworkAudio) {
|
||||
tag = audioSource;
|
||||
} else {
|
||||
tag = audioSource.path;
|
||||
}
|
||||
if (audioPlayerTag == tag) {
|
||||
if (audioPlayer.playing) {
|
||||
await audioPlayer.pause();
|
||||
} else {
|
||||
await audioPlayer.play();
|
||||
}
|
||||
} else {
|
||||
lastAudioPath = audioSource;
|
||||
await audioPlayer.stop();
|
||||
audioPlayerTag = tag;
|
||||
if (isNetworkAudio) {
|
||||
await audioPlayer.setUrl(
|
||||
RequestHelper.baseUrl +
|
||||
|
|
@ -32,7 +50,7 @@ class MediaService {
|
|||
await audioPlayer.setFilePath(audioSource.path);
|
||||
}
|
||||
}
|
||||
await audioPlayer.play();
|
||||
audioPlayer.play();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
14
pubspec.lock
14
pubspec.lock
|
|
@ -15,6 +15,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.1.6+1"
|
||||
audio_video_progress_bar:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: audio_video_progress_bar
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.10.0"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -357,13 +364,6 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.4.2"
|
||||
just_waveform:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: just_waveform
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.0.1"
|
||||
lints:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
|||
|
|
@ -52,7 +52,6 @@ dependencies:
|
|||
record: ^3.0.2
|
||||
just_audio: ^0.9.18
|
||||
record_web: ^0.2.1
|
||||
just_waveform: ^0.0.1
|
||||
persian_datetime_picker: ^2.4.0
|
||||
persian_number_utility: ^1.1.1
|
||||
bot_toast: ^4.0.1
|
||||
|
|
@ -61,6 +60,7 @@ dependencies:
|
|||
crop: ^0.5.2
|
||||
url_launcher: ^6.0.18
|
||||
transparent_image: ^2.0.0
|
||||
audio_video_progress_bar: ^0.10.0
|
||||
|
||||
|
||||
dev_dependencies:
|
||||
|
|
|
|||
Loading…
Reference in New Issue