component updates + restructuring

This commit is contained in:
MohammadTaha Basiri 2022-03-17 15:17:11 +03:30
parent a17f6f5ba5
commit 4ccd50b683
8 changed files with 565 additions and 11 deletions

View File

@ -0,0 +1,179 @@
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/studio_details_data.dart';
import 'package:didvan/services/media/media.dart';
import 'package:didvan/views/home/widgets/audio/audio_slider.dart';
import 'package:didvan/views/home/widgets/bookmark_button.dart';
import 'package:didvan/views/widgets/didvan/icon_button.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:didvan/views/widgets/ink_wrapper.dart';
import 'package:didvan/views/widgets/skeleton_image.dart';
import 'package:flutter/material.dart';
class AudioPlayerWidget extends StatelessWidget {
final StudioDetailsData podcast;
const AudioPlayerWidget({Key? key, required this.podcast}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
borderRadius: const BorderRadius.vertical(top: Radius.circular(8)),
color: Theme.of(context).colorScheme.surface,
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
margin: const EdgeInsets.symmetric(vertical: 20),
height: 3,
width: 50,
color: Theme.of(context).colorScheme.hint,
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: SkeletonImage(
imageUrl: podcast.image,
aspectRatio: 1 / 1,
),
),
const SizedBox(height: 16),
DidvanText(
podcast.title,
style: Theme.of(context).textTheme.bodyText1,
),
const SizedBox(height: 16),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: AudioSlider(
tag: podcast.media,
showTimer: true,
duration: podcast.duration,
),
),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
DidvanIconButton(
icon: DidvanIcons.sleep_timer_regular,
onPressed: () {},
),
Column(
children: [
DidvanIconButton(
size: 32,
icon: DidvanIcons.media_forward_solid,
onPressed: () {
MediaService.audioPlayer.seek(
Duration(
seconds:
MediaService.audioPlayer.position.inSeconds + 30,
),
);
},
),
const DidvanText('30', isEnglishFont: true),
],
),
_PlayPouseAnimatedIcon(
audioSource: podcast.media,
),
Column(
children: [
DidvanIconButton(
size: 32,
icon: DidvanIcons.media_backward_solid,
onPressed: () {
MediaService.audioPlayer.seek(
Duration(
seconds:
MediaService.audioPlayer.position.inSeconds - 10,
),
);
},
),
const DidvanText('10', isEnglishFont: true),
],
),
BookmarkButton(
gestureSize: 48,
value: podcast.marked,
onMarkChanged: (value) {},
),
],
),
DidvanIconButton(
size: 32,
icon: DidvanIcons.angle_down_regular,
onPressed: Navigator.of(context).pop,
),
],
),
);
}
}
class _PlayPouseAnimatedIcon extends StatefulWidget {
final String audioSource;
const _PlayPouseAnimatedIcon({Key? key, required this.audioSource})
: super(key: key);
@override
State<_PlayPouseAnimatedIcon> createState() => __PlayPouseAnimatedIconState();
}
class __PlayPouseAnimatedIconState extends State<_PlayPouseAnimatedIcon>
with SingleTickerProviderStateMixin {
late final AnimationController _animationController;
@override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this,
duration: DesignConfig.lowAnimationDuration,
);
if (MediaService.audioPlayer.playing) {
_animationController.forward();
}
}
@override
Widget build(BuildContext context) {
return InkWrapper(
borderRadius: BorderRadius.circular(100),
onPressed: () {
MediaService.handleAudioPlayback(
audioSource: widget.audioSource,
isVoiceMessage: false,
);
if (MediaService.audioPlayer.playing) {
_animationController.forward();
} else {
_animationController.reverse();
}
},
child: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.title,
shape: BoxShape.circle,
),
child: AnimatedIcon(
size: 40,
color: Theme.of(context).colorScheme.surface,
icon: AnimatedIcons.play_pause,
progress: _animationController,
),
),
);
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
}

View File

@ -0,0 +1,63 @@
import 'package:audio_video_progress_bar/audio_video_progress_bar.dart';
import 'package:didvan/config/design_config.dart';
import 'package:didvan/config/theme_data.dart';
import 'package:didvan/services/media/media.dart';
import 'package:flutter/material.dart';
class AudioSlider extends StatelessWidget {
final String tag;
final bool showTimer;
final int? duration;
final bool disableThumb;
const AudioSlider({
Key? key,
required this.tag,
this.showTimer = false,
this.duration,
this.disableThumb = false,
}) : 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) => ProgressBar(
thumbColor: Theme.of(context).colorScheme.title,
progressBarColor: DesignConfig.isDark
? Theme.of(context).colorScheme.title
: Theme.of(context).colorScheme.primary,
baseBarColor: Theme.of(context).colorScheme.border,
bufferedBarColor: Theme.of(context).colorScheme.splash,
total: MediaService.audioPlayer.duration ??
Duration(seconds: duration ?? 0),
progress: snapshot.data ?? Duration.zero,
buffered: _isPlaying
? MediaService.audioPlayer.bufferedPosition
: Duration.zero,
thumbRadius: disableThumb ? 0 : 6,
barHeight: 3,
timeLabelTextStyle: TextStyle(
fontSize: showTimer ? null : 0,
height: showTimer ? 3 : 0,
fontFamily: DesignConfig.fontFamily.replaceAll(
'-FA',
'',
),
),
onSeek: (value) => _onSeek(value.inMilliseconds),
),
),
),
);
}
void _onSeek(int value) {
MediaService.audioPlayer.seek(Duration(milliseconds: value));
}
}

View File

@ -0,0 +1,314 @@
// 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';
// 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);
// @override
// State<AudioVisualizer> createState() => _AudioVisualizerState();
// }
// 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();
// }
// 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(() {});
// },
// );
// },
// ),
// ],
// ),
// );
// }
// 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()),
// );
// }
// }
// 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);
// @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;
// _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;
// 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;
// }
// double normalise(int s, double height) {
// final y = 32768 + (scale * s).clamp(-32768.0, 32767.0).toDouble();
// return height - 1 - y * height / 65536;
// }
// }

View File

@ -8,14 +8,14 @@ import 'package:flutter/material.dart';
class BookmarkButton extends StatefulWidget {
final bool value;
final void Function(bool value) onMarkChanged;
final bool bigGestureSize;
final bool askForConfirmation;
final double gestureSize;
const BookmarkButton({
Key? key,
required this.value,
this.bigGestureSize = false,
required this.onMarkChanged,
this.askForConfirmation = false,
required this.gestureSize,
}) : super(key: key);
@override
@ -40,7 +40,7 @@ class _BookmarkButtonState extends State<BookmarkButton> {
@override
Widget build(BuildContext context) {
return DidvanIconButton(
gestureSize: widget.bigGestureSize ? 32 : 28,
gestureSize: widget.gestureSize,
icon: _value ? DidvanIcons.bookmark_solid : DidvanIcons.bookmark_regular,
onPressed: () async {
bool confirm = false;

View File

@ -112,7 +112,7 @@ class _FloatingNavigationBarState extends State<FloatingNavigationBar> {
Navigator.of(context).pop();
}
},
bigGestureSize: true,
gestureSize: 32,
),
SizedBox(
width: 60,
@ -151,7 +151,7 @@ class _FloatingNavigationBarState extends State<FloatingNavigationBar> {
Navigator.of(context).pop();
}
},
bigGestureSize: true,
gestureSize: 32,
),
if (widget.isRadar)
DidvanIconButton(

View File

@ -52,6 +52,7 @@ class DidvanAppBar extends StatelessWidget {
appBarData.title!,
style: Theme.of(context).textTheme.headline3,
color: Theme.of(context).colorScheme.title,
overflow: TextOverflow.ellipsis,
),
if (appBarData.subtitle != null)
DidvanText(

View File

@ -3,7 +3,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/utils/date_time.dart';
import 'package:didvan/views/home/widgets/multitype_overview.dart';
import 'package:didvan/views/home/widgets/overview/multitype.dart';
import 'package:didvan/views/home/widgets/tag_item.dart';
import 'package:didvan/views/widgets/animated_visibility.dart';
import 'package:didvan/views/widgets/didvan/card.dart';

View File

@ -42,17 +42,14 @@ class _DidvanScaffoldState extends State<DidvanScaffold> {
slivers: [
if (!widget.reverse && widget.appBarData != null)
SliverAppBar(
toolbarHeight: widget.appBarData!.isSmall ? 56 : 72,
toolbarHeight: (widget.appBarData!.isSmall ? 56 : 72) -
statusBarHeight,
backgroundColor: widget.backgroundColor ??
Theme.of(context).colorScheme.background,
automaticallyImplyLeading: false,
pinned: true,
flexibleSpace: DidvanAppBar(appBarData: widget.appBarData!),
),
if (!widget.reverse)
const SliverToBoxAdapter(
child: SizedBox(height: 16),
),
if (widget.children != null)
SliverPadding(
padding: widget.padding,