Safearea fix for Home widgets

This commit is contained in:
Kingkor Roy Tirtho 2022-07-05 14:48:02 +06:00
parent 42257d9615
commit e2bba4ac94
6 changed files with 353 additions and 330 deletions

View File

@ -94,82 +94,79 @@ class Home extends HookConsumerWidget {
return null; return null;
}, [backgroundColor]); }, [backgroundColor]);
return SafeArea( return Scaffold(
child: Scaffold( body: Column(
body: Column( children: [
children: [ if (_selectedIndex.value != 3)
if (_selectedIndex.value != 3) kIsMobile
kIsMobile ? titleBarContents
? titleBarContents : WindowTitleBarBox(child: titleBarContents),
: WindowTitleBarBox(child: titleBarContents), Expanded(
Expanded( child: Row(
child: Row( children: [
children: [ Sidebar(
Sidebar( selectedIndex: _selectedIndex.value,
selectedIndex: _selectedIndex.value, onSelectedIndexChanged: _onSelectedIndexChanged,
onSelectedIndexChanged: _onSelectedIndexChanged, ),
), // contents of the spotify
// contents of the spotify if (_selectedIndex.value == 0)
if (_selectedIndex.value == 0) Expanded(
Expanded( child: Padding(
child: Padding( padding: const EdgeInsets.only(
padding: const EdgeInsets.only( bottom: 8.0,
bottom: 8.0, top: 8.0,
top: 8.0, left: 8.0,
left: 8.0,
),
child: HookBuilder(builder: (context) {
final pagingController = usePaginatedFutureProvider<
Page<Category>, int, Category>(
(pageKey) => categoriesQuery(pageKey),
ref: ref,
firstPageKey: 0,
onData: (categories, pagingController, pageKey) {
final items = categories.items?.toList();
if (pageKey == 0) {
Category category = Category();
category.id = "user-featured-playlists";
category.name = "Featured";
items?.insert(0, category);
}
if (categories.isLast && items != null) {
pagingController.appendLastPage(items);
} else if (categories.items != null) {
pagingController.appendPage(
items!, categories.nextOffset);
}
},
);
return PagedListView(
pagingController: pagingController,
builderDelegate:
PagedChildBuilderDelegate<Category>(
firstPageProgressIndicatorBuilder: (_) =>
const ShimmerCategories(),
newPageProgressIndicatorBuilder: (_) =>
const ShimmerCategories(),
itemBuilder: (context, item, index) {
return CategoryCard(item);
},
),
);
}),
), ),
child: HookBuilder(builder: (context) {
final pagingController = usePaginatedFutureProvider<
Page<Category>, int, Category>(
(pageKey) => categoriesQuery(pageKey),
ref: ref,
firstPageKey: 0,
onData: (categories, pagingController, pageKey) {
final items = categories.items?.toList();
if (pageKey == 0) {
Category category = Category();
category.id = "user-featured-playlists";
category.name = "Featured";
items?.insert(0, category);
}
if (categories.isLast && items != null) {
pagingController.appendLastPage(items);
} else if (categories.items != null) {
pagingController.appendPage(
items!, categories.nextOffset);
}
},
);
return PagedListView(
pagingController: pagingController,
builderDelegate: PagedChildBuilderDelegate<Category>(
firstPageProgressIndicatorBuilder: (_) =>
const ShimmerCategories(),
newPageProgressIndicatorBuilder: (_) =>
const ShimmerCategories(),
itemBuilder: (context, item, index) {
return CategoryCard(item);
},
),
);
}),
), ),
if (_selectedIndex.value == 1) const Search(), ),
if (_selectedIndex.value == 2) const UserLibrary(), if (_selectedIndex.value == 1) const Search(),
if (_selectedIndex.value == 3) const SyncedLyrics(), if (_selectedIndex.value == 2) const UserLibrary(),
], if (_selectedIndex.value == 3) const SyncedLyrics(),
), ],
), ),
// player itself ),
Player(), // player itself
SpotubeNavigationBar( Player(),
selectedIndex: _selectedIndex.value, SpotubeNavigationBar(
onSelectedIndexChanged: _onSelectedIndexChanged, selectedIndex: _selectedIndex.value,
), onSelectedIndexChanged: _onSelectedIndexChanged,
], ),
), ],
), ),
); );
} }

View File

@ -15,24 +15,27 @@ class UserLibrary extends ConsumerWidget {
return Expanded( return Expanded(
child: DefaultTabController( child: DefaultTabController(
length: 3, length: 3,
child: Scaffold( child: SafeArea(
appBar: TabBar( child: Scaffold(
indicator: const BoxDecoration(color: Colors.transparent), appBar: TabBar(
labelColor: Theme.of(context).primaryColor, indicator: const BoxDecoration(color: Colors.transparent),
unselectedLabelColor: Theme.of(context).textTheme.bodyText1?.color, labelColor: Theme.of(context).primaryColor,
tabs: const [ unselectedLabelColor:
Tab(text: "Playlist"), Theme.of(context).textTheme.bodyText1?.color,
Tab(text: "Artists"), tabs: const [
Tab(text: "Album"), Tab(text: "Playlist"),
], Tab(text: "Artists"),
Tab(text: "Album"),
],
),
body: auth.isLoggedIn
? TabBarView(children: [
const UserPlaylists(),
UserArtists(),
const UserAlbums(),
])
: const AnonymousFallback(),
), ),
body: auth.isLoggedIn
? TabBarView(children: [
const UserPlaylists(),
UserArtists(),
const UserAlbums(),
])
: const AnonymousFallback(),
), ),
), ),
); );

View File

@ -52,7 +52,7 @@ class Lyrics extends HookConsumerWidget {
return Text( return Text(
lyrics == null && playback.track == null lyrics == null && playback.track == null
? "No Track being played currently" ? "No Track being played currently"
: lyrics!, : lyrics ?? "",
style: textTheme.headline6 style: textTheme.headline6
?.copyWith(color: textTheme.headline1?.color), ?.copyWith(color: textTheme.headline1?.color),
); );

View File

@ -14,6 +14,7 @@ import 'package:spotube/helpers/artist-to-string.dart';
import 'package:spotube/helpers/image-to-url-string.dart'; import 'package:spotube/helpers/image-to-url-string.dart';
import 'package:spotube/hooks/useAutoScrollController.dart'; import 'package:spotube/hooks/useAutoScrollController.dart';
import 'package:spotube/hooks/useBreakpoints.dart'; import 'package:spotube/hooks/useBreakpoints.dart';
import 'package:spotube/hooks/useCustomStatusBarColor.dart';
import 'package:spotube/hooks/usePaletteColor.dart'; import 'package:spotube/hooks/usePaletteColor.dart';
import 'package:spotube/hooks/useSyncedLyrics.dart'; import 'package:spotube/hooks/useSyncedLyrics.dart';
import 'package:spotube/provider/Playback.dart'; import 'package:spotube/provider/Playback.dart';
@ -110,6 +111,12 @@ class SyncedLyrics extends HookConsumerWidget {
: textTheme.headline4?.copyWith(fontSize: 25)) : textTheme.headline4?.copyWith(fontSize: 25))
?.copyWith(color: palette.titleTextColor); ?.copyWith(color: palette.titleTextColor);
useCustomStatusBarColor(
palette.color,
true,
noSetBGColor: true,
);
return Expanded( return Expanded(
child: Container( child: Container(
clipBehavior: Clip.hardEdge, clipBehavior: Clip.hardEdge,
@ -126,83 +133,85 @@ class SyncedLyrics extends HookConsumerWidget {
filter: ImageFilter.blur(sigmaX: 15, sigmaY: 15), filter: ImageFilter.blur(sigmaX: 15, sigmaY: 15),
child: Container( child: Container(
color: palette.color.withOpacity(.7), color: palette.color.withOpacity(.7),
child: failed.value child: SafeArea(
? Lyrics(titleBarForegroundColor: palette.bodyTextColor) child: failed.value
: Column( ? Lyrics(titleBarForegroundColor: palette.bodyTextColor)
children: [ : Column(
PageWindowTitleBar( children: [
foregroundColor: palette.bodyTextColor, PageWindowTitleBar(
), foregroundColor: palette.bodyTextColor,
Center(
child: SizedBox(
height: breakpoint >= Breakpoints.md ? 50 : 30,
child: playback.track?.name != null &&
playback.track!.name!.length > 29
? SpotubeMarqueeText(
text: playback.track?.name ?? "Not Playing",
style: headlineTextStyle,
)
: Text(
playback.track?.name ?? "Not Playing",
style: headlineTextStyle,
),
)),
Center(
child: Text(
artistsToString<Artist>(
playback.track?.artists ?? []),
style: breakpoint >= Breakpoints.md
? textTheme.headline5
: textTheme.headline6,
), ),
), Center(
if (lyricValue != null && lyricValue.lyrics.isNotEmpty) child: SizedBox(
Expanded( height: breakpoint >= Breakpoints.md ? 50 : 30,
child: ListView.builder( child: playback.track?.name != null &&
controller: controller, playback.track!.name!.length > 29
itemCount: lyricValue.lyrics.length, ? SpotubeMarqueeText(
itemBuilder: (context, index) { text: playback.track?.name ?? "Not Playing",
final lyricSlice = lyricValue.lyrics[index]; style: headlineTextStyle,
final isActive = )
lyricSlice.time.inSeconds == currentTime; : Text(
if (isActive) { playback.track?.name ?? "Not Playing",
controller.scrollToIndex( style: headlineTextStyle,
index,
preferPosition: AutoScrollPosition.middle,
);
}
return AutoScrollTag(
key: ValueKey(index),
index: index,
controller: controller,
child: Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
lyricSlice.text,
style: TextStyle(
// indicating the active state of that lyric slice
color: isActive
? Theme.of(context).primaryColor
: palette.bodyTextColor,
fontWeight:
isActive ? FontWeight.bold : null,
fontSize: 30,
),
textAlign: TextAlign.center,
),
),
), ),
); )),
}, Center(
child: Text(
artistsToString<Artist>(
playback.track?.artists ?? []),
style: breakpoint >= Breakpoints.md
? textTheme.headline5
: textTheme.headline6,
), ),
), ),
if (playback.track != null && if (lyricValue != null && lyricValue.lyrics.isNotEmpty)
(lyricValue == null || Expanded(
lyricValue.lyrics.isEmpty == true)) child: ListView.builder(
const Expanded(child: ShimmerLyrics()), controller: controller,
], itemCount: lyricValue.lyrics.length,
), itemBuilder: (context, index) {
final lyricSlice = lyricValue.lyrics[index];
final isActive =
lyricSlice.time.inSeconds == currentTime;
if (isActive) {
controller.scrollToIndex(
index,
preferPosition: AutoScrollPosition.middle,
);
}
return AutoScrollTag(
key: ValueKey(index),
index: index,
controller: controller,
child: Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
lyricSlice.text,
style: TextStyle(
// indicating the active state of that lyric slice
color: isActive
? Theme.of(context).primaryColor
: palette.bodyTextColor,
fontWeight:
isActive ? FontWeight.bold : null,
fontSize: 30,
),
textAlign: TextAlign.center,
),
),
),
);
},
),
),
if (playback.track != null &&
(lyricValue == null ||
lyricValue.lyrics.isEmpty == true))
const Expanded(child: ShimmerLyrics()),
],
),
),
), ),
), ),
), ),

View File

@ -120,13 +120,22 @@ class PlayerView extends HookConsumerWidget {
)..repeat(); )..repeat();
return RotationTransition( return RotationTransition(
turns: Tween(begin: 0.0, end: 1.0).animate(controller), turns: Tween(begin: 0.0, end: 1.0).animate(controller),
child: CircleAvatar( child: Container(
backgroundImage: CachedNetworkImageProvider( decoration: BoxDecoration(
albumArt, border: Border.all(
cacheKey: albumArt, color: paletteColor.titleTextColor,
width: 2,
),
shape: BoxShape.circle,
),
child: CircleAvatar(
backgroundImage: CachedNetworkImageProvider(
albumArt,
cacheKey: albumArt,
),
radius: MediaQuery.of(context).size.width *
(breakpoint.isSm ? 0.4 : 0.3),
), ),
radius: MediaQuery.of(context).size.width *
(breakpoint.isSm ? 0.4 : 0.3),
), ),
); );
}), }),

View File

@ -37,183 +37,188 @@ class Search extends HookConsumerWidget {
} }
final searchSnapshot = ref.watch(searchQuery(searchTerm)); final searchSnapshot = ref.watch(searchQuery(searchTerm));
return Expanded( return SafeArea(
child: Container( child: Expanded(
color: Theme.of(context).backgroundColor, child: Container(
child: Column( color: Theme.of(context).backgroundColor,
children: [ child: Column(
Padding( children: [
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), Padding(
child: Row( padding:
children: [ const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
Expanded( child: Row(
child: TextField( children: [
controller: controller, Expanded(
decoration: const InputDecoration(hintText: "Search..."), child: TextField(
onSubmitted: (value) { controller: controller,
decoration:
const InputDecoration(hintText: "Search..."),
onSubmitted: (value) {
ref.read(searchTermStateProvider.notifier).state =
controller.value.text;
},
),
),
const SizedBox(width: 5),
MaterialButton(
elevation: 3,
splashColor: Theme.of(context).primaryColor,
padding: const EdgeInsets.symmetric(vertical: 21),
color: Theme.of(context).primaryColor,
textColor: Colors.white,
child: const Icon(Icons.search_rounded),
onPressed: () {
ref.read(searchTermStateProvider.notifier).state = ref.read(searchTermStateProvider.notifier).state =
controller.value.text; controller.value.text;
}, },
), ),
), ],
const SizedBox(width: 5), ),
MaterialButton(
elevation: 3,
splashColor: Theme.of(context).primaryColor,
padding: const EdgeInsets.symmetric(vertical: 21),
color: Theme.of(context).primaryColor,
textColor: Colors.white,
child: const Icon(Icons.search_rounded),
onPressed: () {
ref.read(searchTermStateProvider.notifier).state =
controller.value.text;
},
),
],
), ),
), searchSnapshot.when(
searchSnapshot.when( data: (data) {
data: (data) { Playback playback = ref.watch(playbackProvider);
Playback playback = ref.watch(playbackProvider); List<AlbumSimple> albums = [];
List<AlbumSimple> albums = []; List<Artist> artists = [];
List<Artist> artists = []; List<Track> tracks = [];
List<Track> tracks = []; List<PlaylistSimple> playlists = [];
List<PlaylistSimple> playlists = []; for (MapEntry<int, Page> page in data.asMap().entries) {
for (MapEntry<int, Page> page in data.asMap().entries) { for (var item in page.value.items ?? []) {
for (var item in page.value.items ?? []) { if (item is AlbumSimple) {
if (item is AlbumSimple) { albums.add(item);
albums.add(item); } else if (item is PlaylistSimple) {
} else if (item is PlaylistSimple) { playlists.add(item);
playlists.add(item); } else if (item is Artist) {
} else if (item is Artist) { artists.add(item);
artists.add(item); } else if (item is Track) {
} else if (item is Track) { tracks.add(item);
tracks.add(item); }
} }
} }
} return Expanded(
return Expanded( child: SingleChildScrollView(
child: SingleChildScrollView( child: Padding(
child: Padding( padding: const EdgeInsets.symmetric(
padding: const EdgeInsets.symmetric( vertical: 8, horizontal: 20),
vertical: 8, horizontal: 20), child: Column(
child: Column( crossAxisAlignment: CrossAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, children: [
children: [ if (tracks.isNotEmpty)
if (tracks.isNotEmpty) Text(
Text( "Songs",
"Songs", style: Theme.of(context).textTheme.headline5,
style: Theme.of(context).textTheme.headline5,
),
...tracks.asMap().entries.map((track) {
String duration =
"${track.value.duration?.inMinutes.remainder(60)}:${zeroPadNumStr(track.value.duration?.inSeconds.remainder(60) ?? 0)}";
return TrackTile(
playback,
track: track,
duration: duration,
thumbnailUrl:
imageToUrlString(track.value.album?.images),
isActive: playback.track?.id == track.value.id,
onTrackPlayButtonPressed: (currentTrack) async {
var isPlaylistPlaying = playback.playlist?.id !=
null &&
playback.playlist?.id == currentTrack.id;
if (!isPlaylistPlaying) {
playback.playPlaylist(
CurrentPlaylist(
tracks: [currentTrack],
id: currentTrack.id!,
name: currentTrack.name!,
thumbnail: imageToUrlString(
currentTrack.album?.images),
),
);
} else if (isPlaylistPlaying &&
currentTrack.id != null &&
currentTrack.id != playback.track?.id) {
playback.play(currentTrack);
}
},
);
}),
if (albums.isNotEmpty)
Text(
"Albums",
style: Theme.of(context).textTheme.headline5,
),
const SizedBox(height: 10),
Scrollbar(
controller: albumController,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
controller: albumController,
child: Row(
children: albums.map((album) {
return AlbumCard(simpleAlbumToAlbum(album));
}).toList(),
), ),
), ...tracks.asMap().entries.map((track) {
), String duration =
const SizedBox(height: 20), "${track.value.duration?.inMinutes.remainder(60)}:${zeroPadNumStr(track.value.duration?.inSeconds.remainder(60) ?? 0)}";
if (artists.isNotEmpty) return TrackTile(
Text( playback,
"Artists", track: track,
style: Theme.of(context).textTheme.headline5, duration: duration,
), thumbnailUrl:
const SizedBox(height: 10), imageToUrlString(track.value.album?.images),
Scrollbar( isActive: playback.track?.id == track.value.id,
controller: artistController, onTrackPlayButtonPressed: (currentTrack) async {
child: SingleChildScrollView( var isPlaylistPlaying =
scrollDirection: Axis.horizontal, playback.playlist?.id != null &&
controller: artistController, playback.playlist?.id ==
child: Row( currentTrack.id;
children: artists if (!isPlaylistPlaying) {
.map( playback.playPlaylist(
(artist) => Container( CurrentPlaylist(
margin: const EdgeInsets.symmetric( tracks: [currentTrack],
horizontal: 15), id: currentTrack.id!,
child: ArtistCard(artist), name: currentTrack.name!,
thumbnail: imageToUrlString(
currentTrack.album?.images),
), ),
) );
.toList(), } else if (isPlaylistPlaying &&
currentTrack.id != null &&
currentTrack.id != playback.track?.id) {
playback.play(currentTrack);
}
},
);
}),
if (albums.isNotEmpty)
Text(
"Albums",
style: Theme.of(context).textTheme.headline5,
),
const SizedBox(height: 10),
Scrollbar(
controller: albumController,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
controller: albumController,
child: Row(
children: albums.map((album) {
return AlbumCard(simpleAlbumToAlbum(album));
}).toList(),
),
), ),
), ),
), const SizedBox(height: 20),
const SizedBox(height: 20), if (artists.isNotEmpty)
if (playlists.isNotEmpty) Text(
Text( "Artists",
"Playlists", style: Theme.of(context).textTheme.headline5,
style: Theme.of(context).textTheme.headline5, ),
const SizedBox(height: 10),
Scrollbar(
controller: artistController,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
controller: artistController,
child: Row(
children: artists
.map(
(artist) => Container(
margin: const EdgeInsets.symmetric(
horizontal: 15),
child: ArtistCard(artist),
),
)
.toList(),
),
),
), ),
const SizedBox(height: 10), const SizedBox(height: 20),
Scrollbar( if (playlists.isNotEmpty)
scrollbarOrientation: breakpoint > Breakpoints.md Text(
? ScrollbarOrientation.bottom "Playlists",
: ScrollbarOrientation.top, style: Theme.of(context).textTheme.headline5,
controller: playlistController, ),
child: SingleChildScrollView( const SizedBox(height: 10),
scrollDirection: Axis.horizontal, Scrollbar(
scrollbarOrientation: breakpoint > Breakpoints.md
? ScrollbarOrientation.bottom
: ScrollbarOrientation.top,
controller: playlistController, controller: playlistController,
child: Row( child: SingleChildScrollView(
children: playlists scrollDirection: Axis.horizontal,
.map( controller: playlistController,
(playlist) => PlaylistCard(playlist), child: Row(
) children: playlists
.toList(), .map(
(playlist) => PlaylistCard(playlist),
)
.toList(),
),
), ),
), ),
), ],
], ),
), ),
), ),
), );
); },
}, error: (error, __) => Text("Error $error"),
error: (error, __) => Text("Error $error"), loading: () => const CircularProgressIndicator(),
loading: () => const CircularProgressIndicator(), )
) ],
], ),
), ),
), ),
); );