mirror of
https://github.com/stonega/tsacdop
synced 2025-02-03 08:57:33 +01:00
deleted: .vscode/launch.json
deleted: .vscode/settings.json modified: android/app/src/main/res/drawable/launch_background.xml modified: android/app/src/main/res/drawable/launch_background_night.xml deleted: android/app/src/main/res/drawable/normal_background.xml modified: android/app/src/main/res/values/styles.xml modified: lib/class/audiostate.dart modified: lib/class/importompl.dart modified: lib/class/podcast_group.dart modified: lib/home/appbar/about.dart modified: lib/home/appbar/addpodcast.dart modified: lib/home/appbar/popupmenu.dart modified: lib/home/audioplayer.dart modified: lib/home/home.dart modified: lib/home/homescroll.dart modified: lib/local_storage/key_value_storage.dart modified: lib/local_storage/sqflite_localpodcast.dart modified: lib/main.dart modified: lib/podcasts/podcastgroup.dart modified: lib/podcasts/podcastlist.dart modified: lib/podcasts/podcastmanage.dart deleted: pubspec.lock modified: pubspec.yaml android/app/src/main/res/mipmap-hdpi/text_light.png android/app/src/main/res/mipmap-mdpi/text_light.png android/app/src/main/res/mipmap-xhdpi/text_light.png android/app/src/main/res/mipmap-xxhdpi/text_light.png android/app/src/main/res/mipmap-xxxhdpi/text_light.png android/app/src/main/res/values/colors.xml assets/listennotes_light.png assets/text_light.png
This commit is contained in:
parent
a6fc34e7bb
commit
f195d62b07
13
.vscode/launch.json
vendored
13
.vscode/launch.json
vendored
@ -1,13 +0,0 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Flutter",
|
||||
"request": "launch",
|
||||
"type": "dart"
|
||||
}
|
||||
]
|
||||
}
|
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -1,3 +0,0 @@
|
||||
{
|
||||
"java.configuration.updateBuildConfiguration": "automatic"
|
||||
}
|
@ -14,4 +14,6 @@
|
||||
android:gravity="bottom"
|
||||
android:src="@mipmap/text" />
|
||||
</item>
|
||||
<!-- <item name="android:navigationBarColor">@android:color/white</item>
|
||||
<item name="android:windowLightNavigationBar">true</item> -->
|
||||
</layer-list>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Modify this file to customize your launch splash screen -->
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@android:color/black" />
|
||||
<item android:drawable="@color/blackGrey" />
|
||||
<!-- You can insert your own image assets here -->
|
||||
<item>
|
||||
<bitmap
|
||||
@ -12,6 +12,6 @@
|
||||
<item android:bottom="100dp">
|
||||
<bitmap
|
||||
android:gravity="bottom"
|
||||
android:src="@mipmap/text" />
|
||||
android:src="@mipmap/text_light" />
|
||||
</item>
|
||||
</layer-list>
|
@ -1,17 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Modify this file to customize your launch splash screen -->
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@android:color/white" />
|
||||
<!-- You can insert your own image assets here -->
|
||||
<item>
|
||||
<bitmap
|
||||
android:gravity="center"
|
||||
android:tileMode="disabled"
|
||||
android:src="@mipmap/ic_launcher" />
|
||||
</item>
|
||||
<item android:bottom="100dp">
|
||||
<bitmap
|
||||
android:gravity="bottom"
|
||||
android:src="@mipmap/text" />
|
||||
</item>
|
||||
</layer-list>
|
@ -5,7 +5,4 @@
|
||||
Flutter draws its first frame -->
|
||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||
</style>
|
||||
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||
<item name="android:windowBackground">@drawable/normal_background</item>
|
||||
</style>
|
||||
</resources>
|
||||
|
@ -11,7 +11,7 @@ import 'package:audiofileplayer/audio_system.dart';
|
||||
import 'package:tsacdop/local_storage/key_value_storage.dart';
|
||||
import 'package:tsacdop/local_storage/sqflite_localpodcast.dart';
|
||||
|
||||
enum AudioState { load, play, pause, complete, error, stop }
|
||||
//enum AudioState { load, play, pause, complete, error, stop }
|
||||
|
||||
class PlayHistory {
|
||||
DBHelper dbHelper = DBHelper();
|
||||
@ -90,6 +90,14 @@ class AudioPlayer extends ChangeNotifier {
|
||||
bool _remoteAudioLoading = false;
|
||||
String _remoteErrorMessage;
|
||||
double _seekSliderValue = 0.0;
|
||||
int _lastPostion;
|
||||
bool _skip = false;
|
||||
bool _stopOnComplete = false;
|
||||
Timer _stopTimer;
|
||||
//Show stopwatch after user setting timer.
|
||||
bool _showStopWatch = false;
|
||||
|
||||
|
||||
final Logger _logger = Logger('audiofileplayer');
|
||||
|
||||
bool get backgroundAudioPlaying => _backgroundAudioPlaying;
|
||||
@ -99,16 +107,20 @@ class AudioPlayer extends ChangeNotifier {
|
||||
double get seekSliderValue => _seekSliderValue;
|
||||
String get remoteErrorMessage => _remoteErrorMessage;
|
||||
bool get playerRunning => _playerRunning;
|
||||
int _lastPostion;
|
||||
int get lastPositin => _lastPostion;
|
||||
Playlist get queue => _queue;
|
||||
|
||||
EpisodeBrief get episode => _episode;
|
||||
bool get stopOnComplete => _stopOnComplete;
|
||||
bool get showStopWatch => _showStopWatch;
|
||||
|
||||
|
||||
set setStopOnComplete(bool boo) {
|
||||
_stopOnComplete = boo;
|
||||
}
|
||||
|
||||
loadPlaylist() async {
|
||||
await _queue.getPlaylist();
|
||||
_lastPostion = await storage.getInt();
|
||||
print(_lastPostion);
|
||||
}
|
||||
|
||||
episodeLoad(EpisodeBrief episode) async {
|
||||
@ -134,22 +146,27 @@ class AudioPlayer extends ChangeNotifier {
|
||||
_backgroundAudioPlaying = false;
|
||||
await _queue.getPlaylist();
|
||||
_episode = _queue.playlist.first;
|
||||
_skip = true;
|
||||
await _play(_episode);
|
||||
_playerRunning = true;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
playNext() async {
|
||||
storage.saveInt(0);
|
||||
_lastPostion = 0;
|
||||
PlayHistory history = PlayHistory(_episode.title, _episode.enclosureUrl,
|
||||
backgroundAudioDuration, seekSliderValue);
|
||||
await dbHelper.saveHistory(history);
|
||||
await _queue.delFromPlaylist(_episode);
|
||||
if (_queue.playlist.length > 0) {
|
||||
if (_queue.playlist.length > 0 && !_stopOnComplete) {
|
||||
playlistLoad();
|
||||
} else {
|
||||
_stopOnComplete = false;
|
||||
_backgroundAudioPlaying = false;
|
||||
_remoteAudioLoading = false;
|
||||
_playerRunning = false;
|
||||
_disposeAudio();
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
@ -170,7 +187,7 @@ class AudioPlayer extends ChangeNotifier {
|
||||
_pauseBackgroundAudio();
|
||||
notifyListeners();
|
||||
PlayHistory history = PlayHistory(_episode.title, _episode.enclosureUrl,
|
||||
backgroundAudioDuration, seekSliderValue);
|
||||
backgroundAudioPosition, seekSliderValue);
|
||||
await dbHelper.saveHistory(history);
|
||||
}
|
||||
|
||||
@ -191,16 +208,42 @@ class AudioPlayer extends ChangeNotifier {
|
||||
_backgroundAudio.seek(positionSeconds);
|
||||
AudioSystem.instance.setPlaybackState(true, positionSeconds);
|
||||
}
|
||||
|
||||
//Set sleep time
|
||||
sleepTimer(int mins) {
|
||||
_showStopWatch = true;
|
||||
notifyListeners();
|
||||
_stopTimer = Timer(Duration(minutes: mins),(){
|
||||
_stopOnComplete = false;
|
||||
_backgroundAudioPlaying = false;
|
||||
_remoteAudioLoading = false;
|
||||
_playerRunning = false;
|
||||
_showStopWatch = false;
|
||||
_disposeAudio();
|
||||
notifyListeners();
|
||||
});
|
||||
}
|
||||
//Cancel sleep timer
|
||||
cancelTimer(){
|
||||
_stopTimer.cancel();
|
||||
_showStopWatch = false;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
_disposeAudio() {
|
||||
pauseAduio();
|
||||
AudioSystem.instance?.stopBackgroundDisplay();
|
||||
AudioSystem.instance?.removeMediaEventListener(_mediaEventListener);
|
||||
_backgroundAudio?.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
dispose() {
|
||||
pauseAduio();
|
||||
AudioSystem.instance.removeMediaEventListener(_mediaEventListener);
|
||||
_backgroundAudio?.dispose();
|
||||
_disposeAudio();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
_play(EpisodeBrief episodeBrief) async {
|
||||
AudioSystem.instance.addMediaEventListener(_mediaEventListener);
|
||||
String url = _queue.playlist.first.enclosureUrl;
|
||||
_getFile(url).then((result) {
|
||||
result == 'NotDownload'
|
||||
@ -225,6 +268,42 @@ class AudioPlayer extends ChangeNotifier {
|
||||
return ByteData.view(audio.buffer);
|
||||
}
|
||||
|
||||
onDuration(double durationSeconds) {
|
||||
_backgroundAudioDurationSeconds = durationSeconds;
|
||||
_remoteAudioLoading = false;
|
||||
_backgroundAudioPlaying = true;
|
||||
if (_skip) {
|
||||
_forwardBackgroundAudio(_lastPostion.toDouble());
|
||||
_backgroundAudioPositionSeconds = _lastPostion.toDouble();
|
||||
}
|
||||
_skip = false;
|
||||
_setNotification(true);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
onPosition(double positionSeconds) {
|
||||
if (_backgroundAudioPositionSeconds < _backgroundAudioDurationSeconds) {
|
||||
_seekSliderValue =
|
||||
_backgroundAudioPositionSeconds / _backgroundAudioDurationSeconds;
|
||||
_backgroundAudioPositionSeconds = positionSeconds;
|
||||
notifyListeners();
|
||||
} else {
|
||||
_seekSliderValue = 1;
|
||||
_backgroundAudioPositionSeconds = _backgroundAudioDurationSeconds;
|
||||
notifyListeners();
|
||||
}
|
||||
_lastPostion = positionSeconds.toInt();
|
||||
storage.saveInt(_lastPostion);
|
||||
}
|
||||
|
||||
onError(String message) {
|
||||
_remoteErrorMessage = message;
|
||||
_backgroundAudio.dispose();
|
||||
_backgroundAudio = null;
|
||||
_backgroundAudioPlaying = false;
|
||||
_remoteAudioLoading = false;
|
||||
}
|
||||
|
||||
void _initbackgroundAudioPlayerLocal(String path) {
|
||||
_remoteErrorMessage = null;
|
||||
_remoteAudioLoading = true;
|
||||
@ -232,35 +311,14 @@ class AudioPlayer extends ChangeNotifier {
|
||||
_backgroundAudio?.pause();
|
||||
_backgroundAudioPositionSeconds = 0;
|
||||
_setNotification(false);
|
||||
_backgroundAudio =
|
||||
Audio.loadFromByteData(audio, onDuration: (double durationSeconds) {
|
||||
_backgroundAudioDurationSeconds = durationSeconds;
|
||||
_remoteAudioLoading = false;
|
||||
_backgroundAudioPlaying = true;
|
||||
_setNotification(true);
|
||||
notifyListeners();
|
||||
}, onPosition: (double positionSeconds) {
|
||||
if (_backgroundAudioPositionSeconds < _backgroundAudioDurationSeconds) {
|
||||
_seekSliderValue =
|
||||
_backgroundAudioPositionSeconds / _backgroundAudioDurationSeconds;
|
||||
_backgroundAudioPositionSeconds = positionSeconds;
|
||||
notifyListeners();
|
||||
} else {
|
||||
_seekSliderValue = 1;
|
||||
_backgroundAudioPositionSeconds = _backgroundAudioDurationSeconds;
|
||||
notifyListeners();
|
||||
}
|
||||
storage.saveInt(positionSeconds.toInt());
|
||||
}, onError: (String message) {
|
||||
_remoteErrorMessage = message;
|
||||
_backgroundAudio.dispose();
|
||||
_backgroundAudio = null;
|
||||
_backgroundAudioPlaying = false;
|
||||
_remoteAudioLoading = false;
|
||||
}, onComplete: () {
|
||||
playNext();
|
||||
}, looping: false, playInBackground: true)
|
||||
..play();
|
||||
_backgroundAudio = Audio.loadFromByteData(audio,
|
||||
onDuration: (double durationSeconds) => onDuration(durationSeconds),
|
||||
onPosition: (double positionSeconds) => onPosition(positionSeconds),
|
||||
onError: (String message) => onError(message),
|
||||
onComplete: () => playNext(),
|
||||
looping: false,
|
||||
playInBackground: true)
|
||||
..play();
|
||||
}
|
||||
|
||||
void _initbackgroundAudioPlayer(String url) {
|
||||
@ -270,35 +328,14 @@ class AudioPlayer extends ChangeNotifier {
|
||||
_backgroundAudio?.pause();
|
||||
_backgroundAudioPositionSeconds = 0;
|
||||
_setNotification(false);
|
||||
_backgroundAudio =
|
||||
Audio.loadFromRemoteUrl(url, onDuration: (double durationSeconds) {
|
||||
_backgroundAudioDurationSeconds = durationSeconds;
|
||||
_remoteAudioLoading = false;
|
||||
_backgroundAudioPlaying = true;
|
||||
_setNotification(true);
|
||||
notifyListeners();
|
||||
}, onPosition: (double positionSeconds) {
|
||||
if (_backgroundAudioPositionSeconds < _backgroundAudioDurationSeconds) {
|
||||
_seekSliderValue =
|
||||
_backgroundAudioPositionSeconds / _backgroundAudioDurationSeconds;
|
||||
_backgroundAudioPositionSeconds = positionSeconds;
|
||||
notifyListeners();
|
||||
} else {
|
||||
_seekSliderValue = 1;
|
||||
_backgroundAudioPositionSeconds = _backgroundAudioDurationSeconds;
|
||||
notifyListeners();
|
||||
}
|
||||
storage.saveInt(positionSeconds.toInt());
|
||||
}, onError: (String message) {
|
||||
_remoteErrorMessage = message;
|
||||
_backgroundAudio.dispose();
|
||||
_backgroundAudio = null;
|
||||
_backgroundAudioPlaying = false;
|
||||
_remoteAudioLoading = false;
|
||||
}, onComplete: () {
|
||||
playNext();
|
||||
}, looping: false, playInBackground: true)
|
||||
..resume();
|
||||
_backgroundAudio = Audio.loadFromRemoteUrl(url,
|
||||
onDuration: (double durationSeconds) => onDuration(durationSeconds),
|
||||
onPosition: (double positionSeconds) => onPosition(positionSeconds),
|
||||
onError: (String message) => onError(message),
|
||||
onComplete: () => playNext(),
|
||||
looping: false,
|
||||
playInBackground: true)
|
||||
..resume();
|
||||
}
|
||||
|
||||
void _mediaEventListener(MediaEvent mediaEvent) {
|
||||
@ -341,7 +378,7 @@ class AudioPlayer extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _setNotification(bool b) async {
|
||||
Future<void> _setNotification(bool boo) async {
|
||||
final Uint8List imageBytes =
|
||||
File('${_episode.imagePath}').readAsBytesSync();
|
||||
AudioSystem.instance.setMetadata(AudioMetadata(
|
||||
@ -351,7 +388,7 @@ class AudioPlayer extends ChangeNotifier {
|
||||
genre: "Podcast",
|
||||
durationSeconds: _backgroundAudioDurationSeconds,
|
||||
artBytes: imageBytes));
|
||||
AudioSystem.instance.setPlaybackState(b, _backgroundAudioPositionSeconds);
|
||||
AudioSystem.instance.setPlaybackState(boo, _backgroundAudioPositionSeconds);
|
||||
AudioSystem.instance.setAndroidNotificationButtons(<dynamic>[
|
||||
AndroidMediaButtonType.pause,
|
||||
_forwardButton,
|
||||
|
@ -10,6 +10,7 @@ class ImportOmpl extends ChangeNotifier{
|
||||
|
||||
set rssTitle(String title){
|
||||
_rssTitle = title;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
ImportState get importState => _importState;
|
||||
|
@ -45,6 +45,15 @@ class PodcastGroup {
|
||||
}
|
||||
}
|
||||
|
||||
Color getColor() {
|
||||
if (color != '#000000') {
|
||||
int colorInt = int.parse('FF' + color.toUpperCase(), radix: 16);
|
||||
return Color(colorInt).withOpacity(1.0);
|
||||
} else {
|
||||
return Colors.blue[400];
|
||||
}
|
||||
}
|
||||
|
||||
List<PodcastLocal> _podcasts;
|
||||
|
||||
List<PodcastLocal> get podcasts => _podcasts;
|
||||
@ -102,15 +111,25 @@ class GroupList extends ChangeNotifier {
|
||||
}
|
||||
|
||||
Future delGroup(PodcastGroup podcastGroup) async {
|
||||
_groups.remove(podcastGroup);
|
||||
_isLoading = true;
|
||||
notifyListeners();
|
||||
podcastGroup.podcastList.forEach((podcast) {
|
||||
if (!_groups.first.podcastList.contains(podcast)) {
|
||||
_groups[0].podcastList.insert(0, podcast);
|
||||
}
|
||||
});
|
||||
_saveGroup();
|
||||
_groups.remove(podcastGroup);
|
||||
await _groups[0].getPodcasts();
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void updateGroup(PodcastGroup podcastGroup) {
|
||||
updateGroup(PodcastGroup podcastGroup) async{
|
||||
var oldGroup = _groups.firstWhere((it) => it.id == podcastGroup.id);
|
||||
var index = _groups.indexOf(oldGroup);
|
||||
_groups.replaceRange(index, index + 1, [podcastGroup]);
|
||||
await podcastGroup.getPodcasts();
|
||||
notifyListeners();
|
||||
_saveGroup();
|
||||
}
|
||||
@ -137,6 +156,7 @@ class GroupList extends ChangeNotifier {
|
||||
return result;
|
||||
}
|
||||
|
||||
//Change podcast groups
|
||||
changeGroup(String id, List<PodcastGroup> list) async {
|
||||
_isLoading = true;
|
||||
notifyListeners();
|
||||
@ -154,15 +174,16 @@ class GroupList extends ChangeNotifier {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
//Unsubscribe podcast
|
||||
removePodcast(String id) async {
|
||||
_isLoading = true;
|
||||
notifyListeners();
|
||||
_groups.forEach((group) async {
|
||||
group.podcastList.remove(id);
|
||||
group.podcastList.remove(id);
|
||||
});
|
||||
_saveGroup();
|
||||
await dbHelper.delPodcastLocal(id);
|
||||
await Future.forEach(_groups, (group) async {
|
||||
await Future.forEach(_groups, (group) async {
|
||||
await group.getPodcasts();
|
||||
});
|
||||
_isLoading = false;
|
||||
|
@ -29,7 +29,7 @@ class AboutApp extends StatelessWidget {
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Icon(icons, color: Colors.grey[700]),
|
||||
Icon(icons, color: Theme.of(context).accentColor),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
||||
),
|
||||
|
@ -10,10 +10,8 @@ import 'package:provider/provider.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:image/image.dart' as img;
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:tsacdop/class/audiostate.dart';
|
||||
import 'package:tsacdop/class/fireside_data.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
import 'package:tsacdop/class/importompl.dart';
|
||||
import 'package:tsacdop/class/podcast_group.dart';
|
||||
@ -32,30 +30,9 @@ class MyHomePage extends StatefulWidget {
|
||||
class _MyHomePageState extends State<MyHomePage> {
|
||||
final _MyHomePageDelegate _delegate = _MyHomePageDelegate();
|
||||
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
|
||||
bool _loadPlay;
|
||||
|
||||
static String _stringForSeconds(int seconds) {
|
||||
if (seconds == null) return null;
|
||||
return '${(seconds ~/ 60)}:${(seconds.truncate() % 60).toString().padLeft(2, '0')}';
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadPlay = false;
|
||||
_getPlaylist();
|
||||
}
|
||||
|
||||
_getPlaylist() async {
|
||||
await Provider.of<AudioPlayer>(context, listen: false).loadPlaylist();
|
||||
setState(() {
|
||||
_loadPlay = true;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var audio = Provider.of<AudioPlayer>(context, listen: false);
|
||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: SystemUiOverlayStyle(
|
||||
statusBarIconBrightness: Theme.of(context).accentColorBrightness,
|
||||
@ -78,44 +55,14 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||
},
|
||||
),
|
||||
title: Image(
|
||||
image: AssetImage('assets/text.png'),
|
||||
image: Theme.of(context).brightness == Brightness.light
|
||||
? AssetImage('assets/text.png') : AssetImage('assets/text_light.png'),
|
||||
height: 30,
|
||||
),
|
||||
actions: <Widget>[
|
||||
PopupMenu(),
|
||||
],
|
||||
),
|
||||
floatingActionButton: Selector<AudioPlayer, Tuple3<bool, Playlist, int>>(
|
||||
selector: (_, audio) => Tuple3(audio.playerRunning, audio.queue, audio.lastPositin),
|
||||
builder: (_, data, __) => !_loadPlay
|
||||
? Center()
|
||||
: data.item1 || data.item2.playlist.length == 0
|
||||
? Center()
|
||||
: FloatingActionButton.extended(
|
||||
tooltip: 'Play from playlist',
|
||||
highlightElevation: 2,
|
||||
hoverElevation: 2,
|
||||
onPressed: () => audio.playlistLoad(),
|
||||
elevation: 1,
|
||||
backgroundColor: Theme.of(context).accentColor,
|
||||
label: Text(_stringForSeconds(data.item3), style: TextStyle(color: Colors.white)),
|
||||
icon: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: <Widget>[
|
||||
CircleAvatar(
|
||||
radius: 15,
|
||||
backgroundImage: FileImage(File(
|
||||
"${data.item2.playlist.first.imagePath}")),
|
||||
),
|
||||
Container(
|
||||
height: 30.0,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Colors.black38),
|
||||
),
|
||||
],
|
||||
)),
|
||||
),
|
||||
body: Home(),
|
||||
),
|
||||
),
|
||||
@ -163,7 +110,8 @@ class _MyHomePageDelegate extends SearchDelegate<int> {
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(top: 400),
|
||||
child: Image(
|
||||
image: AssetImage('assets/listennotes.png'),
|
||||
image: Theme.of(context).brightness == Brightness.light
|
||||
? AssetImage('assets/listennotes.png') : AssetImage('assets/listennotes_light.png'),
|
||||
height: 20,
|
||||
),
|
||||
));
|
||||
|
@ -49,6 +49,7 @@ class PopupMenu extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
ImportOmpl importOmpl = Provider.of<ImportOmpl>(context, listen: false);
|
||||
GroupList groupList = Provider.of<GroupList>(context, listen: false);
|
||||
|
||||
_refreshAll() async {
|
||||
var dbHelper = DBHelper();
|
||||
List<PodcastLocal> podcastList = await dbHelper.getPodcastLocalAll();
|
||||
|
@ -1,3 +1,4 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:math' as math;
|
||||
|
||||
@ -25,12 +26,118 @@ class _PlayerWidgetState extends State<PlayerWidget> {
|
||||
return '${(seconds ~/ 60)}:${(seconds.truncate() % 60).toString().padLeft(2, '0')}';
|
||||
}
|
||||
|
||||
List minsToSelect = [1, 5, 10, 15, 20, 25, 30, 45, 60, 70, 80, 90, 99];
|
||||
//Show playlist widget.
|
||||
bool _showlist;
|
||||
//Show timer choose widget.
|
||||
bool _showTimer;
|
||||
//Store selected timer mins.
|
||||
int _minSelected;
|
||||
//Left time after user setting timer.
|
||||
int _timeLeft;
|
||||
Timer _timer;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_showlist = false;
|
||||
_showTimer = false;
|
||||
_minSelected = 5;
|
||||
_timeLeft = 0;
|
||||
}
|
||||
|
||||
setTimer() {
|
||||
_timeLeft = _minSelected;
|
||||
_timer = Timer.periodic(Duration(minutes: 1), (timer) {
|
||||
setState(() {
|
||||
if(_timeLeft < 1){
|
||||
_timer.cancel();
|
||||
} else{
|
||||
_timeLeft = _timeLeft - 1;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Widget _sleepTimer(BuildContext context) {
|
||||
var audio = Provider.of<AudioPlayer>(context);
|
||||
return Container(
|
||||
height: 50,
|
||||
margin: EdgeInsets.all(10.0),
|
||||
padding: EdgeInsets.symmetric(horizontal: 10.0),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).scaffoldBackgroundColor,
|
||||
borderRadius: BorderRadius.all(Radius.circular(10.0)),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: minsToSelect
|
||||
.map((e) => InkWell(
|
||||
onTap: () => setState(() => _minSelected = e),
|
||||
child: AnimatedContainer(
|
||||
duration: Duration(milliseconds: 300),
|
||||
curve: Curves.elasticOut,
|
||||
margin: EdgeInsets.symmetric(horizontal: 10.0),
|
||||
decoration: BoxDecoration(
|
||||
color: (e == _minSelected)
|
||||
? Theme.of(context).accentColor
|
||||
: Colors.grey[400],
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
height: (e == _minSelected) ? 40 : 30,
|
||||
width: (e == _minSelected) ? 40 : 30,
|
||||
child: Text(e.toString(),
|
||||
style: TextStyle(
|
||||
color: (e == _minSelected)
|
||||
? Colors.white
|
||||
: Colors.black)),
|
||||
),
|
||||
))
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
width: 100,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: <Widget>[
|
||||
Material(
|
||||
color: Colors.transparent,
|
||||
child: IconButton(
|
||||
icon: Icon(Icons.clear),
|
||||
onPressed: () {
|
||||
setState(() => _showTimer = false);
|
||||
},
|
||||
),
|
||||
),
|
||||
Material(
|
||||
color: Colors.transparent,
|
||||
child: IconButton(
|
||||
icon: Icon(Icons.done),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_showTimer = false;
|
||||
});
|
||||
audio.sleepTimer(_minSelected);
|
||||
setTimer();
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _expandedPanel(BuildContext context) {
|
||||
@ -104,13 +211,16 @@ class _PlayerWidgetState extends State<PlayerWidget> {
|
||||
padding: EdgeInsets.only(left: 30, right: 30),
|
||||
child: SliderTheme(
|
||||
data: SliderTheme.of(context).copyWith(
|
||||
activeTrackColor: Colors.blue[100],
|
||||
activeTrackColor: Theme.of(context)
|
||||
.accentColor
|
||||
.withOpacity(0.5),
|
||||
inactiveTrackColor: Colors.grey[300],
|
||||
trackHeight: 3.0,
|
||||
thumbColor: Colors.blue[400],
|
||||
thumbColor: Theme.of(context).accentColor,
|
||||
thumbShape: RoundSliderThumbShape(
|
||||
enabledThumbRadius: 6.0),
|
||||
overlayColor: Colors.blue.withAlpha(32),
|
||||
overlayColor:
|
||||
Theme.of(context).accentColor.withAlpha(32),
|
||||
overlayShape:
|
||||
RoundSliderOverlayShape(overlayRadius: 14.0),
|
||||
),
|
||||
@ -167,113 +277,192 @@ class _PlayerWidgetState extends State<PlayerWidget> {
|
||||
child: Selector<AudioPlayer, bool>(
|
||||
selector: (_, audio) => audio.backgroundAudioPlaying,
|
||||
builder: (_, backplay, __) {
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
IconButton(
|
||||
padding: EdgeInsets.symmetric(horizontal: 30.0),
|
||||
onPressed: backplay
|
||||
? () => audio.forwardAudio(-10)
|
||||
: null,
|
||||
iconSize: 32.0,
|
||||
icon: Icon(Icons.replay_10),
|
||||
color: Theme.of(context).tabBarTheme.labelColor),
|
||||
backplay
|
||||
? IconButton(
|
||||
padding:
|
||||
EdgeInsets.symmetric(horizontal: 30.0),
|
||||
onPressed: backplay
|
||||
? () {
|
||||
audio.pauseAduio();
|
||||
}
|
||||
: null,
|
||||
iconSize: 40.0,
|
||||
icon: Icon(Icons.pause_circle_filled),
|
||||
color:
|
||||
Theme.of(context).tabBarTheme.labelColor)
|
||||
: IconButton(
|
||||
padding:
|
||||
EdgeInsets.symmetric(horizontal: 30.0),
|
||||
onPressed: backplay
|
||||
? null
|
||||
: () {
|
||||
audio.resumeAudio();
|
||||
},
|
||||
iconSize: 40.0,
|
||||
icon: Icon(Icons.play_circle_filled),
|
||||
color:
|
||||
Theme.of(context).tabBarTheme.labelColor),
|
||||
IconButton(
|
||||
padding: EdgeInsets.symmetric(horizontal: 30.0),
|
||||
onPressed: backplay
|
||||
? () => audio.forwardAudio(30)
|
||||
: null,
|
||||
iconSize: 32.0,
|
||||
icon: Icon(Icons.forward_30),
|
||||
color: Theme.of(context).tabBarTheme.labelColor),
|
||||
],
|
||||
return Material(
|
||||
color: Colors.transparent,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
IconButton(
|
||||
padding: EdgeInsets.symmetric(horizontal: 30.0),
|
||||
onPressed: backplay
|
||||
? () => audio.forwardAudio(-10)
|
||||
: null,
|
||||
iconSize: 32.0,
|
||||
icon: Icon(Icons.replay_10),
|
||||
color:
|
||||
Theme.of(context).tabBarTheme.labelColor),
|
||||
backplay
|
||||
? IconButton(
|
||||
padding:
|
||||
EdgeInsets.symmetric(horizontal: 30.0),
|
||||
onPressed: backplay
|
||||
? () {
|
||||
audio.pauseAduio();
|
||||
}
|
||||
: null,
|
||||
iconSize: 40.0,
|
||||
icon: Icon(Icons.pause_circle_filled),
|
||||
color: Theme.of(context)
|
||||
.tabBarTheme
|
||||
.labelColor)
|
||||
: IconButton(
|
||||
padding:
|
||||
EdgeInsets.symmetric(horizontal: 30.0),
|
||||
onPressed: backplay
|
||||
? null
|
||||
: () {
|
||||
audio.resumeAudio();
|
||||
},
|
||||
iconSize: 40.0,
|
||||
icon: Icon(Icons.play_circle_filled),
|
||||
color: Theme.of(context)
|
||||
.tabBarTheme
|
||||
.labelColor),
|
||||
IconButton(
|
||||
padding: EdgeInsets.symmetric(horizontal: 30.0),
|
||||
onPressed: backplay
|
||||
? () => audio.forwardAudio(30)
|
||||
: null,
|
||||
iconSize: 32.0,
|
||||
icon: Icon(Icons.forward_30),
|
||||
color:
|
||||
Theme.of(context).tabBarTheme.labelColor),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
Spacer(),
|
||||
Container(
|
||||
height: 50.0,
|
||||
margin: EdgeInsets.all(10.0),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).scaffoldBackgroundColor,
|
||||
borderRadius: BorderRadius.all(Radius.circular(10.0)),
|
||||
),
|
||||
child: Selector<AudioPlayer, EpisodeBrief>(
|
||||
selector: (_, audio) => audio.episode,
|
||||
builder: (_, episode, __) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: EdgeInsets.all(5.0),
|
||||
),
|
||||
Container(
|
||||
height: 30.0,
|
||||
width: 30.0,
|
||||
child: CircleAvatar(
|
||||
backgroundImage:
|
||||
FileImage(File("${episode.imagePath}")),
|
||||
),
|
||||
),
|
||||
Spacer(),
|
||||
Material(
|
||||
color: Colors.transparent,
|
||||
child: IconButton(
|
||||
onPressed: () => Navigator.push(
|
||||
context,
|
||||
SlideUptRoute(
|
||||
page: EpisodeDetail(
|
||||
episodeItem: episode,
|
||||
heroTag: 'playpanel')),
|
||||
),
|
||||
icon: Icon(Icons.info),
|
||||
),
|
||||
),
|
||||
Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.only(
|
||||
topRight: Radius.circular(10.0),
|
||||
bottomRight: Radius.circular(10.0)),
|
||||
child: Container(
|
||||
height: 50.0,
|
||||
width: 50.0,
|
||||
child: Icon(Icons.keyboard_arrow_up)),
|
||||
onTap: () => setState(() => _showlist = true)),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
!_showTimer
|
||||
// Setting sleep timer
|
||||
? Container(
|
||||
height: 50.0,
|
||||
margin: EdgeInsets.all(10.0),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).scaffoldBackgroundColor,
|
||||
borderRadius: BorderRadius.all(Radius.circular(10.0)),
|
||||
),
|
||||
child: Selector<AudioPlayer,
|
||||
Tuple3<EpisodeBrief, bool, bool>>(
|
||||
selector: (_, audio) => Tuple3(audio.episode,
|
||||
audio.stopOnComplete, audio.showStopWatch),
|
||||
builder: (_, data, __) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: EdgeInsets.all(5.0),
|
||||
),
|
||||
Container(
|
||||
height: 30.0,
|
||||
width: 30.0,
|
||||
child: CircleAvatar(
|
||||
backgroundImage: FileImage(
|
||||
File("${data.item1.imagePath}")),
|
||||
),
|
||||
),
|
||||
Spacer(),
|
||||
Material(
|
||||
color: Colors.transparent,
|
||||
child: !data.item3
|
||||
? PopupMenuButton(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(10))),
|
||||
elevation: 1,
|
||||
tooltip: 'Sleep Timer',
|
||||
icon: Icon(
|
||||
Icons.brightness_2,
|
||||
color: data.item2
|
||||
? Theme.of(context).accentColor
|
||||
: Theme.of(context)
|
||||
.iconTheme
|
||||
.color,
|
||||
),
|
||||
itemBuilder: (context) => [
|
||||
PopupMenuItem(
|
||||
value: 1,
|
||||
child: Text(
|
||||
'End of this episode')),
|
||||
PopupMenuItem(
|
||||
value: 2,
|
||||
child: Text('Timer'),
|
||||
),
|
||||
],
|
||||
onSelected: (value) {
|
||||
if (value == 1) {
|
||||
audio.setStopOnComplete = true;
|
||||
} else if (value == 2) {
|
||||
setState(() => _showTimer = true);
|
||||
}
|
||||
})
|
||||
: PopupMenuButton(
|
||||
tooltip: 'Time Left',
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(10))),
|
||||
elevation: 1,
|
||||
icon: Container(
|
||||
alignment: Alignment.center,
|
||||
height: 25,
|
||||
width: 25,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color:
|
||||
Theme.of(context).accentColor,
|
||||
),
|
||||
child: Text(
|
||||
_timeLeft.toString(),
|
||||
style: TextStyle(
|
||||
color: Colors.white)),
|
||||
),
|
||||
itemBuilder: (context) => [
|
||||
PopupMenuItem(
|
||||
value: 1,
|
||||
child: Text('Cancel')),
|
||||
],
|
||||
onSelected: (value) {
|
||||
audio.cancelTimer();
|
||||
_timer.cancel();
|
||||
setState(() => _timeLeft = 0);
|
||||
},
|
||||
),
|
||||
),
|
||||
Material(
|
||||
color: Colors.transparent,
|
||||
child: IconButton(
|
||||
onPressed: () => Navigator.push(
|
||||
context,
|
||||
SlideUptRoute(
|
||||
page: EpisodeDetail(
|
||||
episodeItem: data.item1,
|
||||
heroTag: 'playpanel')),
|
||||
),
|
||||
icon: Icon(Icons.info),
|
||||
),
|
||||
),
|
||||
Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.only(
|
||||
topRight: Radius.circular(10.0),
|
||||
bottomRight: Radius.circular(10.0)),
|
||||
child: Container(
|
||||
height: 50.0,
|
||||
width: 50.0,
|
||||
child: Icon(Icons.keyboard_arrow_up)),
|
||||
onTap: () =>
|
||||
setState(() => _showlist = true)),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
)
|
||||
: _sleepTimer(context),
|
||||
]),
|
||||
),
|
||||
Container(
|
||||
@ -283,8 +472,8 @@ class _PlayerWidgetState extends State<PlayerWidget> {
|
||||
height: _showlist ? 300 : 0,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
alignment: Alignment.center,
|
||||
margin: EdgeInsets.all(20),
|
||||
padding: EdgeInsets.only(bottom: 10.0),
|
||||
// margin: EdgeInsets.all(20),
|
||||
//padding: EdgeInsets.only(bottom: 10.0),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(10.0)),
|
||||
color: Theme.of(context).scaffoldBackgroundColor,
|
||||
|
@ -1,15 +1,45 @@
|
||||
import 'dart:io';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
import 'hometab.dart';
|
||||
import 'package:tsacdop/home/appbar/importompl.dart';
|
||||
import 'package:tsacdop/home/audioplayer.dart';
|
||||
import 'package:tsacdop/class/audiostate.dart';
|
||||
import 'homescroll.dart';
|
||||
|
||||
class Home extends StatelessWidget {
|
||||
class Home extends StatefulWidget {
|
||||
@override
|
||||
_HomeState createState() => _HomeState();
|
||||
}
|
||||
|
||||
class _HomeState extends State<Home> {
|
||||
bool _loadPlay;
|
||||
|
||||
static String _stringForSeconds(int seconds) {
|
||||
if (seconds == null) return null;
|
||||
return '${(seconds ~/ 60)}:${(seconds.truncate() % 60).toString().padLeft(2, '0')}';
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadPlay = false;
|
||||
_getPlaylist();
|
||||
}
|
||||
|
||||
_getPlaylist() async {
|
||||
await Provider.of<AudioPlayer>(context, listen: false).loadPlaylist();
|
||||
setState(() {
|
||||
_loadPlay = true;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var audio = Provider.of<AudioPlayer>(context, listen: false);
|
||||
return Stack(children: <Widget>[
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
@ -22,8 +52,70 @@ class Home extends StatelessWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
Container(
|
||||
child: PlayerWidget()),
|
||||
AnimatedPositioned(
|
||||
duration: Duration(milliseconds: 2000),
|
||||
curve: Curves.elasticOut,
|
||||
bottom: 50,
|
||||
right: _loadPlay ? 5 : -25,
|
||||
child: Container(
|
||||
child: Selector<AudioPlayer, Tuple3<bool, Playlist, int>>(
|
||||
selector: (_, audio) =>
|
||||
Tuple3(audio.playerRunning, audio.queue, audio.lastPositin),
|
||||
builder: (_, data, __) => !_loadPlay
|
||||
? Center()
|
||||
: data.item1 || data.item2.playlist.length == 0
|
||||
? Center()
|
||||
: InkWell(
|
||||
onTap: () => audio.playlistLoad(),
|
||||
child: Stack(
|
||||
alignment: Alignment.centerLeft,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
padding: EdgeInsets.only(left: 45, right: 10.0),
|
||||
alignment: Alignment.centerRight,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).accentColor,
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(20.0),
|
||||
bottomLeft: Radius.circular(20.0),
|
||||
bottomRight: Radius.circular(10.0),
|
||||
topRight: Radius.circular(10.0)),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Theme.of(context).brightness ==
|
||||
Brightness.light
|
||||
? Colors.grey[400]
|
||||
: Colors.grey[800],
|
||||
blurRadius: 4,
|
||||
offset: Offset(1, 1)),
|
||||
]),
|
||||
height: 40,
|
||||
child: Text(_stringForSeconds(data.item3) + '...',
|
||||
style: TextStyle(color: Colors.white)),
|
||||
),
|
||||
CircleAvatar(
|
||||
radius: 20,
|
||||
backgroundImage: FileImage(File(
|
||||
"${data.item2.playlist.first.imagePath}")),
|
||||
),
|
||||
Container(
|
||||
height: 40.0,
|
||||
width: 40,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Colors.black12),
|
||||
child: Icon(
|
||||
Icons.play_arrow,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(child: PlayerWidget()),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -303,7 +303,7 @@ class _PodcastPreviewState extends State<PodcastPreview> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Color _c = (Theme.of(context).brightness == Brightness.light)
|
||||
? widget.podcastLocal.primaryColor.colorizedark()
|
||||
? widget.podcastLocal.primaryColor.colorizedark()
|
||||
: widget.podcastLocal.primaryColor.colorizeLight();
|
||||
return Column(
|
||||
children: <Widget>[
|
||||
@ -374,9 +374,41 @@ class ShowEpisode extends StatelessWidget {
|
||||
final List<EpisodeBrief> podcast;
|
||||
final PodcastLocal podcastLocal;
|
||||
ShowEpisode({Key key, this.podcast, this.podcastLocal}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
double _width = MediaQuery.of(context).size.width;
|
||||
_showPopupMenu(Offset offset) async {
|
||||
print(offset.dx);
|
||||
double left = offset.dx;
|
||||
double top = offset.dy;
|
||||
await showMenu(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(10))),
|
||||
context: context,
|
||||
position: RelativeRect.fromLTRB(left, top, _width - left, 0),
|
||||
items: [
|
||||
PopupMenuItem(
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Icon(Icons.play_circle_outline),
|
||||
Padding(padding: EdgeInsets.symmetric(horizontal: 2),),
|
||||
Text('Play')
|
||||
],
|
||||
),
|
||||
),
|
||||
PopupMenuItem(child: Row(
|
||||
children: <Widget>[
|
||||
Icon(Icons.favorite_border),
|
||||
Padding(padding: EdgeInsets.symmetric(horizontal: 2),),
|
||||
Text('Like')
|
||||
],
|
||||
)),
|
||||
],
|
||||
elevation: 8.0,
|
||||
);
|
||||
}
|
||||
|
||||
return CustomScrollView(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
primary: false,
|
||||
@ -392,11 +424,12 @@ class ShowEpisode extends StatelessWidget {
|
||||
),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(BuildContext context, int index) {
|
||||
Color _c =
|
||||
(Theme.of(context).brightness == Brightness.light)
|
||||
? podcastLocal.primaryColor.colorizedark()
|
||||
: podcastLocal.primaryColor.colorizeLight();
|
||||
return InkWell(
|
||||
Color _c = (Theme.of(context).brightness == Brightness.light)
|
||||
? podcastLocal.primaryColor.colorizedark()
|
||||
: podcastLocal.primaryColor.colorizeLight();
|
||||
return GestureDetector(
|
||||
onLongPressStart: (details) => _showPopupMenu(Offset(
|
||||
details.globalPosition.dx, details.globalPosition.dy)),
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
|
@ -34,7 +34,6 @@ class KeyValueStorage {
|
||||
|
||||
Future<bool> saveInt(int setting) async{
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
print(setting.toString());
|
||||
return prefs.setInt(key, setting);
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,8 @@
|
||||
import 'package:sqflite/sqflite.dart';
|
||||
import 'dart:async';
|
||||
import 'dart:io' as io;
|
||||
import 'package:path/path.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:flutter_downloader/flutter_downloader.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:tsacdop/class/podcastlocal.dart';
|
||||
import 'package:tsacdop/class/audiostate.dart';
|
||||
@ -20,8 +18,8 @@ class DBHelper {
|
||||
}
|
||||
|
||||
initDb() async {
|
||||
io.Directory documentsDirectory = await getApplicationDocumentsDirectory();
|
||||
String path = join(documentsDirectory.path, "podcasts.db");
|
||||
var documentsDirectory = await getDatabasesPath();
|
||||
String path = join(documentsDirectory, "podcasts.db");
|
||||
Database theDb = await openDatabase(path, version: 1, onCreate: _onCreate);
|
||||
return theDb;
|
||||
}
|
||||
|
@ -32,15 +32,12 @@ Future main() async {
|
||||
callbackDispatcher,
|
||||
isInDebugMode: true,
|
||||
);
|
||||
Workmanager.registerPeriodicTask("update", "simplePeriodicTask",
|
||||
frequency: Duration(hours: 1),
|
||||
initialDelay: Duration(seconds: 10),
|
||||
Workmanager.registerPeriodicTask("2", "update_podcasts",
|
||||
frequency: Duration(minutes: 1),
|
||||
initialDelay: Duration(seconds: 5),
|
||||
constraints: Constraints(
|
||||
networkType: NetworkType.connected,
|
||||
requiresBatteryNotLow: true,
|
||||
requiresCharging: false,
|
||||
requiresDeviceIdle: true,
|
||||
requiresStorageNotLow: true));
|
||||
));
|
||||
|
||||
await FlutterDownloader.initialize();
|
||||
await SystemChrome.setPreferredOrientations(
|
||||
@ -55,7 +52,7 @@ void callbackDispatcher() {
|
||||
await dbHelper.updatePodcastRss(podcastLocal);
|
||||
print('Refresh ' + podcastLocal.title);
|
||||
});
|
||||
return Future.value(true);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,13 @@
|
||||
import 'dart:io';
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter_colorpicker/flutter_colorpicker.dart';
|
||||
|
||||
import 'package:tsacdop/class/podcast_group.dart';
|
||||
import 'package:tsacdop/class/podcastlocal.dart';
|
||||
import 'package:tsacdop/podcasts/podcastdetail.dart';
|
||||
@ -18,97 +21,460 @@ class PodcastGroupList extends StatefulWidget {
|
||||
_PodcastGroupListState createState() => _PodcastGroupListState();
|
||||
}
|
||||
|
||||
class _PodcastGroupListState extends State<PodcastGroupList> {
|
||||
bool _loadSave;
|
||||
class _PodcastGroupListState extends State<PodcastGroupList>
|
||||
with SingleTickerProviderStateMixin {
|
||||
bool _showSetting;
|
||||
AnimationController _controller;
|
||||
Animation _animation;
|
||||
double _fraction;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadSave = false;
|
||||
_showSetting = false;
|
||||
_fraction = 0;
|
||||
_controller = AnimationController(
|
||||
duration: const Duration(milliseconds: 500), vsync: this);
|
||||
_animation = Tween(begin: 0.0, end: 1.0).animate(_controller)
|
||||
..addListener(() {
|
||||
if (mounted)
|
||||
setState(() {
|
||||
_fraction = _animation.value;
|
||||
});
|
||||
});
|
||||
_controller.addStatusListener((status) {
|
||||
if (status == AnimationStatus.completed) {
|
||||
_controller.stop();
|
||||
} else if (status == AnimationStatus.dismissed) {
|
||||
_controller.stop();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Widget _saveButton(BuildContext context) {
|
||||
var podcastList = widget.group.podcasts;
|
||||
var _groupList = Provider.of<GroupList>(context);
|
||||
return Container(
|
||||
child: InkWell(
|
||||
child: AnimatedContainer(
|
||||
duration: Duration(milliseconds: 800),
|
||||
width: _loadSave ? 70 : 0,
|
||||
height: 60,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blue,
|
||||
shape: BoxShape.circle,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.grey[700],
|
||||
blurRadius: 5,
|
||||
offset: Offset(1, 1),
|
||||
),
|
||||
]),
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
'Save',
|
||||
style: TextStyle(color: Colors.white),
|
||||
maxLines: 1,
|
||||
)),
|
||||
onTap: () async {
|
||||
await _groupList.saveOrder(widget.group, podcastList);
|
||||
Fluttertoast.showToast(
|
||||
msg: 'Setting Saved',
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
setState(() {
|
||||
_loadSave = false;
|
||||
});
|
||||
},
|
||||
var _groupList = Provider.of<GroupList>(context, listen: false);
|
||||
return Transform(
|
||||
alignment: FractionalOffset(0.5, 0.5),
|
||||
transform: Matrix4.rotationY(math.pi * _fraction),
|
||||
child: Container(
|
||||
child: InkWell(
|
||||
child: Container(
|
||||
width: 50,
|
||||
height: 50,
|
||||
decoration: BoxDecoration(
|
||||
color: _fraction > 0.5 ? Colors.red : widget.group.getColor(),
|
||||
shape: BoxShape.circle,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.grey[700],
|
||||
blurRadius: 5,
|
||||
offset: Offset(1, 1),
|
||||
),
|
||||
]),
|
||||
alignment: Alignment.center,
|
||||
child: Icon(
|
||||
_fraction > 0.5 ? Icons.save : Icons.settings,
|
||||
color: Colors.white,
|
||||
)),
|
||||
onTap: () async {
|
||||
if (_fraction == 0) {
|
||||
setState(() {
|
||||
_showSetting = true;
|
||||
});
|
||||
} else {
|
||||
await _groupList.saveOrder(widget.group, podcastList);
|
||||
Fluttertoast.showToast(
|
||||
msg: 'Setting Saved',
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
_controller.reverse();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var groupList = Provider.of<GroupList>(context, listen: false);
|
||||
return widget.group.podcastList.length == 0
|
||||
? Container(
|
||||
color: Theme.of(context).primaryColor,
|
||||
)
|
||||
: Container(
|
||||
color: Theme.of(context).primaryColor,
|
||||
child: Stack(
|
||||
children: <Widget>[
|
||||
ReorderableListView(
|
||||
onReorder: (int oldIndex, int newIndex) {
|
||||
setState(() {
|
||||
if (newIndex > oldIndex) {
|
||||
newIndex -= 1;
|
||||
}
|
||||
final PodcastLocal podcast =
|
||||
widget.group.podcasts.removeAt(oldIndex);
|
||||
widget.group.podcasts.insert(newIndex, podcast);
|
||||
_loadSave = true;
|
||||
});
|
||||
},
|
||||
children: widget.group.podcasts
|
||||
.map<Widget>((PodcastLocal podcastLocal) {
|
||||
return Container(
|
||||
decoration:
|
||||
BoxDecoration(color: Theme.of(context).primaryColor),
|
||||
key: ObjectKey(podcastLocal.title),
|
||||
child: PodcastCard(
|
||||
podcastLocal: podcastLocal,
|
||||
group: widget.group,
|
||||
: Stack(
|
||||
children: <Widget>[
|
||||
Container(
|
||||
color: Theme.of(context).primaryColor,
|
||||
child: Stack(
|
||||
children: <Widget>[
|
||||
ReorderableListView(
|
||||
onReorder: (int oldIndex, int newIndex) {
|
||||
setState(() {
|
||||
if (newIndex > oldIndex) {
|
||||
newIndex -= 1;
|
||||
}
|
||||
final PodcastLocal podcast =
|
||||
widget.group.podcasts.removeAt(oldIndex);
|
||||
widget.group.podcasts.insert(newIndex, podcast);
|
||||
_controller.forward();
|
||||
});
|
||||
},
|
||||
children: widget.group.podcasts
|
||||
.map<Widget>((PodcastLocal podcastLocal) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).primaryColor),
|
||||
key: ObjectKey(podcastLocal.title),
|
||||
child: PodcastCard(
|
||||
podcastLocal: podcastLocal,
|
||||
group: widget.group,
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
Positioned(
|
||||
bottom: 30,
|
||||
right: 30,
|
||||
child: _saveButton(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
_showSetting
|
||||
? Positioned.fill(
|
||||
child: GestureDetector(
|
||||
onTap: () => setState(() => _showSetting = false),
|
||||
child: Container(
|
||||
color: Theme.of(context)
|
||||
.scaffoldBackgroundColor
|
||||
.withOpacity(0.5),
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
AnimatedPositioned(
|
||||
duration: Duration(seconds: 1),
|
||||
bottom: 30,
|
||||
right: _loadSave ? 50 : 0,
|
||||
child: _saveButton(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
: Center(),
|
||||
_showSetting
|
||||
? Container(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: Container(
|
||||
height: 150.0,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).primaryColor,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
offset: Offset(0, -1),
|
||||
blurRadius: 4,
|
||||
color: Theme.of(context).brightness ==
|
||||
Brightness.light
|
||||
? Colors.grey[400]
|
||||
: Colors.grey[800],
|
||||
),
|
||||
],
|
||||
),
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: Container(
|
||||
height: 150,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
setState(() => _showSetting = false);
|
||||
showGeneralDialog(
|
||||
context: context,
|
||||
barrierDismissible: true,
|
||||
barrierLabel:
|
||||
MaterialLocalizations.of(context)
|
||||
.modalBarrierDismissLabel,
|
||||
barrierColor: Colors.black54,
|
||||
transitionDuration:
|
||||
const Duration(milliseconds: 200),
|
||||
pageBuilder: (BuildContext context,
|
||||
Animation animaiton,
|
||||
Animation
|
||||
secondaryAnimation) =>
|
||||
AnnotatedRegion<
|
||||
SystemUiOverlayStyle>(
|
||||
value: SystemUiOverlayStyle(
|
||||
statusBarIconBrightness:
|
||||
Brightness.light,
|
||||
systemNavigationBarColor:
|
||||
Theme.of(context)
|
||||
.brightness ==
|
||||
Brightness.light
|
||||
? Color.fromRGBO(
|
||||
113,
|
||||
113,
|
||||
113,
|
||||
1)
|
||||
: Color.fromRGBO(
|
||||
15, 15, 15, 1),
|
||||
statusBarColor: Theme.of(
|
||||
context)
|
||||
.brightness ==
|
||||
Brightness.light
|
||||
? Color.fromRGBO(
|
||||
113, 113, 113, 1)
|
||||
: Color.fromRGBO(
|
||||
5, 5, 5, 1),
|
||||
),
|
||||
child: SafeArea(
|
||||
child: AlertDialog(
|
||||
elevation: 1,
|
||||
titlePadding:
|
||||
EdgeInsets.only(
|
||||
top: 20,
|
||||
left: 40,
|
||||
right: 200,
|
||||
bottom: 20),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.all(
|
||||
Radius.circular(
|
||||
10.0))),
|
||||
title:
|
||||
Text('Choose a color'),
|
||||
content:
|
||||
SingleChildScrollView(
|
||||
child: MaterialPicker(
|
||||
onColorChanged:
|
||||
(value) {
|
||||
PodcastGroup newGroup =
|
||||
PodcastGroup(
|
||||
widget
|
||||
.group.name,
|
||||
color: value
|
||||
.toString()
|
||||
.substring(
|
||||
10,
|
||||
16),
|
||||
id: widget
|
||||
.group.id,
|
||||
podcastList:
|
||||
widget
|
||||
.group
|
||||
.podcastList);
|
||||
groupList.updateGroup(
|
||||
newGroup);
|
||||
Navigator.of(context)
|
||||
.pop();
|
||||
},
|
||||
pickerColor:
|
||||
Colors.blue,
|
||||
),
|
||||
),
|
||||
))));
|
||||
},
|
||||
child: Container(
|
||||
height: 50.0,
|
||||
padding:
|
||||
EdgeInsets.symmetric(horizontal: 20),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Icon(Icons.colorize),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 5.0),
|
||||
),
|
||||
Text('Change Color'),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
setState(() => _showSetting = false);
|
||||
widget.group.name == 'Home'
|
||||
? Fluttertoast.showToast(
|
||||
msg:
|
||||
'Home group is not supported',
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
)
|
||||
: showGeneralDialog(
|
||||
context: context,
|
||||
barrierDismissible: true,
|
||||
barrierLabel:
|
||||
MaterialLocalizations.of(
|
||||
context)
|
||||
.modalBarrierDismissLabel,
|
||||
barrierColor: Colors.black54,
|
||||
transitionDuration:
|
||||
const Duration(
|
||||
milliseconds: 200),
|
||||
pageBuilder: (BuildContext
|
||||
context,
|
||||
Animation animaiton,
|
||||
Animation
|
||||
secondaryAnimation) =>
|
||||
RenameGroup(
|
||||
group: widget.group,
|
||||
));
|
||||
},
|
||||
child: Container(
|
||||
height: 50.0,
|
||||
padding:
|
||||
EdgeInsets.symmetric(horizontal: 20),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Icon(Icons.text_fields),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 5.0),
|
||||
),
|
||||
Text('Rename'),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
setState(() => _showSetting = false);
|
||||
widget.group.name == 'Home'
|
||||
? Fluttertoast.showToast(
|
||||
msg:
|
||||
'Home group is not supported',
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
)
|
||||
: showGeneralDialog(
|
||||
context: context,
|
||||
barrierDismissible: true,
|
||||
barrierLabel:
|
||||
MaterialLocalizations.of(
|
||||
context)
|
||||
.modalBarrierDismissLabel,
|
||||
barrierColor: Colors.black54,
|
||||
transitionDuration:
|
||||
const Duration(
|
||||
milliseconds: 200),
|
||||
pageBuilder: (BuildContext
|
||||
context,
|
||||
Animation animaiton,
|
||||
Animation
|
||||
secondaryAnimation) =>
|
||||
AnnotatedRegion<
|
||||
SystemUiOverlayStyle>(
|
||||
value: SystemUiOverlayStyle(
|
||||
statusBarIconBrightness:
|
||||
Brightness.light,
|
||||
systemNavigationBarColor:
|
||||
Theme.of(context)
|
||||
.brightness ==
|
||||
Brightness
|
||||
.light
|
||||
? Color.fromRGBO(
|
||||
113,
|
||||
113,
|
||||
113,
|
||||
1)
|
||||
: Color.fromRGBO(
|
||||
15,
|
||||
15,
|
||||
15,
|
||||
1),
|
||||
statusBarColor: Theme.of(
|
||||
context)
|
||||
.brightness ==
|
||||
Brightness.light
|
||||
? Color.fromRGBO(
|
||||
113, 113, 113, 1)
|
||||
: Color.fromRGBO(
|
||||
5, 5, 5, 1),
|
||||
),
|
||||
child: SafeArea(
|
||||
child: AlertDialog(
|
||||
elevation: 1,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.all(
|
||||
Radius.circular(
|
||||
10.0))),
|
||||
titlePadding:
|
||||
EdgeInsets.only(
|
||||
top: 20,
|
||||
left: 20,
|
||||
right: 200,
|
||||
bottom: 20),
|
||||
title: Text(
|
||||
'Delete confirm'),
|
||||
content: Text(
|
||||
'Are you sure you want to delete this group? Podcasts will be moved to Home group.'),
|
||||
actions: <Widget>[
|
||||
FlatButton(
|
||||
onPressed: () =>
|
||||
Navigator.of(
|
||||
context)
|
||||
.pop(),
|
||||
child: Text(
|
||||
'CANCEL',
|
||||
style: TextStyle(
|
||||
color: Colors
|
||||
.grey[
|
||||
600]),
|
||||
),
|
||||
),
|
||||
FlatButton(
|
||||
onPressed: () {
|
||||
groupList.delGroup(
|
||||
widget.group);
|
||||
Navigator.of(
|
||||
context)
|
||||
.pop();
|
||||
},
|
||||
child: Text(
|
||||
'CONFIRM',
|
||||
style: TextStyle(
|
||||
color: Colors
|
||||
.red),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
));
|
||||
},
|
||||
child: Container(
|
||||
height: 50,
|
||||
padding:
|
||||
EdgeInsets.symmetric(horizontal: 20),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Icon(Icons.delete_outline),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 5.0),
|
||||
),
|
||||
Text('Delete'),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
: Center(),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -330,7 +696,11 @@ class _PodcastCardState extends State<PodcastCard> {
|
||||
});
|
||||
}),
|
||||
_buttonOnMenu(Icon(Icons.notifications), () {}),
|
||||
_buttonOnMenu(Icon(Icons.remove_circle), () {
|
||||
_buttonOnMenu(
|
||||
Icon(
|
||||
Icons.delete,
|
||||
color: Colors.red,
|
||||
), () {
|
||||
showGeneralDialog(
|
||||
context: context,
|
||||
barrierDismissible: true,
|
||||
@ -375,7 +745,11 @@ class _PodcastCardState extends State<PodcastCard> {
|
||||
FlatButton(
|
||||
onPressed: () =>
|
||||
Navigator.of(context).pop(),
|
||||
child: Text('CANCEL', style: TextStyle(color: Colors.grey[600]),),
|
||||
child: Text(
|
||||
'CANCEL',
|
||||
style: TextStyle(
|
||||
color: Colors.grey[600]),
|
||||
),
|
||||
),
|
||||
FlatButton(
|
||||
onPressed: () {
|
||||
@ -404,3 +778,120 @@ class _PodcastCardState extends State<PodcastCard> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class RenameGroup extends StatefulWidget {
|
||||
final PodcastGroup group;
|
||||
RenameGroup({this.group, Key key}) : super(key: key);
|
||||
@override
|
||||
_RenameGroupState createState() => _RenameGroupState();
|
||||
}
|
||||
|
||||
class _RenameGroupState extends State<RenameGroup> {
|
||||
TextEditingController _controller;
|
||||
String _newName;
|
||||
int _error;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_error = 0;
|
||||
_controller = TextEditingController();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var groupList = Provider.of<GroupList>(context, listen: false);
|
||||
List list = groupList.groups.map((e) => e.name).toList();
|
||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: SystemUiOverlayStyle(
|
||||
statusBarIconBrightness: Brightness.light,
|
||||
systemNavigationBarColor:
|
||||
Theme.of(context).brightness == Brightness.light
|
||||
? Color.fromRGBO(113, 113, 113, 1)
|
||||
: Color.fromRGBO(5, 5, 5, 1),
|
||||
statusBarColor: Theme.of(context).brightness == Brightness.light
|
||||
? Color.fromRGBO(113, 113, 113, 1)
|
||||
: Color.fromRGBO(15, 15, 15, 1),
|
||||
),
|
||||
child: SafeArea(
|
||||
child: AlertDialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(10))),
|
||||
elevation: 1,
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 20),
|
||||
titlePadding:
|
||||
EdgeInsets.only(top: 20, left: 20, right: 200, bottom: 20),
|
||||
actionsPadding: EdgeInsets.all(0),
|
||||
actions: <Widget>[
|
||||
FlatButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: Text(
|
||||
'CANCEL',
|
||||
style: TextStyle(color: Colors.grey[600]),
|
||||
),
|
||||
),
|
||||
FlatButton(
|
||||
onPressed: () async {
|
||||
if (list.contains(_newName)) {
|
||||
setState(() => _error = 1);
|
||||
} else {
|
||||
PodcastGroup newGroup = PodcastGroup(_newName,
|
||||
color: widget.group.color,
|
||||
id: widget.group.id,
|
||||
podcastList: widget.group.podcastList);
|
||||
groupList.updateGroup(newGroup);
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
child: Text('DONE',
|
||||
style: TextStyle(color: Theme.of(context).accentColor)),
|
||||
)
|
||||
],
|
||||
title: Text('Create new group'),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
TextField(
|
||||
decoration: InputDecoration(
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 10),
|
||||
hintText: 'New Group',
|
||||
hintStyle: TextStyle(fontSize: 18),
|
||||
filled: true,
|
||||
focusedBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).accentColor, width: 2.0),
|
||||
),
|
||||
enabledBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).accentColor, width: 2.0),
|
||||
),
|
||||
),
|
||||
cursorRadius: Radius.circular(2),
|
||||
autofocus: true,
|
||||
maxLines: 1,
|
||||
controller: _controller,
|
||||
onChanged: (value) {
|
||||
_newName = value;
|
||||
},
|
||||
),
|
||||
Container(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: (_error == 1)
|
||||
? Text(
|
||||
'Group existed',
|
||||
style: TextStyle(color: Colors.red[400]),
|
||||
)
|
||||
: Center(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -46,6 +46,9 @@ class _AboutPodcastState extends State<AboutPodcast> {
|
||||
Widget build(BuildContext context) {
|
||||
var _groupList = Provider.of<GroupList>(context, listen: false);
|
||||
return AlertDialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(10.0))),
|
||||
titlePadding: EdgeInsets.only(top: 20, left: 20, right: 200, bottom: 20),
|
||||
actions: <Widget>[
|
||||
FlatButton(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
@ -99,7 +102,7 @@ class _PodcastListState extends State<PodcastList> {
|
||||
statusBarColor: Theme.of(context).primaryColor,
|
||||
),
|
||||
child: SafeArea(
|
||||
child: Scaffold(
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Podcasts'),
|
||||
centerTitle: true,
|
||||
@ -116,7 +119,8 @@ class _PodcastListState extends State<PodcastList> {
|
||||
SliverPadding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
sliver: SliverGrid(
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
gridDelegate:
|
||||
SliverGridDelegateWithFixedCrossAxisCount(
|
||||
childAspectRatio: 0.8,
|
||||
crossAxisCount: 3,
|
||||
),
|
||||
@ -133,12 +137,43 @@ class _PodcastListState extends State<PodcastList> {
|
||||
);
|
||||
},
|
||||
onLongPress: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) =>
|
||||
AboutPodcast(
|
||||
podcastLocal: snapshot.data[index]),
|
||||
).then((_) => setState(() {}));
|
||||
showGeneralDialog(
|
||||
context: context,
|
||||
barrierDismissible: true,
|
||||
barrierLabel:
|
||||
MaterialLocalizations.of(context)
|
||||
.modalBarrierDismissLabel,
|
||||
barrierColor: Colors.black54,
|
||||
transitionDuration:
|
||||
const Duration(milliseconds: 200),
|
||||
pageBuilder: (BuildContext context,
|
||||
Animation animaiton,
|
||||
Animation secondaryAnimation) =>
|
||||
AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: SystemUiOverlayStyle(
|
||||
statusBarIconBrightness:
|
||||
Brightness.light,
|
||||
systemNavigationBarColor:
|
||||
Theme.of(context)
|
||||
.brightness ==
|
||||
Brightness.light
|
||||
? Color.fromRGBO(
|
||||
113, 113, 113, 1)
|
||||
: Color.fromRGBO(
|
||||
15, 15, 15, 1),
|
||||
statusBarColor: Theme.of(context)
|
||||
.brightness ==
|
||||
Brightness.light
|
||||
? Color.fromRGBO(
|
||||
113, 113, 113, 1)
|
||||
: Color.fromRGBO(5, 5, 5, 1),
|
||||
),
|
||||
child: SafeArea(
|
||||
child: AboutPodcast(
|
||||
podcastLocal:
|
||||
snapshot.data[index]),
|
||||
),
|
||||
));
|
||||
},
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
|
@ -20,12 +20,6 @@ class _PodcastManageState extends State<PodcastManage> {
|
||||
));
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: SystemUiOverlayStyle(
|
||||
@ -34,85 +28,85 @@ class _PodcastManageState extends State<PodcastManage> {
|
||||
statusBarColor: Theme.of(context).primaryColor,
|
||||
),
|
||||
child: SafeArea(
|
||||
child: SafeArea(
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
centerTitle: true,
|
||||
title: Text('Groups'),
|
||||
actions: <Widget>[
|
||||
IconButton(
|
||||
onPressed: () => showGeneralDialog(
|
||||
context: context,
|
||||
barrierDismissible: true,
|
||||
barrierLabel: MaterialLocalizations.of(context)
|
||||
.modalBarrierDismissLabel,
|
||||
barrierColor: Colors.black54,
|
||||
transitionDuration: const Duration(milliseconds: 200),
|
||||
pageBuilder: (BuildContext context, Animation animaiton,
|
||||
Animation secondaryAnimation) =>
|
||||
AddGroup()),
|
||||
icon: Icon(Icons.add)),
|
||||
OrderMenu(),
|
||||
],
|
||||
),
|
||||
body: Consumer<GroupList>(builder: (_, groupList, __) {
|
||||
bool _isLoading = groupList.isLoading;
|
||||
List<PodcastGroup> _groups = groupList.groups;
|
||||
return _isLoading
|
||||
? Center()
|
||||
: DefaultTabController(
|
||||
length: _groups.length,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
height: 50,
|
||||
padding: EdgeInsets.symmetric(horizontal: 10.0),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: TabBar(
|
||||
// labelColor: Colors.black,
|
||||
// unselectedLabelColor: Colors.grey[500],
|
||||
labelPadding: EdgeInsets.all(5.0),
|
||||
indicator: getIndicator(),
|
||||
isScrollable: true,
|
||||
tabs: _groups.map<Tab>((group) {
|
||||
return Tab(
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
centerTitle: true,
|
||||
title: Text('Groups'),
|
||||
actions: <Widget>[
|
||||
IconButton(
|
||||
onPressed: () => showGeneralDialog(
|
||||
context: context,
|
||||
barrierDismissible: true,
|
||||
barrierLabel: MaterialLocalizations.of(context)
|
||||
.modalBarrierDismissLabel,
|
||||
barrierColor: Colors.black54,
|
||||
transitionDuration: const Duration(milliseconds: 200),
|
||||
pageBuilder: (BuildContext context, Animation animaiton,
|
||||
Animation secondaryAnimation) =>
|
||||
AddGroup()),
|
||||
icon: Icon(Icons.add)),
|
||||
OrderMenu(),
|
||||
],
|
||||
),
|
||||
body: Consumer<GroupList>(builder: (_, groupList, __) {
|
||||
bool _isLoading = groupList.isLoading;
|
||||
List<PodcastGroup> _groups = groupList.groups;
|
||||
return _isLoading
|
||||
? Center()
|
||||
: DefaultTabController(
|
||||
length: _groups.length,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
height: 50,
|
||||
padding: EdgeInsets.symmetric(horizontal: 10.0),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: TabBar(
|
||||
labelColor: Colors.white,
|
||||
unselectedLabelColor: Colors.black,
|
||||
labelPadding: EdgeInsets.all(5.0),
|
||||
indicator: getIndicator(),
|
||||
isScrollable: true,
|
||||
tabs: _groups.map<Tab>((group) {
|
||||
return Tab(
|
||||
child: Container(
|
||||
height: 30.0,
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 10.0),
|
||||
padding:
|
||||
EdgeInsets.symmetric(horizontal: 10.0),
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).brightness ==
|
||||
Brightness.light
|
||||
? Theme.of(context).primaryColorDark
|
||||
: Colors.grey[800],
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(15)),
|
||||
color: group.getColor(),
|
||||
// Theme.of(context).brightness ==
|
||||
// Brightness.light
|
||||
// ? Theme.of(context).primaryColorDark
|
||||
// : Colors.grey[800],
|
||||
borderRadius:
|
||||
BorderRadius.all(Radius.circular(15)),
|
||||
),
|
||||
child: Text(
|
||||
group.name,
|
||||
)),
|
||||
);
|
||||
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Container(
|
||||
child: TabBarView(
|
||||
children: _groups.map<Widget>((group) {
|
||||
return Container(
|
||||
key: ObjectKey(group),
|
||||
child: PodcastGroupList(group: group));
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Container(
|
||||
child: TabBarView(
|
||||
children: _groups.map<Widget>((group) {
|
||||
return Container(
|
||||
key: ObjectKey(group),
|
||||
child: PodcastGroupList(group: group));
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
));
|
||||
}),
|
||||
),
|
||||
)
|
||||
],
|
||||
));
|
||||
}),
|
||||
),
|
||||
),
|
||||
);
|
||||
@ -214,7 +208,8 @@ class _AddGroupState extends State<AddGroup> {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
child: Text('DONE',style: TextStyle(color: Theme.of(context).accentColor)),
|
||||
child: Text('DONE',
|
||||
style: TextStyle(color: Theme.of(context).accentColor)),
|
||||
)
|
||||
],
|
||||
title: Text('Create new group'),
|
||||
@ -228,10 +223,12 @@ class _AddGroupState extends State<AddGroup> {
|
||||
hintStyle: TextStyle(fontSize: 18),
|
||||
filled: true,
|
||||
focusedBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(color: Theme.of(context).accentColor, width: 2.0),
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).accentColor, width: 2.0),
|
||||
),
|
||||
enabledBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(color: Theme.of(context).accentColor, width: 2.0),
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).accentColor, width: 2.0),
|
||||
),
|
||||
),
|
||||
cursorRadius: Radius.circular(2),
|
||||
|
481
pubspec.lock
481
pubspec.lock
@ -1,481 +0,0 @@
|
||||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
archive:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: archive
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.0.11"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: args
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.5.2"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: async
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.4.0"
|
||||
audiofileplayer:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: audiofileplayer
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: boolean_selector
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.0.5"
|
||||
cached_network_image:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: cached_network_image
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
charcode:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: charcode
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.1.2"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.14.11"
|
||||
color_thief_flutter:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: color_thief_flutter
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
convert:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: convert
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: crypto
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.1.3"
|
||||
csslib:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: csslib
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.16.1"
|
||||
cupertino_icons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: cupertino_icons
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.1.3"
|
||||
dio:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: dio
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.0.9"
|
||||
file_picker:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: file_picker
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.4.3+2"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_cache_manager:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_cache_manager
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.1.3"
|
||||
flutter_colorpicker:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: flutter_colorpicker
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.3.2"
|
||||
flutter_downloader:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: flutter_downloader
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.4.1"
|
||||
flutter_html:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: flutter_html
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.11.1"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_web_plugins:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
fluttertoast:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: fluttertoast
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.1.3"
|
||||
font_awesome_flutter:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: font_awesome_flutter
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "8.7.0"
|
||||
google_fonts:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: google_fonts
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.3.9"
|
||||
html:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: html
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.14.0+3"
|
||||
http:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.12.0+4"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_parser
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.1.3"
|
||||
image:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: image
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
intl:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: intl
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.16.1"
|
||||
json_annotation:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: json_annotation
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: logging
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.11.4"
|
||||
marquee:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: marquee
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.3.1"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.12.6"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.1.8"
|
||||
nested:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: nested
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.0.4"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.6.4"
|
||||
path_provider:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: path_provider
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.6.1"
|
||||
pedantic:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pedantic
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.8.0+1"
|
||||
permission_handler:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: permission_handler
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "4.2.0+hotfix.3"
|
||||
petitparser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: petitparser
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.4.0"
|
||||
platform:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: platform
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.2.1"
|
||||
plugin_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: plugin_platform_interface
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
provider:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: provider
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "4.0.4"
|
||||
quantize_dart:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: quantize_dart
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.0.3"
|
||||
quiver:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: quiver
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.0.5"
|
||||
shared_preferences:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: shared_preferences
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.5.6+2"
|
||||
shared_preferences_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_macos
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.0.1+6"
|
||||
shared_preferences_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_platform_interface
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.0.3"
|
||||
shared_preferences_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_web
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.1.2+4"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.99"
|
||||
source_span:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.5.5"
|
||||
sqflite:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: sqflite
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.9.3"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_channel
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
string_scanner:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: string_scanner
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.0.5"
|
||||
synchronized:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: synchronized
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: term_glyph
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.2.11"
|
||||
tuple:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: tuple
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.0.3"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: typed_data
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.1.6"
|
||||
url_launcher:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: url_launcher
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "5.4.2"
|
||||
url_launcher_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_macos
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.0.1+4"
|
||||
url_launcher_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_platform_interface
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.0.6"
|
||||
url_launcher_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_web
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.1.1+1"
|
||||
uuid:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: uuid
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.0.4"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vector_math
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.0.8"
|
||||
workmanager:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: workmanager
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.2.0"
|
||||
xml:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: xml
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.5.0"
|
||||
sdks:
|
||||
dart: ">=2.6.0 <3.0.0"
|
||||
flutter: ">=1.12.13+hotfix.4 <2.0.0"
|
11
pubspec.yaml
11
pubspec.yaml
@ -32,18 +32,18 @@ dev_dependencies:
|
||||
flutter_html: ^0.11.1
|
||||
path_provider: ^1.6.1
|
||||
color_thief_flutter: ^1.0.1
|
||||
provider: ^4.0.1
|
||||
google_fonts: ^0.3.2
|
||||
provider: ^4.0.4
|
||||
google_fonts: ^0.3.9
|
||||
dio: ^3.0.9
|
||||
file_picker: ^1.2.0
|
||||
file_picker: ^1.4.3+2
|
||||
xml: ^3.5.0
|
||||
marquee: ^1.3.1
|
||||
audiofileplayer: ^1.1.1
|
||||
flutter_downloader: ^1.4.1
|
||||
permission_handler: ^4.2.0+hotfix.3
|
||||
permission_handler: ^4.3.0
|
||||
fluttertoast: ^3.1.3
|
||||
intl: ^0.16.1
|
||||
url_launcher: ^5.4.1
|
||||
url_launcher: ^5.4.2
|
||||
image: ^2.1.4
|
||||
shared_preferences: ^0.5.6+1
|
||||
uuid: ^2.0.4
|
||||
@ -52,6 +52,7 @@ dev_dependencies:
|
||||
workmanager: ^0.2.0
|
||||
font_awesome_flutter: ^8.7.0
|
||||
flutter_colorpicker: ^0.3.2
|
||||
lazy_loading_list: ^1.0.0+1
|
||||
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user