Compare commits
3 Commits
49a70fb833
...
e97a493135
Author | SHA1 | Date |
---|---|---|
xijieyin | e97a493135 | |
xijieyin | 731d50935b | |
xijieyin | bb6f57a6a3 |
|
@ -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();
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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)),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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'),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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
|
@ -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');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -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),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -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),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -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()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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))),
|
||||
],
|
||||
),
|
||||
)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)),
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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?>(
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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>[
|
||||
|
|
|
@ -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();
|
||||
},
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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!
|
||||
|
|
|
@ -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(),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
|
@ -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!
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
//
|
||||
//),
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
},
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in New Issue