settings: fix accents
settings_screen: use *ListTile instead of having it into "trailing" lyrics: add the possibility to scroll freely
This commit is contained in:
parent
648a8ff7e8
commit
e30e2bebf1
|
|
@ -20,7 +20,6 @@ Settings settings;
|
||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class Settings {
|
class Settings {
|
||||||
|
|
||||||
//Language
|
//Language
|
||||||
@JsonKey(defaultValue: null)
|
@JsonKey(defaultValue: null)
|
||||||
String language;
|
String language;
|
||||||
|
|
@ -46,7 +45,6 @@ class Settings {
|
||||||
@JsonKey(defaultValue: AudioQuality.FLAC)
|
@JsonKey(defaultValue: AudioQuality.FLAC)
|
||||||
AudioQuality downloadQuality;
|
AudioQuality downloadQuality;
|
||||||
|
|
||||||
|
|
||||||
//Download options
|
//Download options
|
||||||
String downloadPath;
|
String downloadPath;
|
||||||
|
|
||||||
|
|
@ -78,12 +76,26 @@ class Settings {
|
||||||
String singletonFilename;
|
String singletonFilename;
|
||||||
@JsonKey(defaultValue: 1400)
|
@JsonKey(defaultValue: 1400)
|
||||||
int albumArtResolution;
|
int albumArtResolution;
|
||||||
@JsonKey(defaultValue: ["title", "album", "artist", "track", "disc",
|
@JsonKey(defaultValue: [
|
||||||
"albumArtist", "date", "label", "isrc", "upc", "trackTotal", "bpm",
|
"title",
|
||||||
"lyrics", "genre", "contributors", "art"])
|
"album",
|
||||||
|
"artist",
|
||||||
|
"track",
|
||||||
|
"disc",
|
||||||
|
"albumArtist",
|
||||||
|
"date",
|
||||||
|
"label",
|
||||||
|
"isrc",
|
||||||
|
"upc",
|
||||||
|
"trackTotal",
|
||||||
|
"bpm",
|
||||||
|
"lyrics",
|
||||||
|
"genre",
|
||||||
|
"contributors",
|
||||||
|
"art"
|
||||||
|
])
|
||||||
List<String> tags;
|
List<String> tags;
|
||||||
|
|
||||||
|
|
||||||
//Appearance
|
//Appearance
|
||||||
@JsonKey(defaultValue: Themes.Dark)
|
@JsonKey(defaultValue: Themes.Dark)
|
||||||
Themes theme;
|
Themes theme;
|
||||||
|
|
@ -127,13 +139,13 @@ class Settings {
|
||||||
@JsonKey(defaultValue: null)
|
@JsonKey(defaultValue: null)
|
||||||
String lastFMPassword;
|
String lastFMPassword;
|
||||||
|
|
||||||
|
|
||||||
Settings({this.downloadPath, this.arl});
|
Settings({this.downloadPath, this.arl});
|
||||||
|
|
||||||
ThemeData get themeData {
|
ThemeData get themeData {
|
||||||
//System theme
|
//System theme
|
||||||
if (useSystemTheme) {
|
if (useSystemTheme) {
|
||||||
if (SchedulerBinding.instance.window.platformBrightness == Brightness.light) {
|
if (SchedulerBinding.instance.window.platformBrightness ==
|
||||||
|
Brightness.light) {
|
||||||
return _themeData[Themes.Light];
|
return _themeData[Themes.Light];
|
||||||
} else {
|
} else {
|
||||||
if (theme == Themes.Light) return _themeData[Themes.Dark];
|
if (theme == Themes.Light) return _themeData[Themes.Dark];
|
||||||
|
|
@ -158,7 +170,8 @@ class Settings {
|
||||||
useArtColor = v;
|
useArtColor = v;
|
||||||
if (v) {
|
if (v) {
|
||||||
//On media item change set color
|
//On media item change set color
|
||||||
_useArtColorSub = AudioService.currentMediaItemStream.listen((event) async {
|
_useArtColorSub =
|
||||||
|
AudioService.currentMediaItemStream.listen((event) async {
|
||||||
if (event == null || event.artUri == null) return;
|
if (event == null || event.artUri == null) return;
|
||||||
this.primaryColor = await imagesDatabase.getPrimaryColor(event.artUri);
|
this.primaryColor = await imagesDatabase.getPrimaryColor(event.artUri);
|
||||||
updateTheme();
|
updateTheme();
|
||||||
|
|
@ -175,8 +188,7 @@ class Settings {
|
||||||
SliderThemeData get _sliderTheme => SliderThemeData(
|
SliderThemeData get _sliderTheme => SliderThemeData(
|
||||||
thumbColor: primaryColor,
|
thumbColor: primaryColor,
|
||||||
activeTrackColor: primaryColor,
|
activeTrackColor: primaryColor,
|
||||||
inactiveTrackColor: primaryColor.withOpacity(0.2)
|
inactiveTrackColor: primaryColor.withOpacity(0.2));
|
||||||
);
|
|
||||||
|
|
||||||
//Load settings/init
|
//Load settings/init
|
||||||
Future<Settings> loadSettings() async {
|
Future<Settings> loadSettings() async {
|
||||||
|
|
@ -188,7 +200,8 @@ class Settings {
|
||||||
}
|
}
|
||||||
Settings s = Settings.fromJson({});
|
Settings s = Settings.fromJson({});
|
||||||
//Set default path, because async
|
//Set default path, because async
|
||||||
s.downloadPath = (await ExtStorage.getExternalStoragePublicDirectory(ExtStorage.DIRECTORY_MUSIC));
|
s.downloadPath = (await ExtStorage.getExternalStoragePublicDirectory(
|
||||||
|
ExtStorage.DIRECTORY_MUSIC));
|
||||||
s.save();
|
s.save();
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
@ -210,17 +223,22 @@ class Settings {
|
||||||
//AudioQuality to deezer int
|
//AudioQuality to deezer int
|
||||||
int getQualityInt(AudioQuality q) {
|
int getQualityInt(AudioQuality q) {
|
||||||
switch (q) {
|
switch (q) {
|
||||||
case AudioQuality.MP3_128: return 1;
|
case AudioQuality.MP3_128:
|
||||||
case AudioQuality.MP3_320: return 3;
|
return 1;
|
||||||
case AudioQuality.FLAC: return 9;
|
case AudioQuality.MP3_320:
|
||||||
default: return 8;
|
return 3;
|
||||||
|
case AudioQuality.FLAC:
|
||||||
|
return 9;
|
||||||
|
default:
|
||||||
|
return 8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Check if is dark, can't use theme directly, because of system themes, and Theme.of(context).brightness broke
|
//Check if is dark, can't use theme directly, because of system themes, and Theme.of(context).brightness broke
|
||||||
bool get isDark {
|
bool get isDark {
|
||||||
if (useSystemTheme) {
|
if (useSystemTheme) {
|
||||||
if (SchedulerBinding.instance.window.platformBrightness == Brightness.light) return false;
|
if (SchedulerBinding.instance.window.platformBrightness ==
|
||||||
|
Brightness.light) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (theme == Themes.Light) return false;
|
if (theme == Themes.Light) return false;
|
||||||
|
|
@ -229,15 +247,31 @@ class Settings {
|
||||||
|
|
||||||
static const deezerBg = Color(0xFF1F1A16);
|
static const deezerBg = Color(0xFF1F1A16);
|
||||||
static const deezerBottom = Color(0xFF1b1714);
|
static const deezerBottom = Color(0xFF1b1714);
|
||||||
TextTheme get _textTheme => (font == 'Deezer') ? null : GoogleFonts.getTextTheme(font);
|
TextTheme get _textTheme =>
|
||||||
|
(font == 'Deezer') ? null : GoogleFonts.getTextTheme(font);
|
||||||
String get _fontFamily => (font == 'Deezer') ? 'MabryPro' : null;
|
String get _fontFamily => (font == 'Deezer') ? 'MabryPro' : null;
|
||||||
|
|
||||||
|
MaterialColor get _primarySwatch =>
|
||||||
|
MaterialColor(primaryColor.value, <int, Color>{
|
||||||
|
50: primaryColor.withOpacity(.1),
|
||||||
|
100: primaryColor.withOpacity(.2),
|
||||||
|
200: primaryColor.withOpacity(.3),
|
||||||
|
300: primaryColor.withOpacity(.4),
|
||||||
|
400: primaryColor.withOpacity(.5),
|
||||||
|
500: primaryColor.withOpacity(.6),
|
||||||
|
600: primaryColor.withOpacity(.7),
|
||||||
|
700: primaryColor.withOpacity(.8),
|
||||||
|
800: primaryColor.withOpacity(.9),
|
||||||
|
900: primaryColor.withOpacity(1),
|
||||||
|
});
|
||||||
|
|
||||||
Map<Themes, ThemeData> get _themeData => {
|
Map<Themes, ThemeData> get _themeData => {
|
||||||
Themes.Light: ThemeData(
|
Themes.Light: ThemeData(
|
||||||
textTheme: _textTheme,
|
textTheme: _textTheme,
|
||||||
fontFamily: _fontFamily,
|
fontFamily: _fontFamily,
|
||||||
brightness: Brightness.light,
|
brightness: Brightness.light,
|
||||||
primaryColor: primaryColor,
|
primaryColor: primaryColor,
|
||||||
|
primarySwatch: _primarySwatch,
|
||||||
accentColor: primaryColor,
|
accentColor: primaryColor,
|
||||||
sliderTheme: _sliderTheme,
|
sliderTheme: _sliderTheme,
|
||||||
toggleableActiveColor: primaryColor,
|
toggleableActiveColor: primaryColor,
|
||||||
|
|
@ -249,6 +283,7 @@ class Settings {
|
||||||
fontFamily: _fontFamily,
|
fontFamily: _fontFamily,
|
||||||
brightness: Brightness.dark,
|
brightness: Brightness.dark,
|
||||||
primaryColor: primaryColor,
|
primaryColor: primaryColor,
|
||||||
|
primarySwatch: _primarySwatch,
|
||||||
accentColor: primaryColor,
|
accentColor: primaryColor,
|
||||||
sliderTheme: _sliderTheme,
|
sliderTheme: _sliderTheme,
|
||||||
toggleableActiveColor: primaryColor,
|
toggleableActiveColor: primaryColor,
|
||||||
|
|
@ -259,6 +294,7 @@ class Settings {
|
||||||
fontFamily: _fontFamily,
|
fontFamily: _fontFamily,
|
||||||
brightness: Brightness.dark,
|
brightness: Brightness.dark,
|
||||||
primaryColor: primaryColor,
|
primaryColor: primaryColor,
|
||||||
|
primarySwatch: _primarySwatch,
|
||||||
accentColor: primaryColor,
|
accentColor: primaryColor,
|
||||||
sliderTheme: _sliderTheme,
|
sliderTheme: _sliderTheme,
|
||||||
toggleableActiveColor: primaryColor,
|
toggleableActiveColor: primaryColor,
|
||||||
|
|
@ -266,17 +302,16 @@ class Settings {
|
||||||
scaffoldBackgroundColor: deezerBg,
|
scaffoldBackgroundColor: deezerBg,
|
||||||
bottomAppBarColor: deezerBottom,
|
bottomAppBarColor: deezerBottom,
|
||||||
dialogBackgroundColor: deezerBottom,
|
dialogBackgroundColor: deezerBottom,
|
||||||
bottomSheetTheme: BottomSheetThemeData(
|
bottomSheetTheme:
|
||||||
backgroundColor: deezerBottom
|
BottomSheetThemeData(backgroundColor: deezerBottom),
|
||||||
),
|
|
||||||
appBarTheme: AppBarTheme(brightness: Brightness.dark),
|
appBarTheme: AppBarTheme(brightness: Brightness.dark),
|
||||||
cardColor: deezerBg
|
cardColor: deezerBg),
|
||||||
),
|
|
||||||
Themes.Black: ThemeData(
|
Themes.Black: ThemeData(
|
||||||
textTheme: _textTheme,
|
textTheme: _textTheme,
|
||||||
fontFamily: _fontFamily,
|
fontFamily: _fontFamily,
|
||||||
brightness: Brightness.dark,
|
brightness: Brightness.dark,
|
||||||
primaryColor: primaryColor,
|
primaryColor: primaryColor,
|
||||||
|
primarySwatch: _primarySwatch,
|
||||||
accentColor: primaryColor,
|
accentColor: primaryColor,
|
||||||
backgroundColor: Colors.black,
|
backgroundColor: Colors.black,
|
||||||
scaffoldBackgroundColor: Colors.black,
|
scaffoldBackgroundColor: Colors.black,
|
||||||
|
|
@ -291,23 +326,15 @@ class Settings {
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
Future<String> getPath() async => p.join((await getApplicationDocumentsDirectory()).path, 'settings.json');
|
Future<String> getPath() async =>
|
||||||
|
p.join((await getApplicationDocumentsDirectory()).path, 'settings.json');
|
||||||
|
|
||||||
//JSON
|
//JSON
|
||||||
factory Settings.fromJson(Map<String, dynamic> json) => _$SettingsFromJson(json);
|
factory Settings.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$SettingsFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$SettingsToJson(this);
|
Map<String, dynamic> toJson() => _$SettingsToJson(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum AudioQuality {
|
enum AudioQuality { MP3_128, MP3_320, FLAC, ASK }
|
||||||
MP3_128,
|
|
||||||
MP3_320,
|
|
||||||
FLAC,
|
|
||||||
ASK
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Themes {
|
enum Themes { Light, Dark, Deezer, Black }
|
||||||
Light,
|
|
||||||
Dark,
|
|
||||||
Deezer,
|
|
||||||
Black
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ import 'package:freezer/translations.i18n.dart';
|
||||||
import 'package:freezer/ui/error.dart';
|
import 'package:freezer/ui/error.dart';
|
||||||
|
|
||||||
class LyricsScreen extends StatefulWidget {
|
class LyricsScreen extends StatefulWidget {
|
||||||
|
|
||||||
final Lyrics lyrics;
|
final Lyrics lyrics;
|
||||||
final String trackId;
|
final String trackId;
|
||||||
|
|
||||||
|
|
@ -22,7 +21,6 @@ class LyricsScreen extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _LyricsScreenState extends State<LyricsScreen> {
|
class _LyricsScreenState extends State<LyricsScreen> {
|
||||||
|
|
||||||
Lyrics lyrics;
|
Lyrics lyrics;
|
||||||
bool _loading = true;
|
bool _loading = true;
|
||||||
bool _error = false;
|
bool _error = false;
|
||||||
|
|
@ -33,6 +31,8 @@ class _LyricsScreenState extends State<LyricsScreen> {
|
||||||
StreamSubscription _mediaItemSub;
|
StreamSubscription _mediaItemSub;
|
||||||
final double height = 90;
|
final double height = 90;
|
||||||
|
|
||||||
|
bool _freeScroll = false;
|
||||||
|
|
||||||
Future _load() async {
|
Future _load() async {
|
||||||
//Already available
|
//Already available
|
||||||
if (this.lyrics != null) return;
|
if (this.lyrics != null) return;
|
||||||
|
|
@ -59,17 +59,27 @@ class _LyricsScreenState extends State<LyricsScreen> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _scrollToLyric() {
|
||||||
|
//Lyric height, screen height, appbar height
|
||||||
|
double _scrollTo = (height * _currentIndex) -
|
||||||
|
(MediaQuery.of(context).size.height / 2) +
|
||||||
|
(height / 2) +
|
||||||
|
56;
|
||||||
|
if (0 > _scrollTo) return;
|
||||||
|
_controller.animateTo(_scrollTo,
|
||||||
|
duration: Duration(milliseconds: 250), curve: Curves.ease);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
_load();
|
_load();
|
||||||
|
|
||||||
//Enable visualizer
|
//Enable visualizer
|
||||||
if (settings.lyricsVisualizer)
|
if (settings.lyricsVisualizer) playerHelper.startVisualizer();
|
||||||
playerHelper.startVisualizer();
|
|
||||||
|
|
||||||
Timer.periodic(Duration(milliseconds: 350), (timer) {
|
Timer.periodic(Duration(milliseconds: 350), (timer) {
|
||||||
_timer = timer;
|
_timer = timer;
|
||||||
_currentIndex = lyrics?.lyrics?.lastIndexWhere((l) => l.offset <= AudioService.playbackState.currentPosition);
|
_currentIndex = lyrics?.lyrics?.lastIndexWhere(
|
||||||
|
(l) => l.offset <= AudioService.playbackState.currentPosition);
|
||||||
if (_loading) return;
|
if (_loading) return;
|
||||||
//Scroll to current lyric
|
//Scroll to current lyric
|
||||||
if (_currentIndex <= 0) return;
|
if (_currentIndex <= 0) return;
|
||||||
|
|
@ -77,20 +87,13 @@ class _LyricsScreenState extends State<LyricsScreen> {
|
||||||
//Update current lyric index
|
//Update current lyric index
|
||||||
setState(() => null);
|
setState(() => null);
|
||||||
_prevIndex = _currentIndex;
|
_prevIndex = _currentIndex;
|
||||||
//Lyric height, screen height, appbar height
|
if (_freeScroll) return;
|
||||||
double _scrollTo = (height * _currentIndex) - (MediaQuery.of(context).size.height / 2) + (height / 2) + 56;
|
_scrollToLyric();
|
||||||
if (0 > _scrollTo) return;
|
|
||||||
_controller.animateTo(
|
|
||||||
_scrollTo,
|
|
||||||
duration: Duration(milliseconds: 250),
|
|
||||||
curve: Curves.ease
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
//Track change = exit lyrics
|
//Track change = exit lyrics
|
||||||
_mediaItemSub = AudioService.currentMediaItemStream.listen((event) {
|
_mediaItemSub = AudioService.currentMediaItemStream.listen((event) {
|
||||||
if (event.id != widget.trackId)
|
if (event.id != widget.trackId) Navigator.of(context).pop();
|
||||||
Navigator.of(context).pop();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
@ -98,21 +101,50 @@ class _LyricsScreenState extends State<LyricsScreen> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
if (_timer != null)
|
if (_timer != null) _timer.cancel();
|
||||||
_timer.cancel();
|
if (_mediaItemSub != null) _mediaItemSub.cancel();
|
||||||
if (_mediaItemSub != null)
|
|
||||||
_mediaItemSub.cancel();
|
|
||||||
//Stop visualizer
|
//Stop visualizer
|
||||||
if (settings.lyricsVisualizer)
|
if (settings.lyricsVisualizer) playerHelper.stopVisualizer();
|
||||||
playerHelper.stopVisualizer();
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: FreezerAppBar('Lyrics'.i18n),
|
appBar: FreezerAppBar('Lyrics'.i18n,
|
||||||
|
height: _freeScroll ? 100 : 56,
|
||||||
|
bottom: _freeScroll
|
||||||
|
? PreferredSize(
|
||||||
|
preferredSize: Size.fromHeight(46),
|
||||||
|
child: Theme(
|
||||||
|
data: settings.themeData.copyWith(
|
||||||
|
textButtonTheme: TextButtonThemeData(
|
||||||
|
style: ButtonStyle(
|
||||||
|
foregroundColor: MaterialStateProperty.all(
|
||||||
|
Colors.white)))),
|
||||||
|
child: Container(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
setState(() => _freeScroll = false);
|
||||||
|
_scrollToLyric();
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
_currentIndex >= 0
|
||||||
|
? lyrics.lyrics[_currentIndex].text
|
||||||
|
: '...',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
style: ButtonStyle(
|
||||||
|
foregroundColor:
|
||||||
|
MaterialStateProperty.all(Colors.white)))
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
))
|
||||||
|
: null),
|
||||||
body: Stack(
|
body: Stack(
|
||||||
children: [
|
children: [
|
||||||
//Visualizer
|
//Visualizer
|
||||||
|
|
@ -123,68 +155,97 @@ class _LyricsScreenState extends State<LyricsScreen> {
|
||||||
stream: playerHelper.visualizerStream,
|
stream: playerHelper.visualizerStream,
|
||||||
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
||||||
List<double> data = snapshot.data ?? [];
|
List<double> data = snapshot.data ?? [];
|
||||||
double width = MediaQuery.of(context).size.width / data.length - 0.25;
|
double width =
|
||||||
|
MediaQuery.of(context).size.width / data.length -
|
||||||
|
0.25;
|
||||||
return Row(
|
return Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: List.generate(data.length, (i) => AnimatedContainer(
|
children: List.generate(
|
||||||
|
data.length,
|
||||||
|
(i) => AnimatedContainer(
|
||||||
duration: Duration(milliseconds: 130),
|
duration: Duration(milliseconds: 130),
|
||||||
color: Theme.of(context).primaryColor,
|
color: Theme.of(context).primaryColor,
|
||||||
height: data[i] * 100,
|
height: data[i] * 100,
|
||||||
width: width,
|
width: width,
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
}
|
}),
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
|
||||||
//Lyrics
|
//Lyrics
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.fromLTRB(0, 0, 0, settings.lyricsVisualizer ? 100 : 0),
|
padding: EdgeInsets.fromLTRB(
|
||||||
child: _error ?
|
0, 0, 0, settings.lyricsVisualizer ? 100 : 0),
|
||||||
|
child: _error
|
||||||
|
?
|
||||||
//Shouldn't really happen, empty lyrics have own text
|
//Shouldn't really happen, empty lyrics have own text
|
||||||
ErrorScreen() :
|
ErrorScreen()
|
||||||
|
:
|
||||||
// Loading
|
// Loading
|
||||||
_loading ? Padding(
|
_loading
|
||||||
|
? Padding(
|
||||||
padding: EdgeInsets.all(8.0),
|
padding: EdgeInsets.all(8.0),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [CircularProgressIndicator()],
|
||||||
CircularProgressIndicator()
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
) : ListView.builder(
|
)
|
||||||
|
: NotificationListener(
|
||||||
|
onNotification: (Notification notification) {
|
||||||
|
if (notification is! ScrollEndNotification)
|
||||||
|
return false;
|
||||||
|
if (_freeScroll) return false;
|
||||||
|
double _currentScroll = _controller.position.pixels;
|
||||||
|
double _expectedScroll = (height * _currentIndex) -
|
||||||
|
(MediaQuery.of(context).size.height / 2) +
|
||||||
|
(height / 2) +
|
||||||
|
56;
|
||||||
|
print(
|
||||||
|
'current: $_currentScroll, expected: $_expectedScroll');
|
||||||
|
if (_currentScroll != _expectedScroll)
|
||||||
|
setState(() => _freeScroll = true);
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
child: ListView.builder(
|
||||||
controller: _controller,
|
controller: _controller,
|
||||||
itemCount: lyrics.lyrics.length,
|
itemCount: lyrics.lyrics.length,
|
||||||
itemBuilder: (BuildContext context, int i) {
|
itemBuilder: (BuildContext context, int i) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 8.0),
|
padding:
|
||||||
|
EdgeInsets.symmetric(horizontal: 8.0),
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(8.0),
|
borderRadius:
|
||||||
color: _currentIndex == i ? Colors.grey.withOpacity(0.25) : Colors.transparent,
|
BorderRadius.circular(8.0),
|
||||||
|
color: _currentIndex == i
|
||||||
|
? Colors.grey.withOpacity(0.25)
|
||||||
|
: Colors.transparent,
|
||||||
),
|
),
|
||||||
height: height,
|
height: height,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
borderRadius: BorderRadius.circular(8.0),
|
borderRadius:
|
||||||
onTap: lyrics.id != null ? () => AudioService.seekTo(lyrics.lyrics[i].offset) : null,
|
BorderRadius.circular(8.0),
|
||||||
|
onTap: lyrics.id != null
|
||||||
|
? () => AudioService.seekTo(
|
||||||
|
lyrics.lyrics[i].offset)
|
||||||
|
: null,
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
lyrics.lyrics[i].text,
|
lyrics.lyrics[i].text,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 26.0,
|
fontSize: 26.0,
|
||||||
fontWeight: (_currentIndex == i) ? FontWeight.bold : FontWeight.normal
|
fontWeight:
|
||||||
|
(_currentIndex == i)
|
||||||
|
? FontWeight.bold
|
||||||
|
: FontWeight.normal),
|
||||||
),
|
),
|
||||||
),
|
))));
|
||||||
))
|
|
||||||
)
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
),
|
)),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
)
|
));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -141,13 +141,6 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.0"
|
version: "0.2.0"
|
||||||
clipboard:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: clipboard
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "0.1.2+8"
|
|
||||||
clock:
|
clock:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue