Compare commits

...

3 Commits

Author SHA1 Message Date
xijieyin e97a493135 fix: remove unused null check 2022-06-03 22:38:13 +08:00
xijieyin 731d50935b fix: remove unused null check 2022-06-03 22:29:19 +08:00
xijieyin bb6f57a6a3 feat: intgrate material you design 2022-06-03 22:03:21 +08:00
57 changed files with 2031 additions and 1927 deletions

View File

@ -3,23 +3,20 @@ import 'dart:developer' as developer;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:intl/intl.dart';
import 'package:linkify/linkify.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:tsacdop/episodes/menu_bar.dart';
import 'package:tsacdop/episodes/shownote.dart';
import 'package:tsacdop/util/helpers.dart';
import 'package:tuple/tuple.dart'; import 'package:tuple/tuple.dart';
import '../home/audioplayer.dart'; import '../home/audioplayer.dart';
import '../local_storage/sqflite_localpodcast.dart'; import '../local_storage/sqflite_localpodcast.dart';
import '../state/audio_state.dart'; import '../state/audio_state.dart';
import '../state/setting_state.dart';
import '../type/episodebrief.dart'; import '../type/episodebrief.dart';
import '../type/play_histroy.dart'; import '../type/play_histroy.dart';
import '../util/extension_helper.dart'; import '../util/extension_helper.dart';
import '../widgets/audiopanel.dart'; import '../widgets/audiopanel.dart';
import '../widgets/custom_widget.dart'; import '../widgets/custom_widget.dart';
import 'episode_download.dart';
class EpisodeDetail extends StatefulWidget { class EpisodeDetail extends StatefulWidget {
final EpisodeBrief? episodeItem; final EpisodeBrief? episodeItem;
@ -48,23 +45,23 @@ class _EpisodeDetailState extends State<EpisodeDetail> {
return await dbHelper.getPosition(episode); return await dbHelper.getPosition(episode);
} }
ScrollController? _controller; late ScrollController _controller;
_scrollListener() { _scrollListener() {
if (_controller!.position.userScrollDirection == ScrollDirection.reverse) { if (_controller.position.userScrollDirection == ScrollDirection.reverse) {
if (_showMenu && mounted) { if (_showMenu && mounted) {
setState(() { setState(() {
_showMenu = false; _showMenu = false;
}); });
} }
} }
if (_controller!.position.userScrollDirection == ScrollDirection.forward) { if (_controller.position.userScrollDirection == ScrollDirection.forward) {
if (!_showMenu && mounted) { if (!_showMenu && mounted) {
setState(() { setState(() {
_showMenu = true; _showMenu = true;
}); });
} }
} }
if (_controller!.offset > context.textTheme.headline5!.fontSize!) { if (_controller.offset > context.textTheme.headline5!.fontSize!) {
if (!_showTitle) setState(() => _showTitle = true); if (!_showTitle) setState(() => _showTitle = true);
} else if (_showTitle) setState(() => _showTitle = false); } else if (_showTitle) setState(() => _showTitle = false);
} }
@ -75,623 +72,256 @@ class _EpisodeDetailState extends State<EpisodeDetail> {
_showMenu = true; _showMenu = true;
_showTitle = false; _showTitle = false;
_controller = ScrollController(); _controller = ScrollController();
_controller!.addListener(_scrollListener); _controller.addListener(_scrollListener);
} }
@override @override
void dispose() { void dispose() {
_controller!.dispose(); _controller.dispose();
super.dispose(); super.dispose();
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
SystemChrome.setSystemUIOverlayStyle( final s = context.s;
SystemUiOverlayStyle( final audio = context.watch<AudioPlayerNotifier>();
statusBarColor: Colors.transparent, return AnnotatedRegion<SystemUiOverlayStyle>(
systemNavigationBarColor: Colors.transparent, value: SystemUiOverlayStyle(
statusBarColor: context.priamryContainer,
systemNavigationBarColor: context.priamryContainer,
systemNavigationBarContrastEnforced: false, systemNavigationBarContrastEnforced: false,
systemNavigationBarIconBrightness: context.iconBrightness, systemNavigationBarIconBrightness: context.iconBrightness,
statusBarBrightness: context.brightness, statusBarBrightness: context.brightness,
statusBarIconBrightness: context.iconBrightness), statusBarIconBrightness: context.iconBrightness),
); child: WillPopScope(
final s = context.s!; onWillPop: () async {
final audio = context.watch<AudioPlayerNotifier>(); if (_playerKey.currentState != null &&
return WillPopScope( _playerKey.currentState!.initSize! > 100) {
onWillPop: () async { _playerKey.currentState!.backToMini();
if (_playerKey.currentState != null && return false;
_playerKey.currentState!.initSize! > 100) { } else {
_playerKey.currentState!.backToMini(); return true;
return false; }
} else { },
return true; child: Scaffold(
} backgroundColor: Theme.of(context).primaryColor,
}, body: SafeArea(
child: Scaffold( child: Stack(
backgroundColor: Theme.of(context).primaryColor, children: <Widget>[
body: SafeArea( StretchingOverscrollIndicator(
child: Stack( axisDirection: AxisDirection.down,
children: <Widget>[ child: NestedScrollView(
ScrollConfiguration( scrollDirection: Axis.vertical,
behavior: NoGrowBehavior(), controller: _controller,
child: NestedScrollView( headerSliverBuilder: (context, innerBoxScrolled) {
scrollDirection: Axis.vertical, return <Widget>[
controller: _controller, SliverAppBar(
headerSliverBuilder: (context, innerBoxScrolled) { backgroundColor: context.priamryContainer,
return <Widget>[ floating: true,
SliverAppBar( pinned: true,
floating: true, title: _showTitle
pinned: true, ? Text(
title: _showTitle widget.episodeItem?.title ?? '',
? Text( maxLines: 1,
widget.episodeItem?.title ?? '', overflow: TextOverflow.ellipsis,
maxLines: 1, )
overflow: TextOverflow.ellipsis, : Text(
) widget.episodeItem!.feedTitle!,
: Text( maxLines: 1,
widget.episodeItem!.feedTitle!, style: TextStyle(
maxLines: 1, fontSize: 15,
style: TextStyle( color:
fontSize: 15, context.textColor.withOpacity(0.7)),
color: context.textColor!.withOpacity(0.7)), ),
leading: CustomBackButton(),
elevation: 0,
),
];
},
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 20, vertical: 10),
child: Align(
alignment: Alignment.centerLeft,
child: Text(
widget.episodeItem!.title!,
textAlign: TextAlign.left,
style:
Theme.of(context).textTheme.headlineSmall,
), ),
leading: CustomBackButton(),
elevation: _showTitle ? 1 : 0,
),
];
},
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.symmetric(horizontal: 20.0),
child: Align(
alignment: Alignment.centerLeft,
child: Text(
widget.episodeItem!.title!,
textAlign: TextAlign.left,
style: Theme.of(context).textTheme.headline5,
), ),
), ),
), Padding(
Padding( padding: const EdgeInsets.fromLTRB(20, 10, 20, 10),
padding: EdgeInsets.fromLTRB(20, 10, 20, 10), child: Row(
child: Row( children: [
children: [ Text(
Text( s.published(formateDate(
s.published(DateFormat.yMMMd().format( widget.episodeItem!.pubDate!)),
DateTime.fromMillisecondsSinceEpoch( style:
widget.episodeItem!.pubDate!))), TextStyle(color: context.accentColor)),
style: TextStyle(color: context.accentColor)), SizedBox(width: 10),
SizedBox(width: 10), if (widget.episodeItem!.explicit == 1)
if (widget.episodeItem!.explicit == 1) Text('E',
Text('E', style: TextStyle(
style: TextStyle( fontWeight: FontWeight.bold,
fontWeight: FontWeight.bold, color: context.error)),
color: Colors.red)), Spacer(),
Spacer(), ],
], ),
), ),
), Padding(
Padding( padding: EdgeInsets.symmetric(
padding: horizontal: 20, vertical: 5),
EdgeInsets.symmetric(horizontal: 20, vertical: 5), child: Row(
child: Row( children: <Widget>[
children: <Widget>[ if (widget.episodeItem!.duration != 0)
if (widget.episodeItem!.duration != 0) Container(
Container( decoration: BoxDecoration(
color: context.secondary,
borderRadius: BorderRadius.all(
Radius.circular(16.0))),
height: 30.0,
margin: EdgeInsets.only(right: 12.0),
padding: EdgeInsets.symmetric(
horizontal: 10.0),
alignment: Alignment.center,
child: Text(
s.minsCount(
widget.episodeItem!.duration! ~/ 60,
),
style:
TextStyle(color: context.onPrimary),
)),
if (widget.episodeItem!.enclosureLength !=
null &&
widget.episodeItem!.enclosureLength != 0)
Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.cyan[300], color: context.tertiary,
borderRadius: BorderRadius.all( borderRadius: BorderRadius.all(
Radius.circular(16.0))), Radius.circular(16.0))),
height: 28.0, height: 30.0,
margin: EdgeInsets.only(right: 10.0), margin: EdgeInsets.only(right: 12.0),
padding: padding:
EdgeInsets.symmetric(horizontal: 10.0), EdgeInsets.symmetric(horizontal: 10.0),
alignment: Alignment.center, alignment: Alignment.center,
child: Text( child: Text(
s.minsCount( '${widget.episodeItem!.enclosureLength! ~/ 1000000}MB',
widget.episodeItem!.duration! ~/ 60, style:
), TextStyle(color: context.onPrimary),
style: TextStyle(color: Colors.black), ),
)),
if (widget.episodeItem!.enclosureLength != null &&
widget.episodeItem!.enclosureLength != 0)
Container(
decoration: BoxDecoration(
color: Colors.lightBlue[300],
borderRadius: BorderRadius.all(
Radius.circular(16.0))),
height: 28.0,
margin: EdgeInsets.only(right: 10.0),
padding:
EdgeInsets.symmetric(horizontal: 10.0),
alignment: Alignment.center,
child: Text(
'${widget.episodeItem!.enclosureLength! ~/ 1000000}MB',
style: TextStyle(color: Colors.black),
), ),
), FutureBuilder<PlayHistory>(
FutureBuilder<PlayHistory>( future: _getPosition(widget.episodeItem!),
future: _getPosition(widget.episodeItem!), builder: (context, snapshot) {
builder: (context, snapshot) { if (snapshot.hasError) {
if (snapshot.hasError) { developer.log(snapshot.error as String);
developer.log(snapshot.error as String); }
} if (snapshot.hasData &&
if (snapshot.hasData && snapshot.data!.seekValue! < 0.9 &&
snapshot.data!.seekValue! < 0.9 && snapshot.data!.seconds! > 10) {
snapshot.data!.seconds! > 10) { return ButtonTheme(
return ButtonTheme( height: 28,
height: 28, padding: EdgeInsets.symmetric(
padding: horizontal: 0),
EdgeInsets.symmetric(horizontal: 0), child: OutlinedButton(
child: OutlinedButton( style: OutlinedButton.styleFrom(
style: OutlinedButton.styleFrom( shape: RoundedRectangleBorder(
shape: RoundedRectangleBorder( borderRadius:
borderRadius: BorderRadius.circular(
BorderRadius.circular( 100.0),
100.0), side: BorderSide(
side: BorderSide( color:
color: context.accentColor)),
context.accentColor)), ),
), onPressed: () => audio.episodeLoad(
onPressed: () => audio.episodeLoad( widget.episodeItem,
widget.episodeItem, startPosition:
startPosition: (snapshot.data!.seconds! *
(snapshot.data!.seconds! * 1000)
1000) .toInt()),
.toInt()), child: Row(
child: Row( children: [
children: [ SizedBox(
SizedBox( width: 20,
width: 20, height: 20,
height: 20, child: CustomPaint(
child: CustomPaint( painter: ListenedPainter(
painter: ListenedPainter( context.textColor,
context.textColor, stroke: 2.0),
stroke: 2.0), ),
), ),
), SizedBox(width: 5),
SizedBox(width: 5), Text(
Text( snapshot
snapshot.data!.seconds!.toTime, .data!.seconds!.toTime,
), ),
], ],
),
), ),
), );
); } else {
} else { return Center();
return Center(); }
} }),
}), ],
], ),
), ),
), ShowNote(episode: widget.episodeItem),
ShowNote(episode: widget.episodeItem), Selector<AudioPlayerNotifier,
Selector<AudioPlayerNotifier, Tuple2<bool, PlayerHeight?>>(
Tuple2<bool, PlayerHeight?>>( selector: (_, audio) => Tuple2(
selector: (_, audio) => audio.playerRunning, audio.playerHeight),
Tuple2(audio.playerRunning, audio.playerHeight), builder: (_, data, __) {
builder: (_, data, __) { final height =
var height = kMinPlayerHeight[data.item2!.index]; kMinPlayerHeight[data.item2!.index];
return SizedBox( return SizedBox(
height: data.item1 ? height : 0, height: data.item1 ? height : 0,
); );
}), }),
], ],
),
), ),
), ),
), ),
), Selector<AudioPlayerNotifier, Tuple2<bool, PlayerHeight?>>(
Selector<AudioPlayerNotifier, Tuple2<bool, PlayerHeight?>>( selector: (_, audio) =>
selector: (_, audio) => Tuple2(audio.playerRunning, audio.playerHeight),
Tuple2(audio.playerRunning, audio.playerHeight),
builder: (_, data, __) {
var height = kMinPlayerHeight[data.item2!.index];
return Container(
alignment: Alignment.bottomCenter,
padding: EdgeInsets.only(bottom: data.item1 ? height : 0),
child: AnimatedContainer(
duration: Duration(milliseconds: 400),
height: _showMenu ? 50 : 0,
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: _MenuBar(
episodeItem: widget.episodeItem,
heroTag: widget.heroTag,
hide: widget.hide),
),
),
);
}),
Selector<AudioPlayerNotifier, EpisodeBrief?>(
selector: (_, audio) => audio.episode,
builder: (_, data, __) => Container(
child: PlayerWidget(
playerKey: _playerKey,
isPlayingPage: data == widget.episodeItem))),
],
),
),
),
);
}
}
class _MenuBar extends StatefulWidget {
final EpisodeBrief? episodeItem;
final String? heroTag;
final bool? hide;
_MenuBar({this.episodeItem, this.heroTag, this.hide, Key? key})
: super(key: key);
@override
__MenuBarState createState() => __MenuBarState();
}
class __MenuBarState extends State<_MenuBar> {
Future<int> _isListened(EpisodeBrief episode) async {
var dbHelper = DBHelper();
return await dbHelper.isListened(episode.enclosureUrl);
}
Future<void> _saveLiked(String url) async {
var dbHelper = DBHelper();
await dbHelper.setLiked(url);
if (mounted) setState(() {});
}
Future<void> _setUnliked(String url) async {
var dbHelper = DBHelper();
await dbHelper.setUniked(url);
if (mounted) setState(() {});
}
Future<void> _markListened(EpisodeBrief episode) async {
var dbHelper = DBHelper();
final history = PlayHistory(episode.title, episode.enclosureUrl, 0, 1);
await dbHelper.saveHistory(history);
if (mounted) setState(() {});
}
Future<void> _markNotListened(String url) async {
var dbHelper = DBHelper();
await dbHelper.markNotListened(url);
if (mounted) setState(() {});
}
Future<bool> _isLiked(EpisodeBrief episode) async {
var dbHelper = DBHelper();
return await dbHelper.isLiked(episode.enclosureUrl);
}
Widget _buttonOnMenu({Widget? child, VoidCallback? onTap}) => Material(
color: Colors.transparent,
child: InkWell(
onTap: onTap,
child: SizedBox(
height: 50,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 15.0), child: child),
),
),
);
OverlayEntry _createOverlayEntry() {
RenderBox renderBox = context.findRenderObject() as RenderBox;
var offset = renderBox.localToGlobal(Offset.zero);
return OverlayEntry(
builder: (constext) => Positioned(
left: offset.dx + 50,
top: offset.dy - 60,
child: Container(
width: 70,
height: 100,
//color: Colors.grey[200],
child: HeartOpen(width: 50, height: 80)),
),
);
}
@override
Widget build(BuildContext context) {
var audio = Provider.of<AudioPlayerNotifier>(context, listen: false);
final s = context.s;
return Container(
height: 50.0,
decoration: BoxDecoration(
color: context.scaffoldBackgroundColor,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Hero(
tag: widget.episodeItem!.enclosureUrl + widget.heroTag!,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 10.0),
child: Container(
height: 30.0,
width: 30.0,
color: context.scaffoldBackgroundColor,
child: widget.hide!
? Center()
: CircleAvatar(
radius: 15,
backgroundImage:
widget.episodeItem!.avatarImage),
),
),
),
FutureBuilder<bool>(
future: _isLiked(widget.episodeItem!),
initialData: false,
builder: (context, snapshot) {
return (!snapshot.data!)
? _buttonOnMenu(
child: Icon(
Icons.favorite_border,
color: Colors.grey[700],
),
onTap: () async {
await _saveLiked(
widget.episodeItem!.enclosureUrl);
OverlayEntry _overlayEntry;
_overlayEntry = _createOverlayEntry();
Overlay.of(context)!.insert(_overlayEntry);
await Future.delayed(Duration(seconds: 2));
_overlayEntry.remove();
})
: _buttonOnMenu(
child: Icon(
Icons.favorite,
color: Colors.red,
),
onTap: () => _setUnliked(
widget.episodeItem!.enclosureUrl));
},
),
DownloadButton(episode: widget.episodeItem),
Selector<AudioPlayerNotifier, List<EpisodeBrief?>>(
selector: (_, audio) => audio.queue.episodes,
builder: (_, data, __) { builder: (_, data, __) {
final inPlaylist = data.contains(widget.episodeItem); final height = kMinPlayerHeight[data.item2!.index];
return inPlaylist return Container(
? _buttonOnMenu( alignment: Alignment.bottomCenter,
child: Icon(Icons.playlist_add_check, padding:
color: context.accentColor), EdgeInsets.only(bottom: data.item1 ? height : 0),
onTap: () { child: AnimatedContainer(
audio.delFromPlaylist(widget.episodeItem!); duration: Duration(milliseconds: 400),
Fluttertoast.showToast( height: _showMenu ? 50 : 0,
msg: s!.toastRemovePlaylist, child: SingleChildScrollView(
gravity: ToastGravity.BOTTOM, scrollDirection: Axis.vertical,
); child: MenuBar(
}) episodeItem: widget.episodeItem,
: _buttonOnMenu( heroTag: widget.heroTag,
child: Icon(Icons.playlist_add, hide: widget.hide),
color: Colors.grey[700]), ),
onTap: () { ),
audio.addToPlaylist(widget.episodeItem!); );
Fluttertoast.showToast( }),
msg: s!.toastAddPlaylist, Selector<AudioPlayerNotifier, EpisodeBrief?>(
gravity: ToastGravity.BOTTOM, selector: (_, audio) => audio.episode,
); builder: (_, data, __) => Container(
}); child: PlayerWidget(
}, playerKey: _playerKey,
), isPlayingPage: data == widget.episodeItem))),
FutureBuilder<int>( ],
future: _isListened(widget.episodeItem!),
initialData: 0,
builder: (context, snapshot) {
return snapshot.data == 0
? _buttonOnMenu(
child: Padding(
padding: EdgeInsets.symmetric(vertical: 12),
child: CustomPaint(
size: Size(25, 20),
painter: ListenedAllPainter(Colors.grey[700],
stroke: 2.0),
),
),
onTap: () {
_markListened(widget.episodeItem!);
Fluttertoast.showToast(
msg: s!.markListened,
gravity: ToastGravity.BOTTOM,
);
})
: _buttonOnMenu(
child: Padding(
padding: EdgeInsets.symmetric(vertical: 12),
child: CustomPaint(
size: Size(25, 20),
painter: ListenedAllPainter(
context.accentColor,
stroke: 2.0),
),
),
onTap: () {
_markNotListened(
widget.episodeItem!.enclosureUrl);
Fluttertoast.showToast(
msg: s!.markNotListened,
gravity: ToastGravity.BOTTOM,
);
});
},
),
],
),
), ),
), ),
Selector<AudioPlayerNotifier, Tuple2<EpisodeBrief?, bool>>( ),
selector: (_, audio) => Tuple2(audio.episode, audio.playerRunning),
builder: (_, data, __) {
return (widget.episodeItem == data.item1 && data.item2)
? Padding(
padding: EdgeInsets.only(right: 30),
child: SizedBox(
width: 20,
height: 15,
child: WaveLoader(color: context.accentColor)))
: Material(
color: Colors.transparent,
child: InkWell(
onTap: () {
audio.episodeLoad(widget.episodeItem);
},
child: Container(
alignment: Alignment.center,
height: 50.0,
padding: EdgeInsets.symmetric(horizontal: 20.0),
child: Row(
children: <Widget>[
Text(s!.play.toUpperCase(),
style: TextStyle(
color: context.accentColor,
fontSize: 15,
fontWeight: FontWeight.bold,
)),
Icon(
Icons.play_arrow,
color: context.accentColor,
),
],
),
),
),
);
},
),
],
), ),
); );
} }
} }
class ShowNote extends StatelessWidget {
final EpisodeBrief? episode;
const ShowNote({this.episode, Key? key}) : super(key: key);
int? _getTimeStamp(String url) {
final time = url.substring(3).trim();
final data = time.split(':');
var seconds;
if (data.length == 3) {
seconds = int.tryParse(data[0])! * 3600 +
int.tryParse(data[1])! * 60 +
int.tryParse(data[2])!;
} else if (data.length == 2) {
seconds = int.tryParse(data[0])! * 60 + int.tryParse(data[1])!;
}
return seconds;
}
Future<String?> _getSDescription(String url) async {
var description;
var dbHelper = DBHelper();
description = (await dbHelper.getDescription(url))!
.replaceAll(RegExp(r'\s?<p>(<br>)?</p>\s?'), '')
.replaceAll('\r', '')
.trim();
if (!description.contains('<')) {
final linkList = linkify(description,
options: LinkifyOptions(humanize: false),
linkifiers: [UrlLinkifier(), EmailLinkifier()]);
for (var element in linkList) {
if (element is UrlElement) {
description = description.replaceAll(element.url,
'<a rel="nofollow" href = ${element.url}>${element.text}</a>');
}
if (element is EmailElement) {
final address = element.emailAddress;
description = description.replaceAll(address,
'<a rel="nofollow" href = "mailto:$address">$address</a>');
}
}
await dbHelper.saveEpisodeDes(url, description: description);
}
return description;
}
@override
Widget build(BuildContext context) {
var audio = context.watch<AudioPlayerNotifier>();
final s = context.s;
return FutureBuilder<String?>(
future: _getSDescription(episode!.enclosureUrl),
builder: (context, snapshot) {
if (snapshot.hasData) {
var description = snapshot.data;
if (description == null) return Center();
return description.length > 0
? Selector<AudioPlayerNotifier, EpisodeBrief?>(
selector: (_, audio) => audio.episode,
builder: (_, playEpisode, __) {
if (playEpisode == episode &&
!description!.contains('#t=')) {
final linkList = linkify(description,
options: LinkifyOptions(humanize: false),
linkifiers: [TimeStampLinkifier()]);
for (var element in linkList) {
if (element is TimeStampElement) {
final time = element.timeStamp;
description = description!.replaceFirst(time!,
'<a rel="nofollow" href = "#t=$time">$time</a>');
}
}
}
return Selector<SettingState, TextStyle>(
selector: (_, settings) => settings.showNoteFontStyle,
builder: (_, data, __) => Html(
style: {
'a': Style(
color: context.accentColor,
),
},
data: description,
onLinkTap: (url, _, __, ___) {
if (url!.substring(0, 3) == '#t=') {
final seconds = _getTimeStamp(url);
if (playEpisode == episode) {
audio.seekTo(seconds! * 1000);
}
} else {
url.launchUrl;
}
},
),
);
})
: Container(
height: context.width,
alignment: Alignment.center,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image(
image: AssetImage('assets/shownote.png'),
height: 100.0,
),
Padding(padding: EdgeInsets.all(5.0)),
Text(s!.noShownote,
textAlign: TextAlign.center,
style: TextStyle(
color: context.textColor!.withOpacity(0.5))),
],
),
);
} else {
return Center();
}
},
);
}
}

View File

@ -45,7 +45,7 @@ class _DownloadButtonState extends State<DownloadButton> {
void _deleteDownload(EpisodeBrief episode) async { void _deleteDownload(EpisodeBrief episode) async {
Provider.of<DownloadState>(context, listen: false).delTask(episode); Provider.of<DownloadState>(context, listen: false).delTask(episode);
Fluttertoast.showToast( Fluttertoast.showToast(
msg: context.s!.downloadRemovedToast, msg: context.s.downloadRemovedToast,
gravity: ToastGravity.BOTTOM, gravity: ToastGravity.BOTTOM,
); );
} }
@ -78,22 +78,20 @@ class _DownloadButtonState extends State<DownloadButton> {
Future<bool> _useDataConfirm() async { Future<bool> _useDataConfirm() async {
var ifUseData = false; var ifUseData = false;
final s = context.s!; final s = context.s;
await generalDialog( await generalDialog(
context, context,
title: Text(s.cellularConfirm), title: Text(s.cellularConfirm),
content: Text(s.cellularConfirmDes), content: Text(s.cellularConfirmDes),
actions: <Widget>[ actions: <Widget>[
FlatButton( TextButton(
onPressed: () { onPressed: Navigator.of(context).pop,
Navigator.of(context).pop();
},
child: Text( child: Text(
s.cancel, s.cancel,
style: TextStyle(color: Colors.grey[600]), style: TextStyle(color: Colors.grey[600]),
), ),
), ),
FlatButton( TextButton(
onPressed: () { onPressed: () {
ifUseData = true; ifUseData = true;
Navigator.of(context).pop(); Navigator.of(context).pop();
@ -130,7 +128,7 @@ class _DownloadButtonState extends State<DownloadButton> {
AnimatedContainer( AnimatedContainer(
duration: Duration(seconds: 1), duration: Duration(seconds: 1),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Theme.of(context).accentColor, color: context.accentColor,
borderRadius: BorderRadius.all(Radius.circular(15.0))), borderRadius: BorderRadius.all(Radius.circular(15.0))),
height: 20.0, height: 20.0,
width: (_task.status == DownloadTaskStatus.running) ? 50.0 : 0, width: (_task.status == DownloadTaskStatus.running) ? 50.0 : 0,
@ -222,7 +220,6 @@ class _DownloadButtonState extends State<DownloadButton> {
), ),
), ),
); );
break;
case 3: case 3:
Provider.of<AudioPlayerNotifier>(context, listen: false) Provider.of<AudioPlayerNotifier>(context, listen: false)
.updateMediaItem(task.episode!); .updateMediaItem(task.episode!);
@ -251,11 +248,9 @@ class _DownloadButtonState extends State<DownloadButton> {
), ),
), ),
); );
break;
case 4: case 4:
return _buttonOnMenu(Icon(Icons.refresh, color: Colors.red), return _buttonOnMenu(Icon(Icons.refresh, color: Colors.red),
() => _retryDownload(task.episode!)); () => _retryDownload(task.episode!));
break;
default: default:
return Center(); return Center();
} }

271
lib/episodes/menu_bar.dart Normal file
View File

@ -0,0 +1,271 @@
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:tsacdop/local_storage/sqflite_localpodcast.dart';
import 'package:tsacdop/type/play_histroy.dart';
import 'package:tuple/tuple.dart';
import 'package:provider/provider.dart';
import 'package:tsacdop/episodes/episode_download.dart';
import 'package:tsacdop/state/audio_state.dart';
import 'package:tsacdop/type/episodebrief.dart';
import 'package:tsacdop/util/extension_helper.dart';
import 'package:tsacdop/widgets/custom_widget.dart';
class MenuBar extends StatefulWidget {
final EpisodeBrief? episodeItem;
final String? heroTag;
final bool? hide;
MenuBar({this.episodeItem, this.heroTag, this.hide, Key? key})
: super(key: key);
@override
MenuBarState createState() => MenuBarState();
}
class MenuBarState extends State<MenuBar> {
@override
Widget build(BuildContext context) {
final audio = Provider.of<AudioPlayerNotifier>(context, listen: false);
final s = context.s;
return Container(
height: 50.0,
decoration: BoxDecoration(
color: context.priamryContainer,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Hero(
tag: widget.episodeItem!.enclosureUrl + widget.heroTag!,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 10.0),
child: Container(
height: 30.0,
width: 30.0,
color: context.priamryContainer,
child: widget.hide!
? Center()
: CircleAvatar(
radius: 15,
backgroundImage:
widget.episodeItem!.avatarImage),
),
),
),
FutureBuilder<bool>(
future: _isLiked(widget.episodeItem!),
initialData: false,
builder: (context, snapshot) {
return (!snapshot.data!)
? _buttonOnMenu(
child: Icon(
Icons.favorite_border,
color: Colors.grey[700],
),
onTap: () async {
await _saveLiked(
widget.episodeItem!.enclosureUrl);
OverlayEntry _overlayEntry;
_overlayEntry = _createOverlayEntry();
Overlay.of(context)!.insert(_overlayEntry);
await Future.delayed(Duration(seconds: 2));
_overlayEntry.remove();
})
: _buttonOnMenu(
child: Icon(
Icons.favorite,
color: Colors.red,
),
onTap: () => _setUnliked(
widget.episodeItem!.enclosureUrl));
},
),
DownloadButton(episode: widget.episodeItem),
Selector<AudioPlayerNotifier, List<EpisodeBrief?>>(
selector: (_, audio) => audio.queue.episodes,
builder: (_, data, __) {
final inPlaylist = data.contains(widget.episodeItem);
return inPlaylist
? _buttonOnMenu(
child: Icon(Icons.playlist_add_check,
color: context.accentColor),
onTap: () {
audio.delFromPlaylist(widget.episodeItem!);
Fluttertoast.showToast(
msg: s.toastRemovePlaylist,
gravity: ToastGravity.BOTTOM,
);
})
: _buttonOnMenu(
child: Icon(Icons.playlist_add,
color: Colors.grey[700]),
onTap: () {
audio.addToPlaylist(widget.episodeItem!);
Fluttertoast.showToast(
msg: s.toastAddPlaylist,
gravity: ToastGravity.BOTTOM,
);
});
},
),
FutureBuilder<int>(
future: _isListened(widget.episodeItem!),
initialData: 0,
builder: (context, snapshot) {
return snapshot.data == 0
? _buttonOnMenu(
child: Padding(
padding: EdgeInsets.symmetric(vertical: 12),
child: CustomPaint(
size: Size(25, 20),
painter: ListenedAllPainter(Colors.grey[700],
stroke: 2.0),
),
),
onTap: () {
_markListened(widget.episodeItem!);
Fluttertoast.showToast(
msg: s.markListened,
gravity: ToastGravity.BOTTOM,
);
})
: _buttonOnMenu(
child: Padding(
padding: EdgeInsets.symmetric(vertical: 12),
child: CustomPaint(
size: Size(25, 20),
painter: ListenedAllPainter(
context.accentColor,
stroke: 2.0),
),
),
onTap: () {
_markNotListened(
widget.episodeItem!.enclosureUrl);
Fluttertoast.showToast(
msg: s.markNotListened,
gravity: ToastGravity.BOTTOM,
);
},
);
},
),
],
),
),
),
Selector<AudioPlayerNotifier, Tuple2<EpisodeBrief?, bool>>(
selector: (_, audio) => Tuple2(audio.episode, audio.playerRunning),
builder: (_, data, __) {
return (widget.episodeItem == data.item1 && data.item2)
? Padding(
padding: EdgeInsets.only(right: 30),
child: SizedBox(
width: 20,
height: 15,
child: WaveLoader(color: context.accentColor)))
: Material(
color: Colors.transparent,
child: InkWell(
onTap: () {
audio.episodeLoad(widget.episodeItem);
},
child: Container(
alignment: Alignment.center,
height: 50.0,
padding: EdgeInsets.symmetric(horizontal: 20.0),
child: Row(
children: <Widget>[
Text(
s.play.toUpperCase(),
style: TextStyle(
color: context.accentColor,
fontSize: 15,
fontWeight: FontWeight.bold,
),
),
Icon(
Icons.play_arrow,
color: context.accentColor,
),
],
),
),
),
);
},
),
],
),
);
}
Future<int> _isListened(EpisodeBrief episode) async {
final dbHelper = DBHelper();
return await dbHelper.isListened(episode.enclosureUrl);
}
Future<void> _saveLiked(String url) async {
final dbHelper = DBHelper();
await dbHelper.setLiked(url);
if (mounted) setState(() {});
}
Future<void> _setUnliked(String url) async {
final dbHelper = DBHelper();
await dbHelper.setUniked(url);
if (mounted) setState(() {});
}
Future<void> _markListened(EpisodeBrief episode) async {
final dbHelper = DBHelper();
final history = PlayHistory(episode.title, episode.enclosureUrl, 0, 1);
await dbHelper.saveHistory(history);
if (mounted) setState(() {});
}
Future<void> _markNotListened(String url) async {
final dbHelper = DBHelper();
await dbHelper.markNotListened(url);
if (mounted) setState(() {});
}
Future<bool> _isLiked(EpisodeBrief episode) async {
final dbHelper = DBHelper();
return await dbHelper.isLiked(episode.enclosureUrl);
}
Widget _buttonOnMenu({Widget? child, VoidCallback? onTap}) => Material(
color: Colors.transparent,
child: InkWell(
onTap: onTap,
child: SizedBox(
height: 50,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 15.0), child: child),
),
),
);
OverlayEntry _createOverlayEntry() {
RenderBox renderBox = context.findRenderObject() as RenderBox;
var offset = renderBox.localToGlobal(Offset.zero);
return OverlayEntry(
builder: (constext) => Positioned(
left: offset.dx + 50,
top: offset.dy - 60,
child: Container(
width: 70,
height: 100,
//color: Colors.grey[200],
child: HeartOpen(width: 50, height: 80)),
),
);
}
}

136
lib/episodes/shownote.dart Normal file
View File

@ -0,0 +1,136 @@
import 'package:flutter/material.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:linkify/linkify.dart';
import 'package:provider/provider.dart';
import 'package:tsacdop/local_storage/sqflite_localpodcast.dart';
import 'package:tsacdop/state/audio_state.dart';
import 'package:tsacdop/state/setting_state.dart';
import 'package:tsacdop/type/episodebrief.dart';
import 'package:tsacdop/util/extension_helper.dart';
class ShowNote extends StatelessWidget {
final EpisodeBrief? episode;
const ShowNote({this.episode, Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final audio = context.watch<AudioPlayerNotifier>();
final s = context.s;
return FutureBuilder<String?>(
future: _getSDescription(episode!.enclosureUrl),
builder: (context, snapshot) {
if (snapshot.hasData) {
var description = snapshot.data;
if (description == null) return Center();
if (description.length > 0) {
return Selector<AudioPlayerNotifier, EpisodeBrief?>(
selector: (_, audio) => audio.episode,
builder: (_, playEpisode, __) {
if (playEpisode == episode && !description!.contains('#t=')) {
final linkList = linkify(description,
options: LinkifyOptions(humanize: false),
linkifiers: [TimeStampLinkifier()]);
for (final element in linkList) {
if (element is TimeStampElement) {
final time = element.timeStamp;
description = description!.replaceFirst(time!,
'<a rel="nofollow" href = "#t=$time">$time</a>');
}
}
}
return Selector<SettingState, TextStyle>(
selector: (_, settings) => settings.showNoteFontStyle,
builder: (_, data, __) => Html(
style: {
'html': Style.fromTextStyle(data.copyWith(fontSize: 14))
.copyWith(
padding: const EdgeInsets.symmetric(horizontal: 12),
),
'a': Style(
color: context.accentColor,
textDecoration: TextDecoration.none,
),
},
data: description,
onLinkTap: (url, _, __, ___) {
if (url!.substring(0, 3) == '#t=') {
final seconds = _getTimeStamp(url);
if (playEpisode == episode) {
audio.seekTo(seconds! * 1000);
}
} else {
url.launchUrl;
}
},
),
);
},
);
} else {
return Container(
height: context.width,
alignment: Alignment.center,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image(
image: AssetImage('assets/shownote.png'),
height: 100.0,
),
Padding(padding: EdgeInsets.all(5.0)),
Text(s.noShownote,
textAlign: TextAlign.center,
style:
TextStyle(color: context.textColor.withOpacity(0.5))),
],
),
);
}
} else {
return Center();
}
},
);
}
int? _getTimeStamp(String url) {
final time = url.substring(3).trim();
final data = time.split(':');
int? seconds;
if (data.length == 3) {
seconds = int.tryParse(data[0])! * 3600 +
int.tryParse(data[1])! * 60 +
int.tryParse(data[2])!;
} else if (data.length == 2) {
seconds = int.tryParse(data[0])! * 60 + int.tryParse(data[1])!;
}
return seconds;
}
Future<String?> _getSDescription(String url) async {
final dbHelper = DBHelper();
String description;
description = (await dbHelper.getDescription(url))!
.replaceAll(RegExp(r'\s?<p>(<br>)?</p>\s?'), '')
.replaceAll('\r', '')
.trim();
if (!description.contains('<')) {
final linkList = linkify(description,
options: LinkifyOptions(humanize: false),
linkifiers: [UrlLinkifier(), EmailLinkifier()]);
for (var element in linkList) {
if (element is UrlElement) {
description = description.replaceAll(element.url!,
'<a rel="nofollow" href = ${element.url}>${element.text}</a>');
}
if (element is EmailElement) {
final address = element.emailAddress;
description = description.replaceAll(address,
'<a rel="nofollow" href = "mailto:$address">$address</a>');
}
}
await dbHelper.saveEpisodeDes(url, description: description);
}
return description;
}
}

View File

@ -57,7 +57,8 @@ class _AboutAppState extends State<AboutApp> {
), ),
); );
Widget _translatorInfo(BuildContext context, {required String name, String? flag}) => Widget _translatorInfo(BuildContext context,
{required String name, String? flag}) =>
Container( Container(
height: 50.0, height: 50.0,
padding: EdgeInsets.symmetric(horizontal: 20.0), padding: EdgeInsets.symmetric(horizontal: 20.0),
@ -114,7 +115,7 @@ class _AboutAppState extends State<AboutApp> {
); );
} }
final s = context.s!; final s = context.s;
return AnnotatedRegion<SystemUiOverlayStyle>( return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle( value: SystemUiOverlayStyle(
statusBarIconBrightness: Theme.of(context).accentColorBrightness, statusBarIconBrightness: Theme.of(context).accentColorBrightness,
@ -208,7 +209,10 @@ class _AboutAppState extends State<AboutApp> {
children: <Widget>[ children: <Widget>[
_listItem(context, 'Twitter @tsacdop', _listItem(context, 'Twitter @tsacdop',
LineIcons.twitter, 'https://twitter.com/tsacdop'), LineIcons.twitter, 'https://twitter.com/tsacdop'),
_listItem(context, 'GitHub', LineIcons.alternateGithub, _listItem(
context,
'GitHub',
LineIcons.alternateGithub,
'https://github.com/stonega/tsacdop'), 'https://github.com/stonega/tsacdop'),
_listItem(context, 'Telegram', LineIcons.telegram, _listItem(context, 'Telegram', LineIcons.telegram,
'https://t.me/joinchat/Bk3LkRpTHy40QYC78PK7Qg'), 'https://t.me/joinchat/Bk3LkRpTHy40QYC78PK7Qg'),
@ -281,7 +285,7 @@ class _AboutAppState extends State<AboutApp> {
name: 'Bruno Pinheiro', flag: 'pt'), name: 'Bruno Pinheiro', flag: 'pt'),
_translatorInfo(context, _translatorInfo(context,
name: 'Edoardo Maria Elidoro', flag: 'it'), name: 'Edoardo Maria Elidoro', flag: 'it'),
_translatorInfo(context, _translatorInfo(context,
name: 'Murat T. Akyuz', flag: 'tr'), name: 'Murat T. Akyuz', flag: 'tr'),
], ],
), ),

View File

@ -11,6 +11,7 @@ import 'package:google_fonts/google_fonts.dart';
import 'package:line_icons/line_icons.dart'; import 'package:line_icons/line_icons.dart';
import 'package:marquee/marquee.dart'; import 'package:marquee/marquee.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:tsacdop/episodes/shownote.dart';
import 'package:tuple/tuple.dart'; import 'package:tuple/tuple.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:flutter_cache_manager/flutter_cache_manager.dart';
@ -115,12 +116,12 @@ class PlayerWidget extends StatelessWidget {
const TextStyle(color: Color(0xFFFF0000))) const TextStyle(color: Color(0xFFFF0000)))
: data.item1 : data.item1
? Text( ? Text(
s!.buffering, s.buffering,
style: style:
TextStyle(color: context.accentColor), TextStyle(color: context.accentColor),
) )
: Text( : Text(
s!.timeLeft((data.item2).toInt().toTime), s.timeLeft((data.item2).toInt().toTime),
maxLines: 2, maxLines: 2,
), ),
); );
@ -281,26 +282,27 @@ class LastPosition extends StatelessWidget {
children: [ children: [
Selector<AudioPlayerNotifier, bool?>( Selector<AudioPlayerNotifier, bool?>(
selector: (_, audio) => audio.skipSilence, selector: (_, audio) => audio.skipSilence,
builder: (_, data, __) => FlatButton( builder: (_, data, __) => TextButton(
child: Row( child: Row(
children: [ children: [
Icon(Icons.flash_on, size: 18), Icon(Icons.flash_on, size: 18),
SizedBox(width: 5), SizedBox(width: 5),
Text(s!.skipSilence), Text(s.skipSilence),
], ],
), ),
color: data! ? context.accentColor : Colors.transparent, style: TextButton.styleFrom(
padding: EdgeInsets.symmetric(horizontal: 10), primary:
shape: RoundedRectangleBorder( data! ? context.accentColor : Colors.transparent,
borderRadius: BorderRadius.circular(100.0), shape: RoundedRectangleBorder(
side: BorderSide( borderRadius: BorderRadius.circular(100.0),
color: data side: BorderSide(
? context.accentColor color: data
: Theme.of(context) ? context.accentColor
.colorScheme : Theme.of(context)
.onSurface .colorScheme
.withOpacity(0.12))), .onSurface
textColor: data ? Colors.white : null, .withOpacity(0.12))),
),
onPressed: () => onPressed: () =>
audio.setSkipSilence(skipSilence: !data))), audio.setSkipSilence(skipSilence: !data))),
SizedBox(width: 10), SizedBox(width: 10),
@ -311,7 +313,7 @@ class LastPosition extends StatelessWidget {
children: [ children: [
Icon(Icons.volume_up, size: 18), Icon(Icons.volume_up, size: 18),
SizedBox(width: 5), SizedBox(width: 5),
Text(s!.boostVolume), Text(s.boostVolume),
], ],
), ),
color: data! ? context.accentColor : Colors.transparent, color: data! ? context.accentColor : Colors.transparent,
@ -499,8 +501,8 @@ class _PlaylistWidgetState extends State<PlaylistWidget> {
children: <Widget>[ children: <Widget>[
Text( Text(
data.item1!.name == 'Queue' data.item1!.name == 'Queue'
? context.s!.queue ? context.s.queue
: '${context.s!.homeMenuPlaylist}${'-${data.item1!.name}'}', : '${context.s.homeMenuPlaylist}${'-${data.item1!.name}'}',
overflow: TextOverflow.fade, overflow: TextOverflow.fade,
style: TextStyle( style: TextStyle(
color: context.accentColor, color: context.accentColor,
@ -756,7 +758,7 @@ class SleepModeState extends State<SleepMode>
width: 120, width: 120,
child: Center( child: Center(
child: Text( child: Text(
s!.endOfEpisode, s.endOfEpisode,
style: TextStyle( style: TextStyle(
color: (move > 0 color: (move > 0
? Colors.white ? Colors.white
@ -826,7 +828,7 @@ class SleepModeState extends State<SleepMode>
padding: EdgeInsets.symmetric(horizontal: 20.0), padding: EdgeInsets.symmetric(horizontal: 20.0),
child: Row( child: Row(
children: [ children: [
Text(context.s!.sleepTimer, Text(context.s.sleepTimer,
style: TextStyle( style: TextStyle(
color: context.accentColor, color: context.accentColor,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
@ -935,10 +937,9 @@ class _ChaptersWidgetState extends State<ChaptersWidget> {
padding: EdgeInsets.symmetric(horizontal: 0), padding: EdgeInsets.symmetric(horizontal: 0),
child: OutlinedButton( child: OutlinedButton(
style: OutlinedButton.styleFrom( style: OutlinedButton.styleFrom(
shape: RoundedRectangleBorder(
shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(100.0),
borderRadius: BorderRadius.circular(100.0), side: BorderSide(color: context.accentColor)),
side: BorderSide(color: context.accentColor)),
), ),
// highlightedBorderColor: Colors.green[700], // highlightedBorderColor: Colors.green[700],
onPressed: () { onPressed: () {
@ -1088,7 +1089,7 @@ class _ChaptersWidgetState extends State<ChaptersWidget> {
child: Row( child: Row(
children: <Widget>[ children: <Widget>[
Text( Text(
context.s!.homeToprightMenuAbout, context.s.homeToprightMenuAbout,
overflow: TextOverflow.fade, overflow: TextOverflow.fade,
style: TextStyle( style: TextStyle(
color: context.accentColor, color: context.accentColor,
@ -1332,7 +1333,7 @@ class _ControlPanelState extends State<ControlPanel>
.buffering || .buffering ||
data.audioState == data.audioState ==
AudioProcessingState.loading AudioProcessingState.loading
? context.s!.buffering ? context.s.buffering
: '', : '',
style: TextStyle( style: TextStyle(
color: context.accentColor), color: context.accentColor),

View File

@ -64,7 +64,7 @@ class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
_controller = TabController(length: 3, vsync: this); _controller = TabController(length: 3, vsync: this);
// FeatureDiscovery.hasPreviouslyCompleted(context, addFeature).then((value) { // FeatureDiscovery.hasPreviouslyCompleted(context, addFeature).then((value) {
// if (!value) { // if (!value) {
SchedulerBinding.instance!.addPostFrameCallback((_) { SchedulerBinding.instance.addPostFrameCallback((_) {
FeatureDiscovery.discoverFeatures( FeatureDiscovery.discoverFeatures(
context, context,
const <String>{ const <String>{
@ -90,15 +90,15 @@ class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
double top = 0; double top = 0;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var height = (context.width - 20) / 3 + 140; final height = (context.width - 20) / 3 + 140;
var settings = Provider.of<SettingState>(context, listen: false); final settings = Provider.of<SettingState>(context, listen: false);
final s = context.s; final s = context.s;
return AnnotatedRegion<SystemUiOverlayStyle>( return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle( value: SystemUiOverlayStyle(
systemNavigationBarIconBrightness: systemNavigationBarIconBrightness: context.brightness,
context.brightness,
statusBarIconBrightness: context.iconBrightness, statusBarIconBrightness: context.iconBrightness,
systemNavigationBarColor: context.primaryColor, systemNavigationBarColor: context.onPrimary,
statusBarColor: context.onPrimary,
), ),
child: WillPopScope( child: WillPopScope(
onWillPop: () async { onWillPop: () async {
@ -115,12 +115,13 @@ class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
}, },
child: Scaffold( child: Scaffold(
key: _scaffoldKey, key: _scaffoldKey,
backgroundColor: context.onPrimary,
body: Stack( body: Stack(
children: <Widget>[ children: <Widget>[
SafeArea( SafeArea(
bottom: false, bottom: false,
child: ScrollConfiguration( child: StretchingOverscrollIndicator(
behavior: NoGrowBehavior(), axisDirection: AxisDirection.down,
child: NestedScrollView( child: NestedScrollView(
innerScrollPositionKeyBuilder: () { innerScrollPositionKeyBuilder: () {
return Key('tab${_controller!.index}'); return Key('tab${_controller!.index}');
@ -141,7 +142,7 @@ class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
context, context,
featureId: addFeature, featureId: addFeature,
tapTarget: Icon(Icons.add_circle_outline), tapTarget: Icon(Icons.add_circle_outline),
title: s!.featureDiscoverySearch, title: s.featureDiscoverySearch,
backgroundColor: Colors.cyan[600], backgroundColor: Colors.cyan[600],
buttonColor: Colors.cyan[500], buttonColor: Colors.cyan[500],
description: s.featureDiscoverySearchDes, description: s.featureDiscoverySearchDes,
@ -240,14 +241,13 @@ class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
), ),
), ),
Selector<AudioPlayerNotifier, bool>( Selector<AudioPlayerNotifier, bool>(
selector: (_, audio) => selector: (_, audio) => audio.playerRunning,
audio.playerRunning, builder: (_, data, __) {
builder: (_, data, __) { return Padding(
return Padding( padding: EdgeInsets.only(bottom: data ? 60.0 : 0),
padding: );
EdgeInsets.only(bottom: data ? 60.0 : 0), },
); ),
}),
], ],
), ),
), ),
@ -274,9 +274,9 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
@override @override
Widget build( Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) { BuildContext context, double shrinkOffset, bool overlapsContent) {
final s = context.s!; final s = context.s;
return Container( return Container(
color: context.scaffoldBackgroundColor, color: context.background,
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
@ -335,7 +335,7 @@ class __PlaylistButtonState extends State<_PlaylistButton> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final s = context.s!; final s = context.s;
return Material( return Material(
color: Colors.transparent, color: Colors.transparent,
borderRadius: BorderRadius.circular(100), borderRadius: BorderRadius.circular(100),
@ -406,19 +406,23 @@ class __PlaylistButtonState extends State<_PlaylistButton> {
Padding( Padding(
padding: EdgeInsets.symmetric(vertical: 2), padding: EdgeInsets.symmetric(vertical: 2),
), ),
Container( SizedBox(
height: 70, height: 70,
width: 140, width: 140,
child: Column( child: Column(
children: <Widget>[ children: <Widget>[
Text( Text(
(data.item3 ~/ 1000).toTime, (data.item3 ~/ 1000).toTime,
style:
TextStyle(color: context.textColor),
), ),
Text( Text(
data.item2!.title!, data.item2!.title!,
maxLines: 2, maxLines: 2,
textAlign: TextAlign.center, textAlign: TextAlign.center,
overflow: TextOverflow.fade, overflow: TextOverflow.fade,
style:
TextStyle(color: context.textColor),
// style: TextStyle(color: Colors.white), // style: TextStyle(color: Colors.white),
), ),
], ],
@ -443,7 +447,10 @@ class __PlaylistButtonState extends State<_PlaylistButton> {
Padding( Padding(
padding: EdgeInsets.symmetric(horizontal: 5.0), padding: EdgeInsets.symmetric(horizontal: 5.0),
), ),
Text(s.homeMenuPlaylist), Text(
s.homeMenuPlaylist,
style: TextStyle(color: context.textColor),
),
], ],
), ),
), ),
@ -475,7 +482,7 @@ class __PlaylistButtonState extends State<_PlaylistButton> {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => PlaylistHome(), builder: (_) => PlaylistHome(),
), ),
); );
} }
@ -530,7 +537,7 @@ class _RecentUpdateState extends State<_RecentUpdate>
refreshWorker.start(_group); refreshWorker.start(_group);
await Future.delayed(Duration(seconds: 1)); await Future.delayed(Duration(seconds: 1));
Fluttertoast.showToast( Fluttertoast.showToast(
msg: context.s!.refreshStarted, msg: context.s.refreshStarted,
gravity: ToastGravity.BOTTOM, gravity: ToastGravity.BOTTOM,
); );
} }
@ -593,14 +600,14 @@ class _RecentUpdateState extends State<_RecentUpdate>
builder: (context, groupList, child) => PopupMenuButton<String>( builder: (context, groupList, child) => PopupMenuButton<String>(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
elevation: 1, elevation: 1,
tooltip: context.s!.groupFilter, tooltip: context.s.groupFilter,
child: Container( child: Container(
padding: EdgeInsets.symmetric(horizontal: 20), padding: EdgeInsets.symmetric(horizontal: 20),
height: 50, height: 50,
child: Row( child: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: <Widget>[ children: <Widget>[
Text(_groupName == 'All' ? context.s!.all : _groupName!), Text(_groupName == 'All' ? context.s.all : _groupName!),
Padding( Padding(
padding: EdgeInsets.symmetric(horizontal: 5), padding: EdgeInsets.symmetric(horizontal: 5),
), ),
@ -613,7 +620,7 @@ class _RecentUpdateState extends State<_RecentUpdate>
itemBuilder: (context) => [ itemBuilder: (context) => [
PopupMenuItem( PopupMenuItem(
child: Row(children: [ child: Row(children: [
Text(context.s!.all), Text(context.s.all),
Spacer(), Spacer(),
if (_groupName == 'All') DotIndicator() if (_groupName == 'All') DotIndicator()
]), ]),
@ -654,56 +661,57 @@ class _RecentUpdateState extends State<_RecentUpdate>
// final audio = context.read<AudioPlayerNotifier>(); // final audio = context.read<AudioPlayerNotifier>();
final s = context.s; final s = context.s;
return FutureBuilder<int>( return FutureBuilder<int>(
future: _getUpdateCounts(_group!), future: _getUpdateCounts(_group!),
initialData: 0, initialData: 0,
builder: (context, snapshot) { builder: (context, snapshot) {
return snapshot.data != 0 return snapshot.data != 0
? Material( ? Material(
color: Colors.transparent, color: Colors.transparent,
child: Row( child: Row(
children: [ children: [
IconButton( IconButton(
tooltip: s!.removeNewMark, tooltip: s.removeNewMark,
icon: SizedBox( icon: SizedBox(
height: 20, height: 20,
width: 20, width: 20,
child: CustomPaint( child: CustomPaint(
painter: RemoveNewFlagPainter( painter: RemoveNewFlagPainter(
context.textTheme.bodyText1!.color, context.textTheme.bodyText1!.color,
Colors.red))), Colors.red))),
onPressed: () async { onPressed: () async {
_removeNewMark(_group!); _removeNewMark(_group!);
if (mounted) { if (mounted) {
setState(() {}); setState(() {});
} }
}), }),
// IconButton( // IconButton(
// tooltip: s.addNewEpisodeTooltip, // tooltip: s.addNewEpisodeTooltip,
// icon: SizedBox( // icon: SizedBox(
// height: 15, // height: 15,
// width: 20, // width: 20,
// child: CustomPaint( // child: CustomPaint(
// painter: AddToPlaylistPainter( // painter: AddToPlaylistPainter(
// context.textTheme.bodyText1.color, // context.textTheme.bodyText1.color,
// Colors.red))), // Colors.red))),
// onPressed: () async { // onPressed: () async {
// await audio.addNewEpisode(_group); // await audio.addNewEpisode(_group);
// if (mounted) { // if (mounted) {
// setState(() {}); // setState(() {});
// } // }
// Fluttertoast.showToast( // Fluttertoast.showToast(
// msg: _groupName == 'All' // msg: _groupName == 'All'
// ? s.addNewEpisodeAll(snapshot.data) // ? s.addNewEpisodeAll(snapshot.data)
// : s.addEpisodeGroup( // : s.addEpisodeGroup(
// _groupName, snapshot.data), // _groupName, snapshot.data),
// gravity: ToastGravity.BOTTOM, // gravity: ToastGravity.BOTTOM,
// ); // );
// }), // }),
], ],
), ),
) )
: Center(); : Center();
}); },
);
} }
@override @override
@ -729,7 +737,7 @@ class _RecentUpdateState extends State<_RecentUpdate>
Padding( Padding(
padding: EdgeInsets.symmetric(vertical: 10)), padding: EdgeInsets.symmetric(vertical: 10)),
Text( Text(
s!.noEpisodeRecent, s.noEpisodeRecent,
style: TextStyle(color: Colors.grey[500]), style: TextStyle(color: Colors.grey[500]),
) )
], ],
@ -806,14 +814,14 @@ class _RecentUpdateState extends State<_RecentUpdate>
Material( Material(
color: Colors.transparent, color: Colors.transparent,
child: IconButton( child: IconButton(
tooltip: context.s!.refresh, tooltip: context.s.refresh,
icon: Icon( icon: Icon(
LineIcons.alternateRedo, LineIcons.alternateRedo,
size: 16), size: 16),
onPressed: () { onPressed: () {
_updateRssItem(); _updateRssItem();
Fluttertoast.showToast( Fluttertoast.showToast(
msg: s!.refreshStarted, msg: s.refreshStarted,
gravity: gravity:
ToastGravity.BOTTOM, ToastGravity.BOTTOM,
); );
@ -823,7 +831,7 @@ class _RecentUpdateState extends State<_RecentUpdate>
Material( Material(
color: Colors.transparent, color: Colors.transparent,
child: IconButton( child: IconButton(
tooltip: s!.hideListenedSetting, tooltip: s.hideListenedSetting,
icon: SizedBox( icon: SizedBox(
width: 30, width: 30,
height: 15, height: 15,
@ -973,7 +981,7 @@ class _MyFavoriteState extends State<_MyFavorite>
Padding( Padding(
padding: EdgeInsets.symmetric(vertical: 10)), padding: EdgeInsets.symmetric(vertical: 10)),
Text( Text(
s!.noEpisodeFavorite, s.noEpisodeFavorite,
style: TextStyle(color: Colors.grey[500]), style: TextStyle(color: Colors.grey[500]),
) )
], ],
@ -1042,7 +1050,7 @@ class _MyFavoriteState extends State<_MyFavorite>
BorderRadius.all( BorderRadius.all(
Radius.circular(10))), Radius.circular(10))),
elevation: 1, elevation: 1,
tooltip: s!.homeSubMenuSortBy, tooltip: s.homeSubMenuSortBy,
child: Container( child: Container(
height: 50, height: 50,
padding: EdgeInsets.symmetric( padding: EdgeInsets.symmetric(
@ -1228,7 +1236,7 @@ class _MyDownloadState extends State<_MyDownload>
children: <Widget>[ children: <Widget>[
Container( Container(
padding: EdgeInsets.symmetric(horizontal: 20), padding: EdgeInsets.symmetric(horizontal: 20),
child: Text(s!.downloaded)), child: Text(s.downloaded)),
Spacer(), Spacer(),
Material( Material(
color: Colors.transparent, color: Colors.transparent,

File diff suppressed because it is too large Load Diff

View File

@ -23,72 +23,10 @@ class PopupMenu extends StatefulWidget {
} }
class _PopupMenuState extends State<PopupMenu> { class _PopupMenuState extends State<PopupMenu> {
Future<String> _getRefreshDate(BuildContext context) async {
int? refreshDate;
var refreshstorage = KeyValueStorage('refreshdate');
var i = await refreshstorage.getInt();
if (i == 0) {
var refreshstorage = KeyValueStorage('refreshdate');
await refreshstorage.saveInt(DateTime.now().millisecondsSinceEpoch);
refreshDate = DateTime.now().millisecondsSinceEpoch;
} else {
refreshDate = i;
}
return refreshDate.toDate(context);
}
void _saveOmpl(String path) async {
var subscribeWorker = Provider.of<GroupList>(context, listen: false);
var rssExp = RegExp(r'^(https?):\/\/(.*)');
final s = context.s;
var file = File(path);
try {
final opml = file.readAsStringSync();
Map<String, List<OmplOutline>> data = PodcastsBackup.parseOPML(opml);
for (var entry in data.entries) {
var title = entry.key;
var list = entry.value.reversed;
for (var rss in list) {
var rssLink = rssExp.stringMatch(rss.xmlUrl!);
if (rssLink != null) {
var item = SubscribeItem(rssLink, rss.text, group: title);
await subscribeWorker.setSubscribeItem(item);
await Future.delayed(Duration(milliseconds: 200));
}
}
}
} catch (e) {
developer.log(e.toString(), name: 'OMPL parse error');
Fluttertoast.showToast(
msg: s!.toastFileError,
gravity: ToastGravity.TOP,
);
}
}
void _getFilePath() async {
final s = context.s;
try {
var filePickResult =
await FilePicker.platform.pickFiles(type: FileType.any);
if (filePickResult == null) {
return;
}
Fluttertoast.showToast(
msg: s!.toastReadFile,
gravity: ToastGravity.TOP,
);
final filePath = filePickResult.files.first.path!;
_saveOmpl(filePath);
} on PlatformException catch (e) {
developer.log(e.toString(), name: 'Get OMPL file');
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var refreshWorker = Provider.of<RefreshWorker>(context, listen: false); var refreshWorker = Provider.of<RefreshWorker>(context, listen: false);
final s = context.s!; final s = context.s;
return Material( return Material(
color: Colors.transparent, color: Colors.transparent,
borderRadius: BorderRadius.circular(100), borderRadius: BorderRadius.circular(100),
@ -98,10 +36,11 @@ class _PopupMenuState extends State<PopupMenu> {
width: 40, width: 40,
child: PopupMenuButton<int>( child: PopupMenuButton<int>(
icon: Icon(Icons.more_vert), icon: Icon(Icons.more_vert),
shape: RoundedRectangleBorder( shape:
borderRadius: BorderRadius.circular(10)), RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
elevation: 1, elevation: 1,
tooltip: s.menu, tooltip: s.menu,
color: context.priamryContainer,
itemBuilder: (context) => [ itemBuilder: (context) => [
PopupMenuItem( PopupMenuItem(
value: 1, value: 1,
@ -126,8 +65,8 @@ class _PopupMenuState extends State<PopupMenu> {
if (snapshot.hasData) { if (snapshot.hasData) {
return Text( return Text(
snapshot.data!, snapshot.data!,
style: style: TextStyle(
TextStyle(color: Colors.red, fontSize: 12), color: Colors.red, fontSize: 12),
); );
} else { } else {
return Center(); return Center();
@ -141,7 +80,7 @@ class _PopupMenuState extends State<PopupMenu> {
), ),
PopupMenuItem( PopupMenuItem(
value: 2, value: 2,
child: Container( child: Padding(
padding: EdgeInsets.only(left: 10), padding: EdgeInsets.only(left: 10),
child: Row( child: Row(
children: <Widget>[ children: <Widget>[
@ -204,4 +143,66 @@ class _PopupMenuState extends State<PopupMenu> {
), ),
); );
} }
Future<String> _getRefreshDate(BuildContext context) async {
int? refreshDate;
final refreshstorage = KeyValueStorage('refreshdate');
final i = await refreshstorage.getInt();
if (i == 0) {
final refreshstorage = KeyValueStorage('refreshdate');
await refreshstorage.saveInt(DateTime.now().millisecondsSinceEpoch);
refreshDate = DateTime.now().millisecondsSinceEpoch;
} else {
refreshDate = i;
}
return refreshDate.toDate(context);
}
void _saveOmpl(String path) async {
final subscribeWorker = Provider.of<GroupList>(context, listen: false);
final rssExp = RegExp(r'^(https?):\/\/(.*)');
final s = context.s;
final file = File(path);
try {
final opml = file.readAsStringSync();
Map<String, List<OmplOutline>> data = PodcastsBackup.parseOPML(opml);
for (final entry in data.entries) {
var title = entry.key;
var list = entry.value.reversed;
for (var rss in list) {
var rssLink = rssExp.stringMatch(rss.xmlUrl!);
if (rssLink != null) {
var item = SubscribeItem(rssLink, rss.text, group: title);
await subscribeWorker.setSubscribeItem(item);
await Future.delayed(Duration(milliseconds: 200));
}
}
}
} catch (e) {
developer.log(e.toString(), name: 'OMPL parse error');
Fluttertoast.showToast(
msg: s.toastFileError,
gravity: ToastGravity.TOP,
);
}
}
void _getFilePath() async {
final s = context.s;
try {
var filePickResult =
await FilePicker.platform.pickFiles(type: FileType.any);
if (filePickResult == null) {
return;
}
Fluttertoast.showToast(
msg: s.toastReadFile,
gravity: ToastGravity.TOP,
);
final filePath = filePickResult.files.first.path!;
_saveOmpl(filePath);
} on PlatformException catch (e) {
developer.log(e.toString(), name: 'Get OMPL file');
}
}
} }

View File

@ -14,28 +14,29 @@ class Import extends StatelessWidget {
return Container( return Container(
color: context.primaryColorDark, color: context.primaryColorDark,
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: <Widget>[ children: <Widget>[
SizedBox(height: 2.0, child: LinearProgressIndicator()), SizedBox(height: 2.0, child: LinearProgressIndicator()),
Container( Container(
padding: EdgeInsets.symmetric(horizontal: 20.0), padding: EdgeInsets.symmetric(horizontal: 20.0),
height: 20.0, height: 20.0,
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Text(text), child: Text(text),
), ),
]), ],
),
); );
} }
_autoDownloadNew(BuildContext context) async { _autoDownloadNew(BuildContext context) async {
final dbHelper = DBHelper(); final dbHelper = DBHelper();
var downloader = Provider.of<DownloadState>(context, listen: false); final downloader = Provider.of<DownloadState>(context, listen: false);
var result = await Connectivity().checkConnectivity(); final result = await Connectivity().checkConnectivity();
var autoDownloadStorage = KeyValueStorage(autoDownloadNetworkKey); final autoDownloadStorage = KeyValueStorage(autoDownloadNetworkKey);
var autoDownloadNetwork = await autoDownloadStorage.getInt(); final autoDownloadNetwork = await autoDownloadStorage.getInt();
if (autoDownloadNetwork == 1) { if (autoDownloadNetwork == 1) {
var episodes = await dbHelper.getNewEpisodes('all'); final episodes = await dbHelper.getNewEpisodes('all');
// For safety // For safety
if (episodes.length < 100 && episodes.length > 0) { if (episodes.length < 100 && episodes.length > 0) {
for (var episode in episodes) { for (var episode in episodes) {
@ -56,26 +57,27 @@ class Import extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final s = context.s; final s = context.s;
var groupList = Provider.of<GroupList>(context, listen: false); final groupList = Provider.of<GroupList>(context, listen: false);
return Column( return Column(
children: <Widget>[ children: <Widget>[
Consumer<GroupList>( Consumer<GroupList>(
builder: (_, subscribeWorker, __) { builder: (_, subscribeWorker, __) {
var item = subscribeWorker.currentSubscribeItem; final item = subscribeWorker.currentSubscribeItem;
switch (item.subscribeState) { switch (item.subscribeState) {
case SubscribeState.start: case SubscribeState.start:
return importColumn( return importColumn(
s!.notificationSubscribe(item.title!), context); s.notificationSubscribe(item.title!), context);
case SubscribeState.subscribe: case SubscribeState.subscribe:
return importColumn(s!.notificaitonFatch(item.title!), context); return importColumn(s.notificaitonFatch(item.title!), context);
case SubscribeState.fetch: case SubscribeState.fetch:
return importColumn(s!.notificationSuccess(item.title!), context); return importColumn(
s.notificationSuccess(item.title!), context);
case SubscribeState.exist: case SubscribeState.exist:
return importColumn( return importColumn(
s!.notificationSubscribeExisted(item.title!), context); s.notificationSubscribeExisted(item.title!), context);
case SubscribeState.error: case SubscribeState.error:
return importColumn( return importColumn(
s!.notificationNetworkError(item.title!), context); s.notificationNetworkError(item.title!), context);
default: default:
return Center(); return Center();
} }
@ -83,17 +85,17 @@ class Import extends StatelessWidget {
), ),
Consumer<RefreshWorker>( Consumer<RefreshWorker>(
builder: (context, refreshWorker, child) { builder: (context, refreshWorker, child) {
var item = refreshWorker.currentRefreshItem; final item = refreshWorker.currentRefreshItem;
if (refreshWorker.complete) { if (refreshWorker.complete) {
groupList.updateGroups(); groupList.updateGroups();
_autoDownloadNew(context); _autoDownloadNew(context);
} }
switch (item.refreshState) { switch (item.refreshState) {
case RefreshState.fetch: case RefreshState.fetch:
return importColumn(s!.notificationUpdate(item.title), context); return importColumn(s.notificationUpdate(item.title), context);
case RefreshState.error: case RefreshState.error:
return importColumn( return importColumn(
s!.notificationUpdateError(item.title), context); s.notificationUpdateError(item.title), context);
default: default:
return Center(); return Center();
} }

View File

@ -112,7 +112,7 @@ class DiscoveryPageState extends State<DiscoveryPage> {
// disabledTextColor: Colors.grey[500], // disabledTextColor: Colors.grey[500],
// disabledBorderColor: Colors.grey[500], // disabledBorderColor: Colors.grey[500],
), ),
child: Text(context.s!.subscribe), child: Text(context.s.subscribe),
onPressed: () {}), onPressed: () {}),
), ),
), ),
@ -158,7 +158,7 @@ class DiscoveryPageState extends State<DiscoveryPage> {
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
color: context.primaryColor, color: context.primaryColor,
border: border:
Border.all(color: context.textColor!.withOpacity(0.1), width: 1)), Border.all(color: context.textColor.withOpacity(0.1), width: 1)),
width: 120, width: 120,
margin: EdgeInsets.fromLTRB(10, 10, 0, 10), margin: EdgeInsets.fromLTRB(10, 10, 0, 10),
child: Material( child: Material(
@ -277,7 +277,7 @@ class DiscoveryPageState extends State<DiscoveryPage> {
padding: EdgeInsets.fromLTRB(50, 20, 50, 20), padding: EdgeInsets.fromLTRB(50, 20, 50, 20),
child: Center( child: Center(
child: Text( child: Text(
context.s!.searchHelper, context.s.searchHelper,
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: context.textTheme.headline6! style: context.textTheme.headline6!
.copyWith(color: Colors.grey[400]), .copyWith(color: Colors.grey[400]),
@ -477,7 +477,7 @@ class __TopPodcastListState extends State<_TopPodcastList> {
child: CircularProgressIndicator( child: CircularProgressIndicator(
strokeWidth: 2, strokeWidth: 2,
)) ))
: Text(context.s!.loadMore), : Text(context.s.loadMore),
onPressed: () => _loading onPressed: () => _loading
? null ? null
: setState( : setState(

View File

@ -52,7 +52,7 @@ class MyHomePageDelegate extends SearchDelegate<int?> {
Widget _invalidRss(BuildContext context) => Container( Widget _invalidRss(BuildContext context) => Container(
padding: EdgeInsets.only(top: 200), padding: EdgeInsets.only(top: 200),
alignment: Alignment.topCenter, alignment: Alignment.topCenter,
child: Text(context.s!.searchInvalidRss, child: Text(context.s.searchInvalidRss,
style: context.textTheme.headline6!.copyWith(color: Colors.red)), style: context.textTheme.headline6!.copyWith(color: Colors.red)),
); );
@ -86,7 +86,7 @@ class MyHomePageDelegate extends SearchDelegate<int?> {
return false; return false;
}, },
child: IconButton( child: IconButton(
tooltip: context.s!.back, tooltip: context.s.back,
splashRadius: 20, splashRadius: 20,
icon: Icon(_getIconData(Theme.of(context).platform)), icon: Icon(_getIconData(Theme.of(context).platform)),
onPressed: () { onPressed: () {
@ -111,7 +111,7 @@ class MyHomePageDelegate extends SearchDelegate<int?> {
return <Widget>[ return <Widget>[
if (query.isNotEmpty) if (query.isNotEmpty)
IconButton( IconButton(
tooltip: context.s!.clear, tooltip: context.s.clear,
splashRadius: 20, splashRadius: 20,
icon: const Icon(Icons.clear), icon: const Icon(Icons.clear),
onPressed: () { onPressed: () {
@ -216,7 +216,7 @@ class _RssResultState extends State<RssResult> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final s = context.s!; final s = context.s;
var items = widget.rssFeed!.items!; var items = widget.rssFeed!.items!;
return DefaultTabController( return DefaultTabController(
length: 2, length: 2,
@ -339,7 +339,7 @@ class _RssResultState extends State<RssResult> {
borderRadius: borderRadius:
BorderRadius.all(Radius.circular(100))), BorderRadius.all(Radius.circular(100))),
), ),
child: Text(context.s!.loadMore), child: Text(context.s.loadMore),
onPressed: () => setState( onPressed: () => setState(
() => _loadItems += 10, () => _loadItems += 10,
), ),
@ -560,7 +560,7 @@ class __ListenNotesSearchState extends State<_ListenNotesSearch> {
: CircularProgressIndicator( : CircularProgressIndicator(
strokeWidth: 2, strokeWidth: 2,
)) ))
: Text(context.s!.loadMore), : Text(context.s.loadMore),
onPressed: () => _loading onPressed: () => _loading
? null ? null
: setState( : setState(
@ -722,7 +722,7 @@ class __PodcastIndexSearchState extends State<_PodcastIndexSearch> {
child: CircularProgressIndicator( child: CircularProgressIndicator(
strokeWidth: 2, strokeWidth: 2,
)) ))
: Text(context.s!.loadMore), : Text(context.s.loadMore),
onPressed: () => _loading onPressed: () => _loading
? null ? null
: setState( : setState(
@ -1011,7 +1011,7 @@ class _SearchResultDetailState extends State<SearchResultDetail>
child: CircularProgressIndicator( child: CircularProgressIndicator(
strokeWidth: 2, strokeWidth: 2,
)) ))
: Text(context.s!.loadMore), : Text(context.s.loadMore),
onPressed: () { onPressed: () {
if (widget.searchEngine == if (widget.searchEngine ==
SearchEngine.listenNotes && SearchEngine.listenNotes &&
@ -1057,12 +1057,11 @@ class _SearchResultDetailState extends State<SearchResultDetail>
foregroundColor: MaterialStateProperty.all<Color>( foregroundColor: MaterialStateProperty.all<Color>(
context.accentColor), context.accentColor),
overlayColor: MaterialStateProperty.all<Color>( overlayColor: MaterialStateProperty.all<Color>(
context.scaffoldBackgroundColor context.background.withOpacity(0.3)),
.withOpacity(0.3)),
padding: padding:
MaterialStateProperty.all<EdgeInsetsGeometry>( MaterialStateProperty.all<EdgeInsetsGeometry>(
EdgeInsets.symmetric(horizontal: 2))), EdgeInsets.symmetric(horizontal: 2))),
child: Text(context.s!.play.toUpperCase()), child: Text(context.s.play.toUpperCase()),
onPressed: () { onPressed: () {
context.read<AudioPlayerNotifier>().episodeLoad( context.read<AudioPlayerNotifier>().episodeLoad(
content[index].toEpisode, content[index].toEpisode,
@ -1189,7 +1188,7 @@ class _SearchResultDetailState extends State<SearchResultDetail>
indicatorWeight: 3, indicatorWeight: 3,
indicatorSize: TabBarIndicatorSize.label, indicatorSize: TabBarIndicatorSize.label,
tabs: [ tabs: [
Text(s!.homeToprightMenuAbout), Text(s.homeToprightMenuAbout),
Row( Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
@ -1215,7 +1214,7 @@ class _SearchResultDetailState extends State<SearchResultDetail>
), ),
Expanded( Expanded(
child: Container( child: Container(
color: context.scaffoldBackgroundColor, color: context.background,
child: TabBarView(children: [ child: TabBarView(children: [
ListView( ListView(
physics: _animation.value != widget.maxHeight physics: _animation.value != widget.maxHeight
@ -1274,7 +1273,7 @@ class SubscribeButton extends StatelessWidget {
side: BorderSide(color: context.accentColor)), side: BorderSide(color: context.accentColor)),
onSurface: context.accentColor.withOpacity(0.5), onSurface: context.accentColor.withOpacity(0.5),
), ),
child: Text(s!.subscribe, child: Text(s.subscribe,
style: TextStyle(color: context.accentColor)), style: TextStyle(color: context.accentColor)),
onPressed: () { onPressed: () {
Fluttertoast.showToast( Fluttertoast.showToast(
@ -1299,7 +1298,7 @@ class SubscribeButton extends StatelessWidget {
// disabledTextColor: Colors.grey[500], // disabledTextColor: Colors.grey[500],
// disabledBorderColor: Colors.grey[500], // disabledBorderColor: Colors.grey[500],
), ),
child: Text(s!.subscribe), child: Text(s.subscribe),
onPressed: () {}), onPressed: () {}),
); );
}, },
@ -1327,7 +1326,7 @@ class PodcastSlideup extends StatelessWidget {
child: GestureDetector( child: GestureDetector(
onTap: searchState.clearSelect, onTap: searchState.clearSelect,
child: Container( child: Container(
color: context.scaffoldBackgroundColor.withOpacity(0.9), color: context.background.withOpacity(0.9),
), ),
), ),
), ),

View File

@ -142,7 +142,7 @@ class _SlideIntroState extends State<SlideIntro> {
height: 40, height: 40,
width: 80, width: 80,
child: Center( child: Center(
child: Text(context.s!.next, child: Text(context.s.next,
style: TextStyle( style: TextStyle(
color: Colors.black, color: Colors.black,
fontWeight: fontWeight:
@ -164,7 +164,7 @@ class _SlideIntroState extends State<SlideIntro> {
height: 40, height: 40,
width: 80, width: 80,
child: Center( child: Center(
child: Text(context.s!.done, child: Text(context.s.done,
style: TextStyle( style: TextStyle(
color: Colors.black, color: Colors.black,
fontWeight: fontWeight:

View File

@ -24,7 +24,7 @@ class _FourthPageState extends State<FourthPage> {
alignment: Alignment.center, alignment: Alignment.center,
padding: EdgeInsets.fromLTRB(40, context.paddingTop + 20, 40, 20), padding: EdgeInsets.fromLTRB(40, context.paddingTop + 20, 40, 20),
child: Text( child: Text(
context.s!.introFourthPage, context.s.introFourthPage,
style: TextStyle(fontSize: 30, color: Colors.white), style: TextStyle(fontSize: 30, color: Colors.white),
), ),
), ),

View File

@ -24,7 +24,7 @@ class _SecondPageState extends State<SecondPage> {
alignment: Alignment.center, alignment: Alignment.center,
padding: EdgeInsets.fromLTRB(40, context.paddingTop + 20, 40, 20), padding: EdgeInsets.fromLTRB(40, context.paddingTop + 20, 40, 20),
child: Text( child: Text(
context.s!.introSecondPage, context.s.introSecondPage,
style: TextStyle(fontSize: 30, color: Colors.white), style: TextStyle(fontSize: 30, color: Colors.white),
), ),
), ),

View File

@ -24,7 +24,7 @@ class _ThirdPageState extends State<ThirdPage> {
alignment: Alignment.center, alignment: Alignment.center,
padding: EdgeInsets.fromLTRB(40, context.paddingTop + 20, 40, 20), padding: EdgeInsets.fromLTRB(40, context.paddingTop + 20, 40, 20),
child: Text( child: Text(
context.s!.introThirdPage, context.s.introThirdPage,
style: TextStyle(fontSize: 30, color: Colors.white), style: TextStyle(fontSize: 30, color: Colors.white),
), ),
), ),

View File

@ -43,10 +43,7 @@ Future main() async {
child: MyApp(), child: MyApp(),
), ),
); );
var systemUiOverlayStyle = SystemUiOverlayStyle( await SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
statusBarColor: Colors.transparent,
systemNavigationBarColor: Colors.transparent);
SystemChrome.setSystemUIOverlayStyle(systemUiOverlayStyle);
await SystemChrome.setPreferredOrientations( await SystemChrome.setPreferredOrientations(
[DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]); [DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
} }
@ -80,7 +77,6 @@ class MyApp extends StatelessWidget {
), ),
); );
}, },
//child: FeatureDiscovery(child: Home()),
); );
} }
} }

View File

@ -60,7 +60,7 @@ class _PlaylistHomeState extends State<PlaylistHome> {
Color? color}) { Color? color}) {
return OutlinedButton.icon( return OutlinedButton.icon(
style: OutlinedButton.styleFrom( style: OutlinedButton.styleFrom(
side: BorderSide(color: context.scaffoldBackgroundColor), side: BorderSide(color: context.background),
primary: color, primary: color,
backgroundColor: backgroundColor:
isSelected ? context.primaryColorDark : Colors.transparent, isSelected ? context.primaryColorDark : Colors.transparent,
@ -73,7 +73,7 @@ class _PlaylistHomeState extends State<PlaylistHome> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final s = context.s!; final s = context.s;
return AnnotatedRegion<SystemUiOverlayStyle>( return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle( value: SystemUiOverlayStyle(
systemNavigationBarIconBrightness: systemNavigationBarIconBrightness:
@ -100,7 +100,7 @@ class _PlaylistHomeState extends State<PlaylistHome> {
return Text(data?.title ?? '', maxLines: 1); return Text(data?.title ?? '', maxLines: 1);
}, },
), ),
backgroundColor: context.scaffoldBackgroundColor, backgroundColor: context.background,
), ),
body: Column( body: Column(
children: [ children: [
@ -525,7 +525,7 @@ class __HistoryState extends State<_History> {
onPressed: () async { onPressed: () async {
audio.delFromPlaylist(episode!); audio.delFromPlaylist(episode!);
Fluttertoast.showToast( Fluttertoast.showToast(
msg: s!.toastRemovePlaylist, msg: s.toastRemovePlaylist,
gravity: ToastGravity.BOTTOM, gravity: ToastGravity.BOTTOM,
); );
}) })
@ -534,7 +534,7 @@ class __HistoryState extends State<_History> {
onPressed: () async { onPressed: () async {
audio.addToPlaylist(episode!); audio.addToPlaylist(episode!);
Fluttertoast.showToast( Fluttertoast.showToast(
msg: s!.toastAddPlaylist, msg: s.toastAddPlaylist,
gravity: ToastGravity.BOTTOM, gravity: ToastGravity.BOTTOM,
); );
}); });
@ -724,7 +724,7 @@ class __PlaylistsState extends State<_Playlists> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
s!.queue, s.queue,
style: context.textTheme.headline6, style: context.textTheme.headline6,
), ),
Text( Text(
@ -743,14 +743,13 @@ class __PlaylistsState extends State<_Playlists> {
children: <Widget>[ children: <Widget>[
Text(s.play.toUpperCase(), Text(s.play.toUpperCase(),
style: TextStyle( style: TextStyle(
color: color: context.accentColor,
Theme.of(context).accentColor,
fontSize: 15, fontSize: 15,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
)), )),
Icon( Icon(
Icons.play_arrow, Icons.play_arrow,
color: Theme.of(context).accentColor, color: context.accentColor,
), ),
], ],
), ),
@ -798,7 +797,7 @@ class __PlaylistsState extends State<_Playlists> {
), ),
title: Text(data[index].name!), title: Text(data[index].name!),
subtitle: Text( subtitle: Text(
'${data[index].length} ${s!.episode(data[index].length).toLowerCase()}'), '${data[index].length} ${s.episode(data[index].length).toLowerCase()}'),
trailing: TextButton( trailing: TextButton(
style: TextButton.styleFrom( style: TextButton.styleFrom(
primary: context.accentColor, primary: context.accentColor,
@ -808,13 +807,13 @@ class __PlaylistsState extends State<_Playlists> {
children: <Widget>[ children: <Widget>[
Text(s.play.toUpperCase(), Text(s.play.toUpperCase(),
style: TextStyle( style: TextStyle(
color: Theme.of(context).accentColor, color: context.accentColor,
fontSize: 15, fontSize: 15,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
)), )),
Icon( Icon(
Icons.play_arrow, Icons.play_arrow,
color: Theme.of(context).accentColor, color: context.accentColor,
), ),
], ],
), ),
@ -845,7 +844,7 @@ class __PlaylistsState extends State<_Playlists> {
color: context.primaryColorDark, color: context.primaryColorDark,
child: Center(child: Icon(Icons.add)), child: Center(child: Icon(Icons.add)),
), ),
title: Text(s!.createNewPlaylist), title: Text(s.createNewPlaylist),
); );
}), }),
); );
@ -994,7 +993,7 @@ class __NewPlaylistState extends State<_NewPlaylist> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final s = context.s!; final s = context.s;
return AnnotatedRegion<SystemUiOverlayStyle>( return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle( value: SystemUiOverlayStyle(
statusBarIconBrightness: Brightness.light, statusBarIconBrightness: Brightness.light,

View File

@ -36,16 +36,16 @@ class _PlaylistDetailState extends State<PlaylistDetail> {
leading: IconButton( leading: IconButton(
splashRadius: 20, splashRadius: 20,
icon: Icon(Icons.close), icon: Icon(Icons.close),
tooltip: context.s!.back, tooltip: context.s.back,
onPressed: () { onPressed: () {
Navigator.maybePop(context); Navigator.maybePop(context);
}, },
), ),
title: Text(_selectedEpisodes.isEmpty title: Text(_selectedEpisodes.isEmpty
? widget.playlist.isQueue ? widget.playlist.isQueue
? s!.queue ? s.queue
: widget.playlist.name! : widget.playlist.name!
: s!.selected(_selectedEpisodes.length)), : s.selected(_selectedEpisodes.length)),
actions: [ actions: [
if (_selectedEpisodes.isNotEmpty) if (_selectedEpisodes.isNotEmpty)
IconButton( IconButton(
@ -86,39 +86,40 @@ class _PlaylistDetailState extends State<PlaylistDetail> {
], ],
), ),
body: Selector<AudioPlayerNotifier, List<Playlist>>( body: Selector<AudioPlayerNotifier, List<Playlist>>(
selector: (_, audio) => audio.playlists, selector: (_, audio) => audio.playlists,
builder: (_, data, __) { builder: (_, data, __) {
final playlist = data.firstWhereOrNull( final playlist = data.firstWhereOrNull(
(e) => e == widget.playlist, (e) => e == widget.playlist,
); );
final episodes = playlist?.episodes ?? []; final episodes = playlist?.episodes ?? [];
return ReorderableListView( return ReorderableListView(
onReorder: (oldIndex, newIndex) { onReorder: (oldIndex, newIndex) {
if (widget.playlist.isQueue) { if (widget.playlist.isQueue) {
context context
.read<AudioPlayerNotifier>() .read<AudioPlayerNotifier>()
.reorderPlaylist(oldIndex, newIndex); .reorderPlaylist(oldIndex, newIndex);
setState(() {}); setState(() {});
} else { } else {
context context.read<AudioPlayerNotifier>().reorderEpisodesInPlaylist(
.read<AudioPlayerNotifier>() widget.playlist,
.reorderEpisodesInPlaylist(widget.playlist, oldIndex: oldIndex,
oldIndex: oldIndex, newIndex: newIndex); newIndex: newIndex);
setState(() {}); setState(() {});
} }
}, },
scrollDirection: Axis.vertical, scrollDirection: Axis.vertical,
children: episodes.map<Widget>((episode) { children: episodes.map<Widget>((episode) {
return _PlaylistItem(episode, return _PlaylistItem(episode,
key: ValueKey(episode!.enclosureUrl), onSelect: (episode) { key: ValueKey(episode!.enclosureUrl), onSelect: (episode) {
_selectedEpisodes.add(episode); _selectedEpisodes.add(episode);
setState(() {}); setState(() {});
}, onRemove: (episode) { }, onRemove: (episode) {
_selectedEpisodes.remove(episode); _selectedEpisodes.remove(episode);
setState(() {}); setState(() {});
}, reset: _resetSelected); }, reset: _resetSelected);
}).toList()); }).toList());
}), },
),
); );
} }
} }
@ -268,7 +269,7 @@ class __PlaylistItemState extends State<_PlaylistItem>
_episodeTag( _episodeTag(
episode.duration == 0 episode.duration == 0
? '' ? ''
: s!.minsCount(episode.duration! ~/ 60), : s.minsCount(episode.duration! ~/ 60),
Colors.cyan[300]), Colors.cyan[300]),
if (episode.enclosureLength != null) if (episode.enclosureLength != null)
_episodeTag( _episodeTag(
@ -326,7 +327,7 @@ class __PlaylistSettingState extends State<_PlaylistSetting> {
children: [ children: [
Icon(Icons.clear_all_outlined, size: 18), Icon(Icons.clear_all_outlined, size: 18),
SizedBox(width: 20), SizedBox(width: 20),
Text(s!.clearAll, style: textStyle), Text(s.clearAll, style: textStyle),
], ],
), ),
), ),
@ -337,12 +338,12 @@ class __PlaylistSettingState extends State<_PlaylistSetting> {
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [ children: [
FlatButton( TextButton(
onPressed: () => setState(() { onPressed: () => setState(() {
_clearConfirm = false; _clearConfirm = false;
}), }),
child: child:
Text(s!.cancel, style: TextStyle(color: Colors.grey[600])), Text(s.cancel, style: TextStyle(color: Colors.grey[600])),
), ),
FlatButton( FlatButton(
splashColor: Colors.red.withAlpha(70), splashColor: Colors.red.withAlpha(70),
@ -367,7 +368,7 @@ class __PlaylistSettingState extends State<_PlaylistSetting> {
children: [ children: [
Icon(Icons.delete, color: Colors.red, size: 18), Icon(Icons.delete, color: Colors.red, size: 18),
SizedBox(width: 20), SizedBox(width: 20),
Text(s!.remove, Text(s.remove,
style: textStyle!.copyWith( style: textStyle!.copyWith(
color: Colors.red, fontWeight: FontWeight.bold)), color: Colors.red, fontWeight: FontWeight.bold)),
], ],
@ -380,12 +381,12 @@ class __PlaylistSettingState extends State<_PlaylistSetting> {
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [ children: [
FlatButton( TextButton(
onPressed: () => setState(() { onPressed: () => setState(() {
_removeConfirm = false; _removeConfirm = false;
}), }),
child: child:
Text(s!.cancel, style: TextStyle(color: Colors.grey[600])), Text(s.cancel, style: TextStyle(color: Colors.grey[600])),
), ),
FlatButton( FlatButton(
splashColor: Colors.red.withAlpha(70), splashColor: Colors.red.withAlpha(70),
@ -406,9 +407,9 @@ class __PlaylistSettingState extends State<_PlaylistSetting> {
child: Row( child: Row(
children: [ children: [
Icon(Icons.info_outline, Icon(Icons.info_outline,
size: 16, color: context.textColor!.withAlpha(90)), size: 16, color: context.textColor.withAlpha(90)),
Text(s!.defaultQueueReminder, Text(s.defaultQueueReminder,
style: TextStyle(color: context.textColor!.withAlpha(90))), style: TextStyle(color: context.textColor.withAlpha(90))),
], ],
), ),
) )

View File

@ -56,7 +56,7 @@ class _CustomTabsState extends State<CustomTabView>
_currentPosition = widget.itemCount - 1; _currentPosition = widget.itemCount - 1;
_currentPosition = _currentPosition! < 0 ? 0 : _currentPosition; _currentPosition = _currentPosition! < 0 ? 0 : _currentPosition;
if (widget.onPositionChange is ValueChanged<int>) { if (widget.onPositionChange is ValueChanged<int>) {
WidgetsBinding.instance!.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) { if (mounted) {
widget.onPositionChange!(_currentPosition); widget.onPositionChange!(_currentPosition);
} }

View File

@ -117,7 +117,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
final result = await _dbHelper.updatePodcastRss(podcastLocal); final result = await _dbHelper.updatePodcastRss(podcastLocal);
if (result >= 0) { if (result >= 0) {
Fluttertoast.showToast( Fluttertoast.showToast(
msg: context.s!.updateEpisodesCount(result), msg: context.s.updateEpisodesCount(result),
gravity: ToastGravity.TOP, gravity: ToastGravity.TOP,
); );
} }
@ -148,7 +148,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
} }
} else if (result != 0) { } else if (result != 0) {
Fluttertoast.showToast( Fluttertoast.showToast(
msg: context.s!.updateFailed, msg: context.s.updateFailed,
gravity: ToastGravity.TOP, gravity: ToastGravity.TOP,
); );
} }
@ -359,7 +359,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
Container( Container(
padding: EdgeInsets.fromLTRB(15, 10, 15, 10), padding: EdgeInsets.fromLTRB(15, 10, 15, 10),
alignment: Alignment.topLeft, alignment: Alignment.topLeft,
color: context.scaffoldBackgroundColor, color: context.background,
child: AboutPodcast(podcastLocal: widget.podcastLocal), child: AboutPodcast(podcastLocal: widget.podcastLocal),
), ),
], ],
@ -389,7 +389,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
); );
Widget _actionBar(BuildContext context) { Widget _actionBar(BuildContext context) {
final s = context.s!; final s = context.s;
return SizedBox( return SizedBox(
height: 30, height: 30,
child: Row( child: Row(
@ -457,14 +457,16 @@ class _PodcastDetailState extends State<PodcastDetail> {
borderRadius: BorderRadius.circular(5), borderRadius: BorderRadius.circular(5),
border: Border.all( border: Border.all(
width: 2, width: 2,
color: context.textColor!.withOpacity(0.2))), color: context.textColor.withOpacity(0.2))),
child: _query == '' child: _query == ''
? Row( ? Row(
children: [ children: [
Text(s.search, Text(
style: TextStyle( s.search,
color: context.textColor! style: TextStyle(
.withOpacity(0.4))), color: context.textColor.withOpacity(0.4),
),
),
Spacer() Spacer()
], ],
) )
@ -668,7 +670,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final color = widget.podcastLocal!.primaryColor!.colorizedark(); final color = widget.podcastLocal!.primaryColor!.colorizedark();
final s = context.s!; final s = context.s;
return AnnotatedRegion<SystemUiOverlayStyle>( return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle( value: SystemUiOverlayStyle(
statusBarIconBrightness: Brightness.dark, statusBarIconBrightness: Brightness.dark,
@ -732,7 +734,6 @@ class _PodcastDetailState extends State<PodcastDetail> {
physics: const AlwaysScrollableScrollPhysics(), physics: const AlwaysScrollableScrollPhysics(),
slivers: <Widget>[ slivers: <Widget>[
SliverAppBar( SliverAppBar(
brightness: Brightness.dark,
actions: <Widget>[ actions: <Widget>[
IconButton( IconButton(
icon: Icon(Icons.more_vert), icon: Icon(Icons.more_vert),
@ -1138,7 +1139,7 @@ class _SearchEpisodeState extends State<SearchEpisode> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final s = context.s!; final s = context.s;
return AnnotatedRegion<SystemUiOverlayStyle>( return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle( value: SystemUiOverlayStyle(
statusBarIconBrightness: Brightness.light, statusBarIconBrightness: Brightness.light,

View File

@ -141,7 +141,10 @@ class __PodcastCardState extends State<_PodcastCard>
}); });
} }
Widget _buttonOnMenu({required Widget icon, VoidCallback? onTap, required String tooltip}) => Widget _buttonOnMenu(
{required Widget icon,
VoidCallback? onTap,
required String tooltip}) =>
Material( Material(
color: Colors.transparent, color: Colors.transparent,
child: InkWell( child: InkWell(
@ -166,7 +169,7 @@ class __PodcastCardState extends State<_PodcastCard>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final c = widget.podcastLocal!.backgroudColor(context); final c = widget.podcastLocal!.backgroudColor(context);
final s = context.s!; final s = context.s;
var groupList = context.watch<GroupList>(); var groupList = context.watch<GroupList>();
_belongGroups = groupList.getPodcastGroup(widget.podcastLocal!.id); _belongGroups = groupList.getPodcastGroup(widget.podcastLocal!.id);
return Column( return Column(
@ -243,7 +246,7 @@ class __PodcastCardState extends State<_PodcastCard>
: Container( : Container(
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: context.scaffoldBackgroundColor, color: context.background,
), ),
// border: Border( // border: Border(
// bottom: BorderSide( // bottom: BorderSide(
@ -365,7 +368,8 @@ class __PodcastCardState extends State<_PodcastCard>
tooltip: s.autoDownload, tooltip: s.autoDownload,
onTap: () async { onTap: () async {
await _setAutoDownload( await _setAutoDownload(
widget.podcastLocal!.id, !snapshot.data!); widget.podcastLocal!.id,
!snapshot.data!);
setState(() {}); setState(() {});
}, },
); );
@ -504,7 +508,7 @@ class _RenameGroupState extends State<RenameGroup> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
var groupList = Provider.of<GroupList>(context, listen: false); var groupList = Provider.of<GroupList>(context, listen: false);
List list = groupList.groups.map((e) => e!.name).toList(); List list = groupList.groups.map((e) => e!.name).toList();
final s = context.s!; final s = context.s;
return AnnotatedRegion<SystemUiOverlayStyle>( return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle( value: SystemUiOverlayStyle(
statusBarIconBrightness: Brightness.light, statusBarIconBrightness: Brightness.light,

View File

@ -1,7 +1,6 @@
import 'dart:math' as math; import 'dart:math' as math;
import 'package:feature_discovery/feature_discovery.dart'; import 'package:feature_discovery/feature_discovery.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:fluttertoast/fluttertoast.dart'; import 'package:fluttertoast/fluttertoast.dart';
@ -65,7 +64,7 @@ class _PodcastManageState extends State<PodcastManage>
_controller.stop(); _controller.stop();
} }
}); });
WidgetsBinding.instance!.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
FeatureDiscovery.discoverFeatures(context, FeatureDiscovery.discoverFeatures(context,
const <String>{addGroupFeature, configureGroup, configurePodcast}); const <String>{addGroupFeature, configureGroup, configurePodcast});
}); });
@ -91,7 +90,7 @@ class _PodcastManageState extends State<PodcastManage>
context, context,
featureId: configureGroup, featureId: configureGroup,
tapTarget: Icon(Icons.menu), tapTarget: Icon(Icons.menu),
title: s!.featureDiscoveryEditGroup, title: s.featureDiscoveryEditGroup,
backgroundColor: Colors.cyan[600], backgroundColor: Colors.cyan[600],
description: s.featureDiscoveryEditGroupDes, description: s.featureDiscoveryEditGroupDes,
buttonColor: Colors.cyan[500], buttonColor: Colors.cyan[500],
@ -113,9 +112,10 @@ class _PodcastManageState extends State<PodcastManage>
} }
} else { } else {
groupList.saveOrder(groupList.groups[_index!]); groupList.saveOrder(groupList.groups[_index!]);
groupList.drlFromOrderChanged(groupList.groups[_index!]!.name); groupList
.drlFromOrderChanged(groupList.groups[_index!]!.name);
Fluttertoast.showToast( Fluttertoast.showToast(
msg: context.s!.toastSettingSaved, msg: context.s.toastSettingSaved,
gravity: ToastGravity.BOTTOM, gravity: ToastGravity.BOTTOM,
); );
_controller.reverse(); _controller.reverse();
@ -125,9 +125,7 @@ class _PodcastManageState extends State<PodcastManage>
width: 50, width: 50,
height: 50, height: 50,
decoration: BoxDecoration( decoration: BoxDecoration(
color: _fraction! > 0.5 color: _fraction! > 0.5 ? Colors.red : context.accentColor,
? Colors.red
: Theme.of(context).accentColor,
shape: BoxShape.circle, shape: BoxShape.circle,
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
@ -155,18 +153,17 @@ class _PodcastManageState extends State<PodcastManage>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final s = context.s!; final s = context.s;
return AnnotatedRegion<SystemUiOverlayStyle>( return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle( value: SystemUiOverlayStyle(
statusBarIconBrightness: Theme.of(context).accentColorBrightness, statusBarIconBrightness: context.brightness,
systemNavigationBarColor: Theme.of(context).primaryColor, systemNavigationBarColor: context.primaryColor,
systemNavigationBarIconBrightness: systemNavigationBarIconBrightness: context.iconBrightness,
Theme.of(context).accentColorBrightness,
// statusBarColor: Theme.of(context).primaryColor, // statusBarColor: Theme.of(context).primaryColor,
), ),
child: Scaffold( child: Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(context.s!.groups(2)), title: Text(context.s.groups(2)),
leading: CustomBackButton(), leading: CustomBackButton(),
actions: <Widget>[ actions: <Widget>[
featureDiscoveryOverlay( featureDiscoveryOverlay(
@ -218,7 +215,7 @@ class _PodcastManageState extends State<PodcastManage>
: Stack( : Stack(
children: <Widget>[ children: <Widget>[
Container( Container(
color: context.scaffoldBackgroundColor, color: context.background,
child: CustomTabView( child: CustomTabView(
itemCount: _groups.length, itemCount: _groups.length,
tabBuilder: (context, index) => Tab( tabBuilder: (context, index) => Tab(
@ -265,10 +262,8 @@ class _PodcastManageState extends State<PodcastManage>
} }
}, },
child: Container( child: Container(
color: context.scaffoldBackgroundColor color: context.background.withOpacity(0.8 *
.withOpacity(0.8 * math.min(_menuController.value * 2, 1.0)),
math.min(
_menuController.value * 2, 1.0)),
), ),
), ),
), ),
@ -332,7 +327,7 @@ class _PodcastManageState extends State<PodcastManage>
padding: EdgeInsets.symmetric( padding: EdgeInsets.symmetric(
horizontal: 5.0), horizontal: 5.0),
), ),
Text(context.s!.editGroupName, Text(context.s.editGroupName,
style: TextStyle( style: TextStyle(
color: Colors.white)), color: Colors.white)),
], ],
@ -366,7 +361,7 @@ class _PodcastManageState extends State<PodcastManage>
Navigator.of(context) Navigator.of(context)
.pop(), .pop(),
child: Text( child: Text(
context.s!.cancel, context.s.cancel,
style: TextStyle( style: TextStyle(
color: color:
Colors.grey[600]), Colors.grey[600]),
@ -385,7 +380,8 @@ class _PodcastManageState extends State<PodcastManage>
_index = _index! - 1; _index = _index! - 1;
}); });
groupList.delGroup( groupList.delGroup(
_groups[_index! + 1]!); _groups[
_index! + 1]!);
} else { } else {
groupList.delGroup( groupList.delGroup(
_groups[_index!]!); _groups[_index!]!);
@ -393,7 +389,7 @@ class _PodcastManageState extends State<PodcastManage>
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
child: Text( child: Text(
context.s!.confirm, context.s.confirm,
style: TextStyle( style: TextStyle(
color: Colors.red), color: Colors.red),
), ),
@ -441,7 +437,7 @@ class _PodcastManageState extends State<PodcastManage>
class _OrderMenu extends StatelessWidget { class _OrderMenu extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final s = context.s!; final s = context.s;
return PopupMenuButton( return PopupMenuButton(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
elevation: 1, elevation: 1,
@ -494,7 +490,7 @@ class _AddGroupState extends State<AddGroup> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final s = context.s!; final s = context.s;
var groupList = Provider.of<GroupList>(context, listen: false); var groupList = Provider.of<GroupList>(context, listen: false);
List list = groupList.groups.map((e) => e!.name).toList(); List list = groupList.groups.map((e) => e!.name).toList();
return AnnotatedRegion<SystemUiOverlayStyle>( return AnnotatedRegion<SystemUiOverlayStyle>(
@ -530,8 +526,8 @@ class _AddGroupState extends State<AddGroup> {
Navigator.of(context).pop(); Navigator.of(context).pop();
} }
}, },
child: Text(s.confirm, child:
style: TextStyle(color: Theme.of(context).accentColor)), Text(s.confirm, style: TextStyle(color: context.accentColor)),
) )
], ],
title: SizedBox(width: context.width - 160, child: Text(s.newGroup)), title: SizedBox(width: context.width - 160, child: Text(s.newGroup)),

View File

@ -19,6 +19,7 @@ import '../widgets/custom_widget.dart';
import '../widgets/duraiton_picker.dart'; import '../widgets/duraiton_picker.dart';
enum MarkStatus { start, complete, none } enum MarkStatus { start, complete, none }
enum RefreshCoverStatus { start, complete, error, none } enum RefreshCoverStatus { start, complete, error, none }
class PodcastSetting extends StatefulWidget { class PodcastSetting extends StatefulWidget {
@ -200,7 +201,7 @@ class _PodcastSettingState extends State<PodcastSetting> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final s = context.s!; final s = context.s;
final groupList = context.watch<GroupList>(); final groupList = context.watch<GroupList>();
final textStyle = context.textTheme.bodyText2!; final textStyle = context.textTheme.bodyText2!;
return Column( return Column(
@ -233,8 +234,8 @@ class _PodcastSettingState extends State<PodcastSetting> {
), ),
trailing: Transform.scale( trailing: Transform.scale(
scale: 0.8, scale: 0.8,
child: child: Switch(
Switch(value: snapshot.data!, onChanged: _setAutoDownload), value: snapshot.data!, onChanged: _setAutoDownload),
), ),
); );
}), }),
@ -464,7 +465,7 @@ class _TimePicker extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final s = context.s!; final s = context.s;
return Container( return Container(
color: context.primaryColorDark, color: context.primaryColorDark,
child: Column( child: Column(

View File

@ -51,7 +51,7 @@ class _AboutPodcastState extends State<AboutPodcast> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var _groupList = Provider.of<GroupList>(context, listen: false); var _groupList = Provider.of<GroupList>(context, listen: false);
final s = context.s!; final s = context.s;
return AlertDialog( return AlertDialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.0)), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.0)),
titlePadding: EdgeInsets.only( titlePadding: EdgeInsets.only(
@ -116,7 +116,7 @@ class _PodcastListState extends State<PodcastList> {
), ),
child: Scaffold( child: Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(context.s!.podcast(2)), title: Text(context.s.podcast(2)),
leading: CustomBackButton(), leading: CustomBackButton(),
actions: [ actions: [
Selector<SettingState, bool?>( Selector<SettingState, bool?>(

View File

@ -79,7 +79,7 @@ class _DataBackupState extends State<DataBackup> {
} }
Future<void> _importSetting(String path, BuildContext context) async { Future<void> _importSetting(String path, BuildContext context) async {
final s = context.s!; final s = context.s;
var settings = context.read<SettingState>(); var settings = context.read<SettingState>();
var file = File(path); var file = File(path);
try { try {
@ -121,7 +121,7 @@ class _DataBackupState extends State<DataBackup> {
return; return;
} }
Fluttertoast.showToast( Fluttertoast.showToast(
msg: s!.toastReadFile, msg: s.toastReadFile,
gravity: ToastGravity.BOTTOM, gravity: ToastGravity.BOTTOM,
); );
final filePath = filePickResult.files.first.path!; final filePath = filePickResult.files.first.path!;
@ -178,7 +178,7 @@ class _DataBackupState extends State<DataBackup> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final s = context.s!; final s = context.s;
return AnnotatedRegion<SystemUiOverlayStyle>( return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle( value: SystemUiOverlayStyle(
statusBarIconBrightness: context.iconBrightness, statusBarIconBrightness: context.iconBrightness,
@ -634,14 +634,14 @@ class __LoginGpodderState extends State<_LoginGpodder> {
} else { } else {
if (mounted) setState(() => _loginStatus = LoginStatus.error); if (mounted) setState(() => _loginStatus = LoginStatus.error);
Fluttertoast.showToast( Fluttertoast.showToast(
msg: context.s!.loginFailed, msg: context.s.loginFailed,
gravity: ToastGravity.BOTTOM, gravity: ToastGravity.BOTTOM,
); );
} }
} else { } else {
if (mounted) setState(() => _loginStatus = LoginStatus.error); if (mounted) setState(() => _loginStatus = LoginStatus.error);
Fluttertoast.showToast( Fluttertoast.showToast(
msg: context.s!.loginFailed, msg: context.s.loginFailed,
gravity: ToastGravity.BOTTOM, gravity: ToastGravity.BOTTOM,
); );
} }
@ -680,11 +680,11 @@ class __LoginGpodderState extends State<_LoginGpodder> {
String? _validateName(String? value) { String? _validateName(String? value) {
if (value!.isEmpty) { if (value!.isEmpty) {
return context.s!.usernameRequired; return context.s.usernameRequired;
} }
final nameExp = RegExp(r'^[A-Za-z ]+$'); final nameExp = RegExp(r'^[A-Za-z ]+$');
if (!nameExp.hasMatch(value)) { if (!nameExp.hasMatch(value)) {
return context.s!.invalidName; return context.s.invalidName;
} }
return null; return null;
} }
@ -692,7 +692,7 @@ class __LoginGpodderState extends State<_LoginGpodder> {
String? _validatePassword(String? value) { String? _validatePassword(String? value) {
final passwordField = _passwordFieldKey.currentState!; final passwordField = _passwordFieldKey.currentState!;
if (passwordField.value == null || passwordField.value!.isEmpty) { if (passwordField.value == null || passwordField.value!.isEmpty) {
return context.s!.passwdRequired; return context.s.passwdRequired;
} }
return null; return null;
} }
@ -701,13 +701,13 @@ class __LoginGpodderState extends State<_LoginGpodder> {
switch (_loginStatus) { switch (_loginStatus) {
case LoginStatus.none: case LoginStatus.none:
return Text( return Text(
context.s!.login, context.s.login,
style: TextStyle(color: Colors.white), style: TextStyle(color: Colors.white),
); );
break; break;
case LoginStatus.syncing: case LoginStatus.syncing:
return Text( return Text(
context.s!.settingsSyncing, context.s.settingsSyncing,
style: TextStyle(color: Colors.white), style: TextStyle(color: Colors.white),
); );
break; break;
@ -722,7 +722,7 @@ class __LoginGpodderState extends State<_LoginGpodder> {
); );
default: default:
return Text( return Text(
context.s!.login, context.s.login,
style: TextStyle(color: Colors.white), style: TextStyle(color: Colors.white),
); );
break; break;
@ -731,7 +731,7 @@ class __LoginGpodderState extends State<_LoginGpodder> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final s = context.s!; final s = context.s;
return AnnotatedRegion<SystemUiOverlayStyle>( return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle( value: SystemUiOverlayStyle(
statusBarIconBrightness: Brightness.dark, statusBarIconBrightness: Brightness.dark,

View File

@ -84,9 +84,9 @@ class _DownloadsManageState extends State<DownloadsManage> {
var date = DateTime.fromMillisecondsSinceEpoch(downloadDate); var date = DateTime.fromMillisecondsSinceEpoch(downloadDate);
var diffrence = DateTime.now().toUtc().difference(date); var diffrence = DateTime.now().toUtc().difference(date);
if (diffrence.inHours < 24) { if (diffrence.inHours < 24) {
return s!.hoursAgo(diffrence.inHours); return s.hoursAgo(diffrence.inHours);
} else if (diffrence.inDays < 7) { } else if (diffrence.inDays < 7) {
return s!.daysAgo(diffrence.inDays); return s.daysAgo(diffrence.inDays);
} else { } else {
return DateFormat.yMMMd().format( return DateFormat.yMMMd().format(
DateTime.fromMillisecondsSinceEpoch(pubDate!, isUtc: true).toLocal()); DateTime.fromMillisecondsSinceEpoch(pubDate!, isUtc: true).toLocal());
@ -117,7 +117,7 @@ class _DownloadsManageState extends State<DownloadsManage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final s = context.s!; final s = context.s;
return AnnotatedRegion<SystemUiOverlayStyle>( return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle( value: SystemUiOverlayStyle(
statusBarIconBrightness: Theme.of(context).accentColorBrightness, statusBarIconBrightness: Theme.of(context).accentColorBrightness,
@ -219,11 +219,9 @@ class _DownloadsManageState extends State<DownloadsManage> {
), ),
Icon( Icon(
_mode == 0 _mode == 0
? LineIcons ? LineIcons.hourglassStart
.hourglassStart
: _mode == 1 : _mode == 1
? LineIcons ? LineIcons.hourglassHalf
.hourglassHalf
: LineIcons.save, : LineIcons.save,
size: 18, size: 18,
) )
@ -304,7 +302,8 @@ class _DownloadsManageState extends State<DownloadsManage> {
future: _isListened(_episodes[index]), future: _isListened(_episodes[index]),
initialData: 0, initialData: 0,
builder: (context, snapshot) { builder: (context, snapshot) {
return (_onlyListened! && snapshot.data == 0) return (_onlyListened! &&
snapshot.data == 0)
? Center() ? Center()
: Column( : Column(
children: <Widget>[ children: <Widget>[

View File

@ -76,7 +76,7 @@ class _PlayedHistoryState extends State<PlayedHistory>
Future recoverSub(BuildContext context, String url) async { Future recoverSub(BuildContext context, String url) async {
Fluttertoast.showToast( Fluttertoast.showToast(
msg: context.s!.toastPodcastRecovering, msg: context.s.toastPodcastRecovering,
gravity: ToastGravity.BOTTOM, gravity: ToastGravity.BOTTOM,
); );
var subscribeWorker = context.watch<GroupList>(); var subscribeWorker = context.watch<GroupList>();
@ -99,7 +99,7 @@ class _PlayedHistoryState extends State<PlayedHistory>
} catch (e) { } catch (e) {
developer.log(e.toString(), name: 'Recover podcast error'); developer.log(e.toString(), name: 'Recover podcast error');
Fluttertoast.showToast( Fluttertoast.showToast(
msg: context.s!.toastRecoverFailed, msg: context.s.toastRecoverFailed,
gravity: ToastGravity.BOTTOM, gravity: ToastGravity.BOTTOM,
); );
} }
@ -123,10 +123,9 @@ class _PlayedHistoryState extends State<PlayedHistory>
final s = context.s; final s = context.s;
return AnnotatedRegion<SystemUiOverlayStyle>( return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle( value: SystemUiOverlayStyle(
statusBarIconBrightness: Theme.of(context).accentColorBrightness, statusBarIconBrightness: context.brightness,
systemNavigationBarColor: Theme.of(context).primaryColor, systemNavigationBarColor: Theme.of(context).primaryColor,
systemNavigationBarIconBrightness: systemNavigationBarIconBrightness: context.iconBrightness,
Theme.of(context).accentColorBrightness,
), ),
child: Scaffold( child: Scaffold(
backgroundColor: context.primaryColor, backgroundColor: context.primaryColor,
@ -147,7 +146,7 @@ class _PlayedHistoryState extends State<PlayedHistory>
return FlexibleSpaceBar( return FlexibleSpaceBar(
title: top < 70 + MediaQuery.of(context).padding.top title: top < 70 + MediaQuery.of(context).padding.top
? Text( ? Text(
s!.settingsHistory, s.settingsHistory,
) )
: Center(), : Center(),
background: Padding( background: Padding(
@ -174,7 +173,7 @@ class _PlayedHistoryState extends State<PlayedHistory>
labelStyle: context.textTheme.headline6, labelStyle: context.textTheme.headline6,
tabs: <Widget>[ tabs: <Widget>[
Tab( Tab(
child: Text(s!.listen), child: Text(s.listen),
), ),
Tab( Tab(
child: Text(s.subscribe), child: Text(s.subscribe),
@ -220,7 +219,7 @@ class _PlayedHistoryState extends State<PlayedHistory>
return Container( return Container(
padding: padding:
const EdgeInsets.symmetric(vertical: 5), const EdgeInsets.symmetric(vertical: 5),
color: context.scaffoldBackgroundColor, color: context.background,
child: Column( child: Column(
children: <Widget>[ children: <Widget>[
ListTile( ListTile(
@ -234,9 +233,10 @@ class _PlayedHistoryState extends State<PlayedHistory>
DateFormat.yMd() DateFormat.yMd()
.add_jm() .add_jm()
.format(snapshot .format(snapshot
.data![index].playdate!), .data![index]
.playdate!),
style: TextStyle( style: TextStyle(
color: context.textColor! color: context.textColor
.withOpacity(0.8), .withOpacity(0.8),
fontSize: 15, fontSize: 15,
fontStyle: fontStyle:
@ -284,7 +284,7 @@ class _PlayedHistoryState extends State<PlayedHistory>
padding: EdgeInsets.all(2), padding: EdgeInsets.all(2),
child: Text( child: Text(
seconds == 0 && seekValue == 1 seconds == 0 && seekValue == 1
? s!.mark ? s.mark
: seconds!.toInt().toTime, : seconds!.toInt().toTime,
style: TextStyle( style: TextStyle(
color: Colors.white), color: Colors.white),
@ -318,7 +318,7 @@ class _PlayedHistoryState extends State<PlayedHistory>
itemBuilder: (context, index) { itemBuilder: (context, index) {
var _status = snapshot.data![index].status; var _status = snapshot.data![index].status;
return Container( return Container(
color: context.scaffoldBackgroundColor, color: context.background,
child: Column( child: Column(
children: <Widget>[ children: <Widget>[
ListTile( ListTile(
@ -333,7 +333,7 @@ class _PlayedHistoryState extends State<PlayedHistory>
DateFormat.yMd().add_jm().format( DateFormat.yMd().add_jm().format(
snapshot.data![index].subDate), snapshot.data![index].subDate),
style: TextStyle( style: TextStyle(
color: context.textColor! color: context.textColor
.withOpacity(0.8), .withOpacity(0.8),
fontSize: 15, fontSize: 15,
fontStyle: FontStyle.italic), fontStyle: FontStyle.italic),
@ -342,12 +342,12 @@ class _PlayedHistoryState extends State<PlayedHistory>
], ],
), ),
subtitle: _status subtitle: _status
? Text(s!.daysAgo(DateTime.now() ? Text(s.daysAgo(DateTime.now()
.difference( .difference(
snapshot.data![index].subDate) snapshot.data![index].subDate)
.inDays)) .inDays))
: Text( : Text(
s!.removedAt(DateFormat.yMd() s.removedAt(DateFormat.yMd()
.add_jm() .add_jm()
.format(snapshot .format(snapshot
.data![index].delDate)), .data![index].delDate)),
@ -362,7 +362,8 @@ class _PlayedHistoryState extends State<PlayedHistory>
.alternativeTrashRestore), .alternativeTrashRestore),
onPressed: () => recoverSub( onPressed: () => recoverSub(
context, context,
snapshot.data![index].rssUrl!), snapshot
.data![index].rssUrl!),
), ),
) )
: null, : null,
@ -476,11 +477,11 @@ class HistoryChart extends StatelessWidget {
lineTouchData: LineTouchData( lineTouchData: LineTouchData(
enabled: true, enabled: true,
touchTooltipData: LineTouchTooltipData( touchTooltipData: LineTouchTooltipData(
tooltipBgColor: context.scaffoldBackgroundColor, tooltipBgColor: context.background,
fitInsideHorizontally: true, fitInsideHorizontally: true,
getTooltipItems: (touchedBarSpots) { getTooltipItems: (touchedBarSpots) {
return touchedBarSpots.map((barSpot) { return touchedBarSpots.map((barSpot) {
return LineTooltipItem(context.s!.minsCount(barSpot.y.toInt()), return LineTooltipItem(context.s.minsCount(barSpot.y.toInt()),
context.textTheme.subtitle1!); context.textTheme.subtitle1!);
}).toList(); }).toList();
}, },

View File

@ -41,7 +41,7 @@ class _LanguagesSettingState extends State<LanguagesSetting> {
} }
} else { } else {
await localeStorage await localeStorage
.saveStringList([locale!.languageCode, locale.countryCode??'']); .saveStringList([locale!.languageCode, locale.countryCode ?? '']);
await S.load(locale); await S.load(locale);
if (mounted) { if (mounted) {
setState(() {}); setState(() {});
@ -66,7 +66,7 @@ class _LanguagesSettingState extends State<LanguagesSetting> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final textStyle = context.textTheme.bodyText2!; final textStyle = context.textTheme.bodyText2!;
final s = context.s!; final s = context.s;
return Column( return Column(
children: [ children: [
ListTile( ListTile(

View File

@ -65,11 +65,11 @@ class _LayoutSettingState extends State<LayoutSetting> {
final s = context.s; final s = context.s;
switch (mode) { switch (mode) {
case PlayerHeight.short: case PlayerHeight.short:
return s!.playerHeightShort; return s.playerHeightShort;
case PlayerHeight.mid: case PlayerHeight.mid:
return s!.playerHeightMed; return s.playerHeightMed;
case PlayerHeight.tall: case PlayerHeight.tall:
return s!.playerHeightTall; return s.playerHeightTall;
default: default:
return ''; return '';
} }
@ -184,7 +184,7 @@ class _LayoutSettingState extends State<LayoutSetting> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final s = context.s!; final s = context.s;
var audio = Provider.of<AudioPlayerNotifier>(context, listen: false); var audio = Provider.of<AudioPlayerNotifier>(context, listen: false);
return AnnotatedRegion<SystemUiOverlayStyle>( return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle( value: SystemUiOverlayStyle(

View File

@ -26,7 +26,7 @@ class Libries extends StatelessWidget {
), ),
child: Scaffold( child: Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(context.s!.settingsLibraries), title: Text(context.s.settingsLibraries),
leading: CustomBackButton(), leading: CustomBackButton(),
elevation: 0, elevation: 0,
backgroundColor: Theme.of(context).primaryColor, backgroundColor: Theme.of(context).primaryColor,
@ -68,7 +68,7 @@ class Libries extends StatelessWidget {
height: 30.0, height: 30.0,
padding: EdgeInsets.symmetric(horizontal: 70), padding: EdgeInsets.symmetric(horizontal: 70),
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Text(context.s!.fonts, child: Text(context.s.fonts,
style: Theme.of(context) style: Theme.of(context)
.textTheme .textTheme
.bodyText1! .bodyText1!
@ -90,7 +90,7 @@ class Libries extends StatelessWidget {
height: 30.0, height: 30.0,
padding: EdgeInsets.symmetric(horizontal: 70), padding: EdgeInsets.symmetric(horizontal: 70),
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Text(context.s!.plugins, child: Text(context.s.plugins,
style: Theme.of(context) style: Theme.of(context)
.textTheme .textTheme
.bodyText1! .bodyText1!

View File

@ -1,5 +1,3 @@
import 'dart:ui';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:fluttertoast/fluttertoast.dart'; import 'package:fluttertoast/fluttertoast.dart';
@ -43,11 +41,11 @@ class _PlaySettingState extends State<PlaySetting> {
String _volumeEffect(BuildContext context, int? i) { String _volumeEffect(BuildContext context, int? i) {
final s = context.s; final s = context.s;
if (i == 2000) { if (i == 2000) {
return s!.playerHeightShort; return s.playerHeightShort;
} else if (i == 3000) { } else if (i == 3000) {
return s!.playerHeightMed; return s.playerHeightMed;
} }
return s!.playerHeightTall; return s.playerHeightTall;
} }
Future<bool> _getMarkListenedSkip() async { Future<bool> _getMarkListenedSkip() async {
@ -89,7 +87,7 @@ class _PlaySettingState extends State<PlaySetting> {
topLeft: Radius.circular(5)), topLeft: Radius.circular(5)),
), ),
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Text(context.s!.endOfEpisode, child: Text(context.s.endOfEpisode,
style: TextStyle( style: TextStyle(
color: data.item1 == 0 ? Colors.white : null)), color: data.item1 == 0 ? Colors.white : null)),
), ),
@ -113,7 +111,7 @@ class _PlaySettingState extends State<PlaySetting> {
topRight: Radius.circular(5)), topRight: Radius.circular(5)),
), ),
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Text(context.s!.minsCount(data.item2!), child: Text(context.s.minsCount(data.item2!),
style: TextStyle( style: TextStyle(
color: data.item1 == 1 ? Colors.white : null)), color: data.item1 == 1 ? Colors.white : null)),
), ),
@ -142,7 +140,7 @@ class _PlaySettingState extends State<PlaySetting> {
var startTime = data.item1!; var startTime = data.item1!;
final timeOfDay = await showCustomTimePicker( final timeOfDay = await showCustomTimePicker(
context: context, context: context,
cancelText: s!.cancel, cancelText: s.cancel,
confirmText: s.confirm, confirmText: s.confirm,
helpText: '', helpText: '',
initialTime: TimeOfDay( initialTime: TimeOfDay(
@ -171,7 +169,7 @@ class _PlaySettingState extends State<PlaySetting> {
topLeft: Radius.circular(5)), topLeft: Radius.circular(5)),
), ),
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Text(s!.from(data.item1!.toTime)), child: Text(s.from(data.item1!.toTime)),
), ),
), ),
), ),
@ -224,13 +222,12 @@ class _PlaySettingState extends State<PlaySetting> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
var settings = context.watch<SettingState>(); var settings = context.watch<SettingState>();
var audio = context.watch<AudioPlayerNotifier>(); var audio = context.watch<AudioPlayerNotifier>();
final s = context.s!; final s = context.s;
return AnnotatedRegion<SystemUiOverlayStyle>( return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle( value: SystemUiOverlayStyle(
statusBarIconBrightness: Theme.of(context).accentColorBrightness, statusBarIconBrightness: context.brightness,
systemNavigationBarColor: Theme.of(context).primaryColor, systemNavigationBarColor: context.primaryColor,
systemNavigationBarIconBrightness: systemNavigationBarIconBrightness: context.iconBrightness,
Theme.of(context).accentColorBrightness,
), ),
child: Scaffold( child: Scaffold(
appBar: AppBar( appBar: AppBar(
@ -264,11 +261,13 @@ class _PlaySettingState extends State<PlaySetting> {
height: 30.0, height: 30.0,
padding: EdgeInsets.symmetric(horizontal: 70), padding: EdgeInsets.symmetric(horizontal: 70),
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Text(s.homeMenuPlaylist, child: Text(
style: Theme.of(context) s.homeMenuPlaylist,
.textTheme style: Theme.of(context)
.bodyText1! .textTheme
.copyWith(color: Theme.of(context).accentColor)), .bodyText1!
.copyWith(color: context.accentColor),
),
), ),
Selector<SettingState, bool?>( Selector<SettingState, bool?>(
selector: (_, settings) => settings.autoPlay, selector: (_, settings) => settings.autoPlay,
@ -298,7 +297,8 @@ class _PlaySettingState extends State<PlaySetting> {
trailing: Transform.scale( trailing: Transform.scale(
scale: 0.9, scale: 0.9,
child: Switch( child: Switch(
value: snapshot.data!, onChanged: _saveMarkListenedSkip), value: snapshot.data!,
onChanged: _saveMarkListenedSkip),
), ),
), ),
), ),
@ -350,7 +350,8 @@ class _PlaySettingState extends State<PlaySetting> {
displayItemCount: 5, displayItemCount: 5,
isDense: true, isDense: true,
value: data, value: data,
onChanged: (dynamic value) => settings.setRewindSeconds = value, onChanged: (dynamic value) =>
settings.setRewindSeconds = value,
items: kSecondsToSelect.map<DropdownMenuItem<int>>((e) { items: kSecondsToSelect.map<DropdownMenuItem<int>>((e) {
return DropdownMenuItem<int>( return DropdownMenuItem<int>(
value: e, child: Text(s.secCount(e))); value: e, child: Text(s.secCount(e)));
@ -387,9 +388,11 @@ class _PlaySettingState extends State<PlaySetting> {
height: 30.0, height: 30.0,
padding: EdgeInsets.symmetric(horizontal: 70), padding: EdgeInsets.symmetric(horizontal: 70),
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Text(s.sleepTimer, child: Text(
style: context.textTheme.bodyText1! s.sleepTimer,
.copyWith(color: Theme.of(context).accentColor)), style: context.textTheme.bodyText1!
.copyWith(color: context.accentColor),
),
), ),
ListView( ListView(
physics: const BouncingScrollPhysics(), physics: const BouncingScrollPhysics(),
@ -492,7 +495,7 @@ class __NotificationLayoutState extends State<_NotificationLayout> {
SizedBox(height: 8), SizedBox(height: 8),
Text(des, Text(des,
style: TextStyle( style: TextStyle(
fontSize: 12, color: context.textColor!.withOpacity(0.5)), fontSize: 12, color: context.textColor.withOpacity(0.5)),
textAlign: TextAlign.center, textAlign: TextAlign.center,
maxLines: 2, maxLines: 2,
overflow: TextOverflow.clip), overflow: TextOverflow.clip),
@ -525,7 +528,7 @@ class __NotificationLayoutState extends State<_NotificationLayout> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
_notificationIcon( _notificationIcon(
Icon(Icons.pause_circle_filled), '${s!.play}| ${s.pause}'), Icon(Icons.pause_circle_filled), '${s.play}| ${s.pause}'),
_notificationIcon(Icon(Icons.fast_forward), s.fastForward), _notificationIcon(Icon(Icons.fast_forward), s.fastForward),
_notificationIcon(Icon(Icons.skip_next), s.skipToNext), _notificationIcon(Icon(Icons.skip_next), s.skipToNext),
_notificationIcon(Icon(Icons.close), s.stop), _notificationIcon(Icon(Icons.close), s.stop),
@ -537,7 +540,7 @@ class __NotificationLayoutState extends State<_NotificationLayout> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
_notificationIcon(Icon(Icons.pause_circle_filled), _notificationIcon(Icon(Icons.pause_circle_filled),
'${s!.play}| ${s.pause}'), '${s.play}| ${s.pause}'),
_notificationIcon( _notificationIcon(
Icon(Icons.fast_rewind), s.fastRewind), Icon(Icons.fast_rewind), s.fastRewind),
_notificationIcon(Icon(Icons.skip_next), s.skipToNext), _notificationIcon(Icon(Icons.skip_next), s.skipToNext),
@ -547,7 +550,7 @@ class __NotificationLayoutState extends State<_NotificationLayout> {
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
_notificationIcon(Icon(Icons.fast_rewind), s!.fastRewind), _notificationIcon(Icon(Icons.fast_rewind), s.fastRewind),
_notificationIcon(Icon(Icons.pause_circle_filled), _notificationIcon(Icon(Icons.pause_circle_filled),
'${s.play}| ${s.pause}'), '${s.play}| ${s.pause}'),
_notificationIcon( _notificationIcon(
@ -600,7 +603,7 @@ class __SpeedListState extends State<_SpeedList> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final s = context.s!; final s = context.s;
return ListTile( return ListTile(
contentPadding: contentPadding:
EdgeInsets.only(left: 70.0, right: 20, bottom: 10, top: 10), EdgeInsets.only(left: 70.0, right: 20, bottom: 10, top: 10),
@ -610,38 +613,42 @@ class __SpeedListState extends State<_SpeedList> {
children: [ children: [
Text(s.settingsSpeedsDes), Text(s.settingsSpeedsDes),
FutureBuilder<List<double>>( FutureBuilder<List<double>>(
future: _getSpeedList(), future: _getSpeedList(),
initialData: [], initialData: [],
builder: (context, snapshot) { builder: (context, snapshot) {
var speedSelected = snapshot.data; var speedSelected = snapshot.data;
return Wrap( return Wrap(
children: kSpeedToSelect children: kSpeedToSelect
.map((e) => Padding( .map(
padding: const EdgeInsets.only(right: 8.0), (e) => Padding(
child: FilterChip( padding: const EdgeInsets.only(right: 8.0),
key: ValueKey<String>(e.toString()), child: FilterChip(
label: Text('X ${e.toStringAsFixed(1)}'), key: ValueKey<String>(e.toString()),
selectedColor: context.accentColor, label: Text('X ${e.toStringAsFixed(1)}'),
labelStyle: TextStyle( selectedColor: context.accentColor,
color: snapshot.data!.contains(e) labelStyle: TextStyle(
? Colors.white color: snapshot.data!.contains(e)
: context.textColor), ? Colors.white
elevation: 0, : context.textColor),
showCheckmark: false, elevation: 0,
selected: snapshot.data!.contains(e), showCheckmark: false,
onSelected: (value) async { selected: snapshot.data!.contains(e),
if (!value) { onSelected: (value) async {
speedSelected!.remove(e); if (!value) {
} else { speedSelected!.remove(e);
speedSelected!.add(e); } else {
} speedSelected!.add(e);
await _saveSpeedList(speedSelected); }
setState(() {}); await _saveSpeedList(speedSelected);
}, setState(() {});
), },
)) ),
.toList()); ),
}), )
.toList(),
);
},
),
], ],
), ),
); );

View File

@ -134,7 +134,7 @@ class _PopupMenuSettingState extends State<PopupMenuSetting> {
height: 30.0, height: 30.0,
padding: EdgeInsets.symmetric(horizontal: 80), padding: EdgeInsets.symmetric(horizontal: 80),
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Text(s!.settingsPopupMenu, child: Text(s.settingsPopupMenu,
style: Theme.of(context) style: Theme.of(context)
.textTheme .textTheme
.bodyText1! .bodyText1!

View File

@ -68,7 +68,7 @@ class _SettingsState extends State<Settings> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final s = context.s!; final s = context.s;
return AnnotatedRegion<SystemUiOverlayStyle>( return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle( value: SystemUiOverlayStyle(
statusBarIconBrightness: Theme.of(context).accentColorBrightness, statusBarIconBrightness: Theme.of(context).accentColorBrightness,
@ -107,8 +107,7 @@ class _SettingsState extends State<Settings> {
onTap: () => Navigator.push(context, onTap: () => Navigator.push(context,
MaterialPageRoute(builder: (context) => ThemeSetting())), MaterialPageRoute(builder: (context) => ThemeSetting())),
contentPadding: EdgeInsets.symmetric(horizontal: 25.0), contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
leading: leading: Icon(LineIcons.adjust, color: context.accentColor),
Icon(LineIcons.adjust, color: context.accentColor),
title: Text(s.settingsAppearance), title: Text(s.settingsAppearance),
subtitle: Text(s.settingsAppearanceDes), subtitle: Text(s.settingsAppearanceDes),
), ),
@ -117,8 +116,7 @@ class _SettingsState extends State<Settings> {
onTap: () => Navigator.push(context, onTap: () => Navigator.push(context,
MaterialPageRoute(builder: (context) => LayoutSetting())), MaterialPageRoute(builder: (context) => LayoutSetting())),
contentPadding: EdgeInsets.symmetric(horizontal: 25.0), contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
leading: Icon(LineIcons.stopCircle, leading: Icon(LineIcons.stopCircle, color: Colors.blueAccent),
color: Colors.blueAccent),
title: Text(s.settingsLayout), title: Text(s.settingsLayout),
subtitle: Text(s.settingsLayoutDes), subtitle: Text(s.settingsLayoutDes),
), ),
@ -168,8 +166,7 @@ class _SettingsState extends State<Settings> {
title: s.settingsLanguages, child: LanguagesSetting()) title: s.settingsLanguages, child: LanguagesSetting())
.then((value) => setState(() {})), .then((value) => setState(() {})),
contentPadding: EdgeInsets.symmetric(horizontal: 25.0), contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
leading: Icon(LineIcons.language, leading: Icon(LineIcons.language, color: Colors.purpleAccent),
color: Colors.purpleAccent),
title: Text(s.settingsLanguages), title: Text(s.settingsLanguages),
subtitle: Text(s.settingsLanguagesDes), subtitle: Text(s.settingsLanguagesDes),
), ),
@ -181,8 +178,8 @@ class _SettingsState extends State<Settings> {
MaterialPageRoute(builder: (context) => DataBackup())); MaterialPageRoute(builder: (context) => DataBackup()));
}, },
contentPadding: EdgeInsets.symmetric(horizontal: 25.0), contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
leading: Icon(LineIcons.codeFile, leading:
color: Colors.lightGreen[700]), Icon(LineIcons.codeFile, color: Colors.lightGreen[700]),
title: Text(s.settingsBackup), title: Text(s.settingsBackup),
subtitle: Text(s.settingsBackupDes), subtitle: Text(s.settingsBackupDes),
), ),
@ -204,8 +201,7 @@ class _SettingsState extends State<Settings> {
onTap: () => Navigator.push(context, onTap: () => Navigator.push(context,
MaterialPageRoute(builder: (context) => Libries())), MaterialPageRoute(builder: (context) => Libries())),
contentPadding: EdgeInsets.symmetric(horizontal: 25.0), contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
leading: Icon(LineIcons.bookOpen, leading: Icon(LineIcons.bookOpen, color: Colors.purple[700]),
color: Colors.purple[700]),
title: Text(s.settingsLibraries), title: Text(s.settingsLibraries),
subtitle: Text(s.settingsLibrariesDes), subtitle: Text(s.settingsLibrariesDes),
), ),
@ -257,8 +253,7 @@ class _SettingsState extends State<Settings> {
); );
}, },
contentPadding: EdgeInsets.symmetric(horizontal: 25.0), contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
leading: leading: Icon(LineIcons.capsules, color: Colors.pinkAccent),
Icon(LineIcons.capsules, color: Colors.pinkAccent),
title: Text(s.settingsDiscovery), title: Text(s.settingsDiscovery),
), ),
Divider(height: 1), Divider(height: 1),
@ -269,8 +264,7 @@ class _SettingsState extends State<Settings> {
builder: (context) => builder: (context) =>
SlideIntro(goto: Goto.settings))), SlideIntro(goto: Goto.settings))),
contentPadding: EdgeInsets.symmetric(horizontal: 25.0), contentPadding: EdgeInsets.symmetric(horizontal: 25.0),
leading: leading: Icon(LineIcons.columns, color: Colors.blueGrey),
Icon(LineIcons.columns, color: Colors.blueGrey),
title: Text(s.settingsAppIntro), title: Text(s.settingsAppIntro),
), ),
Divider(height: 1), Divider(height: 1),

View File

@ -110,7 +110,7 @@ class _StorageSettingState extends State<StorageSetting>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final s = context.s!; final s = context.s;
var settings = Provider.of<SettingState>(context, listen: false); var settings = Provider.of<SettingState>(context, listen: false);
return AnnotatedRegion<SystemUiOverlayStyle>( return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle( value: SystemUiOverlayStyle(

View File

@ -11,7 +11,7 @@ import '../widgets/custom_widget.dart';
class SyncingSetting extends StatelessWidget { class SyncingSetting extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final s = context.s!; final s = context.s;
var settings = Provider.of<SettingState>(context, listen: false); var settings = Provider.of<SettingState>(context, listen: false);
return AnnotatedRegion<SystemUiOverlayStyle>( return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle( value: SystemUiOverlayStyle(

View File

@ -10,7 +10,7 @@ import '../widgets/general_dialog.dart';
class ThemeSetting extends StatelessWidget { class ThemeSetting extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final s = context.s!; final s = context.s;
var settings = Provider.of<SettingState>(context, listen: false); var settings = Provider.of<SettingState>(context, listen: false);
return AnnotatedRegion<SystemUiOverlayStyle>( return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle( value: SystemUiOverlayStyle(

View File

@ -65,9 +65,10 @@ final showNotesFontStyles = <TextStyle>[
height: 1.8, height: 1.8,
)), )),
GoogleFonts.bitter( GoogleFonts.bitter(
textStyle: TextStyle( textStyle: TextStyle(
height: 1.8, height: 1.8,
)), ),
),
]; ];
class SettingState extends ChangeNotifier { class SettingState extends ChangeNotifier {
@ -133,15 +134,13 @@ class SettingState extends ChangeNotifier {
/// Load locale. /// Load locale.
Locale? get locale => _locale; Locale? get locale => _locale;
/// Spp thememode. default auto. /// Set thememode. default auto.
ThemeMode? _theme; ThemeMode? _theme;
ThemeMode? get theme => _theme; ThemeMode? get theme => _theme;
ThemeData get lightTheme => ThemeData().copyWith( ThemeData get lightTheme => ThemeData.light().copyWith(
colorScheme: ThemeData() colorScheme: _colors(Brightness.light, _accentSetColor!),
.colorScheme brightness: Brightness.light,
.copyWith(brightness: Brightness.light, secondary: _accentSetColor),
brightness: Brightness.dark,
primaryColor: Colors.grey[100], primaryColor: Colors.grey[100],
primaryColorLight: Colors.white, primaryColorLight: Colors.white,
primaryColorDark: Colors.grey[300], primaryColorDark: Colors.grey[300],
@ -154,6 +153,10 @@ class SettingState extends ChangeNotifier {
iconTheme: IconThemeData(color: Colors.black), iconTheme: IconThemeData(color: Colors.black),
systemOverlayStyle: SystemUiOverlayStyle.dark), systemOverlayStyle: SystemUiOverlayStyle.dark),
textTheme: TextTheme( textTheme: TextTheme(
headlineSmall: TextStyle(
fontSize: 20.0,
color: Colors.black,
fontWeight: FontWeight.normal),
bodyLarge: TextStyle( bodyLarge: TextStyle(
fontSize: 17.0, fontSize: 17.0,
color: Colors.black, color: Colors.black,
@ -162,6 +165,34 @@ class SettingState extends ChangeNotifier {
fontSize: 15.0, fontSize: 15.0,
color: Colors.black, color: Colors.black,
fontWeight: FontWeight.normal), fontWeight: FontWeight.normal),
bodySmall: TextStyle(
fontSize: 14.0,
color: Colors.black,
fontWeight: FontWeight.normal),
labelLarge: TextStyle(
fontSize: 16.0,
color: Colors.black,
fontWeight: FontWeight.normal),
labelMedium: TextStyle(
fontSize: 14.0,
color: Colors.black,
fontWeight: FontWeight.normal),
labelSmall: TextStyle(
fontSize: 12.0,
color: Colors.black,
fontWeight: FontWeight.normal),
titleLarge: TextStyle(
fontSize: 16.0,
color: Colors.black,
fontWeight: FontWeight.normal),
titleMedium: TextStyle(
fontSize: 14.0,
color: Colors.black,
fontWeight: FontWeight.normal),
titleSmall: TextStyle(
fontSize: 12.0,
color: Colors.black,
fontWeight: FontWeight.normal),
), ),
tabBarTheme: TabBarTheme( tabBarTheme: TabBarTheme(
labelColor: Colors.black, labelColor: Colors.black,
@ -177,15 +208,18 @@ class SettingState extends ChangeNotifier {
hoverColor: _accentSetColor!.withAlpha(70), hoverColor: _accentSetColor!.withAlpha(70),
splashColor: _accentSetColor!.withAlpha(70), splashColor: _accentSetColor!.withAlpha(70),
), ),
useMaterial3: true,
); );
ThemeData get darkTheme => ThemeData.dark().copyWith( ThemeData get darkTheme => ThemeData.dark().copyWith(
colorScheme: ThemeData.dark() colorScheme: _colors(Brightness.dark, _accentSetColor!),
.colorScheme brightness: Brightness.dark,
.copyWith(brightness: Brightness.dark, secondary: _accentSetColor),
brightness: Brightness.light,
primaryColorDark: Colors.grey[800], primaryColorDark: Colors.grey[800],
textTheme: TextTheme( textTheme: TextTheme(
headlineSmall: TextStyle(
fontSize: 20.0,
color: Colors.white,
fontWeight: FontWeight.normal),
bodyLarge: TextStyle( bodyLarge: TextStyle(
fontSize: 17.0, fontSize: 17.0,
color: Colors.white, color: Colors.white,
@ -194,9 +228,31 @@ class SettingState extends ChangeNotifier {
fontSize: 15.0, fontSize: 15.0,
color: Colors.white, color: Colors.white,
fontWeight: FontWeight.normal), fontWeight: FontWeight.normal),
labelLarge: TextStyle(
fontSize: 16.0,
color: Colors.white,
fontWeight: FontWeight.normal),
labelMedium: TextStyle(
fontSize: 14.0,
color: Colors.white,
fontWeight: FontWeight.normal),
labelSmall: TextStyle(
fontSize: 12.0,
color: Colors.white,
fontWeight: FontWeight.normal),
titleLarge: TextStyle(
fontSize: 16.0,
color: Colors.white,
fontWeight: FontWeight.normal),
titleMedium: TextStyle(
fontSize: 14.0,
color: Colors.white,
fontWeight: FontWeight.normal),
titleSmall: TextStyle(
fontSize: 12.0,
color: Colors.white,
fontWeight: FontWeight.normal),
), ),
scaffoldBackgroundColor:
_realDark! ? Colors.black87 : Color(0XFF212121),
primaryColor: _realDark! ? Colors.black : Color(0XFF1B1B1B), primaryColor: _realDark! ? Colors.black : Color(0XFF1B1B1B),
popupMenuTheme: PopupMenuThemeData() popupMenuTheme: PopupMenuThemeData()
.copyWith(color: _realDark! ? Colors.grey[900] : null), .copyWith(color: _realDark! ? Colors.grey[900] : null),
@ -206,6 +262,7 @@ class SettingState extends ChangeNotifier {
systemOverlayStyle: SystemUiOverlayStyle.light), systemOverlayStyle: SystemUiOverlayStyle.light),
buttonTheme: ButtonThemeData(height: 32), buttonTheme: ButtonThemeData(height: 32),
dialogBackgroundColor: _realDark! ? Colors.grey[900] : null, dialogBackgroundColor: _realDark! ? Colors.grey[900] : null,
useMaterial3: true,
); );
set setTheme(ThemeMode? mode) { set setTheme(ThemeMode? mode) {
@ -214,6 +271,13 @@ class SettingState extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
ColorScheme _colors(Brightness brightness, Color targetColor) {
return ColorScheme.fromSeed(
seedColor: targetColor,
brightness: brightness,
);
}
void setWorkManager(int? hour) { void setWorkManager(int? hour) {
_updateInterval = hour; _updateInterval = hour;
notifyListeners(); notifyListeners();
@ -366,9 +430,9 @@ class SettingState extends ChangeNotifier {
_saveRewindSeconds(); _saveRewindSeconds();
} }
int? _showNotesFontIndex; late int _showNotesFontIndex;
int? get showNotesFontIndex => _showNotesFontIndex; int get showNotesFontIndex => _showNotesFontIndex;
TextStyle get showNoteFontStyle => showNotesFontStyles[_showNotesFontIndex!]; TextStyle get showNoteFontStyle => showNotesFontStyles[_showNotesFontIndex];
set setShowNoteFontStyle(int index) { set setShowNoteFontStyle(int index) {
_showNotesFontIndex = index; _showNotesFontIndex = index;
notifyListeners(); notifyListeners();
@ -538,7 +602,7 @@ class SettingState extends ChangeNotifier {
} }
Future<void> _saveShowNotesFonts() async { Future<void> _saveShowNotesFonts() async {
await _showNotesFontStorage.saveInt(_showNotesFontIndex!); await _showNotesFontStorage.saveInt(_showNotesFontIndex);
} }
Future<SettingsBackup> backup() async { Future<SettingsBackup> backup() async {

View File

@ -9,7 +9,7 @@ part of 'index_episode.dart';
IndexEpisodeResult<P> _$IndexEpisodeResultFromJson<P>( IndexEpisodeResult<P> _$IndexEpisodeResultFromJson<P>(
Map<String, dynamic> json) { Map<String, dynamic> json) {
return IndexEpisodeResult<P>( return IndexEpisodeResult<P>(
items: (json['items'] as List?)?.map(_ConvertP<P>().fromJson)?.toList(), items: (json['items'] as List?)?.map(_ConvertP<P>().fromJson).toList(),
status: json['status'] as String?, status: json['status'] as String?,
count: json['count'] as int?, count: json['count'] as int?,
); );
@ -18,7 +18,7 @@ IndexEpisodeResult<P> _$IndexEpisodeResultFromJson<P>(
Map<String, dynamic> _$IndexEpisodeResultToJson<P>( Map<String, dynamic> _$IndexEpisodeResultToJson<P>(
IndexEpisodeResult<P> instance) => IndexEpisodeResult<P> instance) =>
<String, dynamic>{ <String, dynamic>{
'items': instance.items?.map(_ConvertP<P>().toJson)?.toList(), 'items': instance.items?.map(_ConvertP<P>().toJson).toList(),
'status': instance.status, 'status': instance.status,
'count': instance.count, 'count': instance.count,
}; };

View File

@ -9,7 +9,7 @@ part of 'index_podcast.dart';
PodcastIndexSearchResult<P> _$PodcastIndexSearchResultFromJson<P>( PodcastIndexSearchResult<P> _$PodcastIndexSearchResultFromJson<P>(
Map<String, dynamic> json) { Map<String, dynamic> json) {
return PodcastIndexSearchResult<P>( return PodcastIndexSearchResult<P>(
feeds: (json['feeds'] as List?)?.map(_ConvertP<P>().fromJson)?.toList(), feeds: (json['feeds'] as List?)?.map(_ConvertP<P>().fromJson).toList(),
status: json['status'] as String?, status: json['status'] as String?,
count: json['count'] as int?, count: json['count'] as int?,
); );
@ -18,7 +18,7 @@ PodcastIndexSearchResult<P> _$PodcastIndexSearchResultFromJson<P>(
Map<String, dynamic> _$PodcastIndexSearchResultToJson<P>( Map<String, dynamic> _$PodcastIndexSearchResultToJson<P>(
PodcastIndexSearchResult<P> instance) => PodcastIndexSearchResult<P> instance) =>
<String, dynamic>{ <String, dynamic>{
'feeds': instance.feeds?.map(_ConvertP<P>().toJson)?.toList(), 'feeds': instance.feeds?.map(_ConvertP<P>().toJson).toList(),
'status': instance.status, 'status': instance.status,
'count': instance.count, 'count': instance.count,
}; };

View File

@ -10,14 +10,14 @@ ItunesSearchResult<P> _$ItunesSearchResultFromJson<P>(
Map<String, dynamic> json) { Map<String, dynamic> json) {
return ItunesSearchResult<P>( return ItunesSearchResult<P>(
resultCount: json['resultCount'] as int?, resultCount: json['resultCount'] as int?,
results: (json['results'] as List?)?.map(_ConvertP<P>().fromJson)?.toList(), results: (json['results'] as List?)?.map(_ConvertP<P>().fromJson).toList(),
); );
} }
Map<String, dynamic> _$ItunesSearchResultToJson<P>( Map<String, dynamic> _$ItunesSearchResultToJson<P>(
ItunesSearchResult<P> instance) => ItunesSearchResult<P> instance) =>
<String, dynamic>{ <String, dynamic>{
'results': instance.results?.map(_ConvertP<P>().toJson)?.toList(), 'results': instance.results?.map(_ConvertP<P>().toJson).toList(),
'resultCount': instance.resultCount, 'resultCount': instance.resultCount,
}; };

View File

@ -9,7 +9,7 @@ part of 'search_top_podcast.dart';
SearchTopPodcast<T> _$SearchTopPodcastFromJson<T>(Map<String, dynamic> json) { SearchTopPodcast<T> _$SearchTopPodcastFromJson<T>(Map<String, dynamic> json) {
return SearchTopPodcast<T>( return SearchTopPodcast<T>(
podcasts: podcasts:
(json['podcasts'] as List?)?.map(_ConvertT<T>().fromJson)?.toList(), (json['podcasts'] as List?)?.map(_ConvertT<T>().fromJson).toList(),
id: json['id'] as int?, id: json['id'] as int?,
total: json['total'] as int?, total: json['total'] as int?,
hasNext: json['has_next'] as bool?, hasNext: json['has_next'] as bool?,
@ -20,7 +20,7 @@ SearchTopPodcast<T> _$SearchTopPodcastFromJson<T>(Map<String, dynamic> json) {
Map<String, dynamic> _$SearchTopPodcastToJson<T>( Map<String, dynamic> _$SearchTopPodcastToJson<T>(
SearchTopPodcast<T> instance) => SearchTopPodcast<T> instance) =>
<String, dynamic>{ <String, dynamic>{
'podcasts': instance.podcasts?.map(_ConvertT<T>().toJson)?.toList(), 'podcasts': instance.podcasts?.map(_ConvertT<T>().toJson).toList(),
'id': instance.id, 'id': instance.id,
'page': instance.page, 'page': instance.page,
'total': instance.total, 'total': instance.total,

View File

@ -9,14 +9,14 @@ part of 'searchepisodes.dart';
SearchEpisodes<E> _$SearchEpisodesFromJson<E>(Map<String, dynamic> json) { SearchEpisodes<E> _$SearchEpisodesFromJson<E>(Map<String, dynamic> json) {
return SearchEpisodes<E>( return SearchEpisodes<E>(
episodes: episodes:
(json['episodes'] as List?)?.map(_ConvertE<E>().fromJson)?.toList(), (json['episodes'] as List?)?.map(_ConvertE<E>().fromJson).toList(),
nextEpisodeDate: json['next_episode_pub_date'] as int?, nextEpisodeDate: json['next_episode_pub_date'] as int?,
); );
} }
Map<String, dynamic> _$SearchEpisodesToJson<E>(SearchEpisodes<E> instance) => Map<String, dynamic> _$SearchEpisodesToJson<E>(SearchEpisodes<E> instance) =>
<String, dynamic>{ <String, dynamic>{
'episodes': instance.episodes?.map(_ConvertE<E>().toJson)?.toList(), 'episodes': instance.episodes?.map(_ConvertE<E>().toJson).toList(),
'next_episode_pub_date': instance.nextEpisodeDate, 'next_episode_pub_date': instance.nextEpisodeDate,
}; };

View File

@ -8,7 +8,7 @@ part of 'searchpodcast.dart';
SearchPodcast<P> _$SearchPodcastFromJson<P>(Map<String, dynamic> json) { SearchPodcast<P> _$SearchPodcastFromJson<P>(Map<String, dynamic> json) {
return SearchPodcast<P>( return SearchPodcast<P>(
results: (json['results'] as List?)?.map(_ConvertP<P>().fromJson)?.toList(), results: (json['results'] as List?)?.map(_ConvertP<P>().fromJson).toList(),
nextOffset: json['next_offset'] as int?, nextOffset: json['next_offset'] as int?,
total: json['total'] as int?, total: json['total'] as int?,
count: json['count'] as int?, count: json['count'] as int?,
@ -17,7 +17,7 @@ SearchPodcast<P> _$SearchPodcastFromJson<P>(Map<String, dynamic> json) {
Map<String, dynamic> _$SearchPodcastToJson<P>(SearchPodcast<P> instance) => Map<String, dynamic> _$SearchPodcastToJson<P>(SearchPodcast<P> instance) =>
<String, dynamic>{ <String, dynamic>{
'results': instance.results?.map(_ConvertP<P>().toJson)?.toList(), 'results': instance.results?.map(_ConvertP<P>().toJson).toList(),
'next_offset': instance.nextOffset, 'next_offset': instance.nextOffset,
'total': instance.total, 'total': instance.total,
'count': instance.count, 'count': instance.count,

View File

@ -3,39 +3,49 @@ import 'dart:developer' as developer;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart'; import 'package:fluttertoast/fluttertoast.dart';
import 'package:material_color_utilities/material_color_utilities.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher_string.dart';
import '../generated/l10n.dart'; import '../generated/l10n.dart';
extension ContextExtension on BuildContext { extension ContextExtension on BuildContext {
Color get primaryColor => Theme.of(this).primaryColor; ColorScheme get colorScheme => Theme.of(this).colorScheme;
Color get accentColor => Theme.of(this).colorScheme.secondary; Color get accentColor => Theme.of(this).colorScheme.primary;
Color get scaffoldBackgroundColor => Theme.of(this).scaffoldBackgroundColor; Color get primaryColor => Theme.of(this).colorScheme.onPrimary;
Color get priamryContainer => Theme.of(this).colorScheme.primaryContainer;
Color get onPrimary => Theme.of(this).colorScheme.onPrimary;
Color get background => Theme.of(this).colorScheme.background;
Color get tertiary => colorScheme.tertiary;
Color get onTertiary => colorScheme.onTertiary;
Color get secondary => colorScheme.secondary;
Color get onsecondary => colorScheme.onSecondary;
Color get error => colorScheme.error;
Color get primaryColorDark => Theme.of(this).primaryColorDark; Color get primaryColorDark => Theme.of(this).primaryColorDark;
Color get textColor => textTheme.bodyLarge!.color!; Color get textColor => textTheme.bodyLarge!.color!;
Color get dialogBackgroundColor => Theme.of(this).dialogBackgroundColor; Color get dialogBackgroundColor => Theme.of(this).dialogBackgroundColor;
Brightness get brightness => Theme.of(this).brightness; Brightness get brightness => Theme.of(this).brightness;
Brightness get iconBrightness => Theme.of(this).colorScheme.brightness; Brightness get iconBrightness =>
brightness == Brightness.dark ? Brightness.light : Brightness.dark;
double get width => MediaQuery.of(this).size.width; double get width => MediaQuery.of(this).size.width;
double get height => MediaQuery.of(this).size.height; double get height => MediaQuery.of(this).size.height;
double get paddingTop => MediaQuery.of(this).padding.top; double get paddingTop => MediaQuery.of(this).padding.top;
TextTheme get textTheme => Theme.of(this).textTheme; TextTheme get textTheme => Theme.of(this).textTheme;
S? get s => S.of(this); S get s => S.of(this)!;
} }
extension IntExtension on int { extension IntExtension on int {
String toDate(BuildContext context) { String toDate(BuildContext context) {
final s = context.s; final s = context.s;
var date = DateTime.fromMillisecondsSinceEpoch(this, isUtc: true); final date = DateTime.fromMillisecondsSinceEpoch(this, isUtc: true);
var difference = DateTime.now().toUtc().difference(date); final difference = DateTime.now().toUtc().difference(date);
if (difference.inMinutes < 30) { if (difference.inMinutes < 30) {
return s!.minsAgo(difference.inMinutes); return s.minsAgo(difference.inMinutes);
} else if (difference.inMinutes < 60) { } else if (difference.inMinutes < 60) {
return s!.hoursAgo(0); return s.hoursAgo(0);
} else if (difference.inHours < 24) { } else if (difference.inHours < 24) {
return s!.hoursAgo(difference.inHours); return s.hoursAgo(difference.inHours);
} else if (difference.inDays < 7) { } else if (difference.inDays < 7) {
return s!.daysAgo(difference.inDays); return s.daysAgo(difference.inDays);
} else { } else {
return DateFormat.yMMMd().format( return DateFormat.yMMMd().format(
DateTime.fromMillisecondsSinceEpoch(this, isUtc: true).toLocal()); DateTime.fromMillisecondsSinceEpoch(this, isUtc: true).toLocal());
@ -50,21 +60,21 @@ extension IntExtension on int {
final s = context.s; final s = context.s;
var interval = Duration(milliseconds: this); var interval = Duration(milliseconds: this);
if (interval.inHours <= 48) { if (interval.inHours <= 48) {
return s!.publishedDaily; return s.publishedDaily;
} else if (interval.inDays > 2 && interval.inDays <= 14) { } else if (interval.inDays > 2 && interval.inDays <= 14) {
return s!.publishedWeekly; return s.publishedWeekly;
} else if (interval.inDays > 14 && interval.inDays < 60) { } else if (interval.inDays > 14 && interval.inDays < 60) {
return s!.publishedMonthly; return s.publishedMonthly;
} else { } else {
return s!.publishedYearly; return s.publishedYearly;
} }
} }
} }
extension StringExtension on String { extension StringExtension on String {
Future get launchUrl async { Future get launchUrl async {
if (await canLaunch(this)) { if (await canLaunchUrlString(this)) {
await launch(this); await launchUrlString(this);
} else { } else {
developer.log('Could not launch $this'); developer.log('Could not launch $this');
Fluttertoast.showToast( Fluttertoast.showToast(

View File

@ -2,6 +2,7 @@ import 'dart:ui' as ui;
import 'dart:async'; import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
Future<ui.Image> getImageFromProvider(ImageProvider imageProvider) async { Future<ui.Image> getImageFromProvider(ImageProvider imageProvider) async {
final ImageStream stream = imageProvider.resolve( final ImageStream stream = imageProvider.resolve(
@ -17,3 +18,9 @@ Future<ui.Image> getImageFromProvider(ImageProvider imageProvider) async {
final image = await imageCompleter.future; final image = await imageCompleter.future;
return image; return image;
} }
String formateDate(int timeStamp) {
return DateFormat.yMMMd().format(
DateTime.fromMillisecondsSinceEpoch(timeStamp),
);
}

View File

@ -258,7 +258,6 @@ class _OpenContainerRoute extends ModalRoute<void> {
], ],
); );
} }
return null; // unreachable
} }
static _FlippableTweenSequence<double>? _getClosedOpacityTween( static _FlippableTweenSequence<double>? _getClosedOpacityTween(
@ -273,7 +272,6 @@ class _OpenContainerRoute extends ModalRoute<void> {
), ),
], ],
); );
break;
case ContainerTransitionType.fadeThrough: case ContainerTransitionType.fadeThrough:
return _FlippableTweenSequence<double>( return _FlippableTweenSequence<double>(
<TweenSequenceItem<double>>[ <TweenSequenceItem<double>>[
@ -287,9 +285,7 @@ class _OpenContainerRoute extends ModalRoute<void> {
), ),
], ],
); );
break;
} }
return null; // unreachable
} }
static _FlippableTweenSequence<double>? _getOpenOpacityTween( static _FlippableTweenSequence<double>? _getOpenOpacityTween(
@ -421,7 +417,7 @@ class _OpenContainerRoute extends ModalRoute<void> {
} }
if (delayForSourceRoute) { if (delayForSourceRoute) {
SchedulerBinding.instance! SchedulerBinding.instance
.addPostFrameCallback(takeMeasurementsInSourceRoute); .addPostFrameCallback(takeMeasurementsInSourceRoute);
} else { } else {
takeMeasurementsInSourceRoute(); takeMeasurementsInSourceRoute();
@ -460,6 +456,8 @@ class _OpenContainerRoute extends ModalRoute<void> {
case AnimationStatus.reverse: case AnimationStatus.reverse:
isInProgress = true; isInProgress = true;
break; break;
default:
break;
} }
switch (_lastAnimationStatus) { switch (_lastAnimationStatus) {
case AnimationStatus.completed: case AnimationStatus.completed:
@ -470,6 +468,8 @@ class _OpenContainerRoute extends ModalRoute<void> {
case AnimationStatus.reverse: case AnimationStatus.reverse:
wasInProgress = true; wasInProgress = true;
break; break;
default:
break;
} }
return wasInProgress && isInProgress; return wasInProgress && isInProgress;
} }

View File

@ -94,8 +94,8 @@ class AudioPanelState extends State<AudioPanel> with TickerProviderStateMixin {
child: GestureDetector( child: GestureDetector(
onTap: backToMini, onTap: backToMini,
child: Container( child: Container(
color: context.scaffoldBackgroundColor.withOpacity(0.9 * color: context.background.withOpacity(
math.min(_animation.value / widget.maxHeight, 1)), 0.9 * math.min(_animation.value / widget.maxHeight, 1)),
), ),
), ),
) )
@ -314,7 +314,7 @@ class __AudioPanelRouteState extends State<_AudioPanelRoute> {
// child: // child:
// Container( // Container(
// color: Theme.of(context) // color: Theme.of(context)
// .scaffoldBackgroundColor // .background
// .withOpacity(0.8), // .withOpacity(0.8),
// //
//), //),

View File

@ -1126,16 +1126,16 @@ class _MyDropdownButtonState<T> extends State<MyDropdownButton<T>>
// ActivateAction.key: _createAction, // ActivateAction.key: _createAction,
// }; // };
focusNode!.addListener(_handleFocusChanged); focusNode!.addListener(_handleFocusChanged);
final focusManager = WidgetsBinding.instance!.focusManager; final focusManager = WidgetsBinding.instance.focusManager;
_focusHighlightMode = focusManager.highlightMode; _focusHighlightMode = focusManager.highlightMode;
focusManager.addHighlightModeListener(_handleFocusHighlightModeChange); focusManager.addHighlightModeListener(_handleFocusHighlightModeChange);
} }
@override @override
void dispose() { void dispose() {
WidgetsBinding.instance!.removeObserver(this); WidgetsBinding.instance.removeObserver(this);
_removeDropdownRoute(); _removeDropdownRoute();
WidgetsBinding.instance!.focusManager WidgetsBinding.instance.focusManager
.removeHighlightModeListener(_handleFocusHighlightModeChange); .removeHighlightModeListener(_handleFocusHighlightModeChange);
focusNode!.removeListener(_handleFocusChanged); focusNode!.removeListener(_handleFocusChanged);
_internalNode?.dispose(); _internalNode?.dispose();

View File

@ -224,7 +224,8 @@ class _HourMinuteControl extends StatelessWidget {
final backgroundColor = timePickerTheme.hourMinuteColor ?? final backgroundColor = timePickerTheme.hourMinuteColor ??
MaterialStateColor.resolveWith((states) { MaterialStateColor.resolveWith((states) {
return states.contains(MaterialState.selected) return states.contains(MaterialState.selected)
? themeData.colorScheme.secondary.withOpacity(isDark ? 0.24 : 0.12) ? themeData.colorScheme.secondary
.withOpacity(isDark ? 0.24 : 0.12)
: themeData.colorScheme.onSurface.withOpacity(0.12); : themeData.colorScheme.onSurface.withOpacity(0.12);
}); });
final style = final style =
@ -743,6 +744,8 @@ class _RenderInputPadding extends RenderShiftedBox {
newPosition += const Offset(-1.0, 0.0); newPosition += const Offset(-1.0, 0.0);
} }
break; break;
default:
break;
} }
return result.addWithRawTransform( return result.addWithRawTransform(
@ -783,7 +786,7 @@ class _DialPainter extends CustomPainter {
required this.theta, required this.theta,
required this.textDirection, required this.textDirection,
required this.selectedValue, required this.selectedValue,
}) : super(repaint: PaintingBinding.instance!.systemFonts); }) : super(repaint: PaintingBinding.instance.systemFonts);
final List<_TappableLabel>? primaryLabels; final List<_TappableLabel>? primaryLabels;
final List<_TappableLabel>? secondaryLabels; final List<_TappableLabel>? secondaryLabels;
@ -1734,6 +1737,8 @@ class _TimePickerDialogState extends State<_TimePickerDialog> {
_formKey.currentState!.save(); _formKey.currentState!.save();
_entryMode = TimePickerEntryMode.dial; _entryMode = TimePickerEntryMode.dial;
break; break;
default:
break;
} }
}); });
} }
@ -1837,6 +1842,8 @@ class _TimePickerDialogState extends State<_TimePickerDialog> {
timePickerWidth = _kTimePickerWidthPortrait; timePickerWidth = _kTimePickerWidthPortrait;
timePickerHeight = _kTimePickerHeightInput; timePickerHeight = _kTimePickerHeightInput;
break; break;
default:
break;
} }
return Size(timePickerWidth, timePickerHeight * textScaleFactor); return Size(timePickerWidth, timePickerHeight * textScaleFactor);
} }
@ -1980,6 +1987,8 @@ class _TimePickerDialogState extends State<_TimePickerDialog> {
), ),
); );
break; break;
default:
break;
} }
final dialogSize = _dialogSize(context); final dialogSize = _dialogSize(context);

View File

@ -651,8 +651,7 @@ class _LineLoaderState extends State<LineLoader>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return CustomPaint( return CustomPaint(painter: LinePainter(_fraction, context.accentColor));
painter: LinePainter(_fraction, context.accentColor));
} }
} }
@ -1115,8 +1114,7 @@ class LayoutButton extends StatelessWidget {
height: 10, height: 10,
width: 30, width: 30,
child: CustomPaint( child: CustomPaint(
painter: painter: LayoutPainter(4, context.textColor),
LayoutPainter(4, context.textColor),
), ),
), ),
); );
@ -1265,7 +1263,7 @@ class CustomBackButton extends StatelessWidget {
return IconButton( return IconButton(
splashRadius: 20, splashRadius: 20,
icon: const BackButtonIcon(), icon: const BackButtonIcon(),
tooltip: context.s!.back, tooltip: context.s.back,
onPressed: () { onPressed: () {
Navigator.maybePop(context); Navigator.maybePop(context);
}, },

View File

@ -88,7 +88,7 @@ class _DismissibleContainerState extends State<DismissibleContainer> {
Scaffold.of(context).showSnackBar(SnackBar( Scaffold.of(context).showSnackBar(SnackBar(
behavior: SnackBarBehavior.floating, behavior: SnackBarBehavior.floating,
backgroundColor: Colors.grey[800], backgroundColor: Colors.grey[800],
content: Text(s!.toastRemovePlaylist, content: Text(s.toastRemovePlaylist,
style: TextStyle(color: Colors.white)), style: TextStyle(color: Colors.white)),
action: SnackBarAction( action: SnackBarAction(
textColor: context.accentColor, textColor: context.accentColor,
@ -194,7 +194,7 @@ class EpisodeCard extends StatelessWidget {
episodeTag( episodeTag(
episode.duration == 0 episode.duration == 0
? '' ? ''
: s!.minsCount(episode.duration! ~/ 60), : s.minsCount(episode.duration! ~/ 60),
Colors.cyan[300]), Colors.cyan[300]),
if (episode.enclosureLength != null) if (episode.enclosureLength != null)
episodeTag( episodeTag(

View File

@ -132,7 +132,7 @@ class EpisodeGrid extends StatelessWidget {
if (dataConfirm) { if (dataConfirm) {
context.read<DownloadState>().startTask(episode!); context.read<DownloadState>().startTask(episode!);
Fluttertoast.showToast( Fluttertoast.showToast(
msg: context.s!.downloadStart, msg: context.s.downloadStart,
gravity: ToastGravity.BOTTOM, gravity: ToastGravity.BOTTOM,
); );
} }
@ -155,13 +155,13 @@ class EpisodeGrid extends StatelessWidget {
Future<bool> _useDataConfirm(BuildContext context) async { Future<bool> _useDataConfirm(BuildContext context) async {
var ifUseData = false; var ifUseData = false;
final s = context.s!; final s = context.s;
await generalDialog( await generalDialog(
context, context,
title: Text(s.cellularConfirm), title: Text(s.cellularConfirm),
content: Text(s.cellularConfirmDes), content: Text(s.cellularConfirmDes),
actions: <Widget>[ actions: <Widget>[
FlatButton( TextButton(
onPressed: () { onPressed: () {
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
@ -170,7 +170,7 @@ class EpisodeGrid extends StatelessWidget {
style: TextStyle(color: Colors.grey[600]), style: TextStyle(color: Colors.grey[600]),
), ),
), ),
FlatButton( TextButton(
onPressed: () { onPressed: () {
ifUseData = true; ifUseData = true;
Navigator.of(context).pop(); Navigator.of(context).pop();
@ -205,7 +205,10 @@ class EpisodeGrid extends StatelessWidget {
/// Circel avatar widget. /// Circel avatar widget.
Widget _circleImage(BuildContext context, Widget _circleImage(BuildContext context,
{EpisodeBrief? episode, Color? color, required bool boo, double? radius}) => {EpisodeBrief? episode,
Color? color,
required bool boo,
double? radius}) =>
InkWell( InkWell(
onTap: () async { onTap: () async {
if (openPodcast) { if (openPodcast) {
@ -287,7 +290,8 @@ class EpisodeGrid extends StatelessWidget {
: Center(); : Center();
/// Pubdate widget /// Pubdate widget
Widget _pubDate(BuildContext context, {required EpisodeBrief episode, Color? color}) => Widget _pubDate(BuildContext context,
{required EpisodeBrief episode, Color? color}) =>
Text( Text(
episode.pubDate!.toDate(context), episode.pubDate!.toDate(context),
overflow: TextOverflow.visible, overflow: TextOverflow.visible,
@ -299,7 +303,11 @@ class EpisodeGrid extends StatelessWidget {
fontStyle: FontStyle.italic), fontStyle: FontStyle.italic),
); );
Widget _episodeCard(BuildContext context, Widget _episodeCard(BuildContext context,
{int? index, Color? color, bool? isLiked, bool? isDownloaded, bool? boo}) { {int? index,
Color? color,
bool? isLiked,
bool? isDownloaded,
bool? boo}) {
var width = context.width; var width = context.width;
if (layout == Layout.one) { if (layout == Layout.one) {
return _layoutOneCard(context, return _layoutOneCard(context,
@ -323,7 +331,8 @@ class EpisodeGrid extends StatelessWidget {
layout != Layout.one layout != Layout.one
? _circleImage(context, ? _circleImage(context,
episode: episodes![index!], color: color, boo: boo!) episode: episodes![index!], color: color, boo: boo!)
: _pubDate(context, episode: episodes![index!], color: color), : _pubDate(context,
episode: episodes![index!], color: color),
Spacer(), Spacer(),
_isNewIndicator(episodes![index]), _isNewIndicator(episodes![index]),
_downloadIndicater(context, _downloadIndicater(context,
@ -403,7 +412,11 @@ class EpisodeGrid extends StatelessWidget {
} }
Widget _layoutOneCard(BuildContext context, Widget _layoutOneCard(BuildContext context,
{required int index, Color? color, required bool isLiked, bool? isDownloaded, required bool boo}) { {required int index,
Color? color,
required bool isLiked,
bool? isDownloaded,
required bool boo}) {
var width = context.width; var width = context.width;
return Padding( return Padding(
padding: EdgeInsets.symmetric(vertical: 8), padding: EdgeInsets.symmetric(vertical: 8),
@ -443,7 +456,8 @@ class EpisodeGrid extends StatelessWidget {
), ),
_isNewIndicator(episodes![index]), _isNewIndicator(episodes![index]),
_downloadIndicater(context, _downloadIndicater(context,
episode: episodes![index], isDownloaded: isDownloaded), episode: episodes![index],
isDownloaded: isDownloaded),
_numberIndicater(context, index: index, color: color) _numberIndicater(context, index: index, color: color)
], ],
), ),
@ -574,7 +588,7 @@ class EpisodeGrid extends StatelessWidget {
? context.brightness == Brightness.light ? context.brightness == Brightness.light
? Colors.grey[200] ? Colors.grey[200]
: Color.fromRGBO(50, 50, 50, 1) : Color.fromRGBO(50, 50, 50, 1)
: context.scaffoldBackgroundColor, : context.background,
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: context.brightness == Brightness.light color: context.brightness == Brightness.light
@ -590,7 +604,8 @@ class EpisodeGrid extends StatelessWidget {
color: Colors.transparent, color: Colors.transparent,
child: InkWell( child: InkWell(
onTap: () { onTap: () {
if (!selectedList!.contains(episodes![index])) { if (!selectedList!
.contains(episodes![index])) {
_selectedList = selectedList; _selectedList = selectedList;
_selectedList!.add(episodes![index]); _selectedList!.add(episodes![index]);
} else { } else {
@ -609,7 +624,7 @@ class EpisodeGrid extends StatelessWidget {
: context.brightness == : context.brightness ==
Brightness.light Brightness.light
? context.primaryColor ? context.primaryColor
: context.scaffoldBackgroundColor, : context.background,
width: 1.0, width: 1.0,
), ),
), ),
@ -628,7 +643,7 @@ class EpisodeGrid extends StatelessWidget {
border: Border.all( border: Border.all(
color: context.brightness == Brightness.light color: context.brightness == Brightness.light
? context.primaryColor ? context.primaryColor
: context.scaffoldBackgroundColor, : context.background,
width: 1.0, width: 1.0,
), ),
), ),
@ -658,11 +673,11 @@ class EpisodeGrid extends StatelessWidget {
title: Text( title: Text(
data.item1 != episodes![index] || data.item1 != episodes![index] ||
!data.item4 !data.item4
? s!.play ? s.play
: s!.playing), : s.playing),
trailingIcon: Icon( trailingIcon: Icon(
LineIcons.playCircle, LineIcons.playCircle,
color: Theme.of(context).accentColor, color: context.accentColor,
), ),
onPressed: () { onPressed: () {
if (data.item1 != episodes![index] || if (data.item1 != episodes![index] ||
@ -687,8 +702,8 @@ class EpisodeGrid extends StatelessWidget {
onPressed: () { onPressed: () {
if (!data.item2.contains( if (!data.item2.contains(
episodes![index].enclosureUrl)) { episodes![index].enclosureUrl)) {
audio audio.addToPlaylist(
.addToPlaylist(episodes![index]); episodes![index]);
Fluttertoast.showToast( Fluttertoast.showToast(
msg: s.toastAddPlaylist, msg: s.toastAddPlaylist,
gravity: ToastGravity.BOTTOM, gravity: ToastGravity.BOTTOM,
@ -741,7 +756,7 @@ class EpisodeGrid extends StatelessWidget {
title: isListened > 0 title: isListened > 0
? Text(s.markNotListened, ? Text(s.markNotListened,
style: TextStyle( style: TextStyle(
color: context.textColor! color: context.textColor
.withOpacity(0.5))) .withOpacity(0.5)))
: Text( : Text(
s.markListened, s.markListened,
@ -784,11 +799,10 @@ class EpisodeGrid extends StatelessWidget {
title: isDownloaded title: isDownloaded
? Text(s.downloaded, ? Text(s.downloaded,
style: TextStyle( style: TextStyle(
color: context.textColor! color: context.textColor
.withOpacity(0.5))) .withOpacity(0.5)))
: Text(s.download), : Text(s.download),
trailingIcon: Icon( trailingIcon: Icon(LineIcons.download,
LineIcons.download,
color: Colors.green), color: Colors.green),
onPressed: () async { onPressed: () async {
if (!isDownloaded) { if (!isDownloaded) {
@ -858,9 +872,9 @@ class OpenContainerWrapper extends StatelessWidget {
beginColor: Theme.of(context).primaryColor, beginColor: Theme.of(context).primaryColor,
endColor: Theme.of(context).primaryColor, endColor: Theme.of(context).primaryColor,
closedColor: Theme.of(context).brightness == Brightness.light closedColor: Theme.of(context).brightness == Brightness.light
? Theme.of(context).primaryColor ? context.primaryColor
: Theme.of(context).scaffoldBackgroundColor, : context.background,
openColor: Theme.of(context).scaffoldBackgroundColor, openColor: context.background,
openElevation: 0, openElevation: 0,
closedElevation: 0, closedElevation: 0,
openShape: openShape:

View File

@ -22,7 +22,7 @@ Widget featureDiscoveryOverlay(BuildContext context,
required Widget tapTarget, required Widget tapTarget,
required String title, required String title,
required String description}) { required String description}) {
final s = context.s!; final s = context.s;
return DescribedFeatureOverlay( return DescribedFeatureOverlay(
featureId: featureId, featureId: featureId,
tapTarget: tapTarget, tapTarget: tapTarget,

View File

@ -140,7 +140,7 @@ class _MultiSelectMenuBarState extends State<MultiSelectMenuBar> {
Future<bool> _useDataConfirm() async { Future<bool> _useDataConfirm() async {
var ifUseData = false; var ifUseData = false;
final s = context.s!; final s = context.s;
await generalDialog( await generalDialog(
context, context,
title: Text(s.cellularConfirm), title: Text(s.cellularConfirm),
@ -413,13 +413,13 @@ class _MultiSelectMenuBarState extends State<MultiSelectMenuBar> {
if (!_liked) { if (!_liked) {
await _saveLiked(); await _saveLiked();
Fluttertoast.showToast( Fluttertoast.showToast(
msg: s!.liked, msg: s.liked,
gravity: ToastGravity.BOTTOM, gravity: ToastGravity.BOTTOM,
); );
} else { } else {
await _setUnliked(); await _setUnliked();
Fluttertoast.showToast( Fluttertoast.showToast(
msg: s!.unliked, msg: s.unliked,
gravity: ToastGravity.BOTTOM, gravity: ToastGravity.BOTTOM,
); );
} }
@ -479,7 +479,7 @@ class _MultiSelectMenuBarState extends State<MultiSelectMenuBar> {
for (var episode in widget.selectedList!) { for (var episode in widget.selectedList!) {
audio.addToPlaylist(episode); audio.addToPlaylist(episode);
Fluttertoast.showToast( Fluttertoast.showToast(
msg: s!.toastAddPlaylist, msg: s.toastAddPlaylist,
gravity: ToastGravity.BOTTOM, gravity: ToastGravity.BOTTOM,
); );
} }
@ -488,7 +488,7 @@ class _MultiSelectMenuBarState extends State<MultiSelectMenuBar> {
for (var episode in widget.selectedList!) { for (var episode in widget.selectedList!) {
audio.delFromPlaylist(episode); audio.delFromPlaylist(episode);
Fluttertoast.showToast( Fluttertoast.showToast(
msg: s!.toastRemovePlaylist, msg: s.toastRemovePlaylist,
gravity: ToastGravity.BOTTOM, gravity: ToastGravity.BOTTOM,
); );
} }
@ -511,13 +511,13 @@ class _MultiSelectMenuBarState extends State<MultiSelectMenuBar> {
if (!_marked) { if (!_marked) {
await _markListened(); await _markListened();
Fluttertoast.showToast( Fluttertoast.showToast(
msg: s!.markListened, msg: s.markListened,
gravity: ToastGravity.BOTTOM, gravity: ToastGravity.BOTTOM,
); );
} else { } else {
await _markNotListened(); await _markNotListened();
Fluttertoast.showToast( Fluttertoast.showToast(
msg: s!.markNotListened, msg: s.markNotListened,
gravity: ToastGravity.BOTTOM, gravity: ToastGravity.BOTTOM,
); );
} }
@ -575,7 +575,7 @@ class __NewPlaylistState extends State<_NewPlaylist> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final s = context.s!; final s = context.s;
return AnnotatedRegion<SystemUiOverlayStyle>( return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle( value: SystemUiOverlayStyle(
statusBarIconBrightness: Brightness.light, statusBarIconBrightness: Brightness.light,

View File

@ -58,7 +58,7 @@ class _SettingsSheetState extends State<SettingsSheet>
Navigator.pop(context); Navigator.pop(context);
}, },
child: Container( child: Container(
color: context.scaffoldBackgroundColor.withOpacity( color: context.background.withOpacity(
0.8 * math.min(_animation.value / widget.height, 1.0)), 0.8 * math.min(_animation.value / widget.height, 1.0)),
), ),
), ),

View File

@ -68,6 +68,8 @@ dependencies:
collection: ^1.15.0-nullsafety.4 collection: ^1.15.0-nullsafety.4
shared_preferences_android: ^2.0.12 shared_preferences_android: ^2.0.12
path_provider_android: ^2.0.14 path_provider_android: ^2.0.14
material_color_utilities: ^0.1.4
dynamic_color: ^1.4.0
dependency_overrides: dependency_overrides:
linkify: linkify: