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;
}, [backgroundColor]);
return SafeArea(
child: Scaffold(
body: Column(
children: [
if (_selectedIndex.value != 3)
kIsMobile
? titleBarContents
: WindowTitleBarBox(child: titleBarContents),
Expanded(
child: Row(
children: [
Sidebar(
selectedIndex: _selectedIndex.value,
onSelectedIndexChanged: _onSelectedIndexChanged,
),
// contents of the spotify
if (_selectedIndex.value == 0)
Expanded(
child: Padding(
padding: const EdgeInsets.only(
bottom: 8.0,
top: 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);
},
),
);
}),
return Scaffold(
body: Column(
children: [
if (_selectedIndex.value != 3)
kIsMobile
? titleBarContents
: WindowTitleBarBox(child: titleBarContents),
Expanded(
child: Row(
children: [
Sidebar(
selectedIndex: _selectedIndex.value,
onSelectedIndexChanged: _onSelectedIndexChanged,
),
// contents of the spotify
if (_selectedIndex.value == 0)
Expanded(
child: Padding(
padding: const EdgeInsets.only(
bottom: 8.0,
top: 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);
},
),
);
}),
),
if (_selectedIndex.value == 1) const Search(),
if (_selectedIndex.value == 2) const UserLibrary(),
if (_selectedIndex.value == 3) const SyncedLyrics(),
],
),
),
if (_selectedIndex.value == 1) const Search(),
if (_selectedIndex.value == 2) const UserLibrary(),
if (_selectedIndex.value == 3) const SyncedLyrics(),
],
),
// player itself
Player(),
SpotubeNavigationBar(
selectedIndex: _selectedIndex.value,
onSelectedIndexChanged: _onSelectedIndexChanged,
),
],
),
),
// player itself
Player(),
SpotubeNavigationBar(
selectedIndex: _selectedIndex.value,
onSelectedIndexChanged: _onSelectedIndexChanged,
),
],
),
);
}

View File

@ -15,24 +15,27 @@ class UserLibrary extends ConsumerWidget {
return Expanded(
child: DefaultTabController(
length: 3,
child: Scaffold(
appBar: TabBar(
indicator: const BoxDecoration(color: Colors.transparent),
labelColor: Theme.of(context).primaryColor,
unselectedLabelColor: Theme.of(context).textTheme.bodyText1?.color,
tabs: const [
Tab(text: "Playlist"),
Tab(text: "Artists"),
Tab(text: "Album"),
],
child: SafeArea(
child: Scaffold(
appBar: TabBar(
indicator: const BoxDecoration(color: Colors.transparent),
labelColor: Theme.of(context).primaryColor,
unselectedLabelColor:
Theme.of(context).textTheme.bodyText1?.color,
tabs: const [
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(
lyrics == null && playback.track == null
? "No Track being played currently"
: lyrics!,
: lyrics ?? "",
style: textTheme.headline6
?.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/hooks/useAutoScrollController.dart';
import 'package:spotube/hooks/useBreakpoints.dart';
import 'package:spotube/hooks/useCustomStatusBarColor.dart';
import 'package:spotube/hooks/usePaletteColor.dart';
import 'package:spotube/hooks/useSyncedLyrics.dart';
import 'package:spotube/provider/Playback.dart';
@ -110,6 +111,12 @@ class SyncedLyrics extends HookConsumerWidget {
: textTheme.headline4?.copyWith(fontSize: 25))
?.copyWith(color: palette.titleTextColor);
useCustomStatusBarColor(
palette.color,
true,
noSetBGColor: true,
);
return Expanded(
child: Container(
clipBehavior: Clip.hardEdge,
@ -126,83 +133,85 @@ class SyncedLyrics extends HookConsumerWidget {
filter: ImageFilter.blur(sigmaX: 15, sigmaY: 15),
child: Container(
color: palette.color.withOpacity(.7),
child: failed.value
? Lyrics(titleBarForegroundColor: palette.bodyTextColor)
: Column(
children: [
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,
child: SafeArea(
child: failed.value
? Lyrics(titleBarForegroundColor: palette.bodyTextColor)
: Column(
children: [
PageWindowTitleBar(
foregroundColor: palette.bodyTextColor,
),
),
if (lyricValue != null && lyricValue.lyrics.isNotEmpty)
Expanded(
child: ListView.builder(
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,
),
),
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,
),
),
if (playback.track != null &&
(lyricValue == null ||
lyricValue.lyrics.isEmpty == true))
const Expanded(child: ShimmerLyrics()),
],
),
if (lyricValue != null && lyricValue.lyrics.isNotEmpty)
Expanded(
child: ListView.builder(
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();
return RotationTransition(
turns: Tween(begin: 0.0, end: 1.0).animate(controller),
child: CircleAvatar(
backgroundImage: CachedNetworkImageProvider(
albumArt,
cacheKey: albumArt,
child: Container(
decoration: BoxDecoration(
border: Border.all(
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));
return Expanded(
child: Container(
color: Theme.of(context).backgroundColor,
child: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
child: Row(
children: [
Expanded(
child: TextField(
controller: controller,
decoration: const InputDecoration(hintText: "Search..."),
onSubmitted: (value) {
return SafeArea(
child: Expanded(
child: Container(
color: Theme.of(context).backgroundColor,
child: Column(
children: [
Padding(
padding:
const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
child: Row(
children: [
Expanded(
child: TextField(
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 =
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(
data: (data) {
Playback playback = ref.watch(playbackProvider);
List<AlbumSimple> albums = [];
List<Artist> artists = [];
List<Track> tracks = [];
List<PlaylistSimple> playlists = [];
for (MapEntry<int, Page> page in data.asMap().entries) {
for (var item in page.value.items ?? []) {
if (item is AlbumSimple) {
albums.add(item);
} else if (item is PlaylistSimple) {
playlists.add(item);
} else if (item is Artist) {
artists.add(item);
} else if (item is Track) {
tracks.add(item);
searchSnapshot.when(
data: (data) {
Playback playback = ref.watch(playbackProvider);
List<AlbumSimple> albums = [];
List<Artist> artists = [];
List<Track> tracks = [];
List<PlaylistSimple> playlists = [];
for (MapEntry<int, Page> page in data.asMap().entries) {
for (var item in page.value.items ?? []) {
if (item is AlbumSimple) {
albums.add(item);
} else if (item is PlaylistSimple) {
playlists.add(item);
} else if (item is Artist) {
artists.add(item);
} else if (item is Track) {
tracks.add(item);
}
}
}
}
return Expanded(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 8, horizontal: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (tracks.isNotEmpty)
Text(
"Songs",
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(),
return Expanded(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 8, horizontal: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (tracks.isNotEmpty)
Text(
"Songs",
style: Theme.of(context).textTheme.headline5,
),
),
),
const SizedBox(height: 20),
if (artists.isNotEmpty)
Text(
"Artists",
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),
...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),
),
)
.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),
if (playlists.isNotEmpty)
Text(
"Playlists",
style: Theme.of(context).textTheme.headline5,
const SizedBox(height: 20),
if (artists.isNotEmpty)
Text(
"Artists",
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),
Scrollbar(
scrollbarOrientation: breakpoint > Breakpoints.md
? ScrollbarOrientation.bottom
: ScrollbarOrientation.top,
controller: playlistController,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
const SizedBox(height: 20),
if (playlists.isNotEmpty)
Text(
"Playlists",
style: Theme.of(context).textTheme.headline5,
),
const SizedBox(height: 10),
Scrollbar(
scrollbarOrientation: breakpoint > Breakpoints.md
? ScrollbarOrientation.bottom
: ScrollbarOrientation.top,
controller: playlistController,
child: Row(
children: playlists
.map(
(playlist) => PlaylistCard(playlist),
)
.toList(),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
controller: playlistController,
child: Row(
children: playlists
.map(
(playlist) => PlaylistCard(playlist),
)
.toList(),
),
),
),
),
],
],
),
),
),
),
);
},
error: (error, __) => Text("Error $error"),
loading: () => const CircularProgressIndicator(),
)
],
);
},
error: (error, __) => Text("Error $error"),
loading: () => const CircularProgressIndicator(),
)
],
),
),
),
);