diff --git a/lib/components/Login.dart b/lib/components/Login.dart index 9e415094..75c9ae44 100644 --- a/lib/components/Login.dart +++ b/lib/components/Login.dart @@ -1,4 +1,3 @@ -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -9,6 +8,8 @@ import 'package:spotube/helpers/server_ipc.dart'; import 'package:spotube/models/LocalStorageKeys.dart'; import 'package:spotube/provider/Auth.dart'; +const redirectUri = "http://localhost:4304/auth/spotify/callback"; + class Login extends StatefulWidget { @override _LoginState createState() => _LoginState(); @@ -31,7 +32,6 @@ class _LoginState extends State { } final credentials = SpotifyApiCredentials(clientId, clientSecret); final grant = SpotifyApi.authorizationCodeGrant(credentials); - const redirectUri = "http://localhost:4304/auth/spotify/callback"; final authUri = grant.getAuthorizationUrl(Uri.parse(redirectUri), scopes: spotifyScopes); diff --git a/lib/components/Settings.dart b/lib/components/Settings.dart index c45233e4..976ed5bb 100644 --- a/lib/components/Settings.dart +++ b/lib/components/Settings.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:spotube/components/PageWindowTitleBar.dart'; +import 'package:spotube/main.dart'; import 'package:spotube/models/LocalStorageKeys.dart'; import 'package:spotube/provider/Auth.dart'; import 'package:spotube/provider/UserPreferences.dart'; @@ -50,63 +51,100 @@ class _SettingsState extends State { ), const SizedBox(height: 10), Padding( - padding: const EdgeInsets.all(10), - child: Row( + padding: const EdgeInsets.all(16.0), + child: Column( children: [ - Expanded( - flex: 2, - child: Text( - "Genius Access Token", - style: Theme.of(context).textTheme.subtitle1, - ), - ), - Expanded( - flex: 1, - child: TextField( - controller: _textEditingController, - decoration: InputDecoration( - hintText: preferences.geniusAccessToken, + Row( + children: [ + Expanded( + flex: 2, + child: Text( + "Genius Access Token", + style: Theme.of(context).textTheme.subtitle1, + ), ), - ), + Expanded( + flex: 1, + child: TextField( + controller: _textEditingController, + decoration: InputDecoration( + hintText: preferences.geniusAccessToken, + ), + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: ElevatedButton( + onPressed: _geniusAccessToken != null + ? () async { + SharedPreferences localStorage = + await SharedPreferences.getInstance(); + preferences + .setGeniusAccessToken(_geniusAccessToken); + localStorage.setString( + LocalStorageKeys.geniusAccessToken, + _geniusAccessToken!); + setState(() { + _geniusAccessToken = null; + }); + _textEditingController?.text = ""; + } + : null, + child: const Text("Save"), + ), + ) + ], ), - Padding( - padding: const EdgeInsets.all(8.0), - child: ElevatedButton( - onPressed: _geniusAccessToken != null - ? () async { - SharedPreferences localStorage = - await SharedPreferences.getInstance(); - preferences - .setGeniusAccessToken(_geniusAccessToken); - localStorage.setString( - LocalStorageKeys.geniusAccessToken, - _geniusAccessToken!); - setState(() { - _geniusAccessToken = null; - }); - _textEditingController?.text = ""; - } - : null, - child: const Text("Save"), - ), - ) + const SizedBox(height: 10), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text("Theme"), + DropdownButton( + value: MyApp.of(context)?.getThemeMode(), + items: const [ + DropdownMenuItem( + child: Text( + "Dark", + ), + value: ThemeMode.dark, + ), + DropdownMenuItem( + child: Text( + "Light", + ), + value: ThemeMode.light, + ), + DropdownMenuItem( + child: Text("System"), + value: ThemeMode.system, + ), + ], + onChanged: (value) { + if (value != null) { + MyApp.of(context)?.setThemeMode(value); + } + }, + ) + ], + ), + const SizedBox(height: 10), + Builder(builder: (context) { + var auth = context.read(); + return ElevatedButton( + child: const Text("Logout"), + onPressed: () async { + SharedPreferences localStorage = + await SharedPreferences.getInstance(); + await localStorage.clear(); + auth.logout(); + Navigator.of(context).pop(); + }, + ); + }) ], ), ), - const SizedBox(height: 10), - Builder(builder: (context) { - var auth = context.read(); - return ElevatedButton( - child: const Text("Logout"), - onPressed: () async { - SharedPreferences localStorage = - await SharedPreferences.getInstance(); - await localStorage.clear(); - auth.logout(); - Navigator.of(context).pop(); - }, - ); - }) ], ), ); diff --git a/lib/helpers/server_ipc.dart b/lib/helpers/server_ipc.dart index 33db359c..42b291ee 100644 --- a/lib/helpers/server_ipc.dart +++ b/lib/helpers/server_ipc.dart @@ -4,10 +4,8 @@ import 'package:url_launcher/url_launcher.dart'; Future connectIpc(String authUri, String redirectUri) async { try { - if (await canLaunch(authUri)) { - print("[Launching]: $authUri"); - await launch(authUri); - } + print("[Launching]: $authUri"); + await launch(authUri); HttpServer server = await HttpServer.bind(InternetAddress.loopbackIPv4, 4304); @@ -32,7 +30,9 @@ Future connectIpc(String authUri, String redirectUri) async { } } } - } catch (error) { - throw error; + } catch (error, stack) { + print("[connectIpc]: $error"); + print(stack); + rethrow; } } diff --git a/lib/main.dart b/lib/main.dart index 8cde6764..04d45981 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -24,7 +24,52 @@ void main() async { }); } -class MyApp extends StatelessWidget { +class MyApp extends StatefulWidget { + static _MyAppState? of(BuildContext context) => + context.findAncestorStateOfType<_MyAppState>(); + @override + State createState() => _MyAppState(); +} + +class _MyAppState extends State { + ThemeMode _themeMode = ThemeMode.system; + + @override + void initState() { + WidgetsBinding.instance?.addPostFrameCallback((timeStamp) async { + SharedPreferences localStorage = await SharedPreferences.getInstance(); + String? themeMode = localStorage.getString(LocalStorageKeys.themeMode); + + setState(() { + switch (themeMode) { + case "light": + _themeMode = ThemeMode.light; + break; + case "dark": + _themeMode = ThemeMode.dark; + break; + default: + _themeMode = ThemeMode.system; + } + }); + }); + super.initState(); + } + + void setThemeMode(ThemeMode themeMode) { + SharedPreferences.getInstance().then((localStorage) { + localStorage.setString( + LocalStorageKeys.themeMode, themeMode.toString().split(".").last); + setState(() { + _themeMode = themeMode; + }); + }); + } + + ThemeMode getThemeMode() { + return _themeMode; + } + @override Widget build(BuildContext context) { return MultiProvider( @@ -145,7 +190,7 @@ class MyApp extends StatelessWidget { unselectedIconTheme: const IconThemeData(opacity: 1), ), ), - themeMode: ThemeMode.system, + themeMode: _themeMode, home: const Home(), ), ); diff --git a/lib/models/LocalStorageKeys.dart b/lib/models/LocalStorageKeys.dart index c6d7790f..7d9a6072 100644 --- a/lib/models/LocalStorageKeys.dart +++ b/lib/models/LocalStorageKeys.dart @@ -5,4 +5,6 @@ abstract class LocalStorageKeys { static String refreshToken = 'refresh_token'; static String expiration = "expiration"; static String geniusAccessToken = "genius_access_token"; + + static String themeMode = "theme_mode"; }