mirror of
https://github.com/stonega/tsacdop
synced 2025-02-09 16:18:48 +01:00
modified: .circleci/config.yml
modified: android/app/build.gradle modified: android/app/src/main/AndroidManifest.xml new file: android/app/src/main/res/drawable/launch_background_night.xml new file: android/app/src/main/res/drawable/normal_background.xml new file: android/app/src/main/res/values-night/styles.xml modified: android/app/src/main/res/values/styles.xml new file: assets/fireside.jpg new file: assets/logo.png modified: lib/class/audiostate.dart modified: lib/class/fireside_data.dart modified: lib/class/podcastlocal.dart modified: lib/class/settingstate.dart modified: lib/episodes/episodedetail.dart modified: lib/episodes/episodedownload.dart modified: lib/home/appbar/about.dart modified: lib/home/appbar/addpodcast.dart modified: lib/home/appbar/importompl.dart modified: lib/home/appbar/popupmenu.dart modified: lib/home/audiopanel.dart modified: lib/home/audioplayer.dart modified: lib/home/homescroll.dart modified: lib/home/hometab.dart new file: lib/home/paly_history.dart modified: lib/local_storage/key_value_storage.dart modified: lib/local_storage/sqflite_localpodcast.dart modified: lib/main.dart modified: lib/podcasts/podcastdetail.dart modified: lib/podcasts/podcastgroup.dart modified: lib/podcasts/podcastlist.dart modified: lib/podcasts/podcastmanage.dart new file: lib/settings/settting.dart new file: lib/settings/theme.dart new file: lib/util/colorize.dart modified: lib/util/episodegrid.dart modified: pubspec.lock modified: pubspec.yaml
This commit is contained in:
parent
1021f2eea4
commit
a6fc34e7bb
@ -13,7 +13,13 @@ jobs:
|
|||||||
- run:
|
- run:
|
||||||
name: Run Flutter doctor
|
name: Run Flutter doctor
|
||||||
command: flutter doctor
|
command: flutter doctor
|
||||||
|
- run:
|
||||||
|
name: flutter pub get
|
||||||
|
command: flutter pub get
|
||||||
|
-run:
|
||||||
|
name: flutter run
|
||||||
|
command: flutter run
|
||||||
|
|
||||||
- run: echo $ENCODED_KEYSTORE | base64 -di > ${HOME}/keystore.jks
|
- run: echo $ENCODED_KEYSTORE | base64 -di > ${HOME}/keystore.jks
|
||||||
- run: echo 'export KEYSTORE=${HOME}/keystore.jks' >> $BASH_ENV
|
- run: echo 'export KEYSTORE=${HOME}/keystore.jks' >> $BASH_ENV
|
||||||
|
|
||||||
|
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"java.configuration.updateBuildConfiguration": "automatic"
|
||||||
|
}
|
@ -54,10 +54,10 @@ android {
|
|||||||
|
|
||||||
signingConfigs {
|
signingConfigs {
|
||||||
release {
|
release {
|
||||||
storeFile file(System.getenv("KEYSTORE") ?: "keystore.jks")
|
storeFile file(System.getenv("KEYSTORE") ?: "keystore.jks")
|
||||||
storePassword System.getenv("KEYSTORE_PASSWORD")
|
storePassword System.getenv("KEYSTORE_PASSWORD")
|
||||||
keyAlias System.getenv("KEY_ALIAS")
|
keyAlias System.getenv("KEY_ALIAS")
|
||||||
keyPassword System.getenv("KEY_PASSWORD")
|
keyPassword System.getenv("KEY_PASSWORD")
|
||||||
// keyAlias keystoreProperties['keyAlias']
|
// keyAlias keystoreProperties['keyAlias']
|
||||||
// keyPassword keystoreProperties['keyPassword']
|
// keyPassword keystoreProperties['keyPassword']
|
||||||
// storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
|
// storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
<meta-data android:name="io.flutter.embedding.android.SplashScreenUntilFirstFrame" android:value="true" />
|
||||||
</activity>
|
</activity>
|
||||||
<service android:name="com.google.flutter.plugins.audiofileplayer.AudiofileplayerService">
|
<service android:name="com.google.flutter.plugins.audiofileplayer.AudiofileplayerService">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
<?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" />
|
||||||
|
<!-- 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>
|
17
android/app/src/main/res/drawable/normal_background.xml
Normal file
17
android/app/src/main/res/drawable/normal_background.xml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?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>
|
6
android/app/src/main/res/values-night/styles.xml
Normal file
6
android/app/src/main/res/values-night/styles.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||||
|
<item name="android:windowBackground">@drawable/launch_background_night</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
@ -1,8 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||||
<!-- Show a splash screen on the activity. Automatically removed when
|
<!-- Show a splash screen on the activity. Automatically removed when
|
||||||
Flutter draws its first frame -->
|
Flutter draws its first frame -->
|
||||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||||
</style>
|
</style>
|
||||||
|
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||||
|
<item name="android:windowBackground">@drawable/normal_background</item>
|
||||||
|
</style>
|
||||||
</resources>
|
</resources>
|
||||||
|
BIN
assets/fireside.jpg
Normal file
BIN
assets/fireside.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
BIN
assets/logo.png
Normal file
BIN
assets/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
@ -14,11 +14,18 @@ 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 {
|
class PlayHistory {
|
||||||
|
DBHelper dbHelper = DBHelper();
|
||||||
String title;
|
String title;
|
||||||
String url;
|
String url;
|
||||||
double seconds;
|
double seconds;
|
||||||
double seekValue;
|
double seekValue;
|
||||||
PlayHistory(this.title, this.url, this.seconds, this.seekValue);
|
PlayHistory(this.title, this.url, this.seconds, this.seekValue);
|
||||||
|
EpisodeBrief _episode;
|
||||||
|
EpisodeBrief get episode => _episode;
|
||||||
|
|
||||||
|
getEpisode() async {
|
||||||
|
_episode = await dbHelper.getRssItemWithUrl(url);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Playlist {
|
class Playlist {
|
||||||
@ -30,13 +37,13 @@ class Playlist {
|
|||||||
KeyValueStorage storage = KeyValueStorage('playlist');
|
KeyValueStorage storage = KeyValueStorage('playlist');
|
||||||
Playlist(this.name, {List<String> urls}) : urls = urls ?? [];
|
Playlist(this.name, {List<String> urls}) : urls = urls ?? [];
|
||||||
|
|
||||||
getPlaylist() async{
|
getPlaylist() async {
|
||||||
List<String> _urls = await storage.getStringList();
|
List<String> _urls = await storage.getStringList();
|
||||||
if (_urls.length == 0) {
|
if (_urls.length == 0) {
|
||||||
_playlist = [];
|
_playlist = [];
|
||||||
} else {
|
} else {
|
||||||
_playlist = [];
|
_playlist = [];
|
||||||
await Future.forEach(_urls, (url) async{
|
await Future.forEach(_urls, (url) async {
|
||||||
EpisodeBrief episode = await dbHelper.getRssItemWithUrl(url);
|
EpisodeBrief episode = await dbHelper.getRssItemWithUrl(url);
|
||||||
print(episode.title);
|
print(episode.title);
|
||||||
_playlist.add(episode);
|
_playlist.add(episode);
|
||||||
@ -57,9 +64,10 @@ class Playlist {
|
|||||||
await savePlaylist();
|
await savePlaylist();
|
||||||
}
|
}
|
||||||
|
|
||||||
delFromPlaylist(EpisodeBrief episodeBrief) {
|
delFromPlaylist(EpisodeBrief episodeBrief) async {
|
||||||
_playlist.remove(episodeBrief);
|
_playlist
|
||||||
savePlaylist();
|
.removeWhere((item) => item.enclosureUrl == episodeBrief.enclosureUrl);
|
||||||
|
await savePlaylist();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,6 +79,7 @@ class AudioPlayer extends ChangeNotifier {
|
|||||||
static const String forwardButtonId = 'forwardButtonId';
|
static const String forwardButtonId = 'forwardButtonId';
|
||||||
|
|
||||||
DBHelper dbHelper = DBHelper();
|
DBHelper dbHelper = DBHelper();
|
||||||
|
KeyValueStorage storage = KeyValueStorage('audioposition');
|
||||||
EpisodeBrief _episode;
|
EpisodeBrief _episode;
|
||||||
Playlist _queue = Playlist('now');
|
Playlist _queue = Playlist('now');
|
||||||
bool _playerRunning = false;
|
bool _playerRunning = false;
|
||||||
@ -90,34 +99,43 @@ class AudioPlayer extends ChangeNotifier {
|
|||||||
double get seekSliderValue => _seekSliderValue;
|
double get seekSliderValue => _seekSliderValue;
|
||||||
String get remoteErrorMessage => _remoteErrorMessage;
|
String get remoteErrorMessage => _remoteErrorMessage;
|
||||||
bool get playerRunning => _playerRunning;
|
bool get playerRunning => _playerRunning;
|
||||||
|
int _lastPostion;
|
||||||
|
int get lastPositin => _lastPostion;
|
||||||
Playlist get queue => _queue;
|
Playlist get queue => _queue;
|
||||||
|
|
||||||
AudioState _audioState = AudioState.stop;
|
|
||||||
AudioState get audioState => _audioState;
|
|
||||||
|
|
||||||
EpisodeBrief get episode => _episode;
|
EpisodeBrief get episode => _episode;
|
||||||
|
|
||||||
@override
|
loadPlaylist() async {
|
||||||
void addListener(VoidCallback listener) {
|
await _queue.getPlaylist();
|
||||||
super.addListener(listener);
|
_lastPostion = await storage.getInt();
|
||||||
_queue.getPlaylist();
|
print(_lastPostion);
|
||||||
}
|
}
|
||||||
|
|
||||||
episodeLoad(EpisodeBrief episode) async {
|
episodeLoad(EpisodeBrief episode) async {
|
||||||
|
if (_playerRunning && _episode != null) {
|
||||||
|
PlayHistory history = PlayHistory(_episode.title, _episode.enclosureUrl,
|
||||||
|
backgroundAudioDuration, seekSliderValue);
|
||||||
|
await dbHelper.saveHistory(history);
|
||||||
|
}
|
||||||
AudioSystem.instance.addMediaEventListener(_mediaEventListener);
|
AudioSystem.instance.addMediaEventListener(_mediaEventListener);
|
||||||
|
_backgroundAudioPlaying = false;
|
||||||
_episode = episode;
|
_episode = episode;
|
||||||
await _queue.getPlaylist();
|
await _queue.getPlaylist();
|
||||||
if (_queue.playlist.contains(_episode)) {
|
_queue.playlist
|
||||||
_queue.playlist.remove(_episode);
|
.removeWhere((item) => item.enclosureUrl == _episode.enclosureUrl);
|
||||||
_queue.playlist.insert(0, _episode);
|
_queue.playlist.insert(0, _episode);
|
||||||
} else {
|
|
||||||
_queue.playlist.insert(0, _episode);
|
|
||||||
}
|
|
||||||
await _queue.savePlaylist();
|
await _queue.savePlaylist();
|
||||||
await _play(_episode);
|
await _play(_episode);
|
||||||
_playerRunning = true;
|
_playerRunning = true;
|
||||||
_backgroundAudioPlaying = true;
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
playlistLoad() async {
|
||||||
|
_backgroundAudioPlaying = false;
|
||||||
|
await _queue.getPlaylist();
|
||||||
|
_episode = _queue.playlist.first;
|
||||||
|
await _play(_episode);
|
||||||
|
_playerRunning = true;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,10 +145,7 @@ class AudioPlayer extends ChangeNotifier {
|
|||||||
await dbHelper.saveHistory(history);
|
await dbHelper.saveHistory(history);
|
||||||
await _queue.delFromPlaylist(_episode);
|
await _queue.delFromPlaylist(_episode);
|
||||||
if (_queue.playlist.length > 0) {
|
if (_queue.playlist.length > 0) {
|
||||||
_episode = _queue.playlist.first;
|
playlistLoad();
|
||||||
_play(_episode);
|
|
||||||
_backgroundAudioPlaying = true;
|
|
||||||
notifyListeners();
|
|
||||||
} else {
|
} else {
|
||||||
_backgroundAudioPlaying = false;
|
_backgroundAudioPlaying = false;
|
||||||
_remoteAudioLoading = false;
|
_remoteAudioLoading = false;
|
||||||
@ -145,19 +160,22 @@ class AudioPlayer extends ChangeNotifier {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
delFromPlaylist(EpisodeBrief episode) async {
|
||||||
|
_queue.delFromPlaylist(episode);
|
||||||
|
await _queue.getPlaylist();
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
pauseAduio() async {
|
pauseAduio() async {
|
||||||
_pauseBackgroundAudio();
|
_pauseBackgroundAudio();
|
||||||
_audioState = AudioState.pause;
|
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
PlayHistory history = PlayHistory(_episode.title, _episode.enclosureUrl,
|
PlayHistory history = PlayHistory(_episode.title, _episode.enclosureUrl,
|
||||||
backgroundAudioDuration, seekSliderValue);
|
backgroundAudioDuration, seekSliderValue);
|
||||||
await dbHelper.saveHistory(history);
|
await dbHelper.saveHistory(history);
|
||||||
await _queue.delFromPlaylist(_episode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resumeAudio() {
|
resumeAudio() {
|
||||||
_resumeBackgroundAudio();
|
_resumeBackgroundAudio();
|
||||||
_audioState = AudioState.play;
|
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,10 +191,13 @@ class AudioPlayer extends ChangeNotifier {
|
|||||||
_backgroundAudio.seek(positionSeconds);
|
_backgroundAudio.seek(positionSeconds);
|
||||||
AudioSystem.instance.setPlaybackState(true, positionSeconds);
|
AudioSystem.instance.setPlaybackState(true, positionSeconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
disopse() {
|
@override
|
||||||
|
dispose() {
|
||||||
|
pauseAduio();
|
||||||
AudioSystem.instance.removeMediaEventListener(_mediaEventListener);
|
AudioSystem.instance.removeMediaEventListener(_mediaEventListener);
|
||||||
_backgroundAudio?.dispose();
|
_backgroundAudio?.dispose();
|
||||||
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
_play(EpisodeBrief episodeBrief) async {
|
_play(EpisodeBrief episodeBrief) async {
|
||||||
@ -229,6 +250,7 @@ class AudioPlayer extends ChangeNotifier {
|
|||||||
_backgroundAudioPositionSeconds = _backgroundAudioDurationSeconds;
|
_backgroundAudioPositionSeconds = _backgroundAudioDurationSeconds;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
storage.saveInt(positionSeconds.toInt());
|
||||||
}, onError: (String message) {
|
}, onError: (String message) {
|
||||||
_remoteErrorMessage = message;
|
_remoteErrorMessage = message;
|
||||||
_backgroundAudio.dispose();
|
_backgroundAudio.dispose();
|
||||||
@ -245,7 +267,7 @@ class AudioPlayer extends ChangeNotifier {
|
|||||||
_remoteErrorMessage = null;
|
_remoteErrorMessage = null;
|
||||||
_remoteAudioLoading = true;
|
_remoteAudioLoading = true;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
_backgroundAudio?.pause();
|
_backgroundAudio?.pause();
|
||||||
_backgroundAudioPositionSeconds = 0;
|
_backgroundAudioPositionSeconds = 0;
|
||||||
_setNotification(false);
|
_setNotification(false);
|
||||||
_backgroundAudio =
|
_backgroundAudio =
|
||||||
@ -266,6 +288,7 @@ class AudioPlayer extends ChangeNotifier {
|
|||||||
_backgroundAudioPositionSeconds = _backgroundAudioDurationSeconds;
|
_backgroundAudioPositionSeconds = _backgroundAudioDurationSeconds;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
storage.saveInt(positionSeconds.toInt());
|
||||||
}, onError: (String message) {
|
}, onError: (String message) {
|
||||||
_remoteErrorMessage = message;
|
_remoteErrorMessage = message;
|
||||||
_backgroundAudio.dispose();
|
_backgroundAudio.dispose();
|
||||||
@ -390,7 +413,7 @@ class AudioPlayer extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _pauseBackgroundAudio() {
|
void _pauseBackgroundAudio() {
|
||||||
_backgroundAudio.pause();
|
_backgroundAudio?.pause();
|
||||||
_backgroundAudioPlaying = false;
|
_backgroundAudioPlaying = false;
|
||||||
AudioSystem.instance
|
AudioSystem.instance
|
||||||
.setPlaybackState(false, _backgroundAudioPositionSeconds);
|
.setPlaybackState(false, _backgroundAudioPositionSeconds);
|
||||||
@ -421,7 +444,8 @@ class AudioPlayer extends ChangeNotifier {
|
|||||||
void _forwardBackgroundAudio(double seconds) {
|
void _forwardBackgroundAudio(double seconds) {
|
||||||
final double forwardposition = _backgroundAudioPositionSeconds + seconds;
|
final double forwardposition = _backgroundAudioPositionSeconds + seconds;
|
||||||
_backgroundAudio.seek(forwardposition);
|
_backgroundAudio.seek(forwardposition);
|
||||||
AudioSystem.instance.setPlaybackState(true, _backgroundAudioPositionSeconds);
|
AudioSystem.instance
|
||||||
|
.setPlaybackState(true, _backgroundAudioPositionSeconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
final _pauseButton = AndroidCustomMediaButton(
|
final _pauseButton = AndroidCustomMediaButton(
|
||||||
|
@ -33,7 +33,13 @@ class FiresideData {
|
|||||||
String name = element.text.trim();
|
String name = element.text.trim();
|
||||||
String image =
|
String image =
|
||||||
element.children.first.children.first.attributes.toString();
|
element.children.first.children.first.attributes.toString();
|
||||||
host = PodcastHost(name, reg.stringMatch(image));
|
print(reg.stringMatch(image));
|
||||||
|
|
||||||
|
host = PodcastHost(
|
||||||
|
name,
|
||||||
|
reg.stringMatch(image) ??
|
||||||
|
'http://xuanmei.us/assets/default/avatar_small-170afdc2be97fc6148b283083942d82c101d4c1061f6b28f87c8958b52664af9.jpg');
|
||||||
|
|
||||||
hosts.add(host);
|
hosts.add(host);
|
||||||
});
|
});
|
||||||
List<String> data = [
|
List<String> data = [
|
||||||
@ -45,7 +51,7 @@ class FiresideData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future getData() async{
|
Future getData() async {
|
||||||
List<String> data = await dbHelper.getFiresideData(id);
|
List<String> data = await dbHelper.getFiresideData(id);
|
||||||
_background = data[0];
|
_background = data[0];
|
||||||
_hosts = json
|
_hosts = json
|
||||||
|
@ -7,7 +7,6 @@ class PodcastLocal {
|
|||||||
final String primaryColor;
|
final String primaryColor;
|
||||||
final String id;
|
final String id;
|
||||||
final String imagePath;
|
final String imagePath;
|
||||||
final String email;
|
|
||||||
final String provider;
|
final String provider;
|
||||||
final String link;
|
final String link;
|
||||||
PodcastLocal(
|
PodcastLocal(
|
||||||
@ -18,7 +17,6 @@ class PodcastLocal {
|
|||||||
this.author,
|
this.author,
|
||||||
this.id,
|
this.id,
|
||||||
this.imagePath,
|
this.imagePath,
|
||||||
this.email,
|
|
||||||
this.provider,
|
this.provider,
|
||||||
this.link);
|
this.link);
|
||||||
}
|
}
|
||||||
|
@ -4,27 +4,64 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:tsacdop/local_storage/key_value_storage.dart';
|
import 'package:tsacdop/local_storage/key_value_storage.dart';
|
||||||
|
|
||||||
class SettingState extends ChangeNotifier {
|
class SettingState extends ChangeNotifier {
|
||||||
KeyValueStorage storage = KeyValueStorage('themes');
|
KeyValueStorage themestorage = KeyValueStorage('themes');
|
||||||
int _theme;
|
KeyValueStorage accentstorage = KeyValueStorage('accents');
|
||||||
|
bool _isLoading;
|
||||||
|
bool get isLoagding => _isLoading;
|
||||||
|
|
||||||
int get theme => _theme;
|
Future initData() async {
|
||||||
setTheme(int theme) async{
|
await _getTheme();
|
||||||
_theme = theme;
|
await _getAccentSetColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
ThemeMode _theme;
|
||||||
|
ThemeMode get theme => _theme;
|
||||||
|
set setTheme(ThemeMode mode) {
|
||||||
|
_theme = mode;
|
||||||
|
_saveTheme();
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
Color _accentSetColor;
|
||||||
|
Color get accentSetColor => _accentSetColor;
|
||||||
|
|
||||||
|
set setAccentColor(Color color) {
|
||||||
|
_accentSetColor = color;
|
||||||
|
_saveAccentSetColor();
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
await _saveTheme(theme);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void addListener(VoidCallback listener) {
|
void addListener(VoidCallback listener) {
|
||||||
super.addListener(listener);
|
super.addListener(listener);
|
||||||
_getTheme();
|
_getTheme();
|
||||||
|
_getAccentSetColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
_getTheme() async {
|
_getTheme() async {
|
||||||
_theme = await storage.getTheme();
|
int mode = await themestorage.getInt();
|
||||||
|
_theme = ThemeMode.values[mode];
|
||||||
}
|
}
|
||||||
|
|
||||||
_saveTheme(theme) async {
|
_saveTheme() async {
|
||||||
await storage.saveTheme(theme);
|
await themestorage.saveInt(_theme.index);
|
||||||
|
}
|
||||||
|
|
||||||
|
_getAccentSetColor() async {
|
||||||
|
String colorString = await accentstorage.getString();
|
||||||
|
print(colorString);
|
||||||
|
if (colorString.isNotEmpty) {
|
||||||
|
int color = int.parse('FF' + colorString.toUpperCase(), radix: 16);
|
||||||
|
_accentSetColor = Color(color).withOpacity(1.0);
|
||||||
|
print(_accentSetColor.toString());
|
||||||
|
} else {
|
||||||
|
_accentSetColor = Colors.blue[400];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_saveAccentSetColor() async {
|
||||||
|
await accentstorage
|
||||||
|
.saveString(_accentSetColor.toString().substring(10, 16));
|
||||||
|
print(_accentSetColor.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ class _EpisodeDetailState extends State<EpisodeDetail> {
|
|||||||
final textstyle = TextStyle(fontSize: 15.0, color: Colors.black);
|
final textstyle = TextStyle(fontSize: 15.0, color: Colors.black);
|
||||||
double downloadProgress;
|
double downloadProgress;
|
||||||
bool _loaddes;
|
bool _loaddes;
|
||||||
|
bool _showMenu;
|
||||||
String path;
|
String path;
|
||||||
Future getSDescription(String url) async {
|
Future getSDescription(String url) async {
|
||||||
var dbHelper = DBHelper();
|
var dbHelper = DBHelper();
|
||||||
@ -38,6 +39,18 @@ class _EpisodeDetailState extends State<EpisodeDetail> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ScrollController _controller;
|
||||||
|
_scrollListener() {
|
||||||
|
if (_controller.offset > _controller.position.maxScrollExtent * 0.8) {
|
||||||
|
setState(() {
|
||||||
|
_showMenu = true;
|
||||||
|
});
|
||||||
|
} else
|
||||||
|
setState(() {
|
||||||
|
_showMenu = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
_launchUrl(String url) async {
|
_launchUrl(String url) async {
|
||||||
if (await canLaunch(url)) {
|
if (await canLaunch(url)) {
|
||||||
await launch(url);
|
await launch(url);
|
||||||
@ -50,7 +63,16 @@ class _EpisodeDetailState extends State<EpisodeDetail> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_loaddes = false;
|
_loaddes = false;
|
||||||
|
_showMenu = false;
|
||||||
getSDescription(widget.episodeItem.enclosureUrl);
|
getSDescription(widget.episodeItem.enclosureUrl);
|
||||||
|
_controller = ScrollController();
|
||||||
|
_controller.addListener(_scrollListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_controller.dispose();
|
||||||
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -119,38 +141,44 @@ class _EpisodeDetailState extends State<EpisodeDetail> {
|
|||||||
style:
|
style:
|
||||||
TextStyle(color: Colors.white)))
|
TextStyle(color: Colors.white)))
|
||||||
: Center(),
|
: Center(),
|
||||||
Container(
|
widget.episodeItem.duration != 0
|
||||||
decoration: BoxDecoration(
|
? Container(
|
||||||
color: Colors.cyan[300],
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.all(
|
color: Colors.cyan[300],
|
||||||
Radius.circular(15.0))),
|
borderRadius: BorderRadius.all(
|
||||||
height: 30.0,
|
Radius.circular(15.0))),
|
||||||
margin: EdgeInsets.only(right: 10.0),
|
height: 30.0,
|
||||||
padding:
|
margin: EdgeInsets.only(right: 10.0),
|
||||||
EdgeInsets.symmetric(horizontal: 10.0),
|
padding: EdgeInsets.symmetric(
|
||||||
alignment: Alignment.center,
|
horizontal: 10.0),
|
||||||
child: Text(
|
alignment: Alignment.center,
|
||||||
(widget.episodeItem.duration).toString() +
|
child: Text(
|
||||||
'mins',
|
(widget.episodeItem.duration)
|
||||||
style: textstyle),
|
.toString() +
|
||||||
),
|
'mins',
|
||||||
Container(
|
style: textstyle),
|
||||||
decoration: BoxDecoration(
|
)
|
||||||
color: Colors.lightBlue[300],
|
: Center(),
|
||||||
borderRadius: BorderRadius.all(
|
widget.episodeItem.enclosureLength != null
|
||||||
Radius.circular(15.0))),
|
? Container(
|
||||||
height: 30.0,
|
decoration: BoxDecoration(
|
||||||
margin: EdgeInsets.only(right: 10.0),
|
color: Colors.lightBlue[300],
|
||||||
padding:
|
borderRadius: BorderRadius.all(
|
||||||
EdgeInsets.symmetric(horizontal: 10.0),
|
Radius.circular(15.0))),
|
||||||
alignment: Alignment.center,
|
height: 30.0,
|
||||||
child: Text(
|
margin: EdgeInsets.only(right: 10.0),
|
||||||
((widget.episodeItem.enclosureLength) ~/
|
padding: EdgeInsets.symmetric(
|
||||||
1000000)
|
horizontal: 10.0),
|
||||||
.toString() +
|
alignment: Alignment.center,
|
||||||
'MB',
|
child: Text(
|
||||||
style: textstyle),
|
((widget.episodeItem
|
||||||
),
|
.enclosureLength) ~/
|
||||||
|
1000000)
|
||||||
|
.toString() +
|
||||||
|
'MB',
|
||||||
|
style: textstyle),
|
||||||
|
)
|
||||||
|
: Center(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -162,6 +190,7 @@ class _EpisodeDetailState extends State<EpisodeDetail> {
|
|||||||
padding:
|
padding:
|
||||||
EdgeInsets.only(left: 12.0, right: 12.0, top: 5.0),
|
EdgeInsets.only(left: 12.0, right: 12.0, top: 5.0),
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
|
controller: _controller,
|
||||||
child: _loaddes
|
child: _loaddes
|
||||||
? (widget.episodeItem.description.contains('<'))
|
? (widget.episodeItem.description.contains('<'))
|
||||||
? Html(
|
? Html(
|
||||||
@ -187,11 +216,18 @@ class _EpisodeDetailState extends State<EpisodeDetail> {
|
|||||||
builder: (_, data, __) {
|
builder: (_, data, __) {
|
||||||
return Container(
|
return Container(
|
||||||
alignment: Alignment.bottomCenter,
|
alignment: Alignment.bottomCenter,
|
||||||
padding: EdgeInsets.only(
|
padding:
|
||||||
bottom: data == true ? 60.0 : 10.0),
|
EdgeInsets.only(bottom: data == true ? 60.0 : 10.0),
|
||||||
child: MenuBar(
|
child: AnimatedContainer(
|
||||||
episodeItem: widget.episodeItem,
|
duration: Duration(milliseconds: 400),
|
||||||
heroTag: widget.heroTag,
|
height: !_showMenu ? 50 : 0,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
scrollDirection: Axis.vertical,
|
||||||
|
child: MenuBar(
|
||||||
|
episodeItem: widget.episodeItem,
|
||||||
|
heroTag: widget.heroTag,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
@ -264,7 +300,6 @@ class _MenuBarState extends State<MenuBar> {
|
|||||||
? Colors.grey[200]
|
? Colors.grey[200]
|
||||||
: Theme.of(context).primaryColor,
|
: Theme.of(context).primaryColor,
|
||||||
),
|
),
|
||||||
// borderRadius: BorderRadius.all(Radius.circular(10.0)),
|
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
@ -274,14 +309,13 @@ class _MenuBarState extends State<MenuBar> {
|
|||||||
tag: widget.episodeItem.enclosureUrl + widget.heroTag,
|
tag: widget.episodeItem.enclosureUrl + widget.heroTag,
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 10.0),
|
padding: EdgeInsets.symmetric(horizontal: 10.0),
|
||||||
child: ClipRRect(
|
child: Container(
|
||||||
borderRadius: BorderRadius.all(Radius.circular(15.0)),
|
height: 30.0,
|
||||||
child: Container(
|
width: 30.0,
|
||||||
height: 30.0,
|
color: Theme.of(context).scaffoldBackgroundColor,
|
||||||
width: 30.0,
|
child: CircleAvatar(
|
||||||
color: Theme.of(context).scaffoldBackgroundColor,
|
backgroundImage:
|
||||||
child: Image.file(File("${widget.episodeItem.imagePath}")),
|
FileImage(File("${widget.episodeItem.imagePath}"))),
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -313,13 +347,14 @@ class _MenuBarState extends State<MenuBar> {
|
|||||||
),
|
),
|
||||||
DownloadButton(episodeBrief: widget.episodeItem),
|
DownloadButton(episodeBrief: widget.episodeItem),
|
||||||
Selector<AudioPlayer, List<String>>(
|
Selector<AudioPlayer, List<String>>(
|
||||||
selector: (_, audio) => audio.queue.playlist.map((e)=>e.enclosureUrl).toList(),
|
selector: (_, audio) =>
|
||||||
|
audio.queue.playlist.map((e) => e.enclosureUrl).toList(),
|
||||||
builder: (_, data, __) {
|
builder: (_, data, __) {
|
||||||
print(data.length);
|
|
||||||
return data.contains(widget.episodeItem.enclosureUrl)
|
return data.contains(widget.episodeItem.enclosureUrl)
|
||||||
? _buttonOnMenu(
|
? _buttonOnMenu(
|
||||||
Icon(Icons.playlist_add_check, color: Theme.of(context).accentColor),
|
Icon(Icons.playlist_add_check,
|
||||||
(){})
|
color: Theme.of(context).accentColor),
|
||||||
|
() {})
|
||||||
: _buttonOnMenu(
|
: _buttonOnMenu(
|
||||||
Icon(Icons.playlist_add, color: Colors.grey[700]), () {
|
Icon(Icons.playlist_add, color: Colors.grey[700]), () {
|
||||||
Fluttertoast.showToast(
|
Fluttertoast.showToast(
|
||||||
@ -340,9 +375,6 @@ class _MenuBarState extends State<MenuBar> {
|
|||||||
? Material(
|
? Material(
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
borderRadius: BorderRadius.only(
|
|
||||||
topRight: Radius.circular(5.0),
|
|
||||||
bottomRight: Radius.circular(5.0)),
|
|
||||||
onTap: () {
|
onTap: () {
|
||||||
audio.episodeLoad(widget.episodeItem);
|
audio.episodeLoad(widget.episodeItem);
|
||||||
},
|
},
|
||||||
|
@ -211,14 +211,17 @@ class _DownloadButtonState extends State<DownloadButton> {
|
|||||||
AnimatedContainer(
|
AnimatedContainer(
|
||||||
duration: Duration(seconds: 1),
|
duration: Duration(seconds: 1),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.cyan[300],
|
color: Theme.of(context).accentColor,
|
||||||
borderRadius: BorderRadius.all(Radius.circular(15.0))),
|
borderRadius: BorderRadius.all(Radius.circular(15.0))),
|
||||||
height: 20.0,
|
height: 20.0,
|
||||||
width:
|
width:
|
||||||
(_task.status == DownloadTaskStatus.running) ? 50.0 : 0,
|
(_task.status == DownloadTaskStatus.running) ? 50.0 : 0,
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: Text('${_task.progress}%',
|
child: SingleChildScrollView(
|
||||||
style: TextStyle(color: Colors.white))),
|
scrollDirection: Axis.horizontal,
|
||||||
|
child: Text('${_task.progress}%',
|
||||||
|
style: TextStyle(color: Colors.white)),
|
||||||
|
)),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -236,7 +239,7 @@ class _DownloadButtonState extends State<DownloadButton> {
|
|||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if(task.progress > 0) _pauseDownload(task);
|
if (task.progress > 0) _pauseDownload(task);
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
height: 50.0,
|
height: 50.0,
|
||||||
@ -248,7 +251,8 @@ class _DownloadButtonState extends State<DownloadButton> {
|
|||||||
child: CircularProgressIndicator(
|
child: CircularProgressIndicator(
|
||||||
backgroundColor: Colors.grey[500],
|
backgroundColor: Colors.grey[500],
|
||||||
strokeWidth: 2,
|
strokeWidth: 2,
|
||||||
valueColor: AlwaysStoppedAnimation<Color>(Colors.cyan[300]),
|
valueColor: AlwaysStoppedAnimation<Color>(
|
||||||
|
Theme.of(context).accentColor),
|
||||||
value: task.progress / 100,
|
value: task.progress / 100,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -283,7 +287,7 @@ class _DownloadButtonState extends State<DownloadButton> {
|
|||||||
return _buttonOnMenu(
|
return _buttonOnMenu(
|
||||||
Icon(
|
Icon(
|
||||||
Icons.done_all,
|
Icons.done_all,
|
||||||
color: Colors.blue,
|
color: Theme.of(context).accentColor,
|
||||||
),
|
),
|
||||||
() => _deleteDownload(task));
|
() => _deleteDownload(task));
|
||||||
} else if (task.status == DownloadTaskStatus.failed) {
|
} else if (task.status == DownloadTaskStatus.failed) {
|
||||||
|
@ -1,35 +1,161 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
|
||||||
class AboutApp extends StatelessWidget {
|
class AboutApp extends StatelessWidget {
|
||||||
TextSpan buildTextSpan() {
|
_launchUrl(String url) async {
|
||||||
return TextSpan(children: [
|
if (await canLaunch(url)) {
|
||||||
TextSpan(text: 'Tsacdop\n', style: TextStyle(fontSize: 20)),
|
await launch(url);
|
||||||
TextSpan(
|
} else {
|
||||||
text:
|
throw 'Could not launch $url';
|
||||||
'Tsacdop is a podcast client developed by flutter, is a simple, easy-use player.\n'),
|
}
|
||||||
TextSpan(text: 'Github https://github.com/stonga/tsacdop .\n'),
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _listItem(
|
||||||
|
BuildContext context, String text, IconData icons, String url) =>
|
||||||
|
InkWell(
|
||||||
|
onTap: () => _launchUrl(url),
|
||||||
|
child: Container(
|
||||||
|
height: 50.0,
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 20.0),
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border(
|
||||||
|
bottom: Divider.createBorderSide(context),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: <Widget>[
|
||||||
|
Icon(icons, color: Colors.grey[700]),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 10),
|
||||||
|
),
|
||||||
|
Text(text),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||||
value: SystemUiOverlayStyle(
|
value: SystemUiOverlayStyle(
|
||||||
statusBarIconBrightness: Theme.of(context).accentColorBrightness,
|
statusBarIconBrightness: Theme.of(context).accentColorBrightness,
|
||||||
systemNavigationBarColor: Theme.of(context).primaryColor,
|
systemNavigationBarColor: Theme.of(context).primaryColor,
|
||||||
statusBarColor: Theme.of(context).primaryColor,
|
statusBarColor: Theme.of(context).primaryColor,
|
||||||
),
|
),
|
||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
|
backgroundColor: Theme.of(context).primaryColor,
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text('Tsacdop'),
|
title: Text('About'),
|
||||||
centerTitle: true,
|
|
||||||
),
|
),
|
||||||
body: Container(
|
body: Container(
|
||||||
padding: EdgeInsets.all(20),
|
padding: EdgeInsets.all(20),
|
||||||
alignment: Alignment.topLeft,
|
alignment: Alignment.topLeft,
|
||||||
child: Text.rich(buildTextSpan()),
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: <Widget>[
|
||||||
|
Container(
|
||||||
|
height: 200.0,
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: <Widget>[
|
||||||
|
Image(
|
||||||
|
image: AssetImage('assets/logo.png'),
|
||||||
|
height: 80,
|
||||||
|
),
|
||||||
|
Text('Version: 0.1.1'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 50),
|
||||||
|
height: 50,
|
||||||
|
child: Text(
|
||||||
|
'Tsacdop is a podcast client developed with flutter, a simple, beautiful, and easy-use player.',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.all(5.0),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
top: 20.0,
|
||||||
|
bottom: 10.0
|
||||||
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(10)),
|
||||||
|
border: Border.all(color: Theme.of(context).accentColor, width: 1),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: <Widget>[
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 20.0),
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: Text(
|
||||||
|
'Developer',
|
||||||
|
style:
|
||||||
|
TextStyle(color: Theme.of(context).accentColor),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
_listItem(
|
||||||
|
context,
|
||||||
|
'GitHub',
|
||||||
|
FontAwesomeIcons.githubSquare,
|
||||||
|
'https://github.com/stonaga/tsacdop'),
|
||||||
|
_listItem(
|
||||||
|
context,
|
||||||
|
'Twitter',
|
||||||
|
FontAwesomeIcons.twitterSquare,
|
||||||
|
'https://twitter.com'),
|
||||||
|
_listItem(
|
||||||
|
context,
|
||||||
|
'Gmail',
|
||||||
|
FontAwesomeIcons.envelopeSquare,
|
||||||
|
'mailto:<xijieyin@gmail.com>?subject=Tsacdop Feedback'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Spacer(),
|
||||||
|
Container(
|
||||||
|
height: 50,
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
Image.asset(
|
||||||
|
'assets/text.png',
|
||||||
|
height: 25,
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 5),
|
||||||
|
),
|
||||||
|
Icon(
|
||||||
|
Icons.favorite,
|
||||||
|
color: Colors.blue,
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 5),
|
||||||
|
),
|
||||||
|
FlutterLogo(
|
||||||
|
size: 18,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
)),
|
)),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -10,8 +10,10 @@ import 'package:provider/provider.dart';
|
|||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:image/image.dart' as img;
|
import 'package:image/image.dart' as img;
|
||||||
import 'package:fluttertoast/fluttertoast.dart';
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
|
import 'package:tsacdop/class/audiostate.dart';
|
||||||
import 'package:tsacdop/class/fireside_data.dart';
|
import 'package:tsacdop/class/fireside_data.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
import 'package:tuple/tuple.dart';
|
||||||
|
|
||||||
import 'package:tsacdop/class/importompl.dart';
|
import 'package:tsacdop/class/importompl.dart';
|
||||||
import 'package:tsacdop/class/podcast_group.dart';
|
import 'package:tsacdop/class/podcast_group.dart';
|
||||||
@ -30,9 +32,30 @@ class MyHomePage extends StatefulWidget {
|
|||||||
class _MyHomePageState extends State<MyHomePage> {
|
class _MyHomePageState extends State<MyHomePage> {
|
||||||
final _MyHomePageDelegate _delegate = _MyHomePageDelegate();
|
final _MyHomePageDelegate _delegate = _MyHomePageDelegate();
|
||||||
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
|
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
var audio = Provider.of<AudioPlayer>(context, listen: false);
|
||||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||||
value: SystemUiOverlayStyle(
|
value: SystemUiOverlayStyle(
|
||||||
statusBarIconBrightness: Theme.of(context).accentColorBrightness,
|
statusBarIconBrightness: Theme.of(context).accentColorBrightness,
|
||||||
@ -62,6 +85,37 @@ class _MyHomePageState extends State<MyHomePage> {
|
|||||||
PopupMenu(),
|
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(),
|
body: Home(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -235,9 +289,10 @@ class _SearchResultState extends State<SearchResult> {
|
|||||||
try {
|
try {
|
||||||
Response response = await Dio().get(rss);
|
Response response = await Dio().get(rss);
|
||||||
var dbHelper = DBHelper();
|
var dbHelper = DBHelper();
|
||||||
String _realUrl =
|
String _realUrl = response.realUri.toString();
|
||||||
response.isRedirect ? response.realUri.toString() : rss;
|
|
||||||
bool _checkUrl = await dbHelper.checkPodcast(_realUrl);
|
bool _checkUrl = await dbHelper.checkPodcast(_realUrl);
|
||||||
|
|
||||||
if (_checkUrl) {
|
if (_checkUrl) {
|
||||||
if (mounted) setState(() => _issubscribe = true);
|
if (mounted) setState(() => _issubscribe = true);
|
||||||
|
|
||||||
@ -261,7 +316,6 @@ class _SearchResultState extends State<SearchResult> {
|
|||||||
String _imagePath = "${dir.path}/$_uuid.png";
|
String _imagePath = "${dir.path}/$_uuid.png";
|
||||||
String _primaryColor = await getColor(File("${dir.path}/$_uuid.png"));
|
String _primaryColor = await getColor(File("${dir.path}/$_uuid.png"));
|
||||||
String _author = _p.itunes.author ?? _p.author ?? '';
|
String _author = _p.itunes.author ?? _p.author ?? '';
|
||||||
String _email = _p.itunes.owner?.email ?? '';
|
|
||||||
String _provider = _p.generator ?? '';
|
String _provider = _p.generator ?? '';
|
||||||
String _link = _p.link ?? '';
|
String _link = _p.link ?? '';
|
||||||
PodcastLocal podcastLocal = PodcastLocal(
|
PodcastLocal podcastLocal = PodcastLocal(
|
||||||
@ -272,13 +326,12 @@ class _SearchResultState extends State<SearchResult> {
|
|||||||
_author,
|
_author,
|
||||||
_uuid,
|
_uuid,
|
||||||
_imagePath,
|
_imagePath,
|
||||||
_email,
|
|
||||||
_provider,
|
_provider,
|
||||||
_link);
|
_link);
|
||||||
podcastLocal.description = _p.description;
|
podcastLocal.description = _p.description;
|
||||||
await groupList.subscribe(podcastLocal);
|
await groupList.subscribe(podcastLocal);
|
||||||
if(_provider.contains('fireside'))
|
|
||||||
{
|
if (_provider.contains('fireside')) {
|
||||||
FiresideData data = FiresideData(_uuid, _link);
|
FiresideData data = FiresideData(_uuid, _link);
|
||||||
await data.fatchData();
|
await data.fatchData();
|
||||||
}
|
}
|
||||||
@ -386,7 +439,10 @@ class _SearchResultState extends State<SearchResult> {
|
|||||||
widget.onlinePodcast.description.trim(),
|
widget.onlinePodcast.description.trim(),
|
||||||
maxLines: 3,
|
maxLines: 3,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: Theme.of(context).textTheme.bodyText1.copyWith(color: Colors.white),
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodyText1
|
||||||
|
.copyWith(color: Colors.white),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: Center(),
|
: Center(),
|
||||||
|
@ -6,7 +6,7 @@ class Import extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Consumer<ImportOmpl>(
|
return Consumer<ImportOmpl>(
|
||||||
builder: (context, importOmpl, _) => Container(
|
builder: (_, importOmpl, __) => Container(
|
||||||
color: Theme.of(context).primaryColorDark,
|
color: Theme.of(context).primaryColorDark,
|
||||||
child: importOmpl.importState == ImportState.start
|
child: importOmpl.importState == ImportState.start
|
||||||
? Column(
|
? Column(
|
||||||
|
@ -4,8 +4,7 @@ import 'dart:async';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:tsacdop/class/podcast_group.dart';
|
import 'package:tsacdop/class/fireside_data.dart';
|
||||||
import 'package:tsacdop/class/settingstate.dart';
|
|
||||||
import 'package:xml/xml.dart' as xml;
|
import 'package:xml/xml.dart' as xml;
|
||||||
import 'package:file_picker/file_picker.dart';
|
import 'package:file_picker/file_picker.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
@ -15,6 +14,8 @@ import 'package:image/image.dart' as img;
|
|||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
import 'package:fluttertoast/fluttertoast.dart';
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
|
|
||||||
|
import 'package:tsacdop/class/podcast_group.dart';
|
||||||
|
import 'package:tsacdop/settings/settting.dart';
|
||||||
import 'about.dart';
|
import 'about.dart';
|
||||||
import 'package:tsacdop/class/podcastlocal.dart';
|
import 'package:tsacdop/class/podcastlocal.dart';
|
||||||
import 'package:tsacdop/local_storage/sqflite_localpodcast.dart';
|
import 'package:tsacdop/local_storage/sqflite_localpodcast.dart';
|
||||||
@ -48,7 +49,6 @@ class PopupMenu extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
ImportOmpl importOmpl = Provider.of<ImportOmpl>(context, listen: false);
|
ImportOmpl importOmpl = Provider.of<ImportOmpl>(context, listen: false);
|
||||||
GroupList groupList = Provider.of<GroupList>(context, listen: false);
|
GroupList groupList = Provider.of<GroupList>(context, listen: false);
|
||||||
SettingState setting = Provider.of<SettingState>(context);
|
|
||||||
_refreshAll() async {
|
_refreshAll() async {
|
||||||
var dbHelper = DBHelper();
|
var dbHelper = DBHelper();
|
||||||
List<PodcastLocal> podcastList = await dbHelper.getPodcastLocalAll();
|
List<PodcastLocal> podcastList = await dbHelper.getPodcastLocalAll();
|
||||||
@ -68,28 +68,29 @@ class PopupMenu extends StatelessWidget {
|
|||||||
Response response = await Dio().get(rss);
|
Response response = await Dio().get(rss);
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
var _p = RssFeed.parse(response.data);
|
var _p = RssFeed.parse(response.data);
|
||||||
|
|
||||||
var dir = await getApplicationDocumentsDirectory();
|
var dir = await getApplicationDocumentsDirectory();
|
||||||
|
|
||||||
Response<List<int>> imageResponse = await Dio().get<List<int>>(
|
String _realUrl = response.redirects.isEmpty ? rss : response.realUri.toString();
|
||||||
_p.itunes.image.href,
|
|
||||||
options: Options(responseType: ResponseType.bytes));
|
|
||||||
img.Image image = img.decodeImage(imageResponse.data);
|
|
||||||
img.Image thumbnail = img.copyResize(image, width: 300);
|
|
||||||
String _uuid = Uuid().v4();
|
|
||||||
String _realUrl =
|
|
||||||
response.isRedirect ? response.realUri.toString() : rss;
|
|
||||||
print(_realUrl);
|
print(_realUrl);
|
||||||
|
|
||||||
bool _checkUrl = await dbHelper.checkPodcast(_realUrl);
|
bool _checkUrl = await dbHelper.checkPodcast(_realUrl);
|
||||||
|
|
||||||
if (_checkUrl) {
|
if (_checkUrl) {
|
||||||
|
Response<List<int>> imageResponse = await Dio().get<List<int>>(
|
||||||
|
_p.itunes.image.href,
|
||||||
|
options: Options(responseType: ResponseType.bytes));
|
||||||
|
img.Image image = img.decodeImage(imageResponse.data);
|
||||||
|
img.Image thumbnail = img.copyResize(image, width: 300);
|
||||||
|
String _uuid = Uuid().v4();
|
||||||
File("${dir.path}/$_uuid.png")
|
File("${dir.path}/$_uuid.png")
|
||||||
..writeAsBytesSync(img.encodePng(thumbnail));
|
..writeAsBytesSync(img.encodePng(thumbnail));
|
||||||
|
|
||||||
String _imagePath = "${dir.path}/$_uuid.png";
|
String _imagePath = "${dir.path}/$_uuid.png";
|
||||||
String _primaryColor = await getColor(File("${dir.path}/$_uuid.png"));
|
String _primaryColor = await getColor(File("${dir.path}/$_uuid.png"));
|
||||||
String _author = _p.itunes.author ?? _p.author??'';
|
String _author = _p.itunes.author ?? _p.author ?? '';
|
||||||
String _email = _p.itunes.owner.email?? '';
|
String _provider = _p.generator ?? '';
|
||||||
String _provider = _p.generator??'';
|
String _link = _p.link ?? '';
|
||||||
String _link = _p.link??'';
|
|
||||||
PodcastLocal podcastLocal = PodcastLocal(
|
PodcastLocal podcastLocal = PodcastLocal(
|
||||||
_p.title,
|
_p.title,
|
||||||
_p.itunes.image.href,
|
_p.itunes.image.href,
|
||||||
@ -98,13 +99,18 @@ class PopupMenu extends StatelessWidget {
|
|||||||
_author,
|
_author,
|
||||||
_uuid,
|
_uuid,
|
||||||
_imagePath,
|
_imagePath,
|
||||||
_email,
|
|
||||||
_provider,
|
_provider,
|
||||||
_link);
|
_link);
|
||||||
|
|
||||||
podcastLocal.description = _p.description;
|
podcastLocal.description = _p.description;
|
||||||
|
|
||||||
groupList.subscribe(podcastLocal);
|
await groupList.subscribe(podcastLocal);
|
||||||
|
|
||||||
|
if (_provider.contains('fireside'))
|
||||||
|
{
|
||||||
|
FiresideData data = FiresideData(_uuid, _link);
|
||||||
|
await data.fatchData();
|
||||||
|
}
|
||||||
|
|
||||||
importOmpl.importState = ImportState.parse;
|
importOmpl.importState = ImportState.parse;
|
||||||
|
|
||||||
@ -178,28 +184,70 @@ class PopupMenu extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return PopupMenuButton<int>(
|
return PopupMenuButton<int>(
|
||||||
elevation: 3,
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))),
|
||||||
|
elevation: 1,
|
||||||
tooltip: 'Menu',
|
tooltip: 'Menu',
|
||||||
itemBuilder: (context) => [
|
itemBuilder: (context) => [
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
value: 1,
|
value: 1,
|
||||||
child: Text('Refresh All'),
|
child: Container(
|
||||||
|
padding: EdgeInsets.only(left: 10),
|
||||||
|
child: Row(
|
||||||
|
children: <Widget>[
|
||||||
|
Icon(Icons.refresh),
|
||||||
|
Padding(padding: EdgeInsets.symmetric(horizontal: 5.0),),
|
||||||
|
Text('Refresh All'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
value: 2,
|
value: 2,
|
||||||
child: Text('Impoer OMPL'),
|
child: Container(
|
||||||
),
|
padding: EdgeInsets.only(left: 10),
|
||||||
PopupMenuItem(
|
child: Row(
|
||||||
value: 3,
|
children: <Widget>[
|
||||||
child: setting.theme != 2 ? Text('Night Mode') : Text('Light Mode'),
|
Icon(Icons.attachment),
|
||||||
),
|
Padding(padding: EdgeInsets.symmetric(horizontal: 5.0),),
|
||||||
PopupMenuItem(
|
Text('Import OMPL'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// PopupMenuItem(
|
||||||
|
// value: 3,
|
||||||
|
// child: setting.theme != 2 ? Text('Night Mode') : Text('Light Mode'),
|
||||||
|
// ),
|
||||||
|
PopupMenuItem(
|
||||||
value: 4,
|
value: 4,
|
||||||
child: Text('About'),
|
child: Container(
|
||||||
|
padding: EdgeInsets.only(left: 10),
|
||||||
|
child: Row(
|
||||||
|
children: <Widget>[
|
||||||
|
Icon(Icons.swap_calls),
|
||||||
|
Padding(padding: EdgeInsets.symmetric(horizontal: 5.0),),
|
||||||
|
Text('Settings'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
PopupMenuItem(
|
||||||
|
value: 5,
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.only(left: 10),
|
||||||
|
child: Row(
|
||||||
|
children: <Widget>[
|
||||||
|
Icon(Icons.info_outline),
|
||||||
|
Padding(padding: EdgeInsets.symmetric(horizontal: 5.0),),
|
||||||
|
Text('About'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
onSelected: (value) {
|
onSelected: (value) {
|
||||||
if (value == 4) {
|
if (value == 5) {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context, MaterialPageRoute(builder: (context) => AboutApp()));
|
context, MaterialPageRoute(builder: (context) => AboutApp()));
|
||||||
} else if (value == 2) {
|
} else if (value == 2) {
|
||||||
@ -207,8 +255,11 @@ class PopupMenu extends StatelessWidget {
|
|||||||
} else if (value == 1) {
|
} else if (value == 1) {
|
||||||
_refreshAll();
|
_refreshAll();
|
||||||
} else if (value == 3) {
|
} else if (value == 3) {
|
||||||
setting.theme != 2 ? setting.setTheme(2) : setting.setTheme(1);
|
// setting.theme != 2 ? setting.setTheme(2) : setting.setTheme(1);
|
||||||
}
|
} else if (value == 4) {
|
||||||
|
Navigator.push(
|
||||||
|
context, MaterialPageRoute(builder: (context) => Settings()));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -83,7 +83,9 @@ class _AudioPanelState extends State<AudioPanel>
|
|||||||
BoxShadow(
|
BoxShadow(
|
||||||
offset: Offset(0, -1),
|
offset: Offset(0, -1),
|
||||||
blurRadius: 4,
|
blurRadius: 4,
|
||||||
color: Colors.grey[400],
|
color: Theme.of(context).brightness == Brightness.light
|
||||||
|
? Colors.grey[400]
|
||||||
|
: Colors.grey[800],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -1,16 +1,18 @@
|
|||||||
import 'dart:convert';
|
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:math' as math;
|
import 'dart:math' as math;
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:marquee/marquee.dart';
|
import 'package:marquee/marquee.dart';
|
||||||
import 'package:tsacdop/class/episodebrief.dart';
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
import 'package:tuple/tuple.dart';
|
||||||
|
|
||||||
|
import 'package:tsacdop/class/episodebrief.dart';
|
||||||
import 'package:tsacdop/class/audiostate.dart';
|
import 'package:tsacdop/class/audiostate.dart';
|
||||||
import 'package:tsacdop/episodes/episodedetail.dart';
|
import 'package:tsacdop/episodes/episodedetail.dart';
|
||||||
import 'package:tsacdop/home/audiopanel.dart';
|
import 'package:tsacdop/home/audiopanel.dart';
|
||||||
import 'package:tsacdop/util/pageroute.dart';
|
import 'package:tsacdop/util/pageroute.dart';
|
||||||
|
import 'package:tsacdop/util/colorize.dart';
|
||||||
|
|
||||||
class PlayerWidget extends StatefulWidget {
|
class PlayerWidget extends StatefulWidget {
|
||||||
@override
|
@override
|
||||||
@ -33,325 +35,402 @@ class _PlayerWidgetState extends State<PlayerWidget> {
|
|||||||
|
|
||||||
Widget _expandedPanel(BuildContext context) {
|
Widget _expandedPanel(BuildContext context) {
|
||||||
var audio = Provider.of<AudioPlayer>(context, listen: false);
|
var audio = Provider.of<AudioPlayer>(context, listen: false);
|
||||||
return !_showlist
|
return Stack(
|
||||||
? Container(
|
children: <Widget>[
|
||||||
color: Theme.of(context).primaryColor,
|
Container(
|
||||||
height: 300,
|
color: Theme.of(context).primaryColor,
|
||||||
padding: EdgeInsets.symmetric(horizontal: 10.0),
|
height: 300,
|
||||||
child: Column(
|
padding: EdgeInsets.symmetric(horizontal: 10.0),
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
children: <Widget>[
|
mainAxisSize: MainAxisSize.min,
|
||||||
Container(
|
children: <Widget>[
|
||||||
height: 80.0,
|
Container(
|
||||||
padding: EdgeInsets.all(20),
|
height: 80.0,
|
||||||
alignment: Alignment.center,
|
padding: EdgeInsets.all(20),
|
||||||
child: Selector<AudioPlayer, String>(
|
alignment: Alignment.center,
|
||||||
selector: (_, audio) => audio.episode.title,
|
child: Selector<AudioPlayer, String>(
|
||||||
builder: (_, title, __) {
|
selector: (_, audio) => audio.episode.title,
|
||||||
return Container(
|
builder: (_, title, __) {
|
||||||
child: LayoutBuilder(
|
return Container(
|
||||||
builder: (context, size) {
|
child: LayoutBuilder(
|
||||||
var span = TextSpan(text: title,style: TextStyle(
|
builder: (context, size) {
|
||||||
fontWeight: FontWeight.bold, fontSize: 20));
|
var span = TextSpan(
|
||||||
var tp = TextPainter(
|
text: title,
|
||||||
text: span,
|
style: TextStyle(
|
||||||
maxLines: 1,
|
fontWeight: FontWeight.bold, fontSize: 20));
|
||||||
textDirection: TextDirection.ltr);
|
var tp = TextPainter(
|
||||||
tp.layout(maxWidth: size.maxWidth);
|
text: span,
|
||||||
if (tp.didExceedMaxLines) {
|
maxLines: 1,
|
||||||
return Marquee(
|
textDirection: TextDirection.ltr);
|
||||||
text: title,
|
tp.layout(maxWidth: size.maxWidth);
|
||||||
style: TextStyle(
|
if (tp.didExceedMaxLines) {
|
||||||
fontWeight: FontWeight.bold, fontSize: 18),
|
return Marquee(
|
||||||
scrollAxis: Axis.horizontal,
|
text: title,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
style: TextStyle(
|
||||||
blankSpace: 30.0,
|
fontWeight: FontWeight.bold, fontSize: 18),
|
||||||
velocity: 50.0,
|
scrollAxis: Axis.horizontal,
|
||||||
pauseAfterRound: Duration(seconds: 1),
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
startPadding: 30.0,
|
blankSpace: 30.0,
|
||||||
accelerationDuration: Duration(seconds: 1),
|
velocity: 50.0,
|
||||||
accelerationCurve: Curves.linear,
|
pauseAfterRound: Duration(seconds: 1),
|
||||||
decelerationDuration:
|
startPadding: 30.0,
|
||||||
Duration(milliseconds: 500),
|
accelerationDuration: Duration(seconds: 1),
|
||||||
decelerationCurve: Curves.easeOut,
|
accelerationCurve: Curves.linear,
|
||||||
);
|
decelerationDuration:
|
||||||
} else {
|
Duration(milliseconds: 500),
|
||||||
return Text(
|
decelerationCurve: Curves.easeOut,
|
||||||
title,
|
);
|
||||||
style: TextStyle(
|
} else {
|
||||||
fontWeight: FontWeight.bold, fontSize: 20),
|
return Text(
|
||||||
);
|
title,
|
||||||
}
|
style: TextStyle(
|
||||||
},
|
fontWeight: FontWeight.bold, fontSize: 20),
|
||||||
),
|
);
|
||||||
);
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
Consumer<AudioPlayer>(
|
),
|
||||||
builder: (_, data, __) {
|
Consumer<AudioPlayer>(
|
||||||
return Column(
|
builder: (_, data, __) {
|
||||||
|
return Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: <Widget>[
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.only(left: 30, right: 30),
|
||||||
|
child: SliderTheme(
|
||||||
|
data: SliderTheme.of(context).copyWith(
|
||||||
|
activeTrackColor: Colors.blue[100],
|
||||||
|
inactiveTrackColor: Colors.grey[300],
|
||||||
|
trackHeight: 3.0,
|
||||||
|
thumbColor: Colors.blue[400],
|
||||||
|
thumbShape: RoundSliderThumbShape(
|
||||||
|
enabledThumbRadius: 6.0),
|
||||||
|
overlayColor: Colors.blue.withAlpha(32),
|
||||||
|
overlayShape:
|
||||||
|
RoundSliderOverlayShape(overlayRadius: 14.0),
|
||||||
|
),
|
||||||
|
child: Slider(
|
||||||
|
value: data.seekSliderValue,
|
||||||
|
onChanged: (double val) {
|
||||||
|
audio.sliderSeek(val);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
height: 20.0,
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 50.0),
|
||||||
|
child: Row(
|
||||||
|
children: <Widget>[
|
||||||
|
Text(
|
||||||
|
_stringForSeconds(
|
||||||
|
data.backgroundAudioPosition) ??
|
||||||
|
'',
|
||||||
|
style: TextStyle(fontSize: 10),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Container(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: data.remoteErrorMessage != null
|
||||||
|
? Text(data.remoteErrorMessage,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: const Color(0xFFFF0000)))
|
||||||
|
: Text(
|
||||||
|
data.remoteAudioLoading
|
||||||
|
? 'Buffring...'
|
||||||
|
: '',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.accentColor),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
_stringForSeconds(
|
||||||
|
data.backgroundAudioDuration) ??
|
||||||
|
'',
|
||||||
|
style: TextStyle(fontSize: 10),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
height: 100,
|
||||||
|
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),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
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,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Container(
|
Padding(
|
||||||
padding: EdgeInsets.only(left: 30, right: 30),
|
padding: EdgeInsets.all(5.0),
|
||||||
child: SliderTheme(
|
|
||||||
data: SliderTheme.of(context).copyWith(
|
|
||||||
activeTrackColor: Colors.blue[100],
|
|
||||||
inactiveTrackColor: Colors.grey[300],
|
|
||||||
trackHeight: 3.0,
|
|
||||||
thumbColor: Colors.blue[400],
|
|
||||||
thumbShape: RoundSliderThumbShape(
|
|
||||||
enabledThumbRadius: 6.0),
|
|
||||||
overlayColor: Colors.blue.withAlpha(32),
|
|
||||||
overlayShape: RoundSliderOverlayShape(
|
|
||||||
overlayRadius: 14.0),
|
|
||||||
),
|
|
||||||
child: Slider(
|
|
||||||
value: data.seekSliderValue,
|
|
||||||
onChanged: (double val) {
|
|
||||||
audio.sliderSeek(val);
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
height: 20.0,
|
height: 30.0,
|
||||||
padding: EdgeInsets.symmetric(horizontal: 50.0),
|
width: 30.0,
|
||||||
child: Row(
|
child: CircleAvatar(
|
||||||
children: <Widget>[
|
backgroundImage:
|
||||||
Text(
|
FileImage(File("${episode.imagePath}")),
|
||||||
_stringForSeconds(
|
|
||||||
data.backgroundAudioPosition) ??
|
|
||||||
'',
|
|
||||||
style: TextStyle(fontSize: 10),
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: Container(
|
|
||||||
alignment: Alignment.center,
|
|
||||||
child: data.remoteErrorMessage != null
|
|
||||||
? Text(data.remoteErrorMessage,
|
|
||||||
style: const TextStyle(
|
|
||||||
color: const Color(0xFFFF0000)))
|
|
||||||
: Text(
|
|
||||||
data.remoteAudioLoading
|
|
||||||
? 'Buffring...'
|
|
||||||
: '',
|
|
||||||
style: TextStyle(
|
|
||||||
color: Theme.of(context)
|
|
||||||
.accentColor),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
_stringForSeconds(
|
|
||||||
data.backgroundAudioDuration) ??
|
|
||||||
'',
|
|
||||||
style: TextStyle(fontSize: 10),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
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)),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
Container(
|
),
|
||||||
height: 100,
|
]),
|
||||||
child: Selector<AudioPlayer, bool>(
|
),
|
||||||
selector: (_, audio) => audio.backgroundAudioPlaying,
|
Container(
|
||||||
builder: (_, backplay, __) {
|
alignment: Alignment.bottomLeft,
|
||||||
return Row(
|
child: AnimatedContainer(
|
||||||
mainAxisSize: MainAxisSize.min,
|
duration: Duration(milliseconds: 400),
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
height: _showlist ? 300 : 0,
|
||||||
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.symmetric(vertical: 10.0),
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: 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>[
|
|
||||||
Container(
|
|
||||||
padding: EdgeInsets.all(10.0),
|
|
||||||
child: ClipRRect(
|
|
||||||
borderRadius:
|
|
||||||
BorderRadius.all(Radius.circular(15.0)),
|
|
||||||
child: Container(
|
|
||||||
height: 30.0,
|
|
||||||
width: 30.0,
|
|
||||||
child: Image.file(
|
|
||||||
File("${episode.imagePath}"))),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Spacer(),
|
|
||||||
Material(
|
|
||||||
color: Colors.transparent,
|
|
||||||
child: InkWell(
|
|
||||||
onTap: () => Navigator.push(
|
|
||||||
context,
|
|
||||||
SlideUptRoute(
|
|
||||||
page: EpisodeDetail(
|
|
||||||
episodeItem: episode,
|
|
||||||
heroTag: 'playpanel')),
|
|
||||||
),
|
|
||||||
child: Icon(Icons.info),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(Icons.keyboard_arrow_up),
|
|
||||||
onPressed: () =>
|
|
||||||
setState(() => _showlist = true)),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
)
|
|
||||||
: Container(
|
|
||||||
height: 300,
|
|
||||||
width: MediaQuery.of(context).size.width,
|
width: MediaQuery.of(context).size.width,
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
margin: EdgeInsets.all(20),
|
margin: EdgeInsets.all(20),
|
||||||
padding: EdgeInsets.all(10.0),
|
padding: EdgeInsets.only(bottom: 10.0),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.all(Radius.circular(10)),
|
borderRadius: BorderRadius.all(Radius.circular(10.0)),
|
||||||
color: Theme.of(context).scaffoldBackgroundColor,
|
color: Theme.of(context).scaffoldBackgroundColor,
|
||||||
),
|
),
|
||||||
child: Selector<AudioPlayer, List<EpisodeBrief>>(
|
child: Column(
|
||||||
selector: (_, audio) => audio.queue.playlist,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
builder: (_, playlist, __) {
|
mainAxisSize: MainAxisSize.min,
|
||||||
print(playlist.first.title);
|
children: <Widget>[
|
||||||
double _width = MediaQuery.of(context).size.width;
|
SingleChildScrollView(
|
||||||
return Column(
|
scrollDirection: Axis.vertical,
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
child: Container(
|
||||||
mainAxisSize: MainAxisSize.min,
|
padding: EdgeInsets.symmetric(horizontal: 20.0),
|
||||||
children: <Widget>[
|
alignment: Alignment.center,
|
||||||
Container(
|
child: Row(
|
||||||
height: 30.0,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
alignment: Alignment.centerRight,
|
mainAxisSize: MainAxisSize.min,
|
||||||
child: IconButton(
|
children: <Widget>[
|
||||||
icon: Icon(Icons.keyboard_arrow_down),
|
Text(
|
||||||
onPressed: () => setState(() => _showlist = false),
|
'NEXT',
|
||||||
),
|
style: TextStyle(
|
||||||
),
|
color: Theme.of(context).accentColor,
|
||||||
Expanded(
|
fontWeight: FontWeight.bold),
|
||||||
child: Container(
|
|
||||||
child: ListView.builder(
|
|
||||||
itemCount: playlist.length,
|
|
||||||
itemBuilder: (BuildContext context, int index) {
|
|
||||||
print(playlist.length);
|
|
||||||
return Container(
|
|
||||||
height: 50,
|
|
||||||
alignment: Alignment.centerLeft,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
border: Border(
|
|
||||||
bottom: Divider.createBorderSide(context),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: <Widget>[
|
|
||||||
Container(
|
|
||||||
padding: EdgeInsets.all(10.0),
|
|
||||||
child: ClipRRect(
|
|
||||||
borderRadius: BorderRadius.all(
|
|
||||||
Radius.circular(15.0)),
|
|
||||||
child: Container(
|
|
||||||
height: 30.0,
|
|
||||||
width: 30.0,
|
|
||||||
child: Image.file(File(
|
|
||||||
"${playlist[index].imagePath}"))),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Container(
|
|
||||||
alignment: Alignment.centerLeft,
|
|
||||||
width: _width - 200,
|
|
||||||
child: Text(
|
|
||||||
playlist[index].title,
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Spacer(),
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(Icons.play_arrow),
|
|
||||||
onPressed: null),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
Spacer(),
|
||||||
|
Container(
|
||||||
|
height: 40.0,
|
||||||
|
alignment: Alignment.centerRight,
|
||||||
|
child: IconButton(
|
||||||
|
icon: Icon(Icons.keyboard_arrow_down),
|
||||||
|
onPressed: () => setState(() => _showlist = false),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
);
|
),
|
||||||
},
|
Expanded(
|
||||||
|
child: Selector<AudioPlayer, List<EpisodeBrief>>(
|
||||||
|
selector: (_, audio) => audio.queue.playlist,
|
||||||
|
builder: (_, playlist, __) {
|
||||||
|
return ListView.builder(
|
||||||
|
shrinkWrap: true,
|
||||||
|
scrollDirection: Axis.vertical,
|
||||||
|
itemCount: playlist.length,
|
||||||
|
itemBuilder: (BuildContext context, int index) {
|
||||||
|
print(playlist.length);
|
||||||
|
return index == 0
|
||||||
|
? Center()
|
||||||
|
: Dismissible(
|
||||||
|
background: Container(
|
||||||
|
padding:
|
||||||
|
EdgeInsets.symmetric(horizontal: 20.0),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment:
|
||||||
|
MainAxisAlignment.spaceBetween,
|
||||||
|
children: <Widget>[
|
||||||
|
Icon(
|
||||||
|
Icons.delete,
|
||||||
|
color: Theme.of(context).accentColor,
|
||||||
|
),
|
||||||
|
Icon(
|
||||||
|
Icons.delete,
|
||||||
|
color: Theme.of(context).accentColor,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
height: 50,
|
||||||
|
color: Colors.grey[400],
|
||||||
|
),
|
||||||
|
key: Key(playlist[index].enclosureUrl),
|
||||||
|
onDismissed: (direction) async {
|
||||||
|
await audio
|
||||||
|
.delFromPlaylist(playlist[index]);
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg: 'Removed From Playlist',
|
||||||
|
gravity: ToastGravity.BOTTOM,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
Container(
|
||||||
|
height: 50,
|
||||||
|
padding: EdgeInsets.symmetric(
|
||||||
|
horizontal: 10),
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.scaffoldBackgroundColor,
|
||||||
|
),
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
audio.episodeLoad(playlist[index]);
|
||||||
|
setState(() => _showlist = false);
|
||||||
|
},
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment:
|
||||||
|
MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment:
|
||||||
|
CrossAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: <Widget>[
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.all(10.0),
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius:
|
||||||
|
BorderRadius.all(
|
||||||
|
Radius.circular(
|
||||||
|
15.0)),
|
||||||
|
child: Container(
|
||||||
|
height: 30.0,
|
||||||
|
width: 30.0,
|
||||||
|
child: Image.file(File(
|
||||||
|
"${playlist[index].imagePath}"))),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Container(
|
||||||
|
alignment:
|
||||||
|
Alignment.centerLeft,
|
||||||
|
child: Text(
|
||||||
|
playlist[index].title,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow:
|
||||||
|
TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Divider(height: 2),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _miniPanel(double width, BuildContext context) {
|
Widget _miniPanel(double width, BuildContext context) {
|
||||||
@ -359,13 +438,6 @@ class _PlayerWidgetState extends State<PlayerWidget> {
|
|||||||
return Container(
|
return Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Theme.of(context).primaryColor,
|
color: Theme.of(context).primaryColor,
|
||||||
// boxShadow: [
|
|
||||||
// BoxShadow(
|
|
||||||
// offset: Offset(0, -1),
|
|
||||||
// blurRadius: 4,
|
|
||||||
// color: Colors.grey[400],
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
),
|
),
|
||||||
height: 60,
|
height: 60,
|
||||||
child:
|
child:
|
||||||
@ -374,19 +446,9 @@ class _PlayerWidgetState extends State<PlayerWidget> {
|
|||||||
selector: (_, audio) =>
|
selector: (_, audio) =>
|
||||||
Tuple2(audio.episode?.primaryColor, audio.seekSliderValue),
|
Tuple2(audio.episode?.primaryColor, audio.seekSliderValue),
|
||||||
builder: (_, data, __) {
|
builder: (_, data, __) {
|
||||||
var color = json.decode(data.item1);
|
Color _c = (Theme.of(context).brightness == Brightness.light)
|
||||||
Color _c;
|
? data.item1.colorizedark()
|
||||||
if (Theme.of(context).brightness == Brightness.light) {
|
: data.item1.colorizeLight();
|
||||||
_c = (color[0] > 200 && color[1] > 200 && color[2] > 200)
|
|
||||||
? Color.fromRGBO(
|
|
||||||
(255 - color[0]), 255 - color[1], 255 - color[2], 1.0)
|
|
||||||
: Color.fromRGBO(color[0], color[1], color[2], 1.0);
|
|
||||||
} else {
|
|
||||||
_c = (color[0] < 50 && color[1] < 50 && color[2] < 50)
|
|
||||||
? Color.fromRGBO(
|
|
||||||
(255 - color[0]), 255 - color[1], 255 - color[2], 1.0)
|
|
||||||
: Color.fromRGBO(color[0], color[1], color[2], 1.0);
|
|
||||||
}
|
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: 2,
|
height: 2,
|
||||||
child: LinearProgressIndicator(
|
child: LinearProgressIndicator(
|
||||||
@ -411,7 +473,10 @@ class _PlayerWidgetState extends State<PlayerWidget> {
|
|||||||
builder: (_, title, __) {
|
builder: (_, title, __) {
|
||||||
return LayoutBuilder(
|
return LayoutBuilder(
|
||||||
builder: (context, size) {
|
builder: (context, size) {
|
||||||
var span = TextSpan(text: title, style: TextStyle(fontWeight: FontWeight.bold),);
|
var span = TextSpan(
|
||||||
|
text: title,
|
||||||
|
style: TextStyle(fontWeight: FontWeight.bold),
|
||||||
|
);
|
||||||
var tp = TextPainter(
|
var tp = TextPainter(
|
||||||
text: span,
|
text: span,
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
@ -500,17 +565,30 @@ class _PlayerWidgetState extends State<PlayerWidget> {
|
|||||||
: () {
|
: () {
|
||||||
audio.resumeAudio();
|
audio.resumeAudio();
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Stack(
|
||||||
padding: EdgeInsets.all(10.0),
|
alignment: Alignment.center,
|
||||||
child: ClipRRect(
|
children: <Widget>[
|
||||||
borderRadius: BorderRadius.all(
|
Container(
|
||||||
Radius.circular(15.0)),
|
padding: EdgeInsets.all(10.0),
|
||||||
child: Container(
|
child: Container(
|
||||||
height: 30.0,
|
height: 30.0,
|
||||||
width: 30.0,
|
width: 30.0,
|
||||||
child: Image.file(File(
|
child: CircleAvatar(
|
||||||
"${audio.episode.imagePath}"))),
|
backgroundImage: FileImage(File(
|
||||||
),
|
"${audio.episode.imagePath}")),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
height: 50.0,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
color: Colors.black),
|
||||||
|
),
|
||||||
|
Icon(
|
||||||
|
Icons.play_arrow,
|
||||||
|
color: Colors.white,
|
||||||
|
)
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import 'dart:convert';
|
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
@ -8,14 +7,15 @@ import 'package:provider/provider.dart';
|
|||||||
import 'package:fluttertoast/fluttertoast.dart';
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
|
|
||||||
import 'package:tsacdop/class/episodebrief.dart';
|
import 'package:tsacdop/class/episodebrief.dart';
|
||||||
|
import 'package:tsacdop/class/importompl.dart';
|
||||||
import 'package:tsacdop/class/podcast_group.dart';
|
import 'package:tsacdop/class/podcast_group.dart';
|
||||||
import 'package:tsacdop/class/podcastlocal.dart';
|
import 'package:tsacdop/class/podcastlocal.dart';
|
||||||
import 'package:tsacdop/local_storage/sqflite_localpodcast.dart';
|
import 'package:tsacdop/local_storage/sqflite_localpodcast.dart';
|
||||||
|
|
||||||
import 'package:tsacdop/episodes/episodedetail.dart';
|
import 'package:tsacdop/episodes/episodedetail.dart';
|
||||||
import 'package:tsacdop/podcasts/podcastdetail.dart';
|
import 'package:tsacdop/podcasts/podcastdetail.dart';
|
||||||
import 'package:tsacdop/podcasts/podcastmanage.dart';
|
import 'package:tsacdop/podcasts/podcastmanage.dart';
|
||||||
import 'package:tsacdop/util/pageroute.dart';
|
import 'package:tsacdop/util/pageroute.dart';
|
||||||
|
import 'package:tsacdop/util/colorize.dart';
|
||||||
|
|
||||||
class ScrollPodcasts extends StatefulWidget {
|
class ScrollPodcasts extends StatefulWidget {
|
||||||
@override
|
@override
|
||||||
@ -228,7 +228,8 @@ class _ScrollPodcastsState extends State<ScrollPodcasts> {
|
|||||||
left: 6.0,
|
left: 6.0,
|
||||||
right: 6.0),
|
right: 6.0),
|
||||||
indicator: CircleTabIndicator(
|
indicator: CircleTabIndicator(
|
||||||
color: Colors.blue, radius: 3),
|
color: Theme.of(context).accentColor,
|
||||||
|
radius: 3),
|
||||||
isScrollable: true,
|
isScrollable: true,
|
||||||
tabs: groups[_groupIndex]
|
tabs: groups[_groupIndex]
|
||||||
.podcasts
|
.podcasts
|
||||||
@ -251,29 +252,31 @@ class _ScrollPodcastsState extends State<ScrollPodcasts> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Container(
|
Consumer<ImportOmpl>(
|
||||||
height: (_width - 20) / 3 + 40,
|
builder: (_, ompl, __) => Container(
|
||||||
margin: EdgeInsets.only(left: 10, right: 10),
|
height: (_width - 20) / 3 + 40,
|
||||||
decoration: BoxDecoration(
|
margin: EdgeInsets.only(left: 10, right: 10),
|
||||||
color: Theme.of(context).scaffoldBackgroundColor,
|
decoration: BoxDecoration(
|
||||||
),
|
color: Theme.of(context).scaffoldBackgroundColor,
|
||||||
child: TabBarView(
|
),
|
||||||
children: groups[_groupIndex]
|
child: TabBarView(
|
||||||
.podcasts
|
children: groups[_groupIndex]
|
||||||
.map<Widget>((PodcastLocal podcastLocal) {
|
.podcasts
|
||||||
return Container(
|
.map<Widget>((PodcastLocal podcastLocal) {
|
||||||
decoration: BoxDecoration(
|
return Container(
|
||||||
color: Theme.of(context).brightness ==
|
decoration: BoxDecoration(
|
||||||
Brightness.light
|
color: Theme.of(context).brightness ==
|
||||||
? Theme.of(context).primaryColor
|
Brightness.light
|
||||||
: Colors.black12),
|
? Theme.of(context).primaryColor
|
||||||
margin: EdgeInsets.symmetric(horizontal: 5.0),
|
: Colors.black12),
|
||||||
key: ObjectKey(podcastLocal.title),
|
margin: EdgeInsets.symmetric(horizontal: 5.0),
|
||||||
child: PodcastPreview(
|
key: ObjectKey(podcastLocal.title),
|
||||||
podcastLocal: podcastLocal,
|
child: PodcastPreview(
|
||||||
),
|
podcastLocal: podcastLocal,
|
||||||
);
|
),
|
||||||
}).toList(),
|
);
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -293,27 +296,15 @@ class PodcastPreview extends StatefulWidget {
|
|||||||
class _PodcastPreviewState extends State<PodcastPreview> {
|
class _PodcastPreviewState extends State<PodcastPreview> {
|
||||||
Future<List<EpisodeBrief>> _getRssItemTop(PodcastLocal podcastLocal) async {
|
Future<List<EpisodeBrief>> _getRssItemTop(PodcastLocal podcastLocal) async {
|
||||||
var dbHelper = DBHelper();
|
var dbHelper = DBHelper();
|
||||||
Future<List<EpisodeBrief>> episodes =
|
List<EpisodeBrief> episodes = await dbHelper.getRssItemTop(podcastLocal.id);
|
||||||
dbHelper.getRssItemTop(podcastLocal.id);
|
|
||||||
return episodes;
|
return episodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
Color _c;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var color = json.decode(widget.podcastLocal.primaryColor);
|
Color _c = (Theme.of(context).brightness == Brightness.light)
|
||||||
if (Theme.of(context).brightness == Brightness.light) {
|
? widget.podcastLocal.primaryColor.colorizedark()
|
||||||
(color[0] > 200 && color[1] > 200 && color[2] > 200)
|
: widget.podcastLocal.primaryColor.colorizeLight();
|
||||||
? _c = Color.fromRGBO(
|
|
||||||
(255 - color[0]), 255 - color[1], 255 - color[2], 1.0)
|
|
||||||
: _c = Color.fromRGBO(color[0], color[1], color[2], 1.0);
|
|
||||||
} else {
|
|
||||||
(color[0] < 50 && color[1] < 50 && color[2] < 50)
|
|
||||||
? _c = Color.fromRGBO(
|
|
||||||
(255 - color[0]), 255 - color[1], 255 - color[2], 1.0)
|
|
||||||
: _c = Color.fromRGBO(color[0], color[1], color[2], 1.0);
|
|
||||||
}
|
|
||||||
return Column(
|
return Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
@ -401,20 +392,10 @@ class ShowEpisode extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(BuildContext context, int index) {
|
(BuildContext context, int index) {
|
||||||
Color _c;
|
Color _c =
|
||||||
var color = json.decode(podcast[index].primaryColor);
|
(Theme.of(context).brightness == Brightness.light)
|
||||||
|
? podcastLocal.primaryColor.colorizedark()
|
||||||
if (Theme.of(context).brightness == Brightness.light) {
|
: podcastLocal.primaryColor.colorizeLight();
|
||||||
(color[0] > 200 && color[1] > 200 && color[2] > 200)
|
|
||||||
? _c = Color.fromRGBO(
|
|
||||||
(255 - color[0]), 255 - color[1], 255 - color[2], 1.0)
|
|
||||||
: _c = Color.fromRGBO(color[0], color[1], color[2], 1.0);
|
|
||||||
} else {
|
|
||||||
(color[0] < 50 && color[1] < 50 && color[2] < 50)
|
|
||||||
? _c = Color.fromRGBO(
|
|
||||||
(255 - color[0]), 255 - color[1], 255 - color[2], 1.0)
|
|
||||||
: _c = Color.fromRGBO(color[0], color[1], color[2], 1.0);
|
|
||||||
}
|
|
||||||
return InkWell(
|
return InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
@ -438,13 +419,6 @@ class ShowEpisode extends StatelessWidget {
|
|||||||
// color: Theme.of(context).primaryColor,
|
// color: Theme.of(context).primaryColor,
|
||||||
width: 3.0,
|
width: 3.0,
|
||||||
),
|
),
|
||||||
// boxShadow: [
|
|
||||||
// BoxShadow(
|
|
||||||
// color: Theme.of(context).primaryColor,
|
|
||||||
// blurRadius: 1.0,
|
|
||||||
// spreadRadius: 0.5,
|
|
||||||
// ),
|
|
||||||
// ]
|
|
||||||
),
|
),
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
padding: EdgeInsets.all(10.0),
|
padding: EdgeInsets.all(10.0),
|
||||||
@ -459,15 +433,11 @@ class ShowEpisode extends StatelessWidget {
|
|||||||
Hero(
|
Hero(
|
||||||
tag: podcast[index].enclosureUrl + 'scroll',
|
tag: podcast[index].enclosureUrl + 'scroll',
|
||||||
child: Container(
|
child: Container(
|
||||||
child: ClipRRect(
|
height: _width / 18,
|
||||||
borderRadius: BorderRadius.all(
|
width: _width / 18,
|
||||||
Radius.circular(_width / 36)),
|
child: CircleAvatar(
|
||||||
child: Container(
|
backgroundImage: FileImage(
|
||||||
height: _width / 18,
|
File("${podcastLocal.imagePath}")),
|
||||||
width: _width / 18,
|
|
||||||
child: Image.file(
|
|
||||||
File("${podcastLocal.imagePath}")),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -2,6 +2,7 @@ import 'dart:ui';
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:tsacdop/class/episodebrief.dart';
|
import 'package:tsacdop/class/episodebrief.dart';
|
||||||
|
import 'package:tsacdop/home/paly_history.dart';
|
||||||
import 'package:tsacdop/local_storage/sqflite_localpodcast.dart';
|
import 'package:tsacdop/local_storage/sqflite_localpodcast.dart';
|
||||||
import 'package:tsacdop/util/episodegrid.dart';
|
import 'package:tsacdop/util/episodegrid.dart';
|
||||||
|
|
||||||
@ -22,6 +23,39 @@ class _MainTabState extends State<MainTab> with TickerProviderStateMixin {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget playHistory() {
|
||||||
|
return PopupMenuButton<int>(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(10))),
|
||||||
|
elevation: 1,
|
||||||
|
icon: Icon(Icons.history),
|
||||||
|
tooltip: "Menu",
|
||||||
|
itemBuilder: (context) => [
|
||||||
|
PopupMenuItem(
|
||||||
|
value: 0,
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.only(left: 10),
|
||||||
|
child: Row(
|
||||||
|
children: <Widget>[
|
||||||
|
Icon(Icons.history),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 5.0),
|
||||||
|
),
|
||||||
|
Text('Play History'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
onSelected: (value) {
|
||||||
|
if (value == 0) {
|
||||||
|
Navigator.push(
|
||||||
|
context, MaterialPageRoute(builder: (context) => PlayedHistory()));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@ -40,30 +74,36 @@ class _MainTabState extends State<MainTab> with TickerProviderStateMixin {
|
|||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Container(
|
Row(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 10.0),
|
children: <Widget>[
|
||||||
height: 50,
|
Container(
|
||||||
alignment: Alignment.centerLeft,
|
padding: EdgeInsets.symmetric(horizontal: 10.0),
|
||||||
child: TabBar(
|
height: 50,
|
||||||
isScrollable: true,
|
alignment: Alignment.centerLeft,
|
||||||
labelPadding: EdgeInsets.all(10.0),
|
child: TabBar(
|
||||||
controller: _controller,
|
isScrollable: true,
|
||||||
indicator: getIndicator(context),
|
labelPadding: EdgeInsets.all(10.0),
|
||||||
tabs: <Widget>[
|
controller: _controller,
|
||||||
Text(
|
indicator: getIndicator(context),
|
||||||
'Recent Update',
|
tabs: <Widget>[
|
||||||
style: TextStyle(fontWeight: FontWeight.bold),
|
Text(
|
||||||
|
'Recent Update',
|
||||||
|
style: TextStyle(fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'Favorites',
|
||||||
|
style: TextStyle(fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'Downloads',
|
||||||
|
style: TextStyle(fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
Text(
|
),
|
||||||
'Favorites',
|
Spacer(),
|
||||||
style: TextStyle(fontWeight: FontWeight.bold),
|
playHistory(),
|
||||||
),
|
],
|
||||||
Text(
|
|
||||||
'Downloads',
|
|
||||||
style: TextStyle(fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Container(
|
child: Container(
|
||||||
|
73
lib/home/paly_history.dart
Normal file
73
lib/home/paly_history.dart
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:tsacdop/local_storage/sqflite_localpodcast.dart';
|
||||||
|
import 'package:tsacdop/class/audiostate.dart';
|
||||||
|
|
||||||
|
class PlayedHistory extends StatefulWidget{
|
||||||
|
@override
|
||||||
|
_PlayedHistoryState createState() => _PlayedHistoryState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PlayedHistoryState extends State<PlayedHistory> {
|
||||||
|
|
||||||
|
Future<List<PlayHistory>> gerPlayHistory() async{
|
||||||
|
DBHelper dbHelper = DBHelper();
|
||||||
|
List<PlayHistory> playHistory;
|
||||||
|
playHistory = await dbHelper.getPlayHistory();
|
||||||
|
await Future.forEach(playHistory, (playHistory) async{
|
||||||
|
await playHistory.getEpisode();
|
||||||
|
});
|
||||||
|
return playHistory;
|
||||||
|
}
|
||||||
|
static String _stringForSeconds(double seconds) {
|
||||||
|
if (seconds == null) return null;
|
||||||
|
return '${(seconds ~/ 60)}:${(seconds.truncate() % 60).toString().padLeft(2, '0')}';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||||
|
value: SystemUiOverlayStyle(
|
||||||
|
statusBarIconBrightness: Theme.of(context).accentColorBrightness,
|
||||||
|
systemNavigationBarColor: Theme.of(context).primaryColor,
|
||||||
|
statusBarColor: Theme.of(context).primaryColor,
|
||||||
|
),
|
||||||
|
child: SafeArea(
|
||||||
|
child: Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text('History'),
|
||||||
|
centerTitle: true,
|
||||||
|
elevation: 0,
|
||||||
|
backgroundColor: Theme.of(context).primaryColor,
|
||||||
|
),
|
||||||
|
body: FutureBuilder<List<PlayHistory>>(
|
||||||
|
future: gerPlayHistory(),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
return
|
||||||
|
snapshot.hasData ?
|
||||||
|
ListView.builder(
|
||||||
|
shrinkWrap: true,
|
||||||
|
scrollDirection: Axis.vertical,
|
||||||
|
itemCount: snapshot.data.length,
|
||||||
|
itemBuilder: (BuildContext context, int index){
|
||||||
|
return Column(
|
||||||
|
children: <Widget>[
|
||||||
|
ListTile(
|
||||||
|
title: Text(snapshot.data[index].title),
|
||||||
|
subtitle: Text(_stringForSeconds(snapshot.data[index].seconds)),
|
||||||
|
),
|
||||||
|
Divider(height: 2),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
: Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -32,12 +32,13 @@ class KeyValueStorage {
|
|||||||
{'groups': groupList.map((group) => group.toJson()).toList()}));
|
{'groups': groupList.map((group) => group.toJson()).toList()}));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> saveTheme(int setting) async{
|
Future<bool> saveInt(int setting) async{
|
||||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
|
print(setting.toString());
|
||||||
return prefs.setInt(key, setting);
|
return prefs.setInt(key, setting);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<int> getTheme() async{
|
Future<int> getInt() async{
|
||||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
if(prefs.getInt(key) == null) await prefs.setInt(key, 0);
|
if(prefs.getInt(key) == null) await prefs.setInt(key, 0);
|
||||||
return prefs.getInt(key);
|
return prefs.getInt(key);
|
||||||
@ -51,7 +52,18 @@ class KeyValueStorage {
|
|||||||
Future<List<String>> getStringList() async{
|
Future<List<String>> getStringList() async{
|
||||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
if(prefs.getStringList(key) == null) {await prefs.setStringList(key, []);}
|
if(prefs.getStringList(key) == null) {await prefs.setStringList(key, []);}
|
||||||
print(prefs.getStringList(key).toString());
|
|
||||||
return prefs.getStringList(key);
|
return prefs.getStringList(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<bool> saveString(String string) async{
|
||||||
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
|
return prefs.setString(key, string);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String> getString() async{
|
||||||
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
|
if(prefs.getString(key) == null) {await prefs.setString(key, '');}
|
||||||
|
return prefs.getString(key);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ class DBHelper {
|
|||||||
await db
|
await db
|
||||||
.execute("""CREATE TABLE PodcastLocal(id TEXT PRIMARY KEY,title TEXT,
|
.execute("""CREATE TABLE PodcastLocal(id TEXT PRIMARY KEY,title TEXT,
|
||||||
imageUrl TEXT,rssUrl TEXT UNIQUE,primaryColor TEXT,author TEXT,
|
imageUrl TEXT,rssUrl TEXT UNIQUE,primaryColor TEXT,author TEXT,
|
||||||
description TEXT, add_date INTEGER, imagePath TEXT, email TEXT, provider TEXT, link TEXT,
|
description TEXT, add_date INTEGER, imagePath TEXT, provider TEXT, link TEXT,
|
||||||
background_image TEXT DEFAULT '',hosts TEXT DEFAULT '')""");
|
background_image TEXT DEFAULT '',hosts TEXT DEFAULT '')""");
|
||||||
await db
|
await db
|
||||||
.execute("""CREATE TABLE Episodes(id INTEGER PRIMARY KEY,title TEXT,
|
.execute("""CREATE TABLE Episodes(id INTEGER PRIMARY KEY,title TEXT,
|
||||||
@ -40,7 +40,7 @@ class DBHelper {
|
|||||||
downloaded TEXT DEFAULT 'ND', download_date INTEGER DEFAULT 0)""");
|
downloaded TEXT DEFAULT 'ND', download_date INTEGER DEFAULT 0)""");
|
||||||
await db.execute(
|
await db.execute(
|
||||||
"""CREATE TABLE PlayHistory(id INTEGER PRIMARY KEY, title TEXT, enclosure_url TEXT UNIQUE,
|
"""CREATE TABLE PlayHistory(id INTEGER PRIMARY KEY, title TEXT, enclosure_url TEXT UNIQUE,
|
||||||
seconds INTEGER, seek_value INTEGER, add_date INTEGER)""");
|
seconds REAL, seek_value REAL, add_date INTEGER)""");
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<PodcastLocal>> getPodcastLocal(List<String> podcasts) async {
|
Future<List<PodcastLocal>> getPodcastLocal(List<String> podcasts) async {
|
||||||
@ -49,7 +49,7 @@ class DBHelper {
|
|||||||
await Future.forEach(podcasts, (s) async {
|
await Future.forEach(podcasts, (s) async {
|
||||||
List<Map> list;
|
List<Map> list;
|
||||||
list = await dbClient.rawQuery(
|
list = await dbClient.rawQuery(
|
||||||
'SELECT id, title, imageUrl, rssUrl, primaryColor, author, imagePath , email, provider, link FROM PodcastLocal WHERE id = ?',
|
'SELECT id, title, imageUrl, rssUrl, primaryColor, author, imagePath , provider, link FROM PodcastLocal WHERE id = ?',
|
||||||
[s]);
|
[s]);
|
||||||
podcastLocal.add(PodcastLocal(
|
podcastLocal.add(PodcastLocal(
|
||||||
list.first['title'],
|
list.first['title'],
|
||||||
@ -59,7 +59,6 @@ class DBHelper {
|
|||||||
list.first['author'],
|
list.first['author'],
|
||||||
list.first['id'],
|
list.first['id'],
|
||||||
list.first['imagePath'],
|
list.first['imagePath'],
|
||||||
list.first['email'],
|
|
||||||
list.first['provider'],
|
list.first['provider'],
|
||||||
list.first['link']));
|
list.first['link']));
|
||||||
});
|
});
|
||||||
@ -69,7 +68,7 @@ class DBHelper {
|
|||||||
Future<List<PodcastLocal>> getPodcastLocalAll() async {
|
Future<List<PodcastLocal>> getPodcastLocalAll() async {
|
||||||
var dbClient = await database;
|
var dbClient = await database;
|
||||||
List<Map> list = await dbClient.rawQuery(
|
List<Map> list = await dbClient.rawQuery(
|
||||||
'SELECT id, title, imageUrl, rssUrl, primaryColor, author, imagePath, email, provider, link FROM PodcastLocal ORDER BY add_date DESC');
|
'SELECT id, title, imageUrl, rssUrl, primaryColor, author, imagePath, provider, link FROM PodcastLocal ORDER BY add_date DESC');
|
||||||
|
|
||||||
List<PodcastLocal> podcastLocal = List();
|
List<PodcastLocal> podcastLocal = List();
|
||||||
|
|
||||||
@ -82,7 +81,6 @@ class DBHelper {
|
|||||||
list[i]['author'],
|
list[i]['author'],
|
||||||
list[i]['id'],
|
list[i]['id'],
|
||||||
list[i]['imagePath'],
|
list[i]['imagePath'],
|
||||||
list.first['email'],
|
|
||||||
list.first['provider'],
|
list.first['provider'],
|
||||||
list.first['link']));
|
list.first['link']));
|
||||||
}
|
}
|
||||||
@ -103,7 +101,7 @@ class DBHelper {
|
|||||||
await dbClient.transaction((txn) async {
|
await dbClient.transaction((txn) async {
|
||||||
return await txn.rawInsert(
|
return await txn.rawInsert(
|
||||||
"""INSERT OR IGNORE INTO PodcastLocal (id, title, imageUrl, rssUrl,
|
"""INSERT OR IGNORE INTO PodcastLocal (id, title, imageUrl, rssUrl,
|
||||||
primaryColor, author, description, add_date, imagePath, email, provider, link) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
|
primaryColor, author, description, add_date, imagePath, provider, link) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
|
||||||
[
|
[
|
||||||
podcastLocal.id,
|
podcastLocal.id,
|
||||||
podcastLocal.title,
|
podcastLocal.title,
|
||||||
@ -114,28 +112,26 @@ class DBHelper {
|
|||||||
podcastLocal.description,
|
podcastLocal.description,
|
||||||
_milliseconds,
|
_milliseconds,
|
||||||
podcastLocal.imagePath,
|
podcastLocal.imagePath,
|
||||||
podcastLocal.email,
|
|
||||||
podcastLocal.provider,
|
podcastLocal.provider,
|
||||||
podcastLocal.link
|
podcastLocal.link
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<int> saveFiresideData(List<String> list)async{
|
Future<int> saveFiresideData(List<String> list) async {
|
||||||
var dbClient = await database;
|
var dbClient = await database;
|
||||||
int result = await dbClient.rawUpdate(
|
int result = await dbClient.rawUpdate(
|
||||||
'UPDATE PodcastLocal SET background_image = ? , hosts = ? WHERE id = ?',[list[1],list[2],list[0]]
|
'UPDATE PodcastLocal SET background_image = ? , hosts = ? WHERE id = ?',
|
||||||
);
|
[list[1], list[2], list[0]]);
|
||||||
print('Fireside data save in sqllite');
|
print('Fireside data save in sqllite');
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<String>> getFiresideData(String id)async{
|
Future<List<String>> getFiresideData(String id) async {
|
||||||
var dbClient = await database;
|
var dbClient = await database;
|
||||||
List<Map> list = await dbClient.rawQuery(
|
List<Map> list = await dbClient.rawQuery(
|
||||||
'SELECT background_image, hosts FROM PodcastLocal WHERE id = ?', [id]
|
'SELECT background_image, hosts FROM PodcastLocal WHERE id = ?', [id]);
|
||||||
);
|
List<String> data = [list.first['background_image'], list.first['hosts']];
|
||||||
List<String> data = [list.first['background_image]'],list.first['hosts']];
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,6 +168,32 @@ class DBHelper {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<List<PlayHistory>> getPlayHistory() async {
|
||||||
|
var dbClient = await database;
|
||||||
|
List<Map> list = await dbClient.rawQuery(
|
||||||
|
"""SELECT title, enclosure_url, seconds, seek_value, add_date FROM PlayHistory
|
||||||
|
ORDER BY add_date DESC
|
||||||
|
""");
|
||||||
|
List<PlayHistory> playHistory = [];
|
||||||
|
list.forEach((record) {
|
||||||
|
playHistory.add(PlayHistory(
|
||||||
|
record['title'],
|
||||||
|
record['enclosure_url'],
|
||||||
|
record['seconds'],
|
||||||
|
record['seek_value'],
|
||||||
|
));
|
||||||
|
});
|
||||||
|
return playHistory;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<int> getPosition(EpisodeBrief episodeBrief) async {
|
||||||
|
var dbClient = await database;
|
||||||
|
List<Map> list = await dbClient.rawQuery(
|
||||||
|
"SELECT seconds FROM PlayHistory Where enclosure_url = ?",
|
||||||
|
[episodeBrief.enclosureUrl]);
|
||||||
|
return list.length > 0 ? list.first['seconds'] : 0;
|
||||||
|
}
|
||||||
|
|
||||||
DateTime _parsePubDate(String pubDate) {
|
DateTime _parsePubDate(String pubDate) {
|
||||||
if (pubDate == null) return DateTime.now();
|
if (pubDate == null) return DateTime.now();
|
||||||
print(pubDate);
|
print(pubDate);
|
||||||
|
111
lib/main.dart
111
lib/main.dart
@ -2,61 +2,100 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_downloader/flutter_downloader.dart';
|
import 'package:flutter_downloader/flutter_downloader.dart';
|
||||||
|
import 'package:workmanager/workmanager.dart';
|
||||||
|
|
||||||
|
import 'package:tsacdop/class/podcastlocal.dart';
|
||||||
import 'package:tsacdop/class/podcast_group.dart';
|
import 'package:tsacdop/class/podcast_group.dart';
|
||||||
import 'package:tsacdop/home/appbar/addpodcast.dart';
|
import 'package:tsacdop/home/appbar/addpodcast.dart';
|
||||||
import 'package:tsacdop/class/audiostate.dart';
|
import 'package:tsacdop/class/audiostate.dart';
|
||||||
import 'package:tsacdop/class/importompl.dart';
|
import 'package:tsacdop/class/importompl.dart';
|
||||||
import 'package:tsacdop/class/settingstate.dart';
|
import 'package:tsacdop/class/settingstate.dart';
|
||||||
|
import 'local_storage/sqflite_localpodcast.dart';
|
||||||
|
|
||||||
void main() async {
|
final SettingState themeSetting = SettingState();
|
||||||
|
|
||||||
|
Future main() async {
|
||||||
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
await themeSetting.initData();
|
||||||
runApp(
|
runApp(
|
||||||
MultiProvider(
|
MultiProvider(
|
||||||
providers: [
|
providers: [
|
||||||
ChangeNotifierProvider(create: (context) => AudioPlayer()),
|
ChangeNotifierProvider(create: (_) => themeSetting),
|
||||||
ChangeNotifierProvider(create: (context) => ImportOmpl()),
|
ChangeNotifierProvider(create: (_) => AudioPlayer()),
|
||||||
ChangeNotifierProvider(create: (context) => SettingState()),
|
ChangeNotifierProvider(create: (_) => GroupList()),
|
||||||
ChangeNotifierProvider(create: (context) => GroupList()),
|
ChangeNotifierProvider(create: (_) => ImportOmpl()),
|
||||||
],
|
],
|
||||||
child: MyApp(),
|
child: MyApp(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
Workmanager.initialize(
|
||||||
|
callbackDispatcher,
|
||||||
|
isInDebugMode: true,
|
||||||
|
);
|
||||||
|
Workmanager.registerPeriodicTask("update", "simplePeriodicTask",
|
||||||
|
frequency: Duration(hours: 1),
|
||||||
|
initialDelay: Duration(seconds: 10),
|
||||||
|
constraints: Constraints(
|
||||||
|
networkType: NetworkType.connected,
|
||||||
|
requiresBatteryNotLow: true,
|
||||||
|
requiresCharging: false,
|
||||||
|
requiresDeviceIdle: true,
|
||||||
|
requiresStorageNotLow: true));
|
||||||
|
|
||||||
await FlutterDownloader.initialize();
|
await FlutterDownloader.initialize();
|
||||||
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
|
await SystemChrome.setPreferredOrientations(
|
||||||
|
[DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void callbackDispatcher() {
|
||||||
|
Workmanager.executeTask((task, inputData) async {
|
||||||
|
var dbHelper = DBHelper();
|
||||||
|
List<PodcastLocal> podcastList = await dbHelper.getPodcastLocalAll();
|
||||||
|
await Future.forEach(podcastList, (podcastLocal) async {
|
||||||
|
await dbHelper.updatePodcastRss(podcastLocal);
|
||||||
|
print('Refresh ' + podcastLocal.title);
|
||||||
|
});
|
||||||
|
return Future.value(true);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyApp extends StatelessWidget {
|
class MyApp extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var theme = Provider.of<SettingState>(context).theme;
|
return Consumer<SettingState>(
|
||||||
print(theme);
|
builder: (_, setting, __) {
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
themeMode: ThemeMode.system,
|
themeMode: setting.theme,
|
||||||
debugShowCheckedModeBanner: false,
|
debugShowCheckedModeBanner: false,
|
||||||
title: 'TsacDop',
|
title: 'Tsacdop',
|
||||||
theme: ThemeData(
|
theme: ThemeData(
|
||||||
accentColorBrightness: Brightness.dark,
|
accentColorBrightness: Brightness.dark,
|
||||||
primaryColor: Colors.grey[100],
|
primaryColor: Colors.grey[100],
|
||||||
accentColor: Colors.blue[400],
|
accentColor: setting.accentSetColor,
|
||||||
primaryColorLight: Colors.white,
|
primaryColorLight: Colors.white,
|
||||||
primaryColorDark: Colors.grey[300],
|
primaryColorDark: Colors.grey[300],
|
||||||
dialogBackgroundColor: Colors.white,
|
dialogBackgroundColor: Colors.white,
|
||||||
backgroundColor: Colors.grey[100],
|
backgroundColor: Colors.grey[100],
|
||||||
appBarTheme: AppBarTheme(
|
appBarTheme: AppBarTheme(
|
||||||
color: Colors.grey[100],
|
color: Colors.grey[100],
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
),
|
),
|
||||||
textTheme: TextTheme(
|
textTheme: TextTheme(
|
||||||
headline1: TextStyle(fontSize: 72.0, fontWeight: FontWeight.bold),
|
headline1: TextStyle(fontSize: 72.0, fontWeight: FontWeight.bold),
|
||||||
bodyText2: TextStyle(fontSize: 15.0, fontWeight: FontWeight.normal),
|
bodyText2:
|
||||||
),
|
TextStyle(fontSize: 15.0, fontWeight: FontWeight.normal),
|
||||||
tabBarTheme: TabBarTheme(
|
),
|
||||||
labelColor: Colors.black,
|
tabBarTheme: TabBarTheme(
|
||||||
unselectedLabelColor: Colors.grey[400],
|
labelColor: Colors.black,
|
||||||
),
|
unselectedLabelColor: Colors.grey[400],
|
||||||
),
|
),
|
||||||
darkTheme: ThemeData.dark().copyWith(accentColor: Colors.blue[400],),
|
),
|
||||||
home: MyHomePage(),
|
darkTheme: ThemeData.dark().copyWith(
|
||||||
|
accentColor: setting.accentSetColor,
|
||||||
|
),
|
||||||
|
home: MyHomePage(),
|
||||||
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import 'dart:convert';
|
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
@ -6,9 +5,11 @@ import 'package:flutter/foundation.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:html/parser.dart';
|
import 'package:html/parser.dart';
|
||||||
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
import 'package:fluttertoast/fluttertoast.dart';
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
|
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:tsacdop/class/podcastlocal.dart';
|
import 'package:tsacdop/class/podcastlocal.dart';
|
||||||
import 'package:tsacdop/class/episodebrief.dart';
|
import 'package:tsacdop/class/episodebrief.dart';
|
||||||
@ -18,6 +19,7 @@ import 'package:tsacdop/util/episodegrid.dart';
|
|||||||
import 'package:tsacdop/util/pageroute.dart';
|
import 'package:tsacdop/util/pageroute.dart';
|
||||||
import 'package:tsacdop/home/audioplayer.dart';
|
import 'package:tsacdop/home/audioplayer.dart';
|
||||||
import 'package:tsacdop/class/fireside_data.dart';
|
import 'package:tsacdop/class/fireside_data.dart';
|
||||||
|
import 'package:tsacdop/util/colorize.dart';
|
||||||
|
|
||||||
class PodcastDetail extends StatefulWidget {
|
class PodcastDetail extends StatefulWidget {
|
||||||
PodcastDetail({Key key, this.podcastLocal}) : super(key: key);
|
PodcastDetail({Key key, this.podcastLocal}) : super(key: key);
|
||||||
@ -29,7 +31,7 @@ class PodcastDetail extends StatefulWidget {
|
|||||||
class _PodcastDetailState extends State<PodcastDetail> {
|
class _PodcastDetailState extends State<PodcastDetail> {
|
||||||
final GlobalKey<RefreshIndicatorState> _refreshIndicatorKey =
|
final GlobalKey<RefreshIndicatorState> _refreshIndicatorKey =
|
||||||
GlobalKey<RefreshIndicatorState>();
|
GlobalKey<RefreshIndicatorState>();
|
||||||
String background;
|
String backgroundImage;
|
||||||
List<PodcastHost> hosts;
|
List<PodcastHost> hosts;
|
||||||
Future _updateRssItem(PodcastLocal podcastLocal) async {
|
Future _updateRssItem(PodcastLocal podcastLocal) async {
|
||||||
var dbHelper = DBHelper();
|
var dbHelper = DBHelper();
|
||||||
@ -52,12 +54,23 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
|||||||
if (podcastLocal.provider.contains('fireside')) {
|
if (podcastLocal.provider.contains('fireside')) {
|
||||||
FiresideData data = FiresideData(podcastLocal.id, podcastLocal.link);
|
FiresideData data = FiresideData(podcastLocal.id, podcastLocal.link);
|
||||||
await data.getData();
|
await data.getData();
|
||||||
background = data.background;
|
backgroundImage = data.background;
|
||||||
hosts = data.hosts;
|
hosts = data.hosts;
|
||||||
}
|
}
|
||||||
return episodes;
|
return episodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_launchUrl(String url) async {
|
||||||
|
if (await canLaunch(url)) {
|
||||||
|
await launch(url);
|
||||||
|
} else {
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg: '$url Invalid Link',
|
||||||
|
gravity: ToastGravity.TOP,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Widget podcastInfo(BuildContext context) {
|
Widget podcastInfo(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
height: 170,
|
height: 170,
|
||||||
@ -75,24 +88,87 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget hostsList(BuildContext context, List<PodcastHost> hosts) {
|
||||||
|
return Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: <Widget>[
|
||||||
|
hosts != null
|
||||||
|
? Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
image: DecorationImage(
|
||||||
|
// colorFilter: ColorFilter.mode(_color, BlendMode.color),
|
||||||
|
image: CachedNetworkImageProvider(
|
||||||
|
backgroundImage,
|
||||||
|
),
|
||||||
|
fit: BoxFit.cover)),
|
||||||
|
alignment: Alignment.centerRight,
|
||||||
|
child: Container(
|
||||||
|
color: Theme.of(context).scaffoldBackgroundColor.withOpacity(0.5),
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 5.0),
|
||||||
|
width: MediaQuery.of(context).size.width,
|
||||||
|
alignment: Alignment.centerRight,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: hosts
|
||||||
|
.map((host) => Container(
|
||||||
|
padding: EdgeInsets.all(5.0),
|
||||||
|
width: 80.0,
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: <Widget>[
|
||||||
|
CircleAvatar(
|
||||||
|
backgroundColor: Colors.grey[400],
|
||||||
|
backgroundImage: CachedNetworkImageProvider(
|
||||||
|
host.image,
|
||||||
|
)),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.all(2),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
host.name,
|
||||||
|
style: TextStyle(
|
||||||
|
backgroundColor:
|
||||||
|
Colors.black.withOpacity(0.5),
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.fade,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)))
|
||||||
|
.toList()
|
||||||
|
.cast<Widget>(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
))
|
||||||
|
: Center(),
|
||||||
|
Padding(padding: EdgeInsets.all(10.0)),
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.only(left: 15.0, right: 15.0, bottom: 10.0),
|
||||||
|
alignment: Alignment.topLeft,
|
||||||
|
color: Theme.of(context).scaffoldBackgroundColor,
|
||||||
|
child: AboutPodcast(podcastLocal: widget.podcastLocal),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
double top = 0;
|
double top = 0;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
double _width = MediaQuery.of(context).size.width;
|
double _width = MediaQuery.of(context).size.width;
|
||||||
Color _color;
|
Color _color = widget.podcastLocal.primaryColor.colorizedark();
|
||||||
var color = json.decode(widget.podcastLocal.primaryColor);
|
|
||||||
(color[0] > 200 && color[1] > 200 && color[2] > 200)
|
|
||||||
? _color = Color.fromRGBO(
|
|
||||||
(255 - color[0]), 255 - color[1], 255 - color[2], 1.0)
|
|
||||||
: _color = Color.fromRGBO(color[0], color[1], color[2], 1.0);
|
|
||||||
|
|
||||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||||
value: SystemUiOverlayStyle(
|
value: SystemUiOverlayStyle(
|
||||||
statusBarIconBrightness: Brightness.light,
|
statusBarIconBrightness: Brightness.light,
|
||||||
systemNavigationBarColor: Theme.of(context).primaryColor,
|
systemNavigationBarColor: Theme.of(context).primaryColor,
|
||||||
statusBarColor: _color,
|
statusBarColor: _color,
|
||||||
// statusBarColor: Theme.of(context).primaryColor,
|
|
||||||
),
|
),
|
||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
@ -112,6 +188,64 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
|||||||
primary: true,
|
primary: true,
|
||||||
slivers: <Widget>[
|
slivers: <Widget>[
|
||||||
SliverAppBar(
|
SliverAppBar(
|
||||||
|
actions: <Widget>[
|
||||||
|
PopupMenuButton<String>(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.all(
|
||||||
|
Radius.circular(10))),
|
||||||
|
elevation: 2,
|
||||||
|
tooltip: 'Menu',
|
||||||
|
itemBuilder: (context) => [
|
||||||
|
widget.podcastLocal.link != null
|
||||||
|
? PopupMenuItem(
|
||||||
|
value: widget.podcastLocal.link,
|
||||||
|
child: Container(
|
||||||
|
padding:
|
||||||
|
EdgeInsets.only(left: 10),
|
||||||
|
child: Row(
|
||||||
|
children: <Widget>[
|
||||||
|
Icon(Icons.link,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.tabBarTheme
|
||||||
|
.labelColor),
|
||||||
|
Padding(
|
||||||
|
padding:
|
||||||
|
EdgeInsets.symmetric(
|
||||||
|
horizontal: 5.0),
|
||||||
|
),
|
||||||
|
Text('Visit Site'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Center(),
|
||||||
|
PopupMenuItem(
|
||||||
|
value: widget.podcastLocal.rssUrl,
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.only(left: 10),
|
||||||
|
child: Row(
|
||||||
|
children: <Widget>[
|
||||||
|
Icon(
|
||||||
|
Icons.rss_feed,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.tabBarTheme
|
||||||
|
.labelColor,
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.symmetric(
|
||||||
|
horizontal: 5.0),
|
||||||
|
),
|
||||||
|
Text('View Rss Feed'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
onSelected: (url) {
|
||||||
|
_launchUrl(url);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
],
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
iconTheme: IconThemeData(color: Colors.white),
|
iconTheme: IconThemeData(color: Colors.white),
|
||||||
expandedHeight: 170,
|
expandedHeight: 170,
|
||||||
@ -141,23 +275,19 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
|||||||
Text(
|
Text(
|
||||||
widget.podcastLocal.author ??
|
widget.podcastLocal.author ??
|
||||||
'',
|
'',
|
||||||
style: Theme.of(context)
|
style: TextStyle(
|
||||||
.textTheme
|
color: Colors.white)),
|
||||||
.bodyText1
|
widget.podcastLocal.provider
|
||||||
.copyWith(
|
.isNotEmpty
|
||||||
color:
|
? Text(
|
||||||
Colors.grey[300])),
|
'Hosted on ' +
|
||||||
Text(
|
|
||||||
'Hosted on ' +
|
|
||||||
widget.podcastLocal
|
widget.podcastLocal
|
||||||
.provider ??
|
.provider,
|
||||||
'',
|
maxLines: 1,
|
||||||
style: Theme.of(context)
|
style: TextStyle(
|
||||||
.textTheme
|
color: Colors.white),
|
||||||
.bodyText1
|
)
|
||||||
.copyWith(
|
: Center(),
|
||||||
color:
|
|
||||||
Colors.grey[300]))
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -186,61 +316,14 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
|||||||
SliverList(
|
SliverList(
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(BuildContext context, int index) {
|
(BuildContext context, int index) {
|
||||||
return Column(
|
return hostsList(context, hosts);
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: <Widget>[
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsets.all(10.0),
|
|
||||||
),
|
|
||||||
Container(
|
|
||||||
alignment: Alignment.centerRight,
|
|
||||||
padding: EdgeInsets.symmetric(
|
|
||||||
horizontal: 10.0),
|
|
||||||
child: hosts != null
|
|
||||||
? Wrap(
|
|
||||||
children: hosts
|
|
||||||
.map((host) => Container(
|
|
||||||
padding:
|
|
||||||
EdgeInsets.all(5.0),
|
|
||||||
width: 60.0,
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment:
|
|
||||||
MainAxisAlignment
|
|
||||||
.center,
|
|
||||||
mainAxisSize:
|
|
||||||
MainAxisSize.min,
|
|
||||||
children: <Widget>[
|
|
||||||
CachedNetworkImage(
|
|
||||||
imageUrl:
|
|
||||||
host.image),
|
|
||||||
Text(host.name),
|
|
||||||
],
|
|
||||||
)))
|
|
||||||
.toList()
|
|
||||||
.cast<Widget>(),
|
|
||||||
)
|
|
||||||
: Center(),
|
|
||||||
),
|
|
||||||
Container(
|
|
||||||
padding: EdgeInsets.only(
|
|
||||||
left: 15.0,
|
|
||||||
right: 15.0,
|
|
||||||
bottom: 10.0),
|
|
||||||
alignment: Alignment.topLeft,
|
|
||||||
color: Theme.of(context)
|
|
||||||
.scaffoldBackgroundColor,
|
|
||||||
child: AboutPodcast(
|
|
||||||
podcastLocal: widget.podcastLocal),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
childCount: 1,
|
childCount: 1,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SliverPadding(
|
SliverPadding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal:15.0),
|
padding:
|
||||||
|
const EdgeInsets.symmetric(horizontal: 15.0),
|
||||||
sliver: SliverGrid(
|
sliver: SliverGrid(
|
||||||
gridDelegate:
|
gridDelegate:
|
||||||
SliverGridDelegateWithFixedCrossAxisCount(
|
SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
@ -253,33 +336,13 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
|||||||
(BuildContext context, int index) {
|
(BuildContext context, int index) {
|
||||||
EpisodeBrief episodeBrief =
|
EpisodeBrief episodeBrief =
|
||||||
snapshot.data[index];
|
snapshot.data[index];
|
||||||
Color _c;
|
Color _c = (Theme.of(context).brightness ==
|
||||||
var color = json.decode(
|
Brightness.light)
|
||||||
widget.podcastLocal.primaryColor);
|
? widget.podcastLocal.primaryColor
|
||||||
if (Theme.of(context).brightness ==
|
.colorizedark()
|
||||||
Brightness.light) {
|
: widget.podcastLocal.primaryColor
|
||||||
(color[0] > 200 &&
|
.colorizeLight();
|
||||||
color[1] > 200 &&
|
|
||||||
color[2] > 200)
|
|
||||||
? _c = Color.fromRGBO(
|
|
||||||
(255 - color[0]),
|
|
||||||
255 - color[1],
|
|
||||||
255 - color[2],
|
|
||||||
1.0)
|
|
||||||
: _c = Color.fromRGBO(color[0],
|
|
||||||
color[1], color[2], 1.0);
|
|
||||||
} else {
|
|
||||||
(color[0] < 50 &&
|
|
||||||
color[1] < 50 &&
|
|
||||||
color[2] < 50)
|
|
||||||
? _c = Color.fromRGBO(
|
|
||||||
(255 - color[0]),
|
|
||||||
255 - color[1],
|
|
||||||
255 - color[2],
|
|
||||||
1.0)
|
|
||||||
: _c = Color.fromRGBO(color[0],
|
|
||||||
color[1], color[2], 1.0);
|
|
||||||
}
|
|
||||||
return Material(
|
return Material(
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
@ -334,18 +397,12 @@ class _PodcastDetailState extends State<PodcastDetail> {
|
|||||||
.enclosureUrl +
|
.enclosureUrl +
|
||||||
'podcast',
|
'podcast',
|
||||||
child: Container(
|
child: Container(
|
||||||
child: ClipRRect(
|
height: _width / 16,
|
||||||
borderRadius:
|
width: _width / 16,
|
||||||
BorderRadius.all(
|
child: CircleAvatar(
|
||||||
Radius.circular(
|
backgroundImage:
|
||||||
_width /
|
FileImage(File(
|
||||||
32)),
|
"${episodeBrief.imagePath}")),
|
||||||
child: Container(
|
|
||||||
height: _width / 16,
|
|
||||||
width: _width / 16,
|
|
||||||
child: Image.file(File(
|
|
||||||
"${episodeBrief.imagePath}")),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -527,7 +584,10 @@ class _AboutPodcastState extends State<AboutPodcast> {
|
|||||||
: Text(_description),
|
: Text(_description),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return SelectableText(_description, toolbarOptions: ToolbarOptions(copy: true),);
|
return SelectableText(
|
||||||
|
_description,
|
||||||
|
toolbarOptions: ToolbarOptions(copy: true),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import 'dart:convert';
|
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
@ -10,6 +9,7 @@ import 'package:tsacdop/class/podcast_group.dart';
|
|||||||
import 'package:tsacdop/class/podcastlocal.dart';
|
import 'package:tsacdop/class/podcastlocal.dart';
|
||||||
import 'package:tsacdop/podcasts/podcastdetail.dart';
|
import 'package:tsacdop/podcasts/podcastdetail.dart';
|
||||||
import 'package:tsacdop/util/pageroute.dart';
|
import 'package:tsacdop/util/pageroute.dart';
|
||||||
|
import 'package:tsacdop/util/colorize.dart';
|
||||||
|
|
||||||
class PodcastGroupList extends StatefulWidget {
|
class PodcastGroupList extends StatefulWidget {
|
||||||
final PodcastGroup group;
|
final PodcastGroup group;
|
||||||
@ -126,7 +126,6 @@ class _PodcastCardState extends State<PodcastCard> {
|
|||||||
bool _addGroup;
|
bool _addGroup;
|
||||||
List<PodcastGroup> _selectedGroups;
|
List<PodcastGroup> _selectedGroups;
|
||||||
List<PodcastGroup> _belongGroups;
|
List<PodcastGroup> _belongGroups;
|
||||||
Color _c;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -149,18 +148,10 @@ class _PodcastCardState extends State<PodcastCard> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var color = json.decode(widget.podcastLocal.primaryColor);
|
Color _c = (Theme.of(context).brightness == Brightness.light)
|
||||||
if (Theme.of(context).brightness == Brightness.light) {
|
? widget.podcastLocal.primaryColor.colorizedark()
|
||||||
(color[0] > 200 && color[1] > 200 && color[2] > 200)
|
: widget.podcastLocal.primaryColor.colorizeLight();
|
||||||
? _c = Color.fromRGBO(
|
|
||||||
(255 - color[0]), 255 - color[1], 255 - color[2], 1.0)
|
|
||||||
: _c = Color.fromRGBO(color[0], color[1], color[2], 1.0);
|
|
||||||
} else {
|
|
||||||
(color[0] < 50 && color[1] < 50 && color[2] < 50)
|
|
||||||
? _c = Color.fromRGBO(
|
|
||||||
(255 - color[0]), 255 - color[1], 255 - color[2], 1.0)
|
|
||||||
: _c = Color.fromRGBO(color[0], color[1], color[2], 1.0);
|
|
||||||
}
|
|
||||||
double _width = MediaQuery.of(context).size.width;
|
double _width = MediaQuery.of(context).size.width;
|
||||||
var _groupList = Provider.of<GroupList>(context);
|
var _groupList = Provider.of<GroupList>(context);
|
||||||
_belongGroups = _groupList.getPodcastGroup(widget.podcastLocal.id);
|
_belongGroups = _groupList.getPodcastGroup(widget.podcastLocal.id);
|
||||||
@ -340,25 +331,51 @@ class _PodcastCardState extends State<PodcastCard> {
|
|||||||
}),
|
}),
|
||||||
_buttonOnMenu(Icon(Icons.notifications), () {}),
|
_buttonOnMenu(Icon(Icons.notifications), () {}),
|
||||||
_buttonOnMenu(Icon(Icons.remove_circle), () {
|
_buttonOnMenu(Icon(Icons.remove_circle), () {
|
||||||
showDialog(
|
showGeneralDialog(
|
||||||
context: context,
|
context: context,
|
||||||
child:
|
barrierDismissible: true,
|
||||||
AnnotatedRegion<SystemUiOverlayStyle>(
|
barrierLabel:
|
||||||
value: SystemUiOverlayStyle(
|
MaterialLocalizations.of(context)
|
||||||
systemNavigationBarColor:
|
.modalBarrierDismissLabel,
|
||||||
Colors.black.withOpacity(0.5),
|
barrierColor: Colors.black54,
|
||||||
statusBarColor: Colors.red,
|
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(
|
child: AlertDialog(
|
||||||
elevation: 2.0,
|
elevation: 1,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.all(
|
||||||
|
Radius.circular(10.0))),
|
||||||
|
titlePadding: EdgeInsets.only(
|
||||||
|
top: 20,
|
||||||
|
left: 20,
|
||||||
|
right: 200,
|
||||||
|
bottom: 20),
|
||||||
title: Text('Remove confirm'),
|
title: Text('Remove confirm'),
|
||||||
content: Text(
|
content: Text(
|
||||||
'${widget.podcastLocal.title} will be removed from device.'),
|
'Are you sure you want to unsubscribe?'),
|
||||||
actions: <Widget>[
|
actions: <Widget>[
|
||||||
FlatButton(
|
FlatButton(
|
||||||
onPressed: () =>
|
onPressed: () =>
|
||||||
Navigator.of(context).pop(),
|
Navigator.of(context).pop(),
|
||||||
child: Text('CANCEL'),
|
child: Text('CANCEL', style: TextStyle(color: Colors.grey[600]),),
|
||||||
),
|
),
|
||||||
FlatButton(
|
FlatButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
@ -374,7 +391,9 @@ class _PodcastCardState extends State<PodcastCard> {
|
|||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
));
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -99,7 +99,7 @@ class _PodcastListState extends State<PodcastList> {
|
|||||||
statusBarColor: Theme.of(context).primaryColor,
|
statusBarColor: Theme.of(context).primaryColor,
|
||||||
),
|
),
|
||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text('Podcasts'),
|
title: Text('Podcasts'),
|
||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
@ -116,8 +116,7 @@ class _PodcastListState extends State<PodcastList> {
|
|||||||
SliverPadding(
|
SliverPadding(
|
||||||
padding: const EdgeInsets.all(10.0),
|
padding: const EdgeInsets.all(10.0),
|
||||||
sliver: SliverGrid(
|
sliver: SliverGrid(
|
||||||
gridDelegate:
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
SliverGridDelegateWithFixedCrossAxisCount(
|
|
||||||
childAspectRatio: 0.8,
|
childAspectRatio: 0.8,
|
||||||
crossAxisCount: 3,
|
crossAxisCount: 3,
|
||||||
),
|
),
|
||||||
@ -164,7 +163,9 @@ class _PodcastListState extends State<PodcastList> {
|
|||||||
child: Text(
|
child: Text(
|
||||||
snapshot.data[index].title,
|
snapshot.data[index].title,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: Theme.of(context).textTheme.bodyText1,
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodyText1,
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -34,76 +34,85 @@ class _PodcastManageState extends State<PodcastManage> {
|
|||||||
statusBarColor: Theme.of(context).primaryColor,
|
statusBarColor: Theme.of(context).primaryColor,
|
||||||
),
|
),
|
||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
child: Scaffold(
|
child: SafeArea(
|
||||||
appBar: AppBar(
|
child: Scaffold(
|
||||||
centerTitle: true,
|
appBar: AppBar(
|
||||||
title: Text('Groups'),
|
centerTitle: true,
|
||||||
actions: <Widget>[
|
title: Text('Groups'),
|
||||||
IconButton(
|
actions: <Widget>[
|
||||||
onPressed: () => showDialog(
|
IconButton(
|
||||||
context: context,
|
onPressed: () => showGeneralDialog(
|
||||||
builder: (BuildContext context) => AddGroup()),
|
context: context,
|
||||||
icon: Icon(Icons.add)),
|
barrierDismissible: true,
|
||||||
OrderMenu(),
|
barrierLabel: MaterialLocalizations.of(context)
|
||||||
],
|
.modalBarrierDismissLabel,
|
||||||
),
|
barrierColor: Colors.black54,
|
||||||
body: Consumer<GroupList>(builder: (_, groupList, __) {
|
transitionDuration: const Duration(milliseconds: 200),
|
||||||
bool _isLoading = groupList.isLoading;
|
pageBuilder: (BuildContext context, Animation animaiton,
|
||||||
List<PodcastGroup> _groups = groupList.groups;
|
Animation secondaryAnimation) =>
|
||||||
return _isLoading
|
AddGroup()),
|
||||||
? Center()
|
icon: Icon(Icons.add)),
|
||||||
: DefaultTabController(
|
OrderMenu(),
|
||||||
length: _groups.length,
|
],
|
||||||
child: Column(
|
),
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
body: Consumer<GroupList>(builder: (_, groupList, __) {
|
||||||
mainAxisSize: MainAxisSize.min,
|
bool _isLoading = groupList.isLoading;
|
||||||
children: <Widget>[
|
List<PodcastGroup> _groups = groupList.groups;
|
||||||
Container(
|
return _isLoading
|
||||||
height: 50,
|
? Center()
|
||||||
padding: EdgeInsets.symmetric(horizontal: 10.0),
|
: DefaultTabController(
|
||||||
alignment: Alignment.centerLeft,
|
length: _groups.length,
|
||||||
child: TabBar(
|
child: Column(
|
||||||
// labelColor: Colors.black,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
// unselectedLabelColor: Colors.grey[500],
|
mainAxisSize: MainAxisSize.min,
|
||||||
labelPadding: EdgeInsets.all(5.0),
|
children: <Widget>[
|
||||||
indicator: getIndicator(),
|
Container(
|
||||||
isScrollable: true,
|
height: 50,
|
||||||
tabs: _groups.map<Tab>((group) {
|
padding: EdgeInsets.symmetric(horizontal: 10.0),
|
||||||
return Tab(
|
alignment: Alignment.centerLeft,
|
||||||
child: Container(
|
child: TabBar(
|
||||||
height: 30.0,
|
// labelColor: Colors.black,
|
||||||
padding:
|
// unselectedLabelColor: Colors.grey[500],
|
||||||
EdgeInsets.symmetric(horizontal: 10.0),
|
labelPadding: EdgeInsets.all(5.0),
|
||||||
alignment: Alignment.center,
|
indicator: getIndicator(),
|
||||||
decoration: BoxDecoration(
|
isScrollable: true,
|
||||||
color: Theme.of(context).brightness ==
|
tabs: _groups.map<Tab>((group) {
|
||||||
Brightness.light
|
return Tab(
|
||||||
? Theme.of(context).primaryColorDark
|
child: Container(
|
||||||
: Colors.grey[800],
|
height: 30.0,
|
||||||
borderRadius:
|
padding: EdgeInsets.symmetric(
|
||||||
BorderRadius.all(Radius.circular(15)),
|
horizontal: 10.0),
|
||||||
),
|
alignment: Alignment.center,
|
||||||
child: Text(
|
decoration: BoxDecoration(
|
||||||
group.name,
|
color: Theme.of(context).brightness ==
|
||||||
)),
|
Brightness.light
|
||||||
);
|
? Theme.of(context).primaryColorDark
|
||||||
}).toList(),
|
: Colors.grey[800],
|
||||||
),
|
borderRadius: BorderRadius.all(
|
||||||
),
|
Radius.circular(15)),
|
||||||
Expanded(
|
),
|
||||||
child: Container(
|
child: Text(
|
||||||
child: TabBarView(
|
group.name,
|
||||||
children: _groups.map<Widget>((group) {
|
)),
|
||||||
return Container(
|
);
|
||||||
key: ObjectKey(group),
|
|
||||||
child: PodcastGroupList(group: group));
|
|
||||||
}).toList(),
|
}).toList(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
Expanded(
|
||||||
],
|
child: Container(
|
||||||
));
|
child: TabBarView(
|
||||||
}),
|
children: _groups.map<Widget>((group) {
|
||||||
|
return Container(
|
||||||
|
key: ObjectKey(group),
|
||||||
|
child: PodcastGroupList(group: group));
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -114,26 +123,28 @@ class OrderMenu extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return PopupMenuButton(
|
return PopupMenuButton(
|
||||||
elevation: 3,
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(10))),
|
||||||
|
elevation: 2,
|
||||||
tooltip: 'Menu',
|
tooltip: 'Menu',
|
||||||
itemBuilder: (context) => [
|
itemBuilder: (context) => [
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
value: 1,
|
value: 1,
|
||||||
child: Text('All Podcasts'),
|
child: Row(
|
||||||
|
children: <Widget>[
|
||||||
|
Icon(Icons.all_out),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 5.0),
|
||||||
|
),
|
||||||
|
Text('All Podcasts'),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
PopupMenuItem(
|
|
||||||
value: 2,
|
|
||||||
child: Text('New group'),
|
|
||||||
)
|
|
||||||
],
|
],
|
||||||
onSelected: (value) {
|
onSelected: (value) {
|
||||||
if (value == 1) {
|
if (value == 1) {
|
||||||
Navigator.push(context, ScaleRoute(page: PodcastList()));
|
Navigator.push(context, ScaleRoute(page: PodcastList()));
|
||||||
}
|
}
|
||||||
if (value == 2) {
|
|
||||||
showDialog(
|
|
||||||
context: context, builder: (BuildContext context) => AddGroup());
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -167,72 +178,82 @@ class _AddGroupState extends State<AddGroup> {
|
|||||||
var groupList = Provider.of<GroupList>(context);
|
var groupList = Provider.of<GroupList>(context);
|
||||||
List list = groupList.groups.map((e) => e.name).toList();
|
List list = groupList.groups.map((e) => e.name).toList();
|
||||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||||
value: SystemUiOverlayStyle(
|
value: SystemUiOverlayStyle(
|
||||||
systemNavigationBarColor: Colors.black.withOpacity(0.5),
|
statusBarIconBrightness: Brightness.light,
|
||||||
statusBarColor: Colors.red,
|
systemNavigationBarColor:
|
||||||
),
|
Theme.of(context).brightness == Brightness.light
|
||||||
child: AlertDialog(
|
? Color.fromRGBO(113, 113, 113, 1)
|
||||||
elevation: 1,
|
: Color.fromRGBO(5, 5, 5, 1),
|
||||||
contentPadding: EdgeInsets.symmetric(horizontal: 20),
|
statusBarColor: Theme.of(context).brightness == Brightness.light
|
||||||
titlePadding: EdgeInsets.only(top: 20, left: 20, right: 20, bottom: 20),
|
? Color.fromRGBO(113, 113, 113, 1)
|
||||||
actionsPadding: EdgeInsets.all(0),
|
: Color.fromRGBO(15, 15, 15, 1),
|
||||||
actions: <Widget>[
|
),
|
||||||
FlatButton(
|
child: SafeArea(
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
child: AlertDialog(
|
||||||
child: Text(
|
shape: RoundedRectangleBorder(
|
||||||
'CANCEL',
|
borderRadius: BorderRadius.all(Radius.circular(10))),
|
||||||
style: TextStyle(color: Colors.grey[600]),
|
elevation: 1,
|
||||||
),
|
contentPadding: EdgeInsets.symmetric(horizontal: 20),
|
||||||
),
|
titlePadding:
|
||||||
FlatButton(
|
EdgeInsets.only(top: 20, left: 20, right: 200, bottom: 20),
|
||||||
onPressed: () async {
|
actionsPadding: EdgeInsets.all(0),
|
||||||
if (list.contains(_newGroup)) {
|
actions: <Widget>[
|
||||||
setState(() => _error = 1);
|
FlatButton(
|
||||||
} else {
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
groupList.addGroup(PodcastGroup(_newGroup));
|
child: Text(
|
||||||
Navigator.of(context).pop();
|
'CANCEL',
|
||||||
}
|
style: TextStyle(color: Colors.grey[600]),
|
||||||
},
|
|
||||||
child: Text('DONE'),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
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: Colors.blue, width: 2.0),
|
|
||||||
),
|
|
||||||
enabledBorder: UnderlineInputBorder(
|
|
||||||
borderSide: BorderSide(color: Colors.blue, width: 2.0),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
cursorRadius: Radius.circular(2),
|
FlatButton(
|
||||||
autofocus: true,
|
onPressed: () async {
|
||||||
maxLines: 1,
|
if (list.contains(_newGroup)) {
|
||||||
controller: _controller,
|
setState(() => _error = 1);
|
||||||
onChanged: (value) {
|
} else {
|
||||||
_newGroup = value;
|
groupList.addGroup(PodcastGroup(_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) {
|
||||||
|
_newGroup = value;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: (_error == 1)
|
||||||
|
? Text(
|
||||||
|
'Group existed',
|
||||||
|
style: TextStyle(color: Colors.red[400]),
|
||||||
|
)
|
||||||
|
: Center(),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
Container(
|
),
|
||||||
alignment: Alignment.centerLeft,
|
));
|
||||||
child: (_error == 1)
|
|
||||||
? Text(
|
|
||||||
'Group existed',
|
|
||||||
style: TextStyle(color: Colors.red[400]),
|
|
||||||
)
|
|
||||||
: Center(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
141
lib/settings/settting.dart
Normal file
141
lib/settings/settting.dart
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:tsacdop/settings/theme.dart';
|
||||||
|
|
||||||
|
class Settings extends StatelessWidget {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||||
|
value: SystemUiOverlayStyle(
|
||||||
|
statusBarIconBrightness: Theme.of(context).accentColorBrightness,
|
||||||
|
systemNavigationBarColor: Theme.of(context).primaryColor,
|
||||||
|
statusBarColor: Theme.of(context).primaryColor,
|
||||||
|
),
|
||||||
|
child: SafeArea(
|
||||||
|
child: Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text('Settings'),
|
||||||
|
elevation: 0,
|
||||||
|
backgroundColor: Theme.of(context).primaryColor,
|
||||||
|
),
|
||||||
|
body: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: <Widget>[
|
||||||
|
Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: <Widget>[
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.all(10.0),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
height: 30.0,
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 80),
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: Text('Prefrence',
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodyText1
|
||||||
|
.copyWith(color: Theme.of(context).accentColor)),
|
||||||
|
),
|
||||||
|
ListView(
|
||||||
|
shrinkWrap: true,
|
||||||
|
scrollDirection: Axis.vertical,
|
||||||
|
children: <Widget>[
|
||||||
|
ListTile(
|
||||||
|
onTap: () => Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => ThemeSetting())),
|
||||||
|
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
|
||||||
|
leading: Icon(Icons.colorize),
|
||||||
|
title: Text('Appearance'),
|
||||||
|
subtitle: Text('Colors and themes'),
|
||||||
|
),
|
||||||
|
Divider(height: 2),
|
||||||
|
ListTile(
|
||||||
|
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
|
||||||
|
leading: Icon(Icons.network_check),
|
||||||
|
title: Text('Network'),
|
||||||
|
subtitle: Text('Download network setting'),
|
||||||
|
),
|
||||||
|
Divider(height: 2),
|
||||||
|
ListTile(
|
||||||
|
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
|
||||||
|
leading: Icon(Icons.storage),
|
||||||
|
title: Text('Cache'),
|
||||||
|
subtitle: Text('Manage and clear cache'),
|
||||||
|
),
|
||||||
|
Divider(height: 2),
|
||||||
|
ListTile(
|
||||||
|
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
|
||||||
|
leading: Icon(Icons.update),
|
||||||
|
title: Text('Update'),
|
||||||
|
subtitle: Text('Update in background'),
|
||||||
|
),
|
||||||
|
Divider(height: 2),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.all(10.0),
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: <Widget>[
|
||||||
|
Container(
|
||||||
|
height: 30.0,
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 80),
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: Text('Info',
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodyText1
|
||||||
|
.copyWith(color: Theme.of(context).accentColor)),
|
||||||
|
),
|
||||||
|
ListView(
|
||||||
|
shrinkWrap: true,
|
||||||
|
scrollDirection: Axis.vertical,
|
||||||
|
children: <Widget>[
|
||||||
|
ListTile(
|
||||||
|
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
|
||||||
|
leading: Icon(Icons.colorize),
|
||||||
|
title: Text('Changelog'),
|
||||||
|
subtitle: Text('List of chagnes'),
|
||||||
|
),
|
||||||
|
Divider(height: 2),
|
||||||
|
ListTile(
|
||||||
|
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
|
||||||
|
leading: Icon(Icons.network_check),
|
||||||
|
title: Text('Credit'),
|
||||||
|
subtitle: Text('Open source libraried in application'),
|
||||||
|
),
|
||||||
|
Divider(height: 2),
|
||||||
|
ListTile(
|
||||||
|
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
|
||||||
|
leading: Icon(Icons.storage),
|
||||||
|
title: Text('Cache'),
|
||||||
|
subtitle: Text('Manage and clear cache'),
|
||||||
|
),
|
||||||
|
Divider(height: 2),
|
||||||
|
ListTile(
|
||||||
|
contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
|
||||||
|
leading: Icon(Icons.update),
|
||||||
|
title: Text('Update'),
|
||||||
|
subtitle: Text('Update in background'),
|
||||||
|
),
|
||||||
|
Divider(height: 2),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
191
lib/settings/theme.dart
Normal file
191
lib/settings/theme.dart
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:flutter_colorpicker/flutter_colorpicker.dart';
|
||||||
|
import 'package:tsacdop/class/settingstate.dart';
|
||||||
|
|
||||||
|
class ThemeSetting extends StatelessWidget {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var settings = Provider.of<SettingState>(context);
|
||||||
|
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||||
|
value: SystemUiOverlayStyle(
|
||||||
|
statusBarIconBrightness: Theme.of(context).accentColorBrightness,
|
||||||
|
systemNavigationBarColor: Theme.of(context).primaryColor,
|
||||||
|
statusBarColor: Theme.of(context).primaryColor),
|
||||||
|
child: SafeArea(
|
||||||
|
child: Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text('Appearance'),
|
||||||
|
elevation: 0,
|
||||||
|
backgroundColor: Theme.of(context).primaryColor,
|
||||||
|
),
|
||||||
|
body: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: <Widget>[
|
||||||
|
Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: <Widget>[
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.all(10.0),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
height: 30.0,
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 80),
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: Text('Interface',
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodyText1
|
||||||
|
.copyWith(color: Theme.of(context).accentColor)),
|
||||||
|
),
|
||||||
|
ListView(
|
||||||
|
shrinkWrap: true,
|
||||||
|
scrollDirection: Axis.vertical,
|
||||||
|
children: <Widget>[
|
||||||
|
ListTile(
|
||||||
|
onTap: () => 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(
|
||||||
|
titlePadding: EdgeInsets.only(
|
||||||
|
top: 20,
|
||||||
|
left: 40,
|
||||||
|
right: 200,
|
||||||
|
),
|
||||||
|
elevation: 1,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.all(
|
||||||
|
Radius.circular(10.0))),
|
||||||
|
title: Text('Theme'),
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
mainAxisAlignment:
|
||||||
|
MainAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
RadioListTile(
|
||||||
|
title: Container(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
right: 80),
|
||||||
|
child:
|
||||||
|
Text('System default')),
|
||||||
|
value: ThemeMode.system,
|
||||||
|
groupValue: settings.theme,
|
||||||
|
onChanged: (value) {
|
||||||
|
settings.setTheme = value;
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}),
|
||||||
|
RadioListTile(
|
||||||
|
title: Text('Dark mode'),
|
||||||
|
value: ThemeMode.dark,
|
||||||
|
groupValue: settings.theme,
|
||||||
|
onChanged: (value) {
|
||||||
|
settings.setTheme = value;
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}),
|
||||||
|
RadioListTile(
|
||||||
|
title: Text('Light mode'),
|
||||||
|
value: ThemeMode.light,
|
||||||
|
groupValue: settings.theme,
|
||||||
|
onChanged: (value) {
|
||||||
|
settings.setTheme = value;
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
))),
|
||||||
|
contentPadding: EdgeInsets.symmetric(horizontal: 80.0),
|
||||||
|
// leading: Icon(Icons.colorize),
|
||||||
|
title: Text('Theme'),
|
||||||
|
subtitle: Text('System default'),
|
||||||
|
),
|
||||||
|
Divider(height: 2),
|
||||||
|
ListTile(
|
||||||
|
onTap: () => 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) {
|
||||||
|
settings.setAccentColor = value;
|
||||||
|
},
|
||||||
|
pickerColor: Colors.blue,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)))),
|
||||||
|
contentPadding: EdgeInsets.symmetric(horizontal: 80.0),
|
||||||
|
title: Text('Accent color'),
|
||||||
|
subtitle: Text('Include the overlay color'),
|
||||||
|
),
|
||||||
|
Divider(height: 2),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
29
lib/util/colorize.dart
Normal file
29
lib/util/colorize.dart
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
extension Colorize on String {
|
||||||
|
Color colorizedark() {
|
||||||
|
Color _c;
|
||||||
|
var color = json.decode(this);
|
||||||
|
if (color[0] > 200 && color[1] > 200 && color[2] > 200) {
|
||||||
|
_c =
|
||||||
|
Color.fromRGBO((255 - color[0]), 255 - color[1], 255 - color[2], 1.0);
|
||||||
|
} else {
|
||||||
|
_c = Color.fromRGBO(color[0], color[1] > 200 ? 190 : color[1],
|
||||||
|
color[2] > 200 ? 190 : color[2], 1);
|
||||||
|
}
|
||||||
|
return _c;
|
||||||
|
}
|
||||||
|
|
||||||
|
Color colorizeLight() {
|
||||||
|
Color _c;
|
||||||
|
var color = json.decode(this);
|
||||||
|
if (color[0] < 50 && color[1] < 50 && color[2] < 50) {
|
||||||
|
_c =
|
||||||
|
Color.fromRGBO((255 - color[0]), 255 - color[1], 255 - color[2], 1.0);
|
||||||
|
} else {
|
||||||
|
_c = Color.fromRGBO(color[0], color[1], color[2], 1.0);
|
||||||
|
}
|
||||||
|
return _c;
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,3 @@
|
|||||||
import 'dart:convert';
|
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:isolate';
|
import 'dart:isolate';
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
@ -9,6 +8,7 @@ import 'package:google_fonts/google_fonts.dart';
|
|||||||
import 'package:tsacdop/class/episodebrief.dart';
|
import 'package:tsacdop/class/episodebrief.dart';
|
||||||
import 'package:tsacdop/episodes/episodedetail.dart';
|
import 'package:tsacdop/episodes/episodedetail.dart';
|
||||||
import 'package:tsacdop/util/pageroute.dart';
|
import 'package:tsacdop/util/pageroute.dart';
|
||||||
|
import 'package:tsacdop/util/colorize.dart';
|
||||||
|
|
||||||
class EpisodeGrid extends StatelessWidget {
|
class EpisodeGrid extends StatelessWidget {
|
||||||
final List<EpisodeBrief> podcast;
|
final List<EpisodeBrief> podcast;
|
||||||
@ -28,8 +28,7 @@ class EpisodeGrid extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
double _width = MediaQuery.of(context).size.width;
|
double _width = MediaQuery.of(context).size.width;
|
||||||
return
|
return CustomScrollView(
|
||||||
CustomScrollView(
|
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
primary: false,
|
primary: false,
|
||||||
slivers: <Widget>[
|
slivers: <Widget>[
|
||||||
@ -44,19 +43,10 @@ class EpisodeGrid extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(BuildContext context, int index) {
|
(BuildContext context, int index) {
|
||||||
Color _c;
|
Color _c =
|
||||||
var color = json.decode(podcast[index].primaryColor);
|
(Theme.of(context).brightness == Brightness.light)
|
||||||
if (Theme.of(context).brightness == Brightness.light) {
|
? podcast[index].primaryColor.colorizedark()
|
||||||
(color[0] > 200 && color[1] > 200 && color[2] > 200)
|
: podcast[index].primaryColor.colorizeLight();
|
||||||
? _c = Color.fromRGBO(
|
|
||||||
(255 - color[0]), 255 - color[1], 255 - color[2], 1.0)
|
|
||||||
: _c = Color.fromRGBO(color[0], color[1], color[2], 1.0);
|
|
||||||
} else {
|
|
||||||
(color[0] < 50 && color[1] < 50 && color[2] < 50)
|
|
||||||
? _c = Color.fromRGBO(
|
|
||||||
(255 - color[0]), 255 - color[1], 255 - color[2], 1.0)
|
|
||||||
: _c = Color.fromRGBO(color[0], color[1], color[2], 1.0);
|
|
||||||
}
|
|
||||||
return Material(
|
return Material(
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
@ -101,15 +91,11 @@ class EpisodeGrid extends StatelessWidget {
|
|||||||
Hero(
|
Hero(
|
||||||
tag: podcast[index].enclosureUrl + heroTag,
|
tag: podcast[index].enclosureUrl + heroTag,
|
||||||
child: Container(
|
child: Container(
|
||||||
child: ClipRRect(
|
height: _width / 16,
|
||||||
borderRadius: BorderRadius.all(
|
width: _width / 16,
|
||||||
Radius.circular(_width / 32)),
|
child: CircleAvatar(
|
||||||
child: Container(
|
backgroundImage: FileImage(
|
||||||
height: _width / 16,
|
File("${podcast[index].imagePath}")),
|
||||||
width: _width / 16,
|
|
||||||
child: Image.file(File(
|
|
||||||
"${podcast[index].imagePath}")),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
21
pubspec.lock
21
pubspec.lock
@ -118,6 +118,13 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.3"
|
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:
|
flutter_downloader:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
@ -149,6 +156,13 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.3"
|
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:
|
google_fonts:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
@ -448,6 +462,13 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.8"
|
version: "2.0.8"
|
||||||
|
workmanager:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: workmanager
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.0"
|
||||||
xml:
|
xml:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
|
14
pubspec.yaml
14
pubspec.yaml
@ -14,7 +14,7 @@ description: An easy-use podacasts player.
|
|||||||
version: 0.1.1
|
version: 0.1.1
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.3.0 <3.0.0"
|
sdk: ">=2.6.0 <3.0.0"
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
@ -27,14 +27,14 @@ dependencies:
|
|||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
json_annotation: any
|
json_annotation: ^3.0.1
|
||||||
sqflite: any
|
sqflite: ^1.2.1
|
||||||
flutter_html: ^0.11.1
|
flutter_html: ^0.11.1
|
||||||
path_provider: any
|
path_provider: ^1.6.1
|
||||||
color_thief_flutter: ^1.0.1
|
color_thief_flutter: ^1.0.1
|
||||||
provider: ^4.0.1
|
provider: ^4.0.1
|
||||||
google_fonts: ^0.3.2
|
google_fonts: ^0.3.2
|
||||||
dio: ^3.0.8
|
dio: ^3.0.9
|
||||||
file_picker: ^1.2.0
|
file_picker: ^1.2.0
|
||||||
xml: ^3.5.0
|
xml: ^3.5.0
|
||||||
marquee: ^1.3.1
|
marquee: ^1.3.1
|
||||||
@ -49,7 +49,9 @@ dev_dependencies:
|
|||||||
uuid: ^2.0.4
|
uuid: ^2.0.4
|
||||||
tuple: ^1.0.3
|
tuple: ^1.0.3
|
||||||
cached_network_image: ^2.0.0
|
cached_network_image: ^2.0.0
|
||||||
|
workmanager: ^0.2.0
|
||||||
|
font_awesome_flutter: ^8.7.0
|
||||||
|
flutter_colorpicker: ^0.3.2
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user