Migrate to one isolate audio service.

This commit is contained in:
stonega 2021-05-01 14:17:56 +08:00
parent 7312fdd10c
commit 7474afd7b8
4 changed files with 214 additions and 181 deletions

View File

@ -4,4 +4,4 @@ distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
distributionSha256Sum=abc10bcedb58806e8654210f96031db541bcd2d6fc3161e81cb0572d6a15e821 #distributionSha256Sum=abc10bcedb58806e8654210f96031db541bcd2d6fc3161e81cb0572d6a15e821

View File

@ -303,8 +303,9 @@ class LastPosition extends StatelessWidget {
.onSurface .onSurface
.withOpacity(0.12))), .withOpacity(0.12))),
textColor: data ? Colors.white : null, textColor: data ? Colors.white : null,
onPressed: () => onPressed: () => {}
audio.setSkipSilence(skipSilence: !data))), //audio.setSkipSilence(skipSilence: !data)
)),
SizedBox(width: 10), SizedBox(width: 10),
Selector<AudioPlayerNotifier, bool>( Selector<AudioPlayerNotifier, bool>(
selector: (_, audio) => audio.boostVolume, selector: (_, audio) => audio.boostVolume,
@ -328,8 +329,9 @@ class LastPosition extends StatelessWidget {
.onSurface .onSurface
.withOpacity(0.12))), .withOpacity(0.12))),
textColor: data ? Colors.white : null, textColor: data ? Colors.white : null,
onPressed: () => onPressed: () => {}
audio.setBoostVolume(boostVolume: !data))), // audio.setBoostVolume(boostVolume: !data)
)),
SizedBox(width: 10), SizedBox(width: 10),
FutureBuilder<PlayHistory>( FutureBuilder<PlayHistory>(
future: getPosition(episode), future: getPosition(episode),
@ -1336,13 +1338,7 @@ class _ControlPanelState extends State<ControlPanel>
AudioProcessingState AudioProcessingState
.buffering || .buffering ||
data.audioState == data.audioState ==
AudioProcessingState AudioProcessingState.loading
.connecting ||
data.audioState ==
AudioProcessingState.none ||
data.audioState ==
AudioProcessingState
.skippingToNext
? context.s.buffering ? context.s.buffering
: '', : '',
style: TextStyle( style: TextStyle(

View File

@ -1,5 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'dart:math' as math; import 'dart:developer';
import 'package:audio_service/audio_service.dart'; import 'package:audio_service/audio_service.dart';
import 'package:audio_session/audio_session.dart'; import 'package:audio_session/audio_session.dart';
@ -179,18 +179,19 @@ class AudioPlayerNotifier extends ChangeNotifier {
@override @override
void addListener(VoidCallback listener) async { void addListener(VoidCallback listener) async {
super.addListener(listener); await _initAudioData();
_initAudioData(); final _audioHandler = await AudioService.init(
_audioHandler = await AudioService.init(
builder: () => CustomAudioHandler(), builder: () => CustomAudioHandler(),
config: AudioServiceConfig( config: AudioServiceConfig(
androidNotificationChannelName: 'Tsacdop', androidNotificationChannelName: 'Tsacdop',
androidNotificationIcon: 'drawable/ic_notification', androidNotificationIcon: 'drawable/ic_notification',
androidEnableQueue: true, androidEnableQueue: true,
androidStopForegroundOnPause: true, androidStopForegroundOnPause: true,
preloadArtwork: false,
fastForwardInterval: Duration(seconds: _fastForwardSeconds), fastForwardInterval: Duration(seconds: _fastForwardSeconds),
rewindInterval: Duration(seconds: _rewindSeconds)), rewindInterval: Duration(seconds: _rewindSeconds)),
); );
super.addListener(listener);
} }
/// Audio playing state. /// Audio playing state.
@ -249,6 +250,9 @@ class AudioPlayerNotifier extends ChangeNotifier {
_skipSilence = await _skipSilenceStorage.getBool(defaultValue: false); _skipSilence = await _skipSilenceStorage.getBool(defaultValue: false);
_boostVolume = await _boostVolumeStorage.getBool(defaultValue: false); _boostVolume = await _boostVolumeStorage.getBool(defaultValue: false);
_volumeGain = await _volumeGainStorage.getInt(defaultValue: 3000); _volumeGain = await _volumeGainStorage.getInt(defaultValue: 3000);
_fastForwardSeconds =
await _fastForwardSecondsStorage.getInt(defaultValue: 30);
_rewindSeconds = await _rewindSecondsStorage.getInt(defaultValue: 30);
} }
Future _savePlayerHeight() async { Future _savePlayerHeight() async {
@ -446,11 +450,11 @@ class AudioPlayerNotifier extends ChangeNotifier {
//Check autoplay setting, if true only add one episode, else add playlist. //Check autoplay setting, if true only add one episode, else add playlist.
await _getAutoPlay(); await _getAutoPlay();
if (_autoPlay) { if (_autoPlay) {
for (var episode in playlist.episodes) { await _audioHandler.addQueueItems(
await _audioHandler.addQueueItem(episode.toMediaItem()); ([for (var episode in playlist.episodes) episode.toMediaItem()]));
}
} else { } else {
await _audioHandler.addQueueItem(playlist.episodes[index].toMediaItem()); await _audioHandler
.addQueueItems([playlist.episodes[index].toMediaItem()]);
} }
//Check auto sleep timer setting //Check auto sleep timer setting
await _getAutoSleepTimer(); await _getAutoSleepTimer();
@ -487,40 +491,40 @@ class AudioPlayerNotifier extends ChangeNotifier {
// 'setBoostVolume', [_boostVolume, _volumeGain]); // 'setBoostVolume', [_boostVolume, _volumeGain]);
// } // }
await _audioHandler.play(); _audioHandler.play();
_audioHandler.mediaItem
.where((event) => event != null) _audioHandler.mediaItem.where((event) => event != null).listen(
.listen((item) async { (item) async {
var episode = await _dbHelper.getRssItemWithMediaId(item.id); var episode = await _dbHelper.getRssItemWithMediaId(item.id);
if (episode == null) { if (episode == null) {
episode = _playFromSearchList.firstWhere((e) => e.mediaId == item.id, episode = _playFromSearchList.firstWhere((e) => e.mediaId == item.id,
orElse: () => null); orElse: () => null);
}
_backgroundAudioDuration = item.duration?.inMilliseconds ?? 0;
if (episode != null) {
_episode = episode;
_backgroundAudioDuration = item.duration.inMilliseconds ?? 0;
if (position > 0 &&
_backgroundAudioDuration > 0 &&
_episode.enclosureUrl == _playlist.episodeList[index]) {
await AudioService.seekTo(Duration(milliseconds: position));
position = 0;
} }
notifyListeners(); if (episode != null) {
} else { _episode = episode;
AudioService.skipToNext(); _backgroundAudioDuration = item.duration.inMilliseconds ?? 0;
} if (position > 0 &&
}); _backgroundAudioDuration > 0 &&
_episode.enclosureUrl == _playlist.episodeList[index]) {
await _audioHandler.seek(Duration(milliseconds: position));
position = 0;
}
notifyListeners();
} else {
_audioHandler.skipToNext();
}
},
);
_audioHandler.playbackState _audioHandler.playbackState
.distinct()
.where((event) => event != null) .where((event) => event != null)
.listen((event) async { .listen((event) async {
print(event.toString());
_current = DateTime.now(); _current = DateTime.now();
_audioState = event.processingState; _audioState = event.processingState;
_playing = event?.playing; _playing = event?.playing;
_currentSpeed = event.speed; _currentSpeed = event.speed;
_currentPosition = event.position.inMilliseconds ?? 0; _currentPosition = event.updatePosition.inMilliseconds ?? 0;
if (_audioState == AudioProcessingState.completed) { if (_audioState == AudioProcessingState.completed) {
if (_switchValue > 0) _switchValue = 0; if (_switchValue > 0) _switchValue = 0;
} }
@ -570,23 +574,12 @@ class AudioPlayerNotifier extends ChangeNotifier {
} }
//_episode = null; //_episode = null;
} }
}); if (event is Map && event['duration'] != null) {
_backgroundAudioDuration = event['duration'].inMilliseconds;
//double s = _currentSpeed ?? 1.0; notifyListeners();
var getPosition = 0; }
Timer.periodic(Duration(milliseconds: 500), (timer) { if (event is Map && event['position'] != null) {
var s = _currentSpeed ?? 1.0; _backgroundAudioPosition = event['position'].inMilliseconds;
if (_noSlide) {
if (_playing && !buffering) {
getPosition = _currentPosition +
((DateTime.now().difference(_current).inMilliseconds) * s)
.toInt();
_backgroundAudioPosition =
math.min(getPosition, _backgroundAudioDuration);
} else {
_backgroundAudioPosition = _currentPosition ?? 0;
}
if (_backgroundAudioDuration != null && if (_backgroundAudioDuration != null &&
_backgroundAudioDuration != 0 && _backgroundAudioDuration != 0 &&
_backgroundAudioPosition != null) { _backgroundAudioPosition != null) {
@ -595,19 +588,46 @@ class AudioPlayerNotifier extends ChangeNotifier {
} else { } else {
_seekSliderValue = 0; _seekSliderValue = 0;
} }
if (_backgroundAudioPosition > 0 &&
_backgroundAudioPosition < _backgroundAudioDuration) {
_lastPosition = _backgroundAudioPosition;
_playerStateStorage.savePlayerState(
_playlist.id, _episode.enclosureUrl, _lastPosition);
}
notifyListeners(); notifyListeners();
} }
if (_audioState == AudioProcessingState.completed) {
timer.cancel();
}
}); });
//double s = _currentSpeed ?? 1.0;
// var getPosition = 0;
// Timer.periodic(Duration(milliseconds: 500), (timer) {
// var s = _currentSpeed ?? 1.0;
// if (_noSlide) {
// if (_playing && !buffering) {
// getPosition = _currentPosition +
// ((DateTime.now().difference(_current).inMilliseconds) * s)
// .toInt();
// _backgroundAudioPosition =
// math.min(getPosition, _backgroundAudioDuration);
// } else {
// _backgroundAudioPosition = _currentPosition ?? 0;
// }
// if (_backgroundAudioDuration != null &&
// _backgroundAudioDuration != 0 &&
// _backgroundAudioPosition != null) {
// _seekSliderValue =
// _backgroundAudioPosition / _backgroundAudioDuration ?? 0;
// } else {
// _seekSliderValue = 0;
// }
// if (_backgroundAudioPosition > 0 &&
// _backgroundAudioPosition < _backgroundAudioDuration) {
// _lastPosition = _backgroundAudioPosition;
// _playerStateStorage.savePlayerState(
// _playlist.id, _episode.enclosureUrl, _lastPosition);
// }
// notifyListeners();
// }
// if (_audioState == AudioProcessingState.completed) {
// timer.cancel();
// }
// });
} }
/// Queue management. /// Queue management.
@ -972,32 +992,36 @@ class CustomAudioHandler extends BaseAudioHandler
final cacheStorage = KeyValueStorage(cacheMaxKey); final cacheStorage = KeyValueStorage(cacheMaxKey);
final layoutStorage = KeyValueStorage(notificationLayoutKey); final layoutStorage = KeyValueStorage(notificationLayoutKey);
final AudioPlayer _player = AudioPlayer(); final AudioPlayer _player = AudioPlayer();
AudioSession _session;
bool _playing;
bool _interrupted = false; bool _interrupted = false;
int _layoutIndex;
bool _stopAtEnd; bool _stopAtEnd;
int _cacheMax; int _cacheMax;
bool _isQueue; bool _isQueue = false;
int _index = 0;
bool get hasNext => queue.value.length > 0; bool get hasNext => queue.value.length > 0;
MediaItem get currentMediaItem => mediaItem.value; MediaItem get currentMediaItem => mediaItem.value;
bool get playing => playbackState.value.playing; bool get playing => playbackState.value.playing;
// MediaItem get _currentMediaItem => hasNext ? _queue[_index] : null; // MediaItem get _currentMediaItem => hasNext ? _queue[_index] : null;
BehaviorSubject<Map<String, dynamic>> customEvent; BehaviorSubject<Map<String, dynamic>> customEvent =
BehaviorSubject.seeded({});
CustomAudioHandler() { CustomAudioHandler() {
_player.currentIndexStream _handleInterruption();
.listen((index) => mediaItem.add(queue.value[index])); _player.currentIndexStream.listen(
_player.playbackEventStream.listen((event) { (index) {
if (queue.value.isNotEmpty) {
mediaItem.add(queue.value[index]);
}
},
);
_player.playbackEventStream.listen((event) async {
if (_layoutIndex == null) {
_layoutIndex = await layoutStorage.getInt();
}
playbackState.add(playbackState.value.copyWith( playbackState.add(playbackState.value.copyWith(
controls: [ controls: _getControls(_layoutIndex),
MediaControl.skipToPrevious, androidCompactActionIndices: [0, 1, 2],
playing ? MediaControl.pause : MediaControl.play,
MediaControl.skipToNext,
],
androidCompactActionIndices: [0, 1, 3],
systemActions: { systemActions: {
MediaAction.seek, MediaAction.seek,
MediaAction.seekForward, MediaAction.seekForward,
@ -1016,33 +1040,51 @@ class CustomAudioHandler extends BaseAudioHandler
speed: _player.speed, speed: _player.speed,
)); ));
}); });
_configureSession();
_player.positionStream.listen((event) {
customEvent.add({'position': event});
});
_player.durationStream.listen((event) {
log(event.toString());
mediaItem.add(mediaItem.value.copyWith(duration: _player.duration));
customEvent.add({'duration': event});
});
} }
Future _configureSession() async { Future<void> addQueueItems(List<MediaItem> items) async {
_session = await AudioSession.instance; super.addQueueItems(items);
await _session.configure(AudioSessionConfiguration.speech()); await _player.setAudioSource(
_handleInterruption(_session); ConcatenatingAudioSource(
useLazyPreparation: true,
shuffleOrder: DefaultShuffleOrder(),
children: [
for (var item in items) AudioSource.uri(Uri.parse(item.id)),
],
),
);
} }
void _handleInterruption(AudioSession session) async { void _handleInterruption() async {
final session = await AudioSession.instance;
await session.configure(AudioSessionConfiguration.speech());
session.interruptionEventStream.listen((event) { session.interruptionEventStream.listen((event) {
if (event.begin) { if (event.begin) {
switch (event.type) { switch (event.type) {
case AudioInterruptionType.pause: case AudioInterruptionType.pause:
if (_playing) { if (playing) {
pause(); pause();
_interrupted = true; _interrupted = true;
} }
break; break;
case AudioInterruptionType.duck: case AudioInterruptionType.duck:
if (_playing) { if (playing) {
pause(); pause();
_interrupted = true; _interrupted = true;
} }
break; break;
case AudioInterruptionType.unknown: case AudioInterruptionType.unknown:
if (_playing) { if (playing) {
pause(); pause();
_interrupted = true; _interrupted = true;
} }
@ -1051,12 +1093,12 @@ class CustomAudioHandler extends BaseAudioHandler
} else { } else {
switch (event.type) { switch (event.type) {
case AudioInterruptionType.pause: case AudioInterruptionType.pause:
if (!_playing && _interrupted) { if (!playing && _interrupted) {
play(); play();
} }
break; break;
case AudioInterruptionType.duck: case AudioInterruptionType.duck:
if (!_playing && _interrupted) { if (!playing && _interrupted) {
play(); play();
} }
break; break;
@ -1067,19 +1109,19 @@ class CustomAudioHandler extends BaseAudioHandler
} }
}); });
session.becomingNoisyEventStream.listen((_) { session.becomingNoisyEventStream.listen((_) {
if (_playing) pause(); if (playing) pause();
}); });
} }
void _handlePlaybackCompleted() async { // void _handlePlaybackCompleted() async {
if (hasNext) { // if (hasNext) {
skipToNext(); // skipToNext();
} else { // } else {
_player.stop(); // _player.stop();
removeQueueItemAt(0); // removeQueueItemAt(0);
stop(); // stop();
} // }
} // }
void playPause() { void playPause() {
if (playbackState.value.playing) { if (playbackState.value.playing) {
@ -1090,24 +1132,29 @@ class CustomAudioHandler extends BaseAudioHandler
} }
Future<void> skipToNext() async { Future<void> skipToNext() async {
customEvent.add({'mediaItem': currentMediaItem.title}); if (_isQueue && queue.value.length > 0) {
if (_isQueue) { removeQueueItemAt(0);
if (queue.value.length > 0) {
removeQueueItemAt(0);
}
} }
if (queue.value.length == 0 || _stopAtEnd) { if (queue.value.length == 0 || _stopAtEnd) {
await Future.delayed(Duration(milliseconds: 200)); await Future.delayed(Duration(milliseconds: 200));
await stop(); await stop();
} else { } else {
super.skipToNext(); await super.skipToNext();
var duration = await _player.durationFuture; // _updateDuration();
if (duration != null) {
mediaItem.add(currentMediaItem.copyWith(duration: duration));
}
} }
} }
// _updateDuration() async {
// var duration = await _player.durationFuture;
// if (duration != null) {
// mediaItem.add(currentMediaItem.copyWith(duration: duration));
// }
// }
_setMediaItem(MediaItem item) {
mediaItem.add(item);
}
Future<void> play() async { Future<void> play() async {
if (playing == null) { if (playing == null) {
// _cacheMax = await cacheStorage.getInt( // _cacheMax = await cacheStorage.getInt(
@ -1116,41 +1163,42 @@ class CustomAudioHandler extends BaseAudioHandler
// await cacheStorage.saveInt((200 * 1024 * 1024).toInt()); // await cacheStorage.saveInt((200 * 1024 * 1024).toInt());
// _cacheMax = 200 * 1024 * 1024; // _cacheMax = 200 * 1024 * 1024;
// } // }
await _player.setUrl(queue.value[_index].id); await super.play();
var duration = await _player.durationFuture; _player.play();
if (duration != null) {
mediaItem.add(currentMediaItem.copyWith(duration: duration));
}
_playFromStart(); _playFromStart();
// _updateDuration();
} else { } else {
_session.setActive(true); // _session.setActive(true);
super.play();
await _player.play(); await _player.play();
await _seekRelative(Duration(seconds: -3)); await _seekRelative(Duration(seconds: -3));
} }
} }
Future<void> _playFromStart() async { Future<void> _playFromStart() async {
_playing = true; // _session.setActive(true);
_session.setActive(true);
if (currentMediaItem.extras['skipSecondsStart'] > 0 || if (currentMediaItem.extras['skipSecondsStart'] > 0 ||
currentMediaItem.extras['skipSecondsEnd'] > 0) { currentMediaItem.extras['skipSecondsEnd'] > 0) {
_player _player
.seek(Duration(seconds: mediaItem.value.extras['skipSecondsStart'])); .seek(Duration(seconds: mediaItem.value.extras['skipSecondsStart']));
} }
try {
_player.play();
} catch (e) {
playbackState.add(playbackState.value
.copyWith(processingState: AudioProcessingState.error));
}
} }
Future<void> pause() async { Future<void> pause() async {
_player.pause(); await _player.pause();
} }
Future<void> seekTo(Duration position) async { Future<void> seek(Duration position) async {
await _player.seek(position); await _player.seek(position);
super.seek(position);
}
Future<void> fastForward() async {
_seekRelative(AudioService.config.fastForwardInterval);
}
Future<void> rewind() async {
_seekRelative(-AudioService.config.rewindInterval);
} }
Future<void> onClick(MediaButton button) async { Future<void> onClick(MediaButton button) async {
@ -1174,7 +1222,7 @@ class CustomAudioHandler extends BaseAudioHandler
Future<void> _seekRelative(Duration offset) async { Future<void> _seekRelative(Duration offset) async {
var newPosition = playbackState.value.position + offset; var newPosition = playbackState.value.position + offset;
if (newPosition < Duration.zero) newPosition = Duration.zero; if (newPosition < Duration.zero) newPosition = Duration.zero;
seekTo(newPosition); seek(newPosition);
} }
Future<void> stop() async { Future<void> stop() async {
@ -1198,17 +1246,14 @@ class CustomAudioHandler extends BaseAudioHandler
queue.add(queue.value..insert(index, item)); queue.add(queue.value..insert(index, item));
// await _player.setUrl(mediaItem.id, cacheMax: _cacheMax); // await _player.setUrl(mediaItem.id, cacheMax: _cacheMax);
await _player.setUrl(item.id); await _player.setUrl(item.id);
mediaItem.add(item); super.play();
var duration = await _player.durationFuture ?? Duration.zero; // _updateDuration();
mediaItem.add(mediaItem.value.copyWith(duration: duration));
_playFromStart(); _playFromStart();
//onPlay();
} else { } else {
queue.add(queue.value..insert(index, item)); queue.add(queue.value..insert(index, item));
} }
} }
@override
Future customAction(funtion, argument) async { Future customAction(funtion, argument) async {
switch (funtion) { switch (funtion) {
case 'stopAtEnd': case 'stopAtEnd':
@ -1242,24 +1287,16 @@ class CustomAudioHandler extends BaseAudioHandler
Future _changeQueue(List<MediaItem> newQueue) async { Future _changeQueue(List<MediaItem> newQueue) async {
await _player.stop(); await _player.stop();
customEvent.add({'mediaItem': currentMediaItem.title});
queue.add(newQueue); queue.add(newQueue);
_index = 0; await super.play();
await _player.setUrl(currentMediaItem.id);
var duration = await _player.durationFuture ?? Duration.zero;
mediaItem.add(currentMediaItem.copyWith(duration: duration));
_playFromStart(); _playFromStart();
// _updateDuration();
} }
Future _changeIndex(int index) async { Future _changeIndex(int index) async {
await _player.stop(); await super.skipToQueueItem(index);
customEvent.add({'mediaItem': currentMediaItem.title});
_index = index;
mediaItem.add(currentMediaItem);
await _player.setUrl(currentMediaItem.id);
var duration = await _player.durationFuture ?? Duration.zero;
mediaItem.add(currentMediaItem.copyWith(duration: duration));
_playFromStart(); _playFromStart();
// _updateDuration();
} }
// Future _setSkipSilence(bool boo) async { // Future _setSkipSilence(bool boo) async {
@ -1301,7 +1338,7 @@ class CustomAudioHandler extends BaseAudioHandler
switch (index) { switch (index) {
case 0: case 0:
return [ return [
_playing ? pauseControl : playControl, playing ? pauseControl : playControl,
forwardControl, forwardControl,
skipToNextControl, skipToNextControl,
stopControl stopControl
@ -1309,7 +1346,7 @@ class CustomAudioHandler extends BaseAudioHandler
break; break;
case 1: case 1:
return [ return [
_playing ? pauseControl : playControl, playing ? pauseControl : playControl,
rewindControl, rewindControl,
skipToNextControl, skipToNextControl,
stopControl stopControl
@ -1318,7 +1355,7 @@ class CustomAudioHandler extends BaseAudioHandler
case 2: case 2:
return [ return [
rewindControl, rewindControl,
_playing ? pauseControl : playControl, playing ? pauseControl : playControl,
forwardControl, forwardControl,
stopControl stopControl
]; ];
@ -1326,7 +1363,7 @@ class CustomAudioHandler extends BaseAudioHandler
break; break;
default: default:
return [ return [
_playing ? pauseControl : playControl, playing ? pauseControl : playControl,
forwardControl, forwardControl,
skipToNextControl, skipToNextControl,
stopControl stopControl

View File

@ -13,43 +13,42 @@ dependencies:
sdk: flutter sdk: flutter
auto_animated: ^3.0.0 auto_animated: ^3.0.0
audio_session: ^0.1.0 audio_session: ^0.1.0
cached_network_image: ^2.5.1 cached_network_image: ^3.0.0
color_thief_flutter: ^1.0.2 color_thief_flutter: ^1.0.2
confetti: ^0.5.5 confetti: ^0.5.5
cupertino_icons: ^1.0.2 cupertino_icons: ^1.0.2
connectivity: ^3.0.2 connectivity: ^3.0.3
crypto: ^3.0.0 crypto: ^3.0.1
device_info: ^2.0.0 device_info: ^2.0.0
dio: ^3.0.10
dio_cookie_manager: ^1.0.0 dio_cookie_manager: ^1.0.0
dio: ^3.0.10
extended_nested_scroll_view: ^3.0.0 extended_nested_scroll_view: ^3.0.0
effective_dart: ^1.3.1 effective_dart: ^1.3.1
equatable: ^1.2.5 feature_discovery: ^0.14.0
feature_discovery: ^0.13.0+2 file_picker: ^3.0.1
file_picker: ^2.1.4
flutter_html: ^0.11.1 flutter_html: ^0.11.1
fluttertoast: ^4.0.1 fluttertoast: ^4.0.1
flutter_isolate: ^1.0.0+14 flutter_isolate: ^2.0.0
flutter_linkify: ^4.0.2 flutter_linkify: ^4.1.0
flutter_file_dialog: ^1.0.0 flutter_file_dialog: ^2.0.0
flare_flutter: ^2.0.6 flare_flutter: ^3.0.0
fl_chart: ^0.12.2 fl_chart: ^0.12.2
line_icons: ^1.3.2 line_icons: ^2.0.1
flutter_media_metadata: ^0.0.3+2 flutter_media_metadata: ^0.1.0
marquee: ^1.7.0 marquee: ^2.0.0
google_fonts: ^2.0.0 google_fonts: ^2.0.0
image: ^3.0.1 image: ^3.0.2
intl: ^0.17.0 intl: ^0.17.0
json_serializable: ^3.5.1 json_serializable: ^4.1.0
json_annotation: ^3.1.1 json_annotation: ^4.0.1
permission_handler: ^5.0.1 permission_handler: ^6.1.1
provider: ^4.3.2 provider: ^5.0.0
sqflite: ^2.0.0+2 sqflite: ^2.0.0+3
state_notifier: ^0.6.0 state_notifier: ^0.7.0
tuple: ^1.0.3 tuple: ^2.0.0
url_launcher: ^5.7.10 url_launcher: ^6.0.3
workmanager: ^0.2.3 workmanager: ^0.2.3
wc_flutter_share: ^0.2.2 wc_flutter_share: ^0.4.0
just_audio: ^0.7.3 just_audio: ^0.7.3
audio_service: audio_service:
git: git:
@ -67,6 +66,7 @@ dependencies:
url: https://github.com/stonega/webfeed.git url: https://github.com/stonega/webfeed.git
dependency_overrides: dependency_overrides:
equatable: ^2.0.0
path_provider: ^2.0.1 path_provider: ^2.0.1
http_parser: ^4.0.0 http_parser: ^4.0.0
cookie_jar: ^2.0.0 cookie_jar: ^2.0.0
@ -75,7 +75,7 @@ dependency_overrides:
flutter_cache_manager: ^3.0.1 flutter_cache_manager: ^3.0.1
rxdart: ^0.26.0 rxdart: ^0.26.0
shared_preferences: 2.0.0 shared_preferences: 2.0.0
plugin_platform_interface: ^1.0.1 plugin_platform_interface: ^2.0.0
convert: ^3.0.0 convert: ^3.0.0
xml: ^5.0.2 xml: ^5.0.2
linkify: linkify: