🐛 Beter support for small screen.

This commit is contained in:
stonegate 2020-06-02 22:05:49 +08:00
parent 8ec1793843
commit e9ba82d5db
17 changed files with 417 additions and 263 deletions

View File

@ -20,7 +20,7 @@ Credit to flutter team and all involved plugins, especially [webfeed](https://g
The podcasts search engine is powered by [ListenNotes](https://listennotes.com). The podcasts search engine is powered by [ListenNotes](https://listennotes.com).
## Features ## Features
* Subscriptoin group management * Podcasts group management
* Playlist support * Playlist support
* Sleep timer / Speed setting * Sleep timer / Speed setting
* OMPL file export and import * OMPL file export and import
@ -28,7 +28,7 @@ The podcasts search engine is powered by [ListenNotes](https://listennotes.com).
* Listen and subscribe history record * Listen and subscribe history record
* Dark mode / Accent color * Dark mode / Accent color
* Download for offline playing * Download for offline playing
* Share clip on twitter * Share clip(video format) on twitter
More to come... More to come...

View File

@ -49,7 +49,7 @@ android {
applicationId "com.stonegate.tsacdop" applicationId "com.stonegate.tsacdop"
minSdkVersion 19 minSdkVersion 19
targetSdkVersion 28 targetSdkVersion 28
versionCode 14 versionCode 15
versionName flutterVersionName versionName flutterVersionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
} }

View File

@ -580,7 +580,7 @@ class _MenuBarState extends State<MenuBar> {
padding: EdgeInsets.symmetric(horizontal: 20.0), padding: EdgeInsets.symmetric(horizontal: 20.0),
child: Row( child: Row(
children: <Widget>[ children: <Widget>[
Text('Play Now', Text('Play',
style: TextStyle( style: TextStyle(
color: Theme.of(context).accentColor, color: Theme.of(context).accentColor,
fontSize: 15, fontSize: 15,

View File

@ -4,7 +4,7 @@ import 'package:tsacdop/util/custompaint.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
import 'package:line_icons/line_icons.dart'; import 'package:line_icons/line_icons.dart';
const String version = '0.3.1'; const String version = '0.3.2';
class AboutApp extends StatelessWidget { class AboutApp extends StatelessWidget {
_launchUrl(String url) async { _launchUrl(String url) async {
@ -74,105 +74,110 @@ class AboutApp extends StatelessWidget {
title: Text('About'), title: Text('About'),
), ),
body: SafeArea( body: SafeArea(
child: Container( child: SingleChildScrollView(
padding: EdgeInsets.all(20), scrollDirection: Axis.vertical,
alignment: Alignment.topLeft, child: Padding(
child: Column( padding: const EdgeInsets.all(20.0),
mainAxisAlignment: MainAxisAlignment.start, child: Column(
mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[ mainAxisSize: MainAxisSize.min,
Container( children: <Widget>[
height: 200.0, Container(
alignment: Alignment.center, height: 200.0,
child: Column( alignment: Alignment.center,
mainAxisAlignment: MainAxisAlignment.center, child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Image(
image: AssetImage('assets/logo.png'),
height: 80,
),
Text('Version: $version'),
],
),
),
Container(
padding: EdgeInsets.symmetric(horizontal: 50),
child: Text(
'Tsacdop is a podcast player developed in flutter, a clean, simply beautiful and friendly app.',
textAlign: TextAlign.center,
),
),
Padding(
padding: EdgeInsets.all(10.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, 'Twitter', LineIcons.twitter,
'https://twitter.com/shimenmen'),
_listItem(context, 'GitHub', LineIcons.github_alt,
'https://github.com/stonega'),
_listItem(context, 'Medium', LineIcons.medium,
'https://medium.com/@stonegate'),
],
),
),
Spacer(),
Container(
height: 50,
alignment: Alignment.center,
child: GestureDetector(
onTapDown: (detail) async {
OverlayEntry _overlayEntry;
_overlayEntry = _createOverlayEntry(detail);
Overlay.of(context).insert(_overlayEntry);
await Future.delayed(Duration(seconds: 2));
_overlayEntry?.remove();
},
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[ children: <Widget>[
Image.asset( Image(
'assets/text.png', image: AssetImage('assets/logo.png'),
height: 25, height: 80,
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 5),
),
Icon(
Icons.favorite,
color: Colors.blue,
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 5),
),
FlutterLogo(
size: 18,
), ),
Text('Version: $version'),
], ],
), ),
), ),
), Container(
], padding: EdgeInsets.symmetric(horizontal: 50),
child: Text(
'Tsacdop is a podcast player developed in flutter, a clean, simply beautiful and friendly app.',
textAlign: TextAlign.center,
),
),
Padding(
padding: EdgeInsets.all(10.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, 'Twitter', LineIcons.twitter,
'https://twitter.com/shimenmen'),
_listItem(context, 'GitHub', LineIcons.github_alt,
'https://github.com/stonega'),
_listItem(context, 'Medium', LineIcons.medium,
'https://medium.com/@stonegate'),
],
),
),
//Spacer(),
Padding(
padding: EdgeInsets.symmetric(vertical: 50),
),
Container(
height: 50,
alignment: Alignment.center,
child: GestureDetector(
onTapDown: (detail) async {
OverlayEntry _overlayEntry;
_overlayEntry = _createOverlayEntry(detail);
Overlay.of(context).insert(_overlayEntry);
await Future.delayed(Duration(seconds: 2));
_overlayEntry?.remove();
},
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,
),
],
),
),
),
],
),
), ),
), ),
)), )),

View File

@ -18,24 +18,26 @@ import '../.env.dart';
class MyHomePageDelegate extends SearchDelegate<int> { class MyHomePageDelegate extends SearchDelegate<int> {
final String searchFieldLabel; final String searchFieldLabel;
MyHomePageDelegate({this.searchFieldLabel}) MyHomePageDelegate({this.searchFieldLabel})
: super( : super(
searchFieldLabel: searchFieldLabel, searchFieldLabel: searchFieldLabel,
); );
static Future<List> getList(String searchText) async {
String apiKey = environment['apiKey']; //static Future<List> getList(String searchText) async {
String url = // String apiKey = environment['apiKey'];
"https://listennotes.p.rapidapi.com/api/v1/search?only_in=title%2Cdescription&q=" + // String url =
"$searchText&type=podcast"; // "https://listennotes.p.rapidapi.com/api/v1/search?only_in=title%2Cdescription&q=" +
Response response = await Dio().get(url, // "$searchText&type=podcast";
options: Options(headers: { // Response response = await Dio().get(url,
'X-RapidAPI-Key': "$apiKey", // options: Options(headers: {
'Accept': "application/json" // 'X-RapidAPI-Key': "$apiKey",
})); // 'Accept': "application/json"
Map searchResultMap = jsonDecode(response.toString()); // }));
var searchResult = SearchPodcast.fromJson(searchResultMap); // Map searchResultMap = jsonDecode(response.toString());
return searchResult.results; // var searchResult = SearchPodcast.fromJson(searchResultMap);
} // return searchResult.results;
//}
static Future getRss(String url) async { static Future getRss(String url) async {
try { try {
@ -185,27 +187,130 @@ class MyHomePageDelegate extends SearchDelegate<int> {
}, },
); );
else else
return FutureBuilder( return SearchList(
future: getList(query), query: query,
builder: (BuildContext context, AsyncSnapshot<List> snapshot) {
if (!snapshot.hasData && query != null)
return Container(
padding: EdgeInsets.only(top: 200),
alignment: Alignment.topCenter,
child: CircularProgressIndicator(),
);
List content = snapshot.data;
return ListView.builder(
scrollDirection: Axis.vertical,
itemCount: content.length,
itemBuilder: (BuildContext context, int index) {
return SearchResult(
onlinePodcast: content[index],
);
},
);
},
); );
// return FutureBuilder(
// future: getList(query),
// builder: (BuildContext context, AsyncSnapshot<List> snapshot) {
// if (!snapshot.hasData && query != null)
// return Container(
// padding: EdgeInsets.only(top: 200),
// alignment: Alignment.topCenter,
// child: CircularProgressIndicator(),
// );
// List content = snapshot.data;
// return ListView.builder(
// scrollDirection: Axis.vertical,
// itemCount: content.length,
// itemBuilder: (BuildContext context, int index) {
// return SearchResult(
// onlinePodcast: content[index],
// );
// },
// );
// },
// );
}
}
class SearchList extends StatefulWidget {
final String query;
SearchList({this.query, Key key}) : super(key: key);
@override
_SearchListState createState() => _SearchListState();
}
class _SearchListState extends State<SearchList> {
int _nextOffset;
List<OnlinePodcast> _podcastList;
int _offset;
bool _loading;
@override
void initState() {
super.initState();
_nextOffset = 0;
_podcastList = [];
}
Future<List> _getList(String searchText, int nextOffset) async {
String apiKey = environment['apiKey'];
String url = "https://listen-api.listennotes.com/api/v2/search?q=" +
searchText +
"&sort_by_date=0&type=podcast&offset=$nextOffset";
Response response = await Dio().get(url,
options: Options(headers: {
'X-ListenAPI-Key': "$apiKey",
'Accept': "application/json"
}));
Map searchResultMap = jsonDecode(response.toString());
var searchResult = SearchPodcast.fromJson(searchResultMap);
_offset = searchResult.nextOffset;
_podcastList.addAll(searchResult.results.cast());
_loading = false;
return _podcastList;
}
@override
Widget build(BuildContext context) {
return FutureBuilder<List>(
future: _getList(widget.query, _nextOffset),
builder: (BuildContext context, AsyncSnapshot<List> snapshot) {
if (!snapshot.hasData && widget.query != null)
return Container(
padding: EdgeInsets.only(top: 200),
alignment: Alignment.topCenter,
child: CircularProgressIndicator(),
);
var content = snapshot.data;
return CustomScrollView(
slivers: [
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
return SearchResult(
onlinePodcast: content[index],
);
},
childCount: content.length,
),
),
SliverToBoxAdapter(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.only(top: 10.0, bottom: 20.0),
child: SizedBox(
height: 30,
child: OutlineButton(
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.all(Radius.circular(15))),
child: _loading
? SizedBox(
height: 20,
width: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
))
: Text('Load more'),
onPressed: () => _loading
? null
: setState(() {
_loading = true;
_nextOffset = _offset;
}))),
)
],
),
)
],
);
},
);
} }
} }
@ -292,6 +397,25 @@ class _SearchResultState extends State<SearchResult>
width: 40.0, width: 40.0,
fit: BoxFit.fitWidth, fit: BoxFit.fitWidth,
alignment: Alignment.center, alignment: Alignment.center,
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return Container(
alignment: Alignment.center,
height: 40,
width: 40,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: context.primaryColorDark),
child: SizedBox(
width: 20, height: 2, child: LinearProgressIndicator()),
);
},
errorBuilder: (context, error, stackTrace) => Container(
width: 40,
height: 40,
alignment: Alignment.center,
color: context.primaryColorDark,
child: Icon(Icons.error)),
), ),
), ),
title: Text(widget.onlinePodcast.title), title: Text(widget.onlinePodcast.title),

View File

@ -460,15 +460,10 @@ class _PlayerWidgetState extends State<PlayerWidget> {
style: TextStyle( style: TextStyle(
color: Theme.of(context).accentColor), color: Theme.of(context).accentColor),
) )
: Row( : Text(
children: <Widget>[ (_stringForSeconds(data.item2) ?? '') +
Text( ' Left',
_stringForSeconds(data.item2) ?? '', maxLines: 2,
),
Text(
' Left',
),
],
), ),
); );
}, },
@ -480,10 +475,9 @@ class _PlayerWidgetState extends State<PlayerWidget> {
selector: (_, audio) => audio.audioState, selector: (_, audio) => audio.audioState,
builder: (_, audioplay, __) { builder: (_, audioplay, __) {
return Row( return Row(
mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
children: [ children: [
Spacer(), //Spacer(),
audioplay == BasicPlaybackState.playing audioplay == BasicPlaybackState.playing
? InkWell( ? InkWell(
onTap: onTap:
@ -518,7 +512,7 @@ class _PlayerWidgetState extends State<PlayerWidget> {
)), )),
), ),
Container( Container(
height: 50.0, height: 40.0,
decoration: BoxDecoration( decoration: BoxDecoration(
shape: BoxShape.circle, shape: BoxShape.circle,
color: Colors.black), color: Colors.black),
@ -531,8 +525,9 @@ class _PlayerWidgetState extends State<PlayerWidget> {
), ),
), ),
IconButton( IconButton(
padding: EdgeInsets.zero,
onPressed: () => audio.playNext(), onPressed: () => audio.playNext(),
iconSize: 25.0, iconSize: 20.0,
icon: Icon(Icons.skip_next), icon: Icon(Icons.skip_next),
color: color:
Theme.of(context).tabBarTheme.labelColor), Theme.of(context).tabBarTheme.labelColor),
@ -1251,7 +1246,7 @@ class _ControlPanelState extends State<ControlPanel>
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
IconButton( IconButton(
padding: EdgeInsets.symmetric(horizontal: 30.0), padding: EdgeInsets.symmetric(horizontal: 25.0),
onPressed: onPressed:
backplay == BasicPlaybackState.playing backplay == BasicPlaybackState.playing
? () => audio.forwardAudio(-10) ? () => audio.forwardAudio(-10)
@ -1323,7 +1318,7 @@ class _ControlPanelState extends State<ControlPanel>
), ),
), ),
IconButton( IconButton(
padding: EdgeInsets.symmetric(horizontal: 30.0), padding: EdgeInsets.symmetric(horizontal: 25.0),
onPressed: onPressed:
backplay == BasicPlaybackState.playing backplay == BasicPlaybackState.playing
? () => audio.forwardAudio(30) ? () => audio.forwardAudio(30)

View File

@ -158,7 +158,7 @@ class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
controller: _controller, controller: _controller,
tabs: <Widget>[ tabs: <Widget>[
Tab( Tab(
child: Text('Recent Update'), child: Text('Recent'),
), ),
Tab( Tab(
child: Text('Favorite'), child: Text('Favorite'),

View File

@ -440,7 +440,7 @@ class _PodcastPreviewState extends State<PodcastPreview> {
onPressed: () { onPressed: () {
Navigator.push( Navigator.push(
context, context,
SlideLeftRoute( SlideLeftHideRoute(
page: PodcastDetail( page: PodcastDetail(
podcastLocal: widget.podcastLocal, podcastLocal: widget.podcastLocal,
)), )),

View File

@ -39,7 +39,7 @@ class Import extends StatelessWidget {
groupList.subscribeNewPodcast(item.id); groupList.subscribeNewPodcast(item.id);
return importColumn("Fetch data ${item.title}", context); return importColumn("Fetch data ${item.title}", context);
case SubscribeState.fetch: case SubscribeState.fetch:
groupList.updatePodcast(item.id); // groupList.updatePodcast(item.id);
return importColumn("Subscribe success ${item.title}", context); return importColumn("Subscribe success ${item.title}", context);
case SubscribeState.exist: case SubscribeState.exist:
return importColumn( return importColumn(

View File

@ -253,10 +253,8 @@ class _PodcastCardState extends State<PodcastCard>
child: _addGroup child: _addGroup
? Row( ? Row(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[ children: <Widget>[
Expanded( Expanded(
flex: 4,
child: SingleChildScrollView( child: SingleChildScrollView(
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
child: Row( child: Row(
@ -284,9 +282,10 @@ class _PodcastCardState extends State<PodcastCard>
}).toList()), }).toList()),
), ),
), ),
Expanded( SizedBox(
flex: 1, width: 100,
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[ children: <Widget>[
IconButton( IconButton(
icon: Icon(Icons.clear), icon: Icon(Icons.clear),
@ -374,7 +373,7 @@ class _PodcastCardState extends State<PodcastCard>
titlePadding: EdgeInsets.only( titlePadding: EdgeInsets.only(
top: 20, top: 20,
left: 20, left: 20,
right: 100, right: context.width / 3,
bottom: 20), bottom: 20),
title: title:
Text('Skip seconds at the beginning'), Text('Skip seconds at the beginning'),
@ -452,7 +451,7 @@ class _PodcastCardState extends State<PodcastCard>
titlePadding: EdgeInsets.only( titlePadding: EdgeInsets.only(
top: 20, top: 20,
left: 20, left: 20,
right: 200, right: context.width / 3,
bottom: 20), bottom: 20),
title: Text('Remove confirm'), title: Text('Remove confirm'),
content: Text( content: Text(
@ -535,8 +534,8 @@ class _RenameGroupState extends State<RenameGroup> {
borderRadius: BorderRadius.all(Radius.circular(10))), borderRadius: BorderRadius.all(Radius.circular(10))),
elevation: 1, elevation: 1,
contentPadding: EdgeInsets.symmetric(horizontal: 20), contentPadding: EdgeInsets.symmetric(horizontal: 20),
titlePadding: titlePadding: EdgeInsets.only(
EdgeInsets.only(top: 20, left: 20, right: 200, bottom: 20), top: 20, left: 20, right: context.width / 3, bottom: 20),
actionsPadding: EdgeInsets.all(0), actionsPadding: EdgeInsets.all(0),
actions: <Widget>[ actions: <Widget>[
FlatButton( FlatButton(

View File

@ -13,6 +13,7 @@ import '../type/podcastlocal.dart';
import '../local_storage/sqflite_localpodcast.dart'; import '../local_storage/sqflite_localpodcast.dart';
import '../podcasts/podcastdetail.dart'; import '../podcasts/podcastdetail.dart';
import '../util/pageroute.dart'; import '../util/pageroute.dart';
import '../util/context_extension.dart';
class AboutPodcast extends StatefulWidget { class AboutPodcast extends StatefulWidget {
final PodcastLocal podcastLocal; final PodcastLocal podcastLocal;
@ -48,7 +49,8 @@ class _AboutPodcastState extends State<AboutPodcast> {
return AlertDialog( return AlertDialog(
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(10.0))), borderRadius: BorderRadius.all(Radius.circular(10.0))),
titlePadding: EdgeInsets.only(top: 20, left: 20, right: 200, bottom: 20), titlePadding: EdgeInsets.only(
top: 20, left: 20, right: context.width / 3, bottom: 20),
actions: <Widget>[ actions: <Widget>[
FlatButton( FlatButton(
padding: EdgeInsets.all(10.0), padding: EdgeInsets.all(10.0),
@ -63,18 +65,21 @@ class _AboutPodcastState extends State<AboutPodcast> {
), ),
], ],
title: Text(widget.podcastLocal.title), title: Text(widget.podcastLocal.title),
content: Column( content: SingleChildScrollView(
mainAxisSize: MainAxisSize.min, scrollDirection: Axis.vertical,
crossAxisAlignment: CrossAxisAlignment.start, child: Column(
children: <Widget>[ mainAxisSize: MainAxisSize.min,
!_load crossAxisAlignment: CrossAxisAlignment.start,
? Center() children: <Widget>[
: _description != null ? Html(data: _description) : Center(), !_load
(widget.podcastLocal.author != null) ? Center()
? Text(widget.podcastLocal.author, : _description != null ? Html(data: _description) : Center(),
style: TextStyle(color: Colors.blue)) (widget.podcastLocal.author != null)
: Center(), ? Text(widget.podcastLocal.author,
], style: TextStyle(color: Colors.blue))
: Center(),
],
),
), ),
); );
} }

View File

@ -49,11 +49,11 @@ class _PodcastManageState extends State<PodcastManage>
_fraction = _animation.value; _fraction = _animation.value;
}); });
}); });
_menuAnimation = Tween(begin: 0.0, end: 1.0).animate( _menuAnimation = Tween(begin: 0.0, end: 1.0)
CurvedAnimation(parent: _menuController, curve: Curves.easeIn)) .animate(CurvedAnimation(parent: _menuController, curve: Curves.easeIn))
..addListener(() { ..addListener(() {
if (mounted) setState(() => _menuValue = _menuAnimation.value); if (mounted) setState(() => _menuValue = _menuAnimation.value);
}); });
_controller.addStatusListener((status) { _controller.addStatusListener((status) {
if (status == AnimationStatus.completed) { if (status == AnimationStatus.completed) {
@ -361,7 +361,9 @@ class _PodcastManageState extends State<PodcastManage>
EdgeInsets.only( EdgeInsets.only(
top: 20, top: 20,
left: 20, left: 20,
right: 200, right: context
.width /
3,
bottom: 20), bottom: 20),
title: Text( title: Text(
'Delete confirm'), 'Delete confirm'),
@ -531,8 +533,8 @@ class _AddGroupState extends State<AddGroup> {
borderRadius: BorderRadius.all(Radius.circular(10))), borderRadius: BorderRadius.all(Radius.circular(10))),
elevation: 1, elevation: 1,
contentPadding: EdgeInsets.symmetric(horizontal: 20), contentPadding: EdgeInsets.symmetric(horizontal: 20),
titlePadding: titlePadding: EdgeInsets.only(
EdgeInsets.only(top: 20, left: 20, right: 200, bottom: 20), top: 20, left: 20, right: context.width / 3, bottom: 20),
actionsPadding: EdgeInsets.all(0), actionsPadding: EdgeInsets.all(0),
actions: <Widget>[ actions: <Widget>[
FlatButton( FlatButton(

View File

@ -85,7 +85,10 @@ class _SettingsState extends State<Settings>
Padding( Padding(
padding: EdgeInsets.symmetric(vertical: 5), padding: EdgeInsets.symmetric(vertical: 5),
), ),
Text(name) Text(
name,
maxLines: 2,
)
], ],
), ),
), ),
@ -284,28 +287,31 @@ class _SettingsState extends State<Settings>
), ),
), ),
_showFeedback _showFeedback
? Row( ? SingleChildScrollView(
mainAxisAlignment: scrollDirection: Axis.horizontal,
MainAxisAlignment.spaceEvenly, child: Row(
crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment:
children: <Widget>[ MainAxisAlignment.spaceEvenly,
_feedbackItem( crossAxisAlignment: CrossAxisAlignment.center,
LineIcons.github, children: <Widget>[
'Submit issue', _feedbackItem(
'https://github.com/stonega/tsacdop/issues'), LineIcons.github,
_feedbackItem( 'Submit issue',
LineIcons.telegram, 'https://github.com/stonega/tsacdop/issues'),
'Join group', _feedbackItem(
'https://t.me/joinchat/Bk3LkRpTHy40QYC78PK7Qg'), LineIcons.telegram,
_feedbackItem( 'Join group',
LineIcons.envelope_open_text_solid, 'https://t.me/joinchat/Bk3LkRpTHy40QYC78PK7Qg'),
'Write to me', _feedbackItem(
'mailto:<tsacdop.app@gmail.com>?subject=Tsacdop Feedback'), LineIcons.envelope_open_text_solid,
_feedbackItem( 'Write to me',
LineIcons.google_play, 'mailto:<tsacdop.app@gmail.com>?subject=Tsacdop Feedback'),
'Rate on Play', _feedbackItem(
'https://play.google.com/store/apps/details?id=com.stonegate.tsacdop') LineIcons.google_play,
], 'Rate on Play',
'https://play.google.com/store/apps/details?id=com.stonegate.tsacdop')
],
),
) )
: Center(), : Center(),
Divider( Divider(

View File

@ -72,45 +72,46 @@ class ThemeSetting extends StatelessWidget {
titlePadding: EdgeInsets.only( titlePadding: EdgeInsets.only(
top: 20, top: 20,
left: 40, left: 40,
right: 200, right: context.width / 3,
), ),
elevation: 1, elevation: 1,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all( borderRadius: BorderRadius.all(
Radius.circular(10.0))), Radius.circular(10.0))),
title: Text('Theme'), title: Text('Theme'),
content: Column( content: SingleChildScrollView(
mainAxisSize: MainAxisSize.min, scrollDirection: Axis.vertical,
mainAxisAlignment: MainAxisAlignment.start, child: Column(
children: <Widget>[ mainAxisSize: MainAxisSize.min,
RadioListTile( mainAxisAlignment:
title: Container( MainAxisAlignment.start,
padding: children: <Widget>[
EdgeInsets.only(right: 80), RadioListTile(
child: Text('System default')), title: Text('System default'),
value: ThemeMode.system, value: ThemeMode.system,
groupValue: settings.theme, groupValue: settings.theme,
onChanged: (value) { onChanged: (value) {
settings.setTheme = value; settings.setTheme = value;
Navigator.of(context).pop(); Navigator.of(context).pop();
}), }),
RadioListTile( RadioListTile(
title: Text('Dark mode'), title: Text('Dark mode'),
value: ThemeMode.dark, value: ThemeMode.dark,
groupValue: settings.theme, groupValue: settings.theme,
onChanged: (value) { onChanged: (value) {
settings.setTheme = value; settings.setTheme = value;
Navigator.of(context).pop(); Navigator.of(context).pop();
}), }),
RadioListTile( RadioListTile(
title: Text('Light mode'), title: Text('Light mode'),
value: ThemeMode.light, value: ThemeMode.light,
groupValue: settings.theme, groupValue: settings.theme,
onChanged: (value) { onChanged: (value) {
settings.setTheme = value; settings.setTheme = value;
Navigator.of(context).pop(); Navigator.of(context).pop();
}), }),
], ],
),
), ),
), ),
)), )),
@ -357,4 +358,4 @@ class _ColorPickerState extends State<ColorPicker>
), ),
); );
} }
} }

View File

@ -36,7 +36,7 @@ class SubscribeWorker extends ChangeNotifier {
SubscribeItem _subscribeItem; SubscribeItem _subscribeItem;
SubscribeItem _currentSubscribeItem = SubscribeItem('', ''); SubscribeItem _currentSubscribeItem = SubscribeItem('', '');
bool _created = false; bool _created = false;
bool get created=> _created; bool get created => _created;
setSubscribeItem(SubscribeItem item) async { setSubscribeItem(SubscribeItem item) async {
_subscribeItem = item; _subscribeItem = item;
@ -117,9 +117,23 @@ Future<void> subIsolateEntryPoint(SendPort sendPort) async {
receiveTimeout: 20000, receiveTimeout: 20000,
); );
print(rss); print(rss);
try { try {
Response response = await Dio(options).get(rss); Response response = await Dio(options).get(rss);
var p = RssFeed.parse(response.data); RssFeed p;
try {
p = RssFeed.parse(response.data);
} on ArgumentError catch (e) {
print(e);
sendPort.send([item.title, item.url, 6]);
await Future.delayed(Duration(seconds: 2));
sendPort.send([item.title, item.url, 4]);
items.removeWhere((element) => element.url == item.url);
if (items.length > 0) {
await _subscribe(items.first);
} else
sendPort.send("done");
}
var dir = await getApplicationDocumentsDirectory(); var dir = await getApplicationDocumentsDirectory();
@ -128,31 +142,35 @@ Future<void> subIsolateEntryPoint(SendPort sendPort) async {
print(realUrl); print(realUrl);
bool checkUrl = await dbHelper.checkPodcast(realUrl); bool checkUrl = await dbHelper.checkPodcast(realUrl);
String imageUrl;
if (checkUrl) { if (checkUrl) {
img.Image thumbnail; img.Image thumbnail;
try { try {
Response<List<int>> imageResponse = await Dio().get<List<int>>( Response<List<int>> imageResponse = await Dio().get<List<int>>(
p.itunes.image.href, p.itunes.image.href,
options: Options(responseType: ResponseType.bytes)); options: Options(responseType: ResponseType.bytes));
imageUrl = p.itunes.image.href;
img.Image image = img.decodeImage(imageResponse.data); img.Image image = img.decodeImage(imageResponse.data);
thumbnail = img.copyResize(image, width: 300); thumbnail = img.copyResize(image, width: 300);
} on DioError catch (e) { } catch (e) {
print(e); print(e);
try { try {
Response<List<int>> imageResponse = await Dio().get<List<int>>( Response<List<int>> imageResponse = await Dio().get<List<int>>(
item.imgUrl, item.imgUrl,
options: Options(responseType: ResponseType.bytes)); options: Options(responseType: ResponseType.bytes));
imageUrl = item.imgUrl;
img.Image image = img.decodeImage(imageResponse.data); img.Image image = img.decodeImage(imageResponse.data);
thumbnail = img.copyResize(image, width: 300); thumbnail = img.copyResize(image, width: 300);
} on DioError catch (e) { } catch (e) {
print(e); print(e);
try { try {
Response<List<int>> imageResponse = await Dio().get<List<int>>( Response<List<int>> imageResponse = await Dio().get<List<int>>(
"https://ui-avatars.com/api/?size=300&background=4D91BE&color=fff&name=${item.title}&length=2&bold=true", "https://ui-avatars.com/api/?size=300&background=4D91BE&color=fff&name=${item.title}&length=2&bold=true",
options: Options(responseType: ResponseType.bytes)); options: Options(responseType: ResponseType.bytes));
imageUrl =
"https://ui-avatars.com/api/?size=300&background=4D91BE&color=fff&name=${item.title}&length=2&bold=true";
thumbnail = img.decodeImage(imageResponse.data); thumbnail = img.decodeImage(imageResponse.data);
} on DioError catch (e) { } catch (e) {
print(e); print(e);
sendPort.send([item.title, item.url, 6]); sendPort.send([item.title, item.url, 6]);
await Future.delayed(Duration(seconds: 2)); await Future.delayed(Duration(seconds: 2));
@ -174,8 +192,8 @@ Future<void> subIsolateEntryPoint(SendPort sendPort) async {
String author = p.itunes.author ?? p.author ?? ''; String author = p.itunes.author ?? p.author ?? '';
String provider = p.generator ?? ''; String provider = p.generator ?? '';
String link = p.link ?? ''; String link = p.link ?? '';
PodcastLocal podcastLocal = PodcastLocal(p.title, p.itunes.image.href, PodcastLocal podcastLocal = PodcastLocal(p.title, imageUrl, realUrl,
realUrl, primaryColor, author, uuid, imagePath, provider, link, primaryColor, author, uuid, imagePath, provider, link,
description: p.description); description: p.description);
// await groupList.subscribe(podcastLocal); // await groupList.subscribe(podcastLocal);

View File

@ -30,7 +30,8 @@ class SlideLeftRoute extends PageRouteBuilder {
class SlideLeftHideRoute extends PageRouteBuilder { class SlideLeftHideRoute extends PageRouteBuilder {
final Widget page; final Widget page;
SlideLeftHideRoute({this.page}) final Widget transitionPage;
SlideLeftHideRoute({this.page, this.transitionPage})
: super( : super(
pageBuilder: ( pageBuilder: (
BuildContext context, BuildContext context,
@ -38,25 +39,23 @@ class SlideLeftHideRoute extends PageRouteBuilder {
Animation<double> secondaryAnimation, Animation<double> secondaryAnimation,
) => ) =>
page, page,
transitionDuration: Duration(seconds: 2),
transitionsBuilder: ( transitionsBuilder: (
BuildContext context, BuildContext context,
Animation<double> animation, Animation<double> animation,
Animation<double> secondaryAnimation, Animation<double> secondaryAnimation,
Widget child, Widget child,
) => ) {
SlideTransition( if (animation.isCompleted)
position: Tween<Offset>( return child;
begin: const Offset(1, 0), else
end: Offset.zero, return SlideTransition(
).animate(animation), position: Tween<Offset>(
child: Container( begin: const Offset(1, 0),
alignment: Alignment.topLeft, end: Offset.zero,
child: SizedBox( ).animate(animation),
width: context.width, child: transitionPage);
height: context.height, },
child: child),
),
),
); );
} }

View File

@ -1,7 +1,7 @@
name: tsacdop name: tsacdop
description: An easy-use podacasts player. description: An easy-use podacasts player.
version: 0.3.1 version: 0.3.2
environment: environment:
sdk: ">=2.6.0 <3.0.0" sdk: ">=2.6.0 <3.0.0"