mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 07:55:18 +00:00
fix: generate playlist page max width
This commit is contained in:
parent
c69f81ec6f
commit
4adf6951d9
@ -1,12 +1,25 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
// ignore: constant_identifier_names
|
||||
const Breakpoints = (
|
||||
xs: 480.0,
|
||||
sm: 640.0,
|
||||
md: 820.0,
|
||||
lg: 1024.0,
|
||||
xl: 1280.0,
|
||||
);
|
||||
|
||||
extension ContainerBreakpoints on BoxConstraints {
|
||||
bool get isXs => biggest.width <= 480;
|
||||
bool get isSm => biggest.width > 480 && biggest.width <= 640;
|
||||
bool get isMd => biggest.width > 640 && biggest.width <= 820;
|
||||
bool get isLg => biggest.width > 820 && biggest.width <= 1024;
|
||||
bool get isXl => biggest.width > 1024 && biggest.width <= 1280;
|
||||
bool get is2Xl => biggest.width > 1280;
|
||||
bool get isXs => biggest.width <= Breakpoints.xs;
|
||||
bool get isSm =>
|
||||
biggest.width > Breakpoints.xs && biggest.width <= Breakpoints.sm;
|
||||
bool get isMd =>
|
||||
biggest.width > Breakpoints.sm && biggest.width <= Breakpoints.md;
|
||||
bool get isLg =>
|
||||
biggest.width > Breakpoints.md && biggest.width <= Breakpoints.lg;
|
||||
bool get isXl =>
|
||||
biggest.width > Breakpoints.lg && biggest.width <= Breakpoints.xl;
|
||||
bool get is2Xl => biggest.width > Breakpoints.xl;
|
||||
|
||||
bool get smAndUp => isSm || isMd || isLg || isXl || is2Xl;
|
||||
bool get mdAndUp => isMd || isLg || isXl || is2Xl;
|
||||
@ -20,12 +33,12 @@ extension ContainerBreakpoints on BoxConstraints {
|
||||
}
|
||||
|
||||
extension ScreenBreakpoints on MediaQueryData {
|
||||
bool get isXs => size.width <= 480;
|
||||
bool get isSm => size.width > 480 && size.width <= 640;
|
||||
bool get isMd => size.width > 640 && size.width <= 820;
|
||||
bool get isLg => size.width > 820 && size.width <= 1024;
|
||||
bool get isXl => size.width > 1024 && size.width <= 1280;
|
||||
bool get is2Xl => size.width > 1280;
|
||||
bool get isXs => size.width <= Breakpoints.xs;
|
||||
bool get isSm => size.width > Breakpoints.xs && size.width <= Breakpoints.sm;
|
||||
bool get isMd => size.width > Breakpoints.sm && size.width <= Breakpoints.md;
|
||||
bool get isLg => size.width > Breakpoints.md && size.width <= Breakpoints.lg;
|
||||
bool get isXl => size.width > Breakpoints.lg && size.width <= Breakpoints.xl;
|
||||
bool get is2Xl => size.width > Breakpoints.xl;
|
||||
|
||||
bool get smAndUp => isSm || isMd || isLg || isXl || is2Xl;
|
||||
bool get mdAndUp => isMd || isLg || isXl || is2Xl;
|
||||
|
@ -248,255 +248,263 @@ class PlaylistGeneratorPage extends HookConsumerWidget {
|
||||
title: Text(context.l10n.generate_playlist),
|
||||
centerTitle: true,
|
||||
),
|
||||
body: SliderTheme(
|
||||
data: const SliderThemeData(
|
||||
overlayShape: RoundSliderOverlayShape(),
|
||||
),
|
||||
child: SafeArea(
|
||||
child: LayoutBuilder(builder: (context, constrains) {
|
||||
return ListView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
children: [
|
||||
ValueListenableBuilder(
|
||||
valueListenable: limit,
|
||||
builder: (context, value, child) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
context.l10n.number_of_tracks_generate,
|
||||
style: textTheme.titleMedium,
|
||||
),
|
||||
Row(
|
||||
body: Center(
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(maxWidth: Breakpoints.lg),
|
||||
child: SliderTheme(
|
||||
data: const SliderThemeData(
|
||||
overlayShape: RoundSliderOverlayShape(),
|
||||
),
|
||||
child: SafeArea(
|
||||
child: LayoutBuilder(builder: (context, constrains) {
|
||||
return ListView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
children: [
|
||||
ValueListenableBuilder(
|
||||
valueListenable: limit,
|
||||
builder: (context, value, child) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.primary,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Text(
|
||||
value.round().toString(),
|
||||
style: textTheme.bodyLarge?.copyWith(
|
||||
color: theme.colorScheme.primaryContainer,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
context.l10n.number_of_tracks_generate,
|
||||
style: textTheme.titleMedium,
|
||||
),
|
||||
Expanded(
|
||||
child: Slider(
|
||||
value: value.toDouble(),
|
||||
min: 10,
|
||||
max: 100,
|
||||
divisions: 9,
|
||||
label: value.round().toString(),
|
||||
onChanged: (value) {
|
||||
limit.value = value.round();
|
||||
},
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.primary,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Text(
|
||||
value.round().toString(),
|
||||
style: textTheme.bodyLarge?.copyWith(
|
||||
color: theme.colorScheme.primaryContainer,
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Slider(
|
||||
value: value.toDouble(),
|
||||
min: 10,
|
||||
max: 100,
|
||||
divisions: 9,
|
||||
label: value.round().toString(),
|
||||
onChanged: (value) {
|
||||
limit.value = value.round();
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
if (constrains.mdAndUp)
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: countrySelector,
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: genreSelector,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
if (constrains.mdAndUp)
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: countrySelector,
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: genreSelector,
|
||||
),
|
||||
],
|
||||
)
|
||||
else ...[
|
||||
countrySelector,
|
||||
const SizedBox(height: 16),
|
||||
genreSelector,
|
||||
],
|
||||
)
|
||||
else ...[
|
||||
countrySelector,
|
||||
const SizedBox(height: 16),
|
||||
genreSelector,
|
||||
],
|
||||
const SizedBox(height: 16),
|
||||
if (constrains.mdAndUp)
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: artistAutoComplete,
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: tracksAutocomplete,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
if (constrains.mdAndUp)
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: artistAutoComplete,
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: tracksAutocomplete,
|
||||
),
|
||||
],
|
||||
)
|
||||
else ...[
|
||||
artistAutoComplete,
|
||||
const SizedBox(height: 16),
|
||||
tracksAutocomplete,
|
||||
],
|
||||
)
|
||||
else ...[
|
||||
artistAutoComplete,
|
||||
const SizedBox(height: 16),
|
||||
tracksAutocomplete,
|
||||
],
|
||||
const SizedBox(height: 16),
|
||||
RecommendationAttributeDials(
|
||||
title: Text(context.l10n.acousticness),
|
||||
values: acousticness.value,
|
||||
onChanged: (value) {
|
||||
acousticness.value = value;
|
||||
},
|
||||
),
|
||||
RecommendationAttributeDials(
|
||||
title: Text(context.l10n.danceability),
|
||||
values: danceability.value,
|
||||
onChanged: (value) {
|
||||
danceability.value = value;
|
||||
},
|
||||
),
|
||||
RecommendationAttributeDials(
|
||||
title: Text(context.l10n.energy),
|
||||
values: energy.value,
|
||||
onChanged: (value) {
|
||||
energy.value = value;
|
||||
},
|
||||
),
|
||||
RecommendationAttributeDials(
|
||||
title: Text(context.l10n.instrumentalness),
|
||||
values: instrumentalness.value,
|
||||
onChanged: (value) {
|
||||
instrumentalness.value = value;
|
||||
},
|
||||
),
|
||||
RecommendationAttributeDials(
|
||||
title: Text(context.l10n.liveness),
|
||||
values: liveness.value,
|
||||
onChanged: (value) {
|
||||
liveness.value = value;
|
||||
},
|
||||
),
|
||||
RecommendationAttributeDials(
|
||||
title: Text(context.l10n.loudness),
|
||||
values: loudness.value,
|
||||
onChanged: (value) {
|
||||
loudness.value = value;
|
||||
},
|
||||
),
|
||||
RecommendationAttributeDials(
|
||||
title: Text(context.l10n.speechiness),
|
||||
values: speechiness.value,
|
||||
onChanged: (value) {
|
||||
speechiness.value = value;
|
||||
},
|
||||
),
|
||||
RecommendationAttributeDials(
|
||||
title: Text(context.l10n.valence),
|
||||
values: valence.value,
|
||||
onChanged: (value) {
|
||||
valence.value = value;
|
||||
},
|
||||
),
|
||||
RecommendationAttributeDials(
|
||||
title: Text(context.l10n.popularity),
|
||||
values: popularity.value,
|
||||
base: 100,
|
||||
onChanged: (value) {
|
||||
popularity.value = value;
|
||||
},
|
||||
),
|
||||
RecommendationAttributeDials(
|
||||
title: Text(context.l10n.key),
|
||||
values: key.value,
|
||||
base: 11,
|
||||
onChanged: (value) {
|
||||
key.value = value;
|
||||
},
|
||||
),
|
||||
RecommendationAttributeFields(
|
||||
title: Text(context.l10n.duration),
|
||||
values: (
|
||||
max: durationMs.value.max / 1000,
|
||||
target: durationMs.value.target / 1000,
|
||||
min: durationMs.value.min / 1000,
|
||||
),
|
||||
onChanged: (value) {
|
||||
durationMs.value = (
|
||||
max: value.max * 1000,
|
||||
target: value.target * 1000,
|
||||
min: value.min * 1000,
|
||||
);
|
||||
},
|
||||
presets: {
|
||||
context.l10n.short: (min: 50, target: 90, max: 120),
|
||||
context.l10n.medium: (min: 120, target: 180, max: 200),
|
||||
context.l10n.long: (min: 480, target: 560, max: 640)
|
||||
},
|
||||
),
|
||||
RecommendationAttributeFields(
|
||||
title: Text(context.l10n.tempo),
|
||||
values: tempo.value,
|
||||
onChanged: (value) {
|
||||
tempo.value = value;
|
||||
},
|
||||
),
|
||||
RecommendationAttributeFields(
|
||||
title: Text(context.l10n.mode),
|
||||
values: mode.value,
|
||||
onChanged: (value) {
|
||||
mode.value = value;
|
||||
},
|
||||
),
|
||||
RecommendationAttributeFields(
|
||||
title: Text(context.l10n.time_signature),
|
||||
values: timeSignature.value,
|
||||
onChanged: (value) {
|
||||
timeSignature.value = value;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
FilledButton.icon(
|
||||
icon: const Icon(SpotubeIcons.magic),
|
||||
label: Text(context.l10n.generate_playlist),
|
||||
onPressed: artists.value.isEmpty &&
|
||||
tracks.value.isEmpty &&
|
||||
genres.value.isEmpty
|
||||
? null
|
||||
: () {
|
||||
final PlaylistGenerateResultRouteState routeState = (
|
||||
seeds: (
|
||||
artists: artists.value.map((a) => a.id!).toList(),
|
||||
tracks: tracks.value.map((t) => t.id!).toList(),
|
||||
genres: genres.value
|
||||
),
|
||||
market: market.value,
|
||||
limit: limit.value,
|
||||
parameters: (
|
||||
acousticness: acousticness.value,
|
||||
danceability: danceability.value,
|
||||
energy: energy.value,
|
||||
instrumentalness: instrumentalness.value,
|
||||
liveness: liveness.value,
|
||||
loudness: loudness.value,
|
||||
speechiness: speechiness.value,
|
||||
valence: valence.value,
|
||||
popularity: popularity.value,
|
||||
key: key.value,
|
||||
duration_ms: durationMs.value,
|
||||
tempo: tempo.value,
|
||||
mode: mode.value,
|
||||
time_signature: timeSignature.value,
|
||||
)
|
||||
);
|
||||
GoRouter.of(context).push(
|
||||
"/library/generate/result",
|
||||
extra: routeState,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
const SizedBox(height: 16),
|
||||
RecommendationAttributeDials(
|
||||
title: Text(context.l10n.acousticness),
|
||||
values: acousticness.value,
|
||||
onChanged: (value) {
|
||||
acousticness.value = value;
|
||||
},
|
||||
),
|
||||
RecommendationAttributeDials(
|
||||
title: Text(context.l10n.danceability),
|
||||
values: danceability.value,
|
||||
onChanged: (value) {
|
||||
danceability.value = value;
|
||||
},
|
||||
),
|
||||
RecommendationAttributeDials(
|
||||
title: Text(context.l10n.energy),
|
||||
values: energy.value,
|
||||
onChanged: (value) {
|
||||
energy.value = value;
|
||||
},
|
||||
),
|
||||
RecommendationAttributeDials(
|
||||
title: Text(context.l10n.instrumentalness),
|
||||
values: instrumentalness.value,
|
||||
onChanged: (value) {
|
||||
instrumentalness.value = value;
|
||||
},
|
||||
),
|
||||
RecommendationAttributeDials(
|
||||
title: Text(context.l10n.liveness),
|
||||
values: liveness.value,
|
||||
onChanged: (value) {
|
||||
liveness.value = value;
|
||||
},
|
||||
),
|
||||
RecommendationAttributeDials(
|
||||
title: Text(context.l10n.loudness),
|
||||
values: loudness.value,
|
||||
onChanged: (value) {
|
||||
loudness.value = value;
|
||||
},
|
||||
),
|
||||
RecommendationAttributeDials(
|
||||
title: Text(context.l10n.speechiness),
|
||||
values: speechiness.value,
|
||||
onChanged: (value) {
|
||||
speechiness.value = value;
|
||||
},
|
||||
),
|
||||
RecommendationAttributeDials(
|
||||
title: Text(context.l10n.valence),
|
||||
values: valence.value,
|
||||
onChanged: (value) {
|
||||
valence.value = value;
|
||||
},
|
||||
),
|
||||
RecommendationAttributeDials(
|
||||
title: Text(context.l10n.popularity),
|
||||
values: popularity.value,
|
||||
base: 100,
|
||||
onChanged: (value) {
|
||||
popularity.value = value;
|
||||
},
|
||||
),
|
||||
RecommendationAttributeDials(
|
||||
title: Text(context.l10n.key),
|
||||
values: key.value,
|
||||
base: 11,
|
||||
onChanged: (value) {
|
||||
key.value = value;
|
||||
},
|
||||
),
|
||||
RecommendationAttributeFields(
|
||||
title: Text(context.l10n.duration),
|
||||
values: (
|
||||
max: durationMs.value.max / 1000,
|
||||
target: durationMs.value.target / 1000,
|
||||
min: durationMs.value.min / 1000,
|
||||
),
|
||||
onChanged: (value) {
|
||||
durationMs.value = (
|
||||
max: value.max * 1000,
|
||||
target: value.target * 1000,
|
||||
min: value.min * 1000,
|
||||
);
|
||||
},
|
||||
presets: {
|
||||
context.l10n.short: (min: 50, target: 90, max: 120),
|
||||
context.l10n.medium: (min: 120, target: 180, max: 200),
|
||||
context.l10n.long: (min: 480, target: 560, max: 640)
|
||||
},
|
||||
),
|
||||
RecommendationAttributeFields(
|
||||
title: Text(context.l10n.tempo),
|
||||
values: tempo.value,
|
||||
onChanged: (value) {
|
||||
tempo.value = value;
|
||||
},
|
||||
),
|
||||
RecommendationAttributeFields(
|
||||
title: Text(context.l10n.mode),
|
||||
values: mode.value,
|
||||
onChanged: (value) {
|
||||
mode.value = value;
|
||||
},
|
||||
),
|
||||
RecommendationAttributeFields(
|
||||
title: Text(context.l10n.time_signature),
|
||||
values: timeSignature.value,
|
||||
onChanged: (value) {
|
||||
timeSignature.value = value;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
FilledButton.icon(
|
||||
icon: const Icon(SpotubeIcons.magic),
|
||||
label: Text(context.l10n.generate_playlist),
|
||||
onPressed: artists.value.isEmpty &&
|
||||
tracks.value.isEmpty &&
|
||||
genres.value.isEmpty
|
||||
? null
|
||||
: () {
|
||||
final PlaylistGenerateResultRouteState
|
||||
routeState = (
|
||||
seeds: (
|
||||
artists:
|
||||
artists.value.map((a) => a.id!).toList(),
|
||||
tracks:
|
||||
tracks.value.map((t) => t.id!).toList(),
|
||||
genres: genres.value
|
||||
),
|
||||
market: market.value,
|
||||
limit: limit.value,
|
||||
parameters: (
|
||||
acousticness: acousticness.value,
|
||||
danceability: danceability.value,
|
||||
energy: energy.value,
|
||||
instrumentalness: instrumentalness.value,
|
||||
liveness: liveness.value,
|
||||
loudness: loudness.value,
|
||||
speechiness: speechiness.value,
|
||||
valence: valence.value,
|
||||
popularity: popularity.value,
|
||||
key: key.value,
|
||||
duration_ms: durationMs.value,
|
||||
tempo: tempo.value,
|
||||
mode: mode.value,
|
||||
time_signature: timeSignature.value,
|
||||
)
|
||||
);
|
||||
GoRouter.of(context).push(
|
||||
"/library/generate/result",
|
||||
extra: routeState,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user