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/rendering.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:tsacdop/episodes/menu_bar.dart';
import 'package:tsacdop/episodes/shownote.dart';
import 'package:tsacdop/util/helpers.dart';
import 'package:tuple/tuple.dart';
import '../home/audioplayer.dart';
import '../local_storage/sqflite_localpodcast.dart';
import '../state/audio_state.dart';
import '../state/setting_state.dart';
import '../type/episodebrief.dart';
import '../type/play_histroy.dart';
import '../util/extension_helper.dart';
import '../widgets/audiopanel.dart';
import '../widgets/custom_widget.dart';
import 'episode_download.dart';
class EpisodeDetail extends StatefulWidget {
final EpisodeBrief? episodeItem;
@ -48,23 +45,23 @@ class _EpisodeDetailState extends State<EpisodeDetail> {
return await dbHelper.getPosition(episode);
}
ScrollController? _controller;
late ScrollController _controller;
_scrollListener() {
if (_controller!.position.userScrollDirection == ScrollDirection.reverse) {
if (_controller.position.userScrollDirection == ScrollDirection.reverse) {
if (_showMenu && mounted) {
setState(() {
_showMenu = false;
});
}
}
if (_controller!.position.userScrollDirection == ScrollDirection.forward) {
if (_controller.position.userScrollDirection == ScrollDirection.forward) {
if (!_showMenu && mounted) {
setState(() {
_showMenu = true;
});
}
}
if (_controller!.offset > context.textTheme.headline5!.fontSize!) {
if (_controller.offset > context.textTheme.headline5!.fontSize!) {
if (!_showTitle) setState(() => _showTitle = true);
} else if (_showTitle) setState(() => _showTitle = false);
}
@ -75,623 +72,256 @@ class _EpisodeDetailState extends State<EpisodeDetail> {
_showMenu = true;
_showTitle = false;
_controller = ScrollController();
_controller!.addListener(_scrollListener);
_controller.addListener(_scrollListener);
}
@override
void dispose() {
_controller!.dispose();
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
SystemChrome.setSystemUIOverlayStyle(
SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
systemNavigationBarColor: Colors.transparent,
final s = context.s;
final audio = context.watch<AudioPlayerNotifier>();
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
statusBarColor: context.priamryContainer,
systemNavigationBarColor: context.priamryContainer,
systemNavigationBarContrastEnforced: false,
systemNavigationBarIconBrightness: context.iconBrightness,
statusBarBrightness: context.brightness,
statusBarIconBrightness: context.iconBrightness),
);
final s = context.s!;
final audio = context.watch<AudioPlayerNotifier>();
return WillPopScope(
onWillPop: () async {
if (_playerKey.currentState != null &&
_playerKey.currentState!.initSize! > 100) {
_playerKey.currentState!.backToMini();
return false;
} else {
return true;
}
},
child: Scaffold(
backgroundColor: Theme.of(context).primaryColor,
body: SafeArea(
child: Stack(
children: <Widget>[
ScrollConfiguration(
behavior: NoGrowBehavior(),
child: NestedScrollView(
scrollDirection: Axis.vertical,
controller: _controller,
headerSliverBuilder: (context, innerBoxScrolled) {
return <Widget>[
SliverAppBar(
floating: true,
pinned: true,
title: _showTitle
? Text(
widget.episodeItem?.title ?? '',
maxLines: 1,
overflow: TextOverflow.ellipsis,
)
: Text(
widget.episodeItem!.feedTitle!,
maxLines: 1,
style: TextStyle(
fontSize: 15,
color: context.textColor!.withOpacity(0.7)),
child: WillPopScope(
onWillPop: () async {
if (_playerKey.currentState != null &&
_playerKey.currentState!.initSize! > 100) {
_playerKey.currentState!.backToMini();
return false;
} else {
return true;
}
},
child: Scaffold(
backgroundColor: Theme.of(context).primaryColor,
body: SafeArea(
child: Stack(
children: <Widget>[
StretchingOverscrollIndicator(
axisDirection: AxisDirection.down,
child: NestedScrollView(
scrollDirection: Axis.vertical,
controller: _controller,
headerSliverBuilder: (context, innerBoxScrolled) {
return <Widget>[
SliverAppBar(
backgroundColor: context.priamryContainer,
floating: true,
pinned: true,
title: _showTitle
? Text(
widget.episodeItem?.title ?? '',
maxLines: 1,
overflow: TextOverflow.ellipsis,
)
: Text(
widget.episodeItem!.feedTitle!,
maxLines: 1,
style: TextStyle(
fontSize: 15,
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: EdgeInsets.fromLTRB(20, 10, 20, 10),
child: Row(
children: [
Text(
s.published(DateFormat.yMMMd().format(
DateTime.fromMillisecondsSinceEpoch(
widget.episodeItem!.pubDate!))),
style: TextStyle(color: context.accentColor)),
SizedBox(width: 10),
if (widget.episodeItem!.explicit == 1)
Text('E',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.red)),
Spacer(),
],
Padding(
padding: const EdgeInsets.fromLTRB(20, 10, 20, 10),
child: Row(
children: [
Text(
s.published(formateDate(
widget.episodeItem!.pubDate!)),
style:
TextStyle(color: context.accentColor)),
SizedBox(width: 10),
if (widget.episodeItem!.explicit == 1)
Text('E',
style: TextStyle(
fontWeight: FontWeight.bold,
color: context.error)),
Spacer(),
],
),
),
),
Padding(
padding:
EdgeInsets.symmetric(horizontal: 20, vertical: 5),
child: Row(
children: <Widget>[
if (widget.episodeItem!.duration != 0)
Container(
Padding(
padding: EdgeInsets.symmetric(
horizontal: 20, vertical: 5),
child: Row(
children: <Widget>[
if (widget.episodeItem!.duration != 0)
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(
color: Colors.cyan[300],
color: context.tertiary,
borderRadius: BorderRadius.all(
Radius.circular(16.0))),
height: 28.0,
margin: EdgeInsets.only(right: 10.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: 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),
'${widget.episodeItem!.enclosureLength! ~/ 1000000}MB',
style:
TextStyle(color: context.onPrimary),
),
),
),
FutureBuilder<PlayHistory>(
future: _getPosition(widget.episodeItem!),
builder: (context, snapshot) {
if (snapshot.hasError) {
developer.log(snapshot.error as String);
}
if (snapshot.hasData &&
snapshot.data!.seekValue! < 0.9 &&
snapshot.data!.seconds! > 10) {
return ButtonTheme(
height: 28,
padding:
EdgeInsets.symmetric(horizontal: 0),
child: OutlinedButton(
style: OutlinedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(
100.0),
side: BorderSide(
color:
context.accentColor)),
),
onPressed: () => audio.episodeLoad(
widget.episodeItem,
startPosition:
(snapshot.data!.seconds! *
1000)
.toInt()),
child: Row(
children: [
SizedBox(
width: 20,
height: 20,
child: CustomPaint(
painter: ListenedPainter(
context.textColor,
stroke: 2.0),
FutureBuilder<PlayHistory>(
future: _getPosition(widget.episodeItem!),
builder: (context, snapshot) {
if (snapshot.hasError) {
developer.log(snapshot.error as String);
}
if (snapshot.hasData &&
snapshot.data!.seekValue! < 0.9 &&
snapshot.data!.seconds! > 10) {
return ButtonTheme(
height: 28,
padding: EdgeInsets.symmetric(
horizontal: 0),
child: OutlinedButton(
style: OutlinedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(
100.0),
side: BorderSide(
color:
context.accentColor)),
),
onPressed: () => audio.episodeLoad(
widget.episodeItem,
startPosition:
(snapshot.data!.seconds! *
1000)
.toInt()),
child: Row(
children: [
SizedBox(
width: 20,
height: 20,
child: CustomPaint(
painter: ListenedPainter(
context.textColor,
stroke: 2.0),
),
),
),
SizedBox(width: 5),
Text(
snapshot.data!.seconds!.toTime,
),
],
SizedBox(width: 5),
Text(
snapshot
.data!.seconds!.toTime,
),
],
),
),
),
);
} else {
return Center();
}
}),
],
);
} else {
return Center();
}
}),
],
),
),
),
ShowNote(episode: widget.episodeItem),
Selector<AudioPlayerNotifier,
Tuple2<bool, PlayerHeight?>>(
selector: (_, audio) =>
Tuple2(audio.playerRunning, audio.playerHeight),
builder: (_, data, __) {
var height = kMinPlayerHeight[data.item2!.index];
return SizedBox(
height: data.item1 ? height : 0,
);
}),
],
ShowNote(episode: widget.episodeItem),
Selector<AudioPlayerNotifier,
Tuple2<bool, PlayerHeight?>>(
selector: (_, audio) => Tuple2(
audio.playerRunning, audio.playerHeight),
builder: (_, data, __) {
final height =
kMinPlayerHeight[data.item2!.index];
return SizedBox(
height: data.item1 ? height : 0,
);
}),
],
),
),
),
),
),
Selector<AudioPlayerNotifier, Tuple2<bool, PlayerHeight?>>(
selector: (_, audio) =>
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,
Selector<AudioPlayerNotifier, Tuple2<bool, PlayerHeight?>>(
selector: (_, audio) =>
Tuple2(audio.playerRunning, audio.playerHeight),
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,
);
});
},
),
],
),
final 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))),
],
),
),
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 {
Provider.of<DownloadState>(context, listen: false).delTask(episode);
Fluttertoast.showToast(
msg: context.s!.downloadRemovedToast,
msg: context.s.downloadRemovedToast,
gravity: ToastGravity.BOTTOM,
);
}
@ -78,22 +78,20 @@ class _DownloadButtonState extends State<DownloadButton> {
Future<bool> _useDataConfirm() async {
var ifUseData = false;
final s = context.s!;
final s = context.s;
await generalDialog(
context,
title: Text(s.cellularConfirm),
content: Text(s.cellularConfirmDes),
actions: <Widget>[
FlatButton(
onPressed: () {
Navigator.of(context).pop();
},
TextButton(
onPressed: Navigator.of(context).pop,
child: Text(
s.cancel,
style: TextStyle(color: Colors.grey[600]),
),
),
FlatButton(
TextButton(
onPressed: () {
ifUseData = true;
Navigator.of(context).pop();
@ -130,7 +128,7 @@ class _DownloadButtonState extends State<DownloadButton> {
AnimatedContainer(
duration: Duration(seconds: 1),
decoration: BoxDecoration(
color: Theme.of(context).accentColor,
color: context.accentColor,
borderRadius: BorderRadius.all(Radius.circular(15.0))),
height: 20.0,
width: (_task.status == DownloadTaskStatus.running) ? 50.0 : 0,
@ -222,7 +220,6 @@ class _DownloadButtonState extends State<DownloadButton> {
),
),
);
break;
case 3:
Provider.of<AudioPlayerNotifier>(context, listen: false)
.updateMediaItem(task.episode!);
@ -251,11 +248,9 @@ class _DownloadButtonState extends State<DownloadButton> {
),
),
);
break;
case 4:
return _buttonOnMenu(Icon(Icons.refresh, color: Colors.red),
() => _retryDownload(task.episode!));
break;
default:
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(
height: 50.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>(
value: SystemUiOverlayStyle(
statusBarIconBrightness: Theme.of(context).accentColorBrightness,
@ -208,7 +209,10 @@ class _AboutAppState extends State<AboutApp> {
children: <Widget>[
_listItem(context, 'Twitter @tsacdop',
LineIcons.twitter, 'https://twitter.com/tsacdop'),
_listItem(context, 'GitHub', LineIcons.alternateGithub,
_listItem(
context,
'GitHub',
LineIcons.alternateGithub,
'https://github.com/stonega/tsacdop'),
_listItem(context, 'Telegram', LineIcons.telegram,
'https://t.me/joinchat/Bk3LkRpTHy40QYC78PK7Qg'),
@ -281,7 +285,7 @@ class _AboutAppState extends State<AboutApp> {
name: 'Bruno Pinheiro', flag: 'pt'),
_translatorInfo(context,
name: 'Edoardo Maria Elidoro', flag: 'it'),
_translatorInfo(context,
_translatorInfo(context,
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:marquee/marquee.dart';
import 'package:provider/provider.dart';
import 'package:tsacdop/episodes/shownote.dart';
import 'package:tuple/tuple.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
@ -115,12 +116,12 @@ class PlayerWidget extends StatelessWidget {
const TextStyle(color: Color(0xFFFF0000)))
: data.item1
? Text(
s!.buffering,
s.buffering,
style:
TextStyle(color: context.accentColor),
)
: Text(
s!.timeLeft((data.item2).toInt().toTime),
s.timeLeft((data.item2).toInt().toTime),
maxLines: 2,
),
);
@ -281,26 +282,27 @@ class LastPosition extends StatelessWidget {
children: [
Selector<AudioPlayerNotifier, bool?>(
selector: (_, audio) => audio.skipSilence,
builder: (_, data, __) => FlatButton(
builder: (_, data, __) => TextButton(
child: Row(
children: [
Icon(Icons.flash_on, size: 18),
SizedBox(width: 5),
Text(s!.skipSilence),
Text(s.skipSilence),
],
),
color: data! ? context.accentColor : Colors.transparent,
padding: EdgeInsets.symmetric(horizontal: 10),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(100.0),
side: BorderSide(
color: data
? context.accentColor
: Theme.of(context)
.colorScheme
.onSurface
.withOpacity(0.12))),
textColor: data ? Colors.white : null,
style: TextButton.styleFrom(
primary:
data! ? context.accentColor : Colors.transparent,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(100.0),
side: BorderSide(
color: data
? context.accentColor
: Theme.of(context)
.colorScheme
.onSurface
.withOpacity(0.12))),
),
onPressed: () =>
audio.setSkipSilence(skipSilence: !data))),
SizedBox(width: 10),
@ -311,7 +313,7 @@ class LastPosition extends StatelessWidget {
children: [
Icon(Icons.volume_up, size: 18),
SizedBox(width: 5),
Text(s!.boostVolume),
Text(s.boostVolume),
],
),
color: data! ? context.accentColor : Colors.transparent,
@ -499,8 +501,8 @@ class _PlaylistWidgetState extends State<PlaylistWidget> {
children: <Widget>[
Text(
data.item1!.name == 'Queue'
? context.s!.queue
: '${context.s!.homeMenuPlaylist}${'-${data.item1!.name}'}',
? context.s.queue
: '${context.s.homeMenuPlaylist}${'-${data.item1!.name}'}',
overflow: TextOverflow.fade,
style: TextStyle(
color: context.accentColor,
@ -756,7 +758,7 @@ class SleepModeState extends State<SleepMode>
width: 120,
child: Center(
child: Text(
s!.endOfEpisode,
s.endOfEpisode,
style: TextStyle(
color: (move > 0
? Colors.white
@ -826,7 +828,7 @@ class SleepModeState extends State<SleepMode>
padding: EdgeInsets.symmetric(horizontal: 20.0),
child: Row(
children: [
Text(context.s!.sleepTimer,
Text(context.s.sleepTimer,
style: TextStyle(
color: context.accentColor,
fontWeight: FontWeight.bold,
@ -935,10 +937,9 @@ class _ChaptersWidgetState extends State<ChaptersWidget> {
padding: EdgeInsets.symmetric(horizontal: 0),
child: OutlinedButton(
style: OutlinedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(100.0),
side: BorderSide(color: context.accentColor)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(100.0),
side: BorderSide(color: context.accentColor)),
),
// highlightedBorderColor: Colors.green[700],
onPressed: () {
@ -1088,7 +1089,7 @@ class _ChaptersWidgetState extends State<ChaptersWidget> {
child: Row(
children: <Widget>[
Text(
context.s!.homeToprightMenuAbout,
context.s.homeToprightMenuAbout,
overflow: TextOverflow.fade,
style: TextStyle(
color: context.accentColor,
@ -1332,7 +1333,7 @@ class _ControlPanelState extends State<ControlPanel>
.buffering ||
data.audioState ==
AudioProcessingState.loading
? context.s!.buffering
? context.s.buffering
: '',
style: TextStyle(
color: context.accentColor),

View File

@ -64,7 +64,7 @@ class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
_controller = TabController(length: 3, vsync: this);
// FeatureDiscovery.hasPreviouslyCompleted(context, addFeature).then((value) {
// if (!value) {
SchedulerBinding.instance!.addPostFrameCallback((_) {
SchedulerBinding.instance.addPostFrameCallback((_) {
FeatureDiscovery.discoverFeatures(
context,
const <String>{
@ -90,15 +90,15 @@ class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
double top = 0;
@override
Widget build(BuildContext context) {
var height = (context.width - 20) / 3 + 140;
var settings = Provider.of<SettingState>(context, listen: false);
final height = (context.width - 20) / 3 + 140;
final settings = Provider.of<SettingState>(context, listen: false);
final s = context.s;
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
systemNavigationBarIconBrightness:
context.brightness,
systemNavigationBarIconBrightness: context.brightness,
statusBarIconBrightness: context.iconBrightness,
systemNavigationBarColor: context.primaryColor,
systemNavigationBarColor: context.onPrimary,
statusBarColor: context.onPrimary,
),
child: WillPopScope(
onWillPop: () async {
@ -115,12 +115,13 @@ class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
},
child: Scaffold(
key: _scaffoldKey,
backgroundColor: context.onPrimary,
body: Stack(
children: <Widget>[
SafeArea(
bottom: false,
child: ScrollConfiguration(
behavior: NoGrowBehavior(),
child: StretchingOverscrollIndicator(
axisDirection: AxisDirection.down,
child: NestedScrollView(
innerScrollPositionKeyBuilder: () {
return Key('tab${_controller!.index}');
@ -141,7 +142,7 @@ class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
context,
featureId: addFeature,
tapTarget: Icon(Icons.add_circle_outline),
title: s!.featureDiscoverySearch,
title: s.featureDiscoverySearch,
backgroundColor: Colors.cyan[600],
buttonColor: Colors.cyan[500],
description: s.featureDiscoverySearchDes,
@ -240,14 +241,13 @@ class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
),
),
Selector<AudioPlayerNotifier, bool>(
selector: (_, audio) =>
audio.playerRunning,
builder: (_, data, __) {
return Padding(
padding:
EdgeInsets.only(bottom: data ? 60.0 : 0),
);
}),
selector: (_, audio) => audio.playerRunning,
builder: (_, data, __) {
return Padding(
padding: EdgeInsets.only(bottom: data ? 60.0 : 0),
);
},
),
],
),
),
@ -274,9 +274,9 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
@override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
final s = context.s!;
final s = context.s;
return Container(
color: context.scaffoldBackgroundColor,
color: context.background,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
@ -335,7 +335,7 @@ class __PlaylistButtonState extends State<_PlaylistButton> {
@override
Widget build(BuildContext context) {
final s = context.s!;
final s = context.s;
return Material(
color: Colors.transparent,
borderRadius: BorderRadius.circular(100),
@ -406,19 +406,23 @@ class __PlaylistButtonState extends State<_PlaylistButton> {
Padding(
padding: EdgeInsets.symmetric(vertical: 2),
),
Container(
SizedBox(
height: 70,
width: 140,
child: Column(
children: <Widget>[
Text(
(data.item3 ~/ 1000).toTime,
style:
TextStyle(color: context.textColor),
),
Text(
data.item2!.title!,
maxLines: 2,
textAlign: TextAlign.center,
overflow: TextOverflow.fade,
style:
TextStyle(color: context.textColor),
// style: TextStyle(color: Colors.white),
),
],
@ -443,7 +447,10 @@ class __PlaylistButtonState extends State<_PlaylistButton> {
Padding(
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(
context,
MaterialPageRoute(
builder: (context) => PlaylistHome(),
builder: (_) => PlaylistHome(),
),
);
}
@ -530,7 +537,7 @@ class _RecentUpdateState extends State<_RecentUpdate>
refreshWorker.start(_group);
await Future.delayed(Duration(seconds: 1));
Fluttertoast.showToast(
msg: context.s!.refreshStarted,
msg: context.s.refreshStarted,
gravity: ToastGravity.BOTTOM,
);
}
@ -593,14 +600,14 @@ class _RecentUpdateState extends State<_RecentUpdate>
builder: (context, groupList, child) => PopupMenuButton<String>(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
elevation: 1,
tooltip: context.s!.groupFilter,
tooltip: context.s.groupFilter,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 20),
height: 50,
child: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(_groupName == 'All' ? context.s!.all : _groupName!),
Text(_groupName == 'All' ? context.s.all : _groupName!),
Padding(
padding: EdgeInsets.symmetric(horizontal: 5),
),
@ -613,7 +620,7 @@ class _RecentUpdateState extends State<_RecentUpdate>
itemBuilder: (context) => [
PopupMenuItem(
child: Row(children: [
Text(context.s!.all),
Text(context.s.all),
Spacer(),
if (_groupName == 'All') DotIndicator()
]),
@ -654,56 +661,57 @@ class _RecentUpdateState extends State<_RecentUpdate>
// final audio = context.read<AudioPlayerNotifier>();
final s = context.s;
return FutureBuilder<int>(
future: _getUpdateCounts(_group!),
initialData: 0,
builder: (context, snapshot) {
return snapshot.data != 0
? Material(
color: Colors.transparent,
child: Row(
children: [
IconButton(
tooltip: s!.removeNewMark,
icon: SizedBox(
height: 20,
width: 20,
child: CustomPaint(
painter: RemoveNewFlagPainter(
context.textTheme.bodyText1!.color,
Colors.red))),
onPressed: () async {
_removeNewMark(_group!);
if (mounted) {
setState(() {});
}
}),
// IconButton(
// tooltip: s.addNewEpisodeTooltip,
// icon: SizedBox(
// height: 15,
// width: 20,
// child: CustomPaint(
// painter: AddToPlaylistPainter(
// context.textTheme.bodyText1.color,
// Colors.red))),
// onPressed: () async {
// await audio.addNewEpisode(_group);
// if (mounted) {
// setState(() {});
// }
// Fluttertoast.showToast(
// msg: _groupName == 'All'
// ? s.addNewEpisodeAll(snapshot.data)
// : s.addEpisodeGroup(
// _groupName, snapshot.data),
// gravity: ToastGravity.BOTTOM,
// );
// }),
],
),
)
: Center();
});
future: _getUpdateCounts(_group!),
initialData: 0,
builder: (context, snapshot) {
return snapshot.data != 0
? Material(
color: Colors.transparent,
child: Row(
children: [
IconButton(
tooltip: s.removeNewMark,
icon: SizedBox(
height: 20,
width: 20,
child: CustomPaint(
painter: RemoveNewFlagPainter(
context.textTheme.bodyText1!.color,
Colors.red))),
onPressed: () async {
_removeNewMark(_group!);
if (mounted) {
setState(() {});
}
}),
// IconButton(
// tooltip: s.addNewEpisodeTooltip,
// icon: SizedBox(
// height: 15,
// width: 20,
// child: CustomPaint(
// painter: AddToPlaylistPainter(
// context.textTheme.bodyText1.color,
// Colors.red))),
// onPressed: () async {
// await audio.addNewEpisode(_group);
// if (mounted) {
// setState(() {});
// }
// Fluttertoast.showToast(
// msg: _groupName == 'All'
// ? s.addNewEpisodeAll(snapshot.data)
// : s.addEpisodeGroup(
// _groupName, snapshot.data),
// gravity: ToastGravity.BOTTOM,
// );
// }),
],
),
)
: Center();
},
);
}
@override
@ -729,7 +737,7 @@ class _RecentUpdateState extends State<_RecentUpdate>
Padding(
padding: EdgeInsets.symmetric(vertical: 10)),
Text(
s!.noEpisodeRecent,
s.noEpisodeRecent,
style: TextStyle(color: Colors.grey[500]),
)
],
@ -806,14 +814,14 @@ class _RecentUpdateState extends State<_RecentUpdate>
Material(
color: Colors.transparent,
child: IconButton(
tooltip: context.s!.refresh,
tooltip: context.s.refresh,
icon: Icon(
LineIcons.alternateRedo,
size: 16),
onPressed: () {
_updateRssItem();
Fluttertoast.showToast(
msg: s!.refreshStarted,
msg: s.refreshStarted,
gravity:
ToastGravity.BOTTOM,
);
@ -823,7 +831,7 @@ class _RecentUpdateState extends State<_RecentUpdate>
Material(
color: Colors.transparent,
child: IconButton(
tooltip: s!.hideListenedSetting,
tooltip: s.hideListenedSetting,
icon: SizedBox(
width: 30,
height: 15,
@ -973,7 +981,7 @@ class _MyFavoriteState extends State<_MyFavorite>
Padding(
padding: EdgeInsets.symmetric(vertical: 10)),
Text(
s!.noEpisodeFavorite,
s.noEpisodeFavorite,
style: TextStyle(color: Colors.grey[500]),
)
],
@ -1042,7 +1050,7 @@ class _MyFavoriteState extends State<_MyFavorite>
BorderRadius.all(
Radius.circular(10))),
elevation: 1,
tooltip: s!.homeSubMenuSortBy,
tooltip: s.homeSubMenuSortBy,
child: Container(
height: 50,
padding: EdgeInsets.symmetric(
@ -1228,7 +1236,7 @@ class _MyDownloadState extends State<_MyDownload>
children: <Widget>[
Container(
padding: EdgeInsets.symmetric(horizontal: 20),
child: Text(s!.downloaded)),
child: Text(s.downloaded)),
Spacer(),
Material(
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> {
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
Widget build(BuildContext context) {
var refreshWorker = Provider.of<RefreshWorker>(context, listen: false);
final s = context.s!;
final s = context.s;
return Material(
color: Colors.transparent,
borderRadius: BorderRadius.circular(100),
@ -98,10 +36,11 @@ class _PopupMenuState extends State<PopupMenu> {
width: 40,
child: PopupMenuButton<int>(
icon: Icon(Icons.more_vert),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)),
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
elevation: 1,
tooltip: s.menu,
color: context.priamryContainer,
itemBuilder: (context) => [
PopupMenuItem(
value: 1,
@ -126,8 +65,8 @@ class _PopupMenuState extends State<PopupMenu> {
if (snapshot.hasData) {
return Text(
snapshot.data!,
style:
TextStyle(color: Colors.red, fontSize: 12),
style: TextStyle(
color: Colors.red, fontSize: 12),
);
} else {
return Center();
@ -141,7 +80,7 @@ class _PopupMenuState extends State<PopupMenu> {
),
PopupMenuItem(
value: 2,
child: Container(
child: Padding(
padding: EdgeInsets.only(left: 10),
child: Row(
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(
color: context.primaryColorDark,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
SizedBox(height: 2.0, child: LinearProgressIndicator()),
Container(
padding: EdgeInsets.symmetric(horizontal: 20.0),
height: 20.0,
alignment: Alignment.centerLeft,
child: Text(text),
),
]),
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
SizedBox(height: 2.0, child: LinearProgressIndicator()),
Container(
padding: EdgeInsets.symmetric(horizontal: 20.0),
height: 20.0,
alignment: Alignment.centerLeft,
child: Text(text),
),
],
),
);
}
_autoDownloadNew(BuildContext context) async {
final dbHelper = DBHelper();
var downloader = Provider.of<DownloadState>(context, listen: false);
var result = await Connectivity().checkConnectivity();
var autoDownloadStorage = KeyValueStorage(autoDownloadNetworkKey);
var autoDownloadNetwork = await autoDownloadStorage.getInt();
final downloader = Provider.of<DownloadState>(context, listen: false);
final result = await Connectivity().checkConnectivity();
final autoDownloadStorage = KeyValueStorage(autoDownloadNetworkKey);
final autoDownloadNetwork = await autoDownloadStorage.getInt();
if (autoDownloadNetwork == 1) {
var episodes = await dbHelper.getNewEpisodes('all');
final episodes = await dbHelper.getNewEpisodes('all');
// For safety
if (episodes.length < 100 && episodes.length > 0) {
for (var episode in episodes) {
@ -56,26 +57,27 @@ class Import extends StatelessWidget {
@override
Widget build(BuildContext context) {
final s = context.s;
var groupList = Provider.of<GroupList>(context, listen: false);
final groupList = Provider.of<GroupList>(context, listen: false);
return Column(
children: <Widget>[
Consumer<GroupList>(
builder: (_, subscribeWorker, __) {
var item = subscribeWorker.currentSubscribeItem;
final item = subscribeWorker.currentSubscribeItem;
switch (item.subscribeState) {
case SubscribeState.start:
return importColumn(
s!.notificationSubscribe(item.title!), context);
s.notificationSubscribe(item.title!), context);
case SubscribeState.subscribe:
return importColumn(s!.notificaitonFatch(item.title!), context);
return importColumn(s.notificaitonFatch(item.title!), context);
case SubscribeState.fetch:
return importColumn(s!.notificationSuccess(item.title!), context);
return importColumn(
s.notificationSuccess(item.title!), context);
case SubscribeState.exist:
return importColumn(
s!.notificationSubscribeExisted(item.title!), context);
s.notificationSubscribeExisted(item.title!), context);
case SubscribeState.error:
return importColumn(
s!.notificationNetworkError(item.title!), context);
s.notificationNetworkError(item.title!), context);
default:
return Center();
}
@ -83,17 +85,17 @@ class Import extends StatelessWidget {
),
Consumer<RefreshWorker>(
builder: (context, refreshWorker, child) {
var item = refreshWorker.currentRefreshItem;
final item = refreshWorker.currentRefreshItem;
if (refreshWorker.complete) {
groupList.updateGroups();
_autoDownloadNew(context);
}
switch (item.refreshState) {
case RefreshState.fetch:
return importColumn(s!.notificationUpdate(item.title), context);
return importColumn(s.notificationUpdate(item.title), context);
case RefreshState.error:
return importColumn(
s!.notificationUpdateError(item.title), context);
s.notificationUpdateError(item.title), context);
default:
return Center();
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -117,7 +117,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
final result = await _dbHelper.updatePodcastRss(podcastLocal);
if (result >= 0) {
Fluttertoast.showToast(
msg: context.s!.updateEpisodesCount(result),
msg: context.s.updateEpisodesCount(result),
gravity: ToastGravity.TOP,
);
}
@ -148,7 +148,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
}
} else if (result != 0) {
Fluttertoast.showToast(
msg: context.s!.updateFailed,
msg: context.s.updateFailed,
gravity: ToastGravity.TOP,
);
}
@ -359,7 +359,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
Container(
padding: EdgeInsets.fromLTRB(15, 10, 15, 10),
alignment: Alignment.topLeft,
color: context.scaffoldBackgroundColor,
color: context.background,
child: AboutPodcast(podcastLocal: widget.podcastLocal),
),
],
@ -389,7 +389,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
);
Widget _actionBar(BuildContext context) {
final s = context.s!;
final s = context.s;
return SizedBox(
height: 30,
child: Row(
@ -457,14 +457,16 @@ class _PodcastDetailState extends State<PodcastDetail> {
borderRadius: BorderRadius.circular(5),
border: Border.all(
width: 2,
color: context.textColor!.withOpacity(0.2))),
color: context.textColor.withOpacity(0.2))),
child: _query == ''
? Row(
children: [
Text(s.search,
style: TextStyle(
color: context.textColor!
.withOpacity(0.4))),
Text(
s.search,
style: TextStyle(
color: context.textColor.withOpacity(0.4),
),
),
Spacer()
],
)
@ -668,7 +670,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
@override
Widget build(BuildContext context) {
final color = widget.podcastLocal!.primaryColor!.colorizedark();
final s = context.s!;
final s = context.s;
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
statusBarIconBrightness: Brightness.dark,
@ -732,7 +734,6 @@ class _PodcastDetailState extends State<PodcastDetail> {
physics: const AlwaysScrollableScrollPhysics(),
slivers: <Widget>[
SliverAppBar(
brightness: Brightness.dark,
actions: <Widget>[
IconButton(
icon: Icon(Icons.more_vert),
@ -1138,7 +1139,7 @@ class _SearchEpisodeState extends State<SearchEpisode> {
@override
Widget build(BuildContext context) {
final s = context.s!;
final s = context.s;
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
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(
color: Colors.transparent,
child: InkWell(
@ -166,7 +169,7 @@ class __PodcastCardState extends State<_PodcastCard>
@override
Widget build(BuildContext context) {
final c = widget.podcastLocal!.backgroudColor(context);
final s = context.s!;
final s = context.s;
var groupList = context.watch<GroupList>();
_belongGroups = groupList.getPodcastGroup(widget.podcastLocal!.id);
return Column(
@ -243,7 +246,7 @@ class __PodcastCardState extends State<_PodcastCard>
: Container(
child: Container(
decoration: BoxDecoration(
color: context.scaffoldBackgroundColor,
color: context.background,
),
// border: Border(
// bottom: BorderSide(
@ -365,7 +368,8 @@ class __PodcastCardState extends State<_PodcastCard>
tooltip: s.autoDownload,
onTap: () async {
await _setAutoDownload(
widget.podcastLocal!.id, !snapshot.data!);
widget.podcastLocal!.id,
!snapshot.data!);
setState(() {});
},
);
@ -504,7 +508,7 @@ class _RenameGroupState extends State<RenameGroup> {
Widget build(BuildContext context) {
var groupList = Provider.of<GroupList>(context, listen: false);
List list = groupList.groups.map((e) => e!.name).toList();
final s = context.s!;
final s = context.s;
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
statusBarIconBrightness: Brightness.light,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -10,14 +10,14 @@ ItunesSearchResult<P> _$ItunesSearchResultFromJson<P>(
Map<String, dynamic> json) {
return ItunesSearchResult<P>(
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>(
ItunesSearchResult<P> instance) =>
<String, dynamic>{
'results': instance.results?.map(_ConvertP<P>().toJson)?.toList(),
'results': instance.results?.map(_ConvertP<P>().toJson).toList(),
'resultCount': instance.resultCount,
};

View File

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

View File

@ -9,14 +9,14 @@ part of 'searchepisodes.dart';
SearchEpisodes<E> _$SearchEpisodesFromJson<E>(Map<String, dynamic> json) {
return SearchEpisodes<E>(
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?,
);
}
Map<String, dynamic> _$SearchEpisodesToJson<E>(SearchEpisodes<E> instance) =>
<String, dynamic>{
'episodes': instance.episodes?.map(_ConvertE<E>().toJson)?.toList(),
'episodes': instance.episodes?.map(_ConvertE<E>().toJson).toList(),
'next_episode_pub_date': instance.nextEpisodeDate,
};

View File

@ -8,7 +8,7 @@ part of 'searchpodcast.dart';
SearchPodcast<P> _$SearchPodcastFromJson<P>(Map<String, dynamic> json) {
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?,
total: json['total'] 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) =>
<String, dynamic>{
'results': instance.results?.map(_ConvertP<P>().toJson)?.toList(),
'results': instance.results?.map(_ConvertP<P>().toJson).toList(),
'next_offset': instance.nextOffset,
'total': instance.total,
'count': instance.count,

View File

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

View File

@ -2,6 +2,7 @@ import 'dart:ui' as ui;
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
Future<ui.Image> getImageFromProvider(ImageProvider imageProvider) async {
final ImageStream stream = imageProvider.resolve(
@ -17,3 +18,9 @@ Future<ui.Image> getImageFromProvider(ImageProvider imageProvider) async {
final image = await imageCompleter.future;
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(
@ -273,7 +272,6 @@ class _OpenContainerRoute extends ModalRoute<void> {
),
],
);
break;
case ContainerTransitionType.fadeThrough:
return _FlippableTweenSequence<double>(
<TweenSequenceItem<double>>[
@ -287,9 +285,7 @@ class _OpenContainerRoute extends ModalRoute<void> {
),
],
);
break;
}
return null; // unreachable
}
static _FlippableTweenSequence<double>? _getOpenOpacityTween(
@ -421,7 +417,7 @@ class _OpenContainerRoute extends ModalRoute<void> {
}
if (delayForSourceRoute) {
SchedulerBinding.instance!
SchedulerBinding.instance
.addPostFrameCallback(takeMeasurementsInSourceRoute);
} else {
takeMeasurementsInSourceRoute();
@ -460,6 +456,8 @@ class _OpenContainerRoute extends ModalRoute<void> {
case AnimationStatus.reverse:
isInProgress = true;
break;
default:
break;
}
switch (_lastAnimationStatus) {
case AnimationStatus.completed:
@ -470,6 +468,8 @@ class _OpenContainerRoute extends ModalRoute<void> {
case AnimationStatus.reverse:
wasInProgress = true;
break;
default:
break;
}
return wasInProgress && isInProgress;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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