refactor: platform alert dialog for all dialogs

This commit is contained in:
Kingkor Roy Tirtho 2022-11-03 09:12:43 +06:00
parent e086b520e7
commit c201624f99
15 changed files with 194 additions and 182 deletions

View File

@ -3,6 +3,7 @@ import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotube/components/Home/Sidebar.dart';
import 'package:spotube/components/Home/SpotubeNavigationBar.dart';
import 'package:spotube/components/Player/Player.dart';
@ -35,8 +36,8 @@ class Shell extends HookConsumerWidget {
useEffect(() {
downloader.onFileExists = (track) async {
if (!isMounted()) return false;
return await showDialog<bool>(
context: context,
return await showPlatformAlertDialog<bool>(
context,
builder: (context) => ReplaceDownloadedFileDialog(
track: track,
),

View File

@ -31,14 +31,14 @@ class UserDownloads extends HookConsumerWidget {
),
const SizedBox(width: 10),
PlatformFilledButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red[50],
foregroundColor: Colors.red[400],
style: ButtonStyle(
backgroundColor: MaterialStatePropertyAll(Colors.red[50]),
foregroundColor: MaterialStatePropertyAll(Colors.red[400]),
),
onPressed: downloader.currentlyRunning > 0
? downloader.cancelAll
: null,
macOSIsSecondary: true,
isSecondary: true,
child: const PlatformText("Cancel All"),
),
],
@ -50,7 +50,7 @@ class UserDownloads extends HookConsumerWidget {
itemBuilder: (context, index) {
final track = downloader.inQueue.elementAt(index);
return PlatformListTile(
title: Text(track.name!),
title: Text(track.name ?? ''),
leading: Padding(
padding: const EdgeInsets.symmetric(horizontal: 5),
child: ClipRRect(

View File

@ -16,15 +16,18 @@ class LyricDelayAdjustDialog extends HookConsumerWidget {
double getValue() =>
double.tryParse(controller.text.replaceAll("ms", "")) ?? 0;
return AlertDialog(
return PlatformAlertDialog(
title: const Center(child: Text("Adjust Lyrics Delay")),
actions: [
secondaryActions: [
PlatformFilledButton(
child: const Text("Cancel"),
isSecondary: true,
onPressed: () {
Navigator.of(context).pop();
},
child: const Text("Cancel"),
),
],
primaryActions: [
PlatformFilledButton(
child: const Text("Done"),
onPressed: () {

View File

@ -90,8 +90,8 @@ class SyncedLyrics extends HookConsumerWidget {
tooltip: "Lyrics Delay",
icon: const Icon(Icons.av_timer_rounded),
onPressed: () async {
final delay = await showDialog(
context: context,
final delay = await showPlatformAlertDialog(
context,
builder: (context) => const LyricDelayAdjustDialog(),
);
if (delay != null) {

View File

@ -15,8 +15,8 @@ class PlaylistCreateDialog extends HookConsumerWidget {
return PlatformTextButton(
onPressed: () {
showDialog(
context: context,
showPlatformAlertDialog(
context,
builder: (context) {
return HookBuilder(builder: (context) {
final playlistName = useTextEditingController();
@ -24,13 +24,9 @@ class PlaylistCreateDialog extends HookConsumerWidget {
final public = useState(false);
final collaborative = useState(false);
return AlertDialog(
return PlatformAlertDialog(
title: const Text("Create a Playlist"),
actions: [
PlatformTextButton(
child: const Text("Cancel"),
onPressed: () => Navigator.of(context).pop(),
),
primaryActions: [
PlatformFilledButton(
child: const Text("Create"),
onPressed: () async {
@ -53,6 +49,13 @@ class PlaylistCreateDialog extends HookConsumerWidget {
},
)
],
secondaryActions: [
PlatformFilledButton(
isSecondary: true,
onPressed: () => Navigator.of(context).pop(),
child: const Text("Cancel"),
),
],
content: Container(
width: MediaQuery.of(context).size.width,
constraints: const BoxConstraints(maxWidth: 500),

View File

@ -66,15 +66,9 @@ class ColorSchemePickerDialog extends HookConsumerWidget {
},
).key);
return AlertDialog(
return PlatformAlertDialog(
title: Text("Pick ${schemeType.name} color scheme"),
actions: [
PlatformTextButton(
child: const Text("Cancel"),
onPressed: () {
Navigator.pop(context);
},
),
primaryActions: [
PlatformFilledButton(
child: const Text("Save"),
onPressed: () {
@ -91,6 +85,15 @@ class ColorSchemePickerDialog extends HookConsumerWidget {
},
)
],
secondaryActions: [
PlatformFilledButton(
isSecondary: true,
child: const Text("Cancel"),
onPressed: () {
Navigator.pop(context);
},
),
],
content: SizedBox(
height: 200,
width: 400,

View File

@ -28,9 +28,7 @@ class Settings extends HookConsumerWidget {
final Auth auth = ref.watch(authProvider);
final pickColorScheme = useCallback((ColorSchemeType schemeType) {
return () => showDialog(
context: context,
builder: (context) {
return () => showPlatformAlertDialog(context, builder: (context) {
return ColorSchemePickerDialog(
schemeType: schemeType,
);

View File

@ -34,11 +34,11 @@ class AdaptiveListTile extends HookWidget {
onTap: breakpoint.isLessThan(breakOn)
? () {
onTap?.call();
showDialog(
context: context,
showPlatformAlertDialog(
context,
builder: (context) {
return StatefulBuilder(builder: (context, update) {
return AlertDialog(
return PlatformAlertDialog(
title: title != null
? Row(
crossAxisAlignment: CrossAxisAlignment.center,
@ -50,7 +50,7 @@ class AdaptiveListTile extends HookWidget {
Flexible(child: title!),
],
)
: null,
: Container(),
content: trailing?.call(context, update),
);
});

View File

@ -7,22 +7,24 @@ class DownloadConfirmationDialog extends StatelessWidget {
@override
Widget build(BuildContext context) {
return AlertDialog(
contentPadding: const EdgeInsets.all(15),
title: Row(
children: const [
Text("Are you sure?"),
SizedBox(width: 10),
UniversalImage(
path:
"https://c.tenor.com/kHcmsxlKHEAAAAAM/rock-one-eyebrow-raised-rock-staring.gif",
height: 40,
width: 40,
)
],
return PlatformAlertDialog(
title: Padding(
padding: const EdgeInsets.all(15),
child: Row(
children: const [
Text("Are you sure?"),
SizedBox(width: 10),
UniversalImage(
path:
"https://c.tenor.com/kHcmsxlKHEAAAAAM/rock-one-eyebrow-raised-rock-staring.gif",
height: 40,
width: 40,
)
],
),
),
content: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 400),
content: Padding(
padding: const EdgeInsets.all(15),
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
@ -57,19 +59,22 @@ class DownloadConfirmationDialog extends StatelessWidget {
),
),
),
actions: [
primaryActions: [
PlatformFilledButton(
style: const ButtonStyle(
foregroundColor: MaterialStatePropertyAll(Colors.white),
backgroundColor: MaterialStatePropertyAll(Colors.red),
),
onPressed: () => Navigator.of(context).pop(true),
child: const Text("Accept"),
)
],
secondaryActions: [
PlatformFilledButton(
isSecondary: true,
child: const Text("Decline"),
onPressed: () => Navigator.of(context).pop(false),
),
PlatformFilledButton(
onPressed: () => Navigator.of(context).pop(true),
style: ElevatedButton.styleFrom(
foregroundColor: Colors.white,
backgroundColor: Colors.red,
),
child: const Text("Accept"),
)
],
);
}

View File

@ -14,7 +14,7 @@ class ReplaceDownloadedFileDialog extends ConsumerWidget {
Widget build(BuildContext context, ref) {
final groupValue = ref.watch(replaceDownloadedFileState);
return AlertDialog(
return PlatformAlertDialog(
title: Text("Track ${track.name} Already Exists"),
content: Column(
mainAxisSize: MainAxisSize.min,
@ -48,20 +48,23 @@ class ReplaceDownloadedFileDialog extends ConsumerWidget {
),
],
),
actions: [
PlatformTextButton(
child: const Text("No"),
onPressed: () {
Navigator.pop(context, false);
},
),
PlatformTextButton(
primaryActions: [
PlatformFilledButton(
child: const Text("Yes"),
onPressed: () {
Navigator.pop(context, true);
},
)
],
secondaryActions: [
PlatformFilledButton(
isSecondary: true,
child: const Text("No"),
onPressed: () {
Navigator.pop(context, false);
},
),
],
);
}
}

View File

@ -84,81 +84,79 @@ class TrackTile extends HookConsumerWidget {
}
Future<void> actionAddToPlaylist() async {
showDialog(
context: context,
builder: (context) {
return FutureBuilder<Iterable<PlaylistSimple>>(
future: spotify.playlists.me.all().then((playlists) async {
final me = await spotify.me.get();
return playlists.where((playlist) =>
playlist.owner?.id != null &&
playlist.owner!.id == me.id);
}),
builder: (context, snapshot) {
return HookBuilder(builder: (context) {
final playlistsCheck = useState(<String, bool>{});
return AlertDialog(
title: PlatformText(
"Add `${track.value.name}` to following Playlists"),
titleTextStyle:
Theme.of(context).textTheme.bodyText1?.copyWith(
fontSize: 18,
fontWeight: FontWeight.bold,
),
actions: [
PlatformTextButton(
child: const PlatformText("Cancel"),
onPressed: () => Navigator.pop(context),
),
PlatformFilledButton(
child: const PlatformText("Add"),
onPressed: () async {
final selectedPlaylists = playlistsCheck
.value.entries
.where((entry) => entry.value)
.map((entry) => entry.key);
showPlatformAlertDialog(context, builder: (context) {
return FutureBuilder<Iterable<PlaylistSimple>>(
future: spotify.playlists.me.all().then((playlists) async {
final me = await spotify.me.get();
return playlists.where((playlist) =>
playlist.owner?.id != null && playlist.owner!.id == me.id);
}),
builder: (context, snapshot) {
return HookBuilder(builder: (context) {
final playlistsCheck = useState(<String, bool>{});
return PlatformAlertDialog(
title: PlatformText(
"Add `${track.value.name}` to following Playlists",
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
secondaryActions: [
PlatformFilledButton(
isSecondary: true,
child: const PlatformText("Cancel"),
onPressed: () => Navigator.pop(context),
),
],
primaryActions: [
PlatformFilledButton(
child: const PlatformText("Add"),
onPressed: () async {
final selectedPlaylists = playlistsCheck.value.entries
.where((entry) => entry.value)
.map((entry) => entry.key);
await Future.wait(
selectedPlaylists.map(
(playlistId) => spotify.playlists
.addTrack(track.value.uri!, playlistId),
),
).then((_) => Navigator.pop(context));
},
)
],
content: SizedBox(
height: 300,
width: 300,
child: !snapshot.hasData
? const Center(
child: PlatformCircularProgressIndicator())
: ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
final playlist =
snapshot.data!.elementAt(index);
return CheckboxListTile(
title: PlatformText(playlist.name!),
controlAffinity:
ListTileControlAffinity.leading,
value: playlistsCheck.value[playlist.id] ??
false,
onChanged: (val) {
playlistsCheck.value = {
...playlistsCheck.value,
playlist.id!: val == true
};
},
);
await Future.wait(
selectedPlaylists.map(
(playlistId) => spotify.playlists
.addTrack(track.value.uri!, playlistId),
),
).then((_) => Navigator.pop(context));
},
)
],
content: SizedBox(
height: 300,
width: 300,
child: !snapshot.hasData
? const Center(
child: PlatformCircularProgressIndicator())
: ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
final playlist = snapshot.data!.elementAt(index);
return CheckboxListTile(
title: PlatformText(playlist.name!),
controlAffinity:
ListTileControlAffinity.leading,
value:
playlistsCheck.value[playlist.id] ?? false,
onChanged: (val) {
playlistsCheck.value = {
...playlistsCheck.value,
playlist.id!: val == true
};
},
),
),
);
});
});
});
);
},
),
),
);
});
});
});
}
final String thumbnailUrl = TypeConversionUtils.image_X_UrlString(

View File

@ -146,11 +146,10 @@ class TracksTableView extends HookConsumerWidget {
switch (action) {
case "download":
{
final isConfirmed = await showDialog(
context: context,
builder: (context) {
return const DownloadConfirmationDialog();
});
final isConfirmed = await showPlatformAlertDialog(
context, builder: (context) {
return const DownloadConfirmationDialog();
});
if (isConfirmed != true) return;
for (final selectedTrack in selectedTracks) {
downloader.addToQueue(selectedTrack);

View File

@ -52,41 +52,39 @@ void useUpdateChecker(WidgetRef ref) {
final latestVersion = value.last;
if (currentVersion == null || latestVersion == null) return;
if (latestVersion <= currentVersion) return;
showDialog(
context: context,
builder: (context) {
const url =
"https://spotube.netlify.app/other-downloads/stable-downloads";
return AlertDialog(
title: const Text("Spotube has an update"),
actions: [
PlatformFilledButton(
child: const Text("Download Now"),
onPressed: () => download(url),
),
],
content: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
showPlatformAlertDialog(context, builder: (context) {
const url =
"https://spotube.netlify.app/other-downloads/stable-downloads";
return PlatformAlertDialog(
title: const PlatformText("Spotube has an update"),
primaryActions: [
PlatformFilledButton(
child: const Text("Download Now"),
onPressed: () => download(url),
),
],
content: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text("Spotube v${value.last} has been released"),
Row(
children: [
Text("Spotube v${value.last} has been released"),
Row(
children: [
const Text("Read the latest "),
AnchorButton(
"release notes",
style: const TextStyle(color: Colors.blue),
onTap: () => launchUrlString(
url,
mode: LaunchMode.externalApplication,
),
),
],
const PlatformText("Read the latest "),
AnchorButton(
"release notes",
style: const TextStyle(color: Colors.blue),
onTap: () => launchUrlString(
url,
mode: LaunchMode.externalApplication,
),
),
],
),
);
});
],
),
);
});
});
return null;
}, [packageInfo, isCheckUpdateEnabled]);

View File

@ -109,8 +109,8 @@ void main() async {
logger.v(
"[onFileExists] download confirmation for ${track.name}",
);
return showDialog<bool>(
context: context,
return showPlatformAlertDialog<bool>(
context,
builder: (_) =>
ReplaceDownloadedFileDialog(track: track),
).then((s) => s ?? false);
@ -201,7 +201,7 @@ class SpotubeState extends ConsumerState<Spotube> with WidgetsBindingObserver {
};
}, []);
platform = TargetPlatform.macOS;
platform = TargetPlatform.windows;
return PlatformApp.router(
routeInformationParser: router.routeInformationParser,
@ -217,7 +217,7 @@ class SpotubeState extends ConsumerState<Spotube> with WidgetsBindingObserver {
accentMaterialColor: accentMaterialColor,
backgroundMaterialColor: backgroundMaterialColor,
),
iosTheme: iosTheme,
iosTheme: themeMode == ThemeMode.dark ? iosDarkTheme : iosTheme,
windowsTheme: windowsTheme,
windowsDarkTheme: windowsDarkTheme,
macosTheme: macosTheme,

View File

@ -147,4 +147,5 @@ final macosDarkTheme = MacosThemeData.dark().copyWith(
iconTheme: const MacosIconThemeData(size: 14),
typography: MacosTypography(color: MacosColors.textColor),
);
final iosTheme = CupertinoThemeData();
const iosTheme = CupertinoThemeData(brightness: Brightness.light);
const iosDarkTheme = CupertinoThemeData(brightness: Brightness.dark);