Login guide added

This commit is contained in:
Kingkor Roy Tirtho 2022-07-08 14:33:57 +06:00
parent 68d10f10c4
commit 89bcfecfb5
9 changed files with 350 additions and 119 deletions

BIN
assets/success.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -0,0 +1,69 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotube/components/Login/LoginForm.dart';
import 'package:spotube/components/Shared/Hyperlink.dart';
import 'package:spotube/components/Shared/LinkText.dart';
import 'package:spotube/components/Shared/PageWindowTitleBar.dart';
import 'package:spotube/hooks/useBreakpoints.dart';
import 'package:spotube/models/Logger.dart';
class Login extends HookConsumerWidget {
Login({Key? key}) : super(key: key);
final log = getLogger(Login);
@override
Widget build(BuildContext context, ref) {
final breakpoint = useBreakpoints();
final textTheme = Theme.of(context).textTheme;
return Scaffold(
appBar: const PageWindowTitleBar(leading: BackButton()),
body: SingleChildScrollView(
child: Center(
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 10),
child: Column(
children: [
Image.asset(
"assets/spotube-logo.png",
width: MediaQuery.of(context).size.width *
(breakpoint <= Breakpoints.md ? .5 : .3),
),
Text("Add your spotify credentials to get started",
style: breakpoint <= Breakpoints.md
? textTheme.headline5
: textTheme.headline4),
Text(
"Don't worry, any of your credentials won't be collected or shared with anyone",
style: Theme.of(context).textTheme.caption,
),
const SizedBox(height: 10),
LoginForm(
onDone: () => GoRouter.of(context).pop(),
),
const SizedBox(height: 10),
Wrap(
alignment: WrapAlignment.center,
crossAxisAlignment: WrapCrossAlignment.center,
children: [
const Text("Don't know how to do this?"),
TextButton(
child: const Text(
"Follow along the Step by Step guid",
),
onPressed: () => GoRouter.of(context).push(
"/login-tutorial",
),
),
],
),
],
),
),
),
),
);
}
}

View File

@ -0,0 +1,74 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotube/helpers/oauth-login.dart';
import 'package:spotube/models/Logger.dart';
import 'package:spotube/provider/Auth.dart';
class LoginForm extends HookConsumerWidget {
final void Function()? onDone;
LoginForm({this.onDone, Key? key}) : super(key: key);
final log = getLogger(LoginForm);
@override
Widget build(BuildContext context, ref) {
Auth authState = ref.watch(authProvider);
final clientIdController = useTextEditingController();
final clientSecretController = useTextEditingController();
final fieldError = useState(false);
Future handleLogin(Auth authState) async {
try {
if (clientIdController.value.text == "" ||
clientSecretController.value.text == "") {
fieldError.value = true;
}
await oauthLogin(
ref.read(authProvider),
clientId: clientIdController.value.text,
clientSecret: clientSecretController.value.text,
).then(
(value) => onDone?.call(),
);
} catch (e) {
log.e("[Login.handleLogin] $e");
}
}
return ConstrainedBox(
constraints: const BoxConstraints(
maxWidth: 400,
),
child: Column(
children: [
TextField(
controller: clientIdController,
decoration: const InputDecoration(
hintText: "Spotify Client ID",
label: Text("ClientID"),
),
keyboardType: TextInputType.visiblePassword,
),
const SizedBox(height: 10),
TextField(
controller: clientSecretController,
decoration: const InputDecoration(
hintText: "Spotify Client Secret",
label: Text("Client Secret"),
),
keyboardType: TextInputType.visiblePassword,
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () async {
await handleLogin(authState);
},
child: const Text("Submit"),
)
],
),
);
}
}

View File

@ -0,0 +1,180 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:introduction_screen/introduction_screen.dart';
import 'package:spotube/components/Login/LoginForm.dart';
import 'package:spotube/components/Shared/Hyperlink.dart';
import 'package:spotube/components/Shared/PageWindowTitleBar.dart';
import 'package:spotube/provider/Auth.dart';
class LoginTutorial extends ConsumerWidget {
const LoginTutorial({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, ref) {
final auth = ref.watch(authProvider);
return Scaffold(
appBar: PageWindowTitleBar(
leading: TextButton(
child: const Text("Exit"),
onPressed: () {
Navigator.of(context).pop();
},
),
),
body: IntroductionScreen(
next: const Text("Next"),
back: const Text("Previous"),
showBackButton: true,
overrideDone: TextButton(
child: const Text("Done"),
onPressed: auth.isLoggedIn
? () {
GoRouter.of(context).go("/");
}
: null,
),
pages: [
PageViewModel(
title: "Step 1",
image: CachedNetworkImage(
imageUrl:
"https://user-images.githubusercontent.com/61944859/111762106-d1d37680-88ca-11eb-9884-ec7a40c0dd27.png"),
bodyWidget: Wrap(
children: [
Text("First, Go to ",
style: Theme.of(context).textTheme.bodyText1),
Hyperlink(
"developer.spotify.com/dashboard ",
"https://developer.spotify.com/dashboard",
style: Theme.of(context).textTheme.bodyText1!,
),
Text(
"and Login if you're not logged in",
style: Theme.of(context).textTheme.bodyText1,
),
],
),
),
PageViewModel(
title: "Step 2",
image: CachedNetworkImage(
imageUrl:
"https://user-images.githubusercontent.com/61944859/111762507-473f4700-88cb-11eb-91f3-d480e9584883.png"),
bodyWidget: Text(
"Now, create an Spotify Developer Application by Clicking on the \"CREATE AN APP\" button. Give it a name and description too",
textAlign: TextAlign.left,
style: Theme.of(context).textTheme.bodyText1,
),
),
PageViewModel(
title: "Step 3 [Really Important!]",
bodyWidget: Column(
children: [
Text(
"Tap on the \"EDIT SETTINGS\" Button & navigate to \"Redirect URIs\" section",
textAlign: TextAlign.left,
style: Theme.of(context).textTheme.bodyText1,
),
const SizedBox(height: 10),
Wrap(
crossAxisAlignment: WrapCrossAlignment.center,
children: [
Text(
"Add",
style: Theme.of(context).textTheme.bodyText1,
),
TextButton(
child: Text(
"http://localhost:4304/auth/spotify/callback",
style: Theme.of(context).textTheme.bodyText1?.copyWith(
color: Theme.of(context).primaryColor,
),
),
onPressed: () async {
await Clipboard.setData(
const ClipboardData(
text: "http://localhost:4304/auth/spotify/callback",
),
);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
width: 300,
behavior: SnackBarBehavior.floating,
content: Text(
"Copied http://localhost:4304/auth/spotify/callback to clipboard",
textAlign: TextAlign.center,
),
),
);
},
),
Text(
"to \"Redirect URIs\"",
style: Theme.of(context).textTheme.bodyText1,
),
],
),
const SizedBox(height: 10),
Wrap(
runSpacing: 10,
spacing: 10,
children: [
ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 500),
child: CachedNetworkImage(
imageUrl:
"https://user-images.githubusercontent.com/61944859/172991668-fa40f247-1118-4aba-a749-e669b732fa4d.jpg",
),
),
ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 700),
child: CachedNetworkImage(
imageUrl:
"https://user-images.githubusercontent.com/61944859/111768971-d308a180-88d2-11eb-9108-3e7444cef049.png",
),
),
],
),
],
),
),
PageViewModel(
title: "Step 4",
image: CachedNetworkImage(
imageUrl:
"https://user-images.githubusercontent.com/61944859/111769501-7fe31e80-88d3-11eb-8fc1-f3655dbd4711.png"),
body:
"Finally, reveal the \"Client Secret\" by clicking on the \"SHOW CLIENT SECRET\" text\n Copy the Client ID & Client Secret then Paste them in the next Screen",
),
PageViewModel(
title: "Step 5",
bodyWidget: Column(
children: [
Text(
"Paste the Copied \"Client ID\" and \"Client Secret\" Here",
style: Theme.of(context).textTheme.bodyText1,
),
const SizedBox(height: 10),
LoginForm(),
],
),
),
if (auth.isLoggedIn)
PageViewModel(
decoration: const PageDecoration(
bodyAlignment: Alignment.center,
),
title: "Success🥳",
image: Image.asset("assets/success.png"),
body:
"Now you're successfully Logged In with your Spotify account. Good Job, mate!",
),
],
),
);
}
}

View File

@ -1,108 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotube/components/Shared/Hyperlink.dart';
import 'package:spotube/components/Shared/PageWindowTitleBar.dart';
import 'package:spotube/helpers/oauth-login.dart';
import 'package:spotube/hooks/useBreakpoints.dart';
import 'package:spotube/models/Logger.dart';
import 'package:spotube/provider/Auth.dart';
class Login extends HookConsumerWidget {
Login({Key? key}) : super(key: key);
final log = getLogger(Login);
@override
Widget build(BuildContext context, ref) {
Auth authState = ref.watch(authProvider);
final clientIdController = useTextEditingController();
final clientSecretController = useTextEditingController();
final fieldError = useState(false);
final breakpoint = useBreakpoints();
Future handleLogin(Auth authState) async {
try {
if (clientIdController.value.text == "" ||
clientSecretController.value.text == "") {
fieldError.value = true;
}
await oauthLogin(
ref.read(authProvider),
clientId: clientIdController.value.text,
clientSecret: clientSecretController.value.text,
).then(
(value) => GoRouter.of(context).pop(),
);
} catch (e) {
log.e("[Login.handleLogin] $e");
}
}
final textTheme = Theme.of(context).textTheme;
return Scaffold(
appBar: const PageWindowTitleBar(leading: BackButton()),
body: SingleChildScrollView(
child: Center(
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 10),
child: Column(
children: [
Image.asset(
"assets/spotube-logo.png",
width: MediaQuery.of(context).size.width *
(breakpoint <= Breakpoints.md ? .5 : .3),
),
Text("Add your spotify credentials to get started",
style: breakpoint <= Breakpoints.md
? textTheme.headline5
: textTheme.headline4),
const Text(
"Don't worry, any of your credentials won't be collected or shared with anyone"),
const Hyperlink("How to get these client-id & client-secret?",
"https://github.com/KRTirtho/spotube#optional-configurations"),
const SizedBox(
height: 10,
),
Container(
constraints: const BoxConstraints(
maxWidth: 400,
),
child: Column(
children: [
TextField(
controller: clientIdController,
decoration: const InputDecoration(
hintText: "Spotify Client ID",
label: Text("ClientID"),
),
keyboardType: TextInputType.visiblePassword,
),
const SizedBox(height: 10),
TextField(
controller: clientSecretController,
decoration: const InputDecoration(
hintText: "Spotify Client Secret",
label: Text("Client Secret"),
),
keyboardType: TextInputType.visiblePassword,
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () async {
await handleLogin(authState);
},
child: const Text("Submit"),
)
],
),
),
],
),
),
),
),
);
}
}

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:spotube/components/Shared/AnchorButton.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:url_launcher/url_launcher_string.dart';
class Hyperlink extends StatelessWidget {
final String text;
@ -22,7 +23,7 @@ class Hyperlink extends StatelessWidget {
return AnchorButton(
text,
onTap: () async {
await launch(url);
await launchUrlString(url);
},
key: key,
overflow: overflow,

View File

@ -4,7 +4,8 @@ import 'package:spotube/components/Album/AlbumView.dart';
import 'package:spotube/components/Artist/ArtistAlbumView.dart';
import 'package:spotube/components/Artist/ArtistProfile.dart';
import 'package:spotube/components/Home/Home.dart';
import 'package:spotube/components/Settings/Login.dart';
import 'package:spotube/components/Login/Login.dart';
import 'package:spotube/components/Login/LoginTutorial.dart';
import 'package:spotube/components/Player/PlayerView.dart';
import 'package:spotube/components/Playlist/PlaylistView.dart';
import 'package:spotube/components/Settings/Settings.dart';
@ -22,6 +23,12 @@ GoRouter createGoRouter() => GoRouter(
child: Login(),
),
),
GoRoute(
path: "/login-tutorial",
pageBuilder: (context, state) => const SpotubePage(
child: LoginTutorial(),
),
),
GoRoute(
path: "/settings",
pageBuilder: (context, state) => const SpotubePage(
@ -71,6 +78,6 @@ GoRouter createGoRouter() => GoRouter(
child: PlayerView(),
);
},
)
),
],
);

View File

@ -37,7 +37,7 @@ packages:
source: hosted
version: "2.3.1"
async:
dependency: "direct main"
dependency: transitive
description:
name: async
url: "https://pub.dartlang.org"
@ -323,6 +323,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.7.3"
dots_indicator:
dependency: transitive
description:
name: dots_indicator
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
fading_edge_scrollview:
dependency: transitive
description:
@ -527,6 +534,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "3.2.0"
introduction_screen:
dependency: "direct main"
description:
name: introduction_screen
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.2"
io:
dependency: transitive
description:
@ -828,13 +842,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
resizable_widget:
dependency: "direct main"
description:
name: resizable_widget
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.5"
riverpod:
dependency: transitive
description:

View File

@ -63,6 +63,7 @@ dependencies:
hive_flutter: ^1.1.0
dbus: ^0.7.3
audioplayers: ^1.0.1
introduction_screen: ^3.0.2
dev_dependencies:
flutter_test: