Working auth and added shared notes
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:helpers/helpers/misc_build/build_media.dart';
|
||||
import 'package:rluv/features/account/signup.dart';
|
||||
import 'package:rluv/global/styles.dart';
|
||||
|
||||
import '../../global/api.dart';
|
||||
import '../../global/widgets/ui_button.dart';
|
||||
import '../../main.dart';
|
||||
import 'login.dart';
|
||||
|
||||
class AccountCreateScreen extends ConsumerStatefulWidget {
|
||||
const AccountCreateScreen({super.key});
|
||||
|
||||
@override
|
||||
ConsumerState<AccountCreateScreen> createState() =>
|
||||
_AccountCreateScreenState();
|
||||
}
|
||||
|
||||
enum _AccountScreen { options, login, signup }
|
||||
|
||||
class _AccountCreateScreenState extends ConsumerState<AccountCreateScreen> {
|
||||
_AccountScreen currentScreen = _AccountScreen.options;
|
||||
static final signupFormKey = GlobalKey<FormState>();
|
||||
static final loginFormKey = GlobalKey<FormState>();
|
||||
|
||||
bool usingUsername = true;
|
||||
bool hasFamilyCode = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (ref.watch(tokenProvider) != null) {
|
||||
WidgetsBinding.instance.addPostFrameCallback(
|
||||
(_) {
|
||||
Navigator.pushAndRemoveUntil(
|
||||
context,
|
||||
MaterialPageRoute(builder: (ctx) => const Home()),
|
||||
(r) => false,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
final screen = BuildMedia(context).size;
|
||||
return Scaffold(
|
||||
backgroundColor: Styles.purpleNurple,
|
||||
body: SafeArea(
|
||||
child: AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
child: currentScreen == _AccountScreen.options
|
||||
? Center(
|
||||
child: SizedBox(
|
||||
width: screen.width * 0.5 > 400 ? 400 : screen.width * 0.5,
|
||||
child: Column(
|
||||
children: [
|
||||
const Spacer(),
|
||||
const Text(
|
||||
'Welcome!',
|
||||
style: TextStyle(fontSize: 28),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 40.0),
|
||||
child: Image.asset("assets/app_icon.png",
|
||||
height:
|
||||
screen.width > 500 ? 250 : screen.width * 0.5,
|
||||
width: screen.width > 500
|
||||
? 250
|
||||
: screen.width * 0.5),
|
||||
),
|
||||
UiButton(
|
||||
color: Styles.sunflower,
|
||||
onPressed: () {
|
||||
setState(
|
||||
() => currentScreen = _AccountScreen.signup,
|
||||
);
|
||||
},
|
||||
text: 'Signup',
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
UiButton(
|
||||
color: Styles.flounderBlue,
|
||||
onPressed: () {
|
||||
setState(
|
||||
() => currentScreen = _AccountScreen.login,
|
||||
);
|
||||
},
|
||||
text: 'Login',
|
||||
),
|
||||
const Spacer(),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
: currentScreen == _AccountScreen.login
|
||||
? Login(formKey: loginFormKey, exitNav: exitNav())
|
||||
: Signup(
|
||||
formKey: signupFormKey,
|
||||
exitNav: exitNav(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget exitNav() => Align(
|
||||
alignment: Alignment.topLeft,
|
||||
child: IconButton(
|
||||
icon: const Icon(Icons.chevron_left),
|
||||
onPressed: () {
|
||||
setState(
|
||||
() => currentScreen = _AccountScreen.options,
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:helpers/helpers/misc_build/build_media.dart';
|
||||
import 'package:helpers/helpers/print.dart';
|
||||
|
||||
import '../../global/api.dart';
|
||||
import '../../global/styles.dart';
|
||||
import '../../global/utils.dart';
|
||||
import '../../global/widgets/ui_button.dart';
|
||||
|
||||
class Login extends ConsumerStatefulWidget {
|
||||
const Login({super.key, required this.formKey, required this.exitNav});
|
||||
|
||||
final GlobalKey<FormState> formKey;
|
||||
final Widget exitNav;
|
||||
|
||||
@override
|
||||
ConsumerState<Login> createState() => _LoginState();
|
||||
}
|
||||
|
||||
class _LoginState extends ConsumerState<Login> {
|
||||
bool usingUsername = false;
|
||||
final emailController = TextEditingController();
|
||||
final usernameController = TextEditingController();
|
||||
final passwordController = TextEditingController();
|
||||
|
||||
final focusNodes = [
|
||||
FocusNode(),
|
||||
FocusNode(),
|
||||
FocusNode(),
|
||||
];
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final screen = BuildMedia(context).size;
|
||||
return Form(
|
||||
key: widget.formKey,
|
||||
child: Stack(
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
const Spacer(),
|
||||
const Text(
|
||||
'Login',
|
||||
style: TextStyle(fontSize: 24),
|
||||
),
|
||||
const Spacer(flex: 2),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(2.0),
|
||||
child: Text(
|
||||
'Username',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
),
|
||||
Checkbox(
|
||||
activeColor: Styles.lavender,
|
||||
onChanged: (bool? value) {
|
||||
setState(() => usingUsername = true);
|
||||
},
|
||||
value: usingUsername,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
width: 35,
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(2.0),
|
||||
child: Text(
|
||||
'Email',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
),
|
||||
Checkbox(
|
||||
activeColor: Styles.lavender,
|
||||
onChanged: (bool? value) {
|
||||
setState(() => usingUsername = false);
|
||||
},
|
||||
value: !usingUsername,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
child: !usingUsername
|
||||
? generateTextField(
|
||||
controller: emailController,
|
||||
size: screen,
|
||||
text: 'Email: ',
|
||||
validatorFunc: (s) {
|
||||
if (s != null && s.isNotEmpty && !isEmailValid(s)) {
|
||||
return 'Email entered is invalid';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
index: 0)
|
||||
: generateTextField(
|
||||
controller: usernameController,
|
||||
size: screen,
|
||||
text: 'Username: ',
|
||||
validatorFunc: (s) {
|
||||
if (s == null || s.isEmpty) {
|
||||
return 'Invalid Username';
|
||||
}
|
||||
if (s.length < 3) {
|
||||
return 'Username Not 3 Letters Long';
|
||||
}
|
||||
if (s.length > 20) {
|
||||
return 'Username Too Long';
|
||||
}
|
||||
if (!isUsernameValid(s)) {
|
||||
return 'Letters, Numbers, and ., -, _ Allowed';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
index: 0),
|
||||
),
|
||||
generateTextField(
|
||||
controller: passwordController,
|
||||
size: screen,
|
||||
text: 'Password: ',
|
||||
validatorFunc: (s) {
|
||||
if (s != null && s.length < 6) {
|
||||
return 'Please do a better password (you have to)';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
index: 1,
|
||||
isPassword: true),
|
||||
const Spacer(flex: 2),
|
||||
UiButton(
|
||||
width: screen.width * 0.85 > 400 ? 400 : screen.width * 0.85,
|
||||
showLoading: true,
|
||||
onPressed: () => login(),
|
||||
text: 'Submit',
|
||||
color: Styles.seaweedGreen,
|
||||
),
|
||||
const Spacer(),
|
||||
],
|
||||
),
|
||||
widget.exitNav,
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget generateTextField({
|
||||
required String text,
|
||||
String? label,
|
||||
required int index,
|
||||
required TextEditingController controller,
|
||||
isPassword = false,
|
||||
required Size size,
|
||||
required String? Function(String?) validatorFunc,
|
||||
}) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
text,
|
||||
style: TextStyle(fontSize: size.width < 350 ? 16 : 20),
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
Container(
|
||||
decoration: Styles.boxLavenderBubble,
|
||||
width: size.width * 0.5 > 300 ? 300 : size.width * 0.5,
|
||||
child: TextFormField(
|
||||
validator: validatorFunc,
|
||||
controller: controller,
|
||||
decoration: Styles.inputLavenderBubble(labelText: label),
|
||||
obscureText: isPassword,
|
||||
focusNode: focusNodes[index],
|
||||
style: const TextStyle(fontSize: 16),
|
||||
onFieldSubmitted: (_) {
|
||||
if (index != focusNodes.length - 1) {
|
||||
focusNodes[index + 1].requestFocus();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: size.width * 0.05,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future login() async {
|
||||
try {
|
||||
if (widget.formKey.currentState != null &&
|
||||
!widget.formKey.currentState!.validate()) {
|
||||
return;
|
||||
}
|
||||
final data =
|
||||
await ref.read(apiProvider.notifier).post(path: 'auth/login', data: {
|
||||
'username':
|
||||
usernameController.text.isEmpty ? null : usernameController.text,
|
||||
'email': emailController.text.isEmpty ? null : emailController.text,
|
||||
'password': passwordController.text,
|
||||
});
|
||||
|
||||
final bool success = data?['success'] ?? false;
|
||||
showSnack(
|
||||
ref: ref,
|
||||
text: data?['message'] ?? success
|
||||
? 'Login successful'
|
||||
: 'Login unsuccessful',
|
||||
type: !success ? SnackType.error : SnackType.success);
|
||||
printAmber(data);
|
||||
} catch (err, st) {
|
||||
printRed('Error in login: $err\n$st');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:helpers/helpers/misc_build/build_media.dart';
|
||||
import 'package:helpers/helpers/print.dart';
|
||||
|
||||
import '../../global/api.dart';
|
||||
import '../../global/styles.dart';
|
||||
import '../../global/utils.dart';
|
||||
import '../../global/widgets/ui_button.dart';
|
||||
|
||||
class Signup extends ConsumerStatefulWidget {
|
||||
const Signup({super.key, required this.formKey, required this.exitNav});
|
||||
|
||||
final GlobalKey<FormState> formKey;
|
||||
final Widget exitNav;
|
||||
|
||||
@override
|
||||
ConsumerState<Signup> createState() => _SignupState();
|
||||
}
|
||||
|
||||
class _SignupState extends ConsumerState<Signup> {
|
||||
bool hasFamilyCode = false;
|
||||
final nameCotroller = TextEditingController();
|
||||
final usernameController = TextEditingController();
|
||||
final emailController = TextEditingController();
|
||||
final passwordController = TextEditingController();
|
||||
final familyCodeController = TextEditingController();
|
||||
|
||||
final focusNodes = [
|
||||
FocusNode(),
|
||||
FocusNode(),
|
||||
FocusNode(),
|
||||
FocusNode(),
|
||||
FocusNode(),
|
||||
];
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final screen = BuildMedia(context).size;
|
||||
return Form(
|
||||
key: widget.formKey,
|
||||
child: Stack(
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
const Spacer(),
|
||||
const Text(
|
||||
'Signup',
|
||||
style: TextStyle(fontSize: 24),
|
||||
),
|
||||
const Spacer(),
|
||||
generateTextField(
|
||||
controller: nameCotroller,
|
||||
size: screen,
|
||||
text: 'Name:',
|
||||
validatorFunc: (s) {
|
||||
if (s == null || s.isEmpty) {
|
||||
return 'You matter! Enter a name :)';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
index: 0),
|
||||
generateTextField(
|
||||
controller: usernameController,
|
||||
size: screen,
|
||||
text: 'Username: ',
|
||||
validatorFunc: (s) {
|
||||
if (s == null || s.isEmpty) {
|
||||
return 'Invalid Username';
|
||||
}
|
||||
if (s.length < 3) {
|
||||
return 'Username Not 3 Letters Long';
|
||||
}
|
||||
if (s.length > 20) {
|
||||
return 'Username Too Long';
|
||||
}
|
||||
if (!isUsernameValid(s)) {
|
||||
return 'Letters, Numbers, and ., -, _ Allowed';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
index: 1),
|
||||
generateTextField(
|
||||
controller: emailController,
|
||||
size: screen,
|
||||
validatorFunc: (s) {
|
||||
if (s != null && s.isNotEmpty && !isEmailValid(s)) {
|
||||
return 'Email entered is invalid';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
text: 'Email: (optional)',
|
||||
index: 2),
|
||||
generateTextField(
|
||||
controller: passwordController,
|
||||
size: screen,
|
||||
validatorFunc: (s) {
|
||||
if (s != null && s.length < 6) {
|
||||
return 'Please do a better password (you have to)';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
text: 'Password:',
|
||||
index: 3,
|
||||
isPassword: true),
|
||||
const SizedBox(height: 30),
|
||||
AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
child: hasFamilyCode
|
||||
? Column(
|
||||
children: [
|
||||
generateTextField(
|
||||
controller: familyCodeController,
|
||||
size: screen,
|
||||
validatorFunc: (s) {
|
||||
if (hasFamilyCode) {
|
||||
if (s == null || s.length != 5) {
|
||||
return 'Invalid Code';
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
text: 'Family Code:',
|
||||
index: 4,
|
||||
),
|
||||
UiButton(
|
||||
width: screen.width * 0.4,
|
||||
icon: const Icon(Icons.cancel,
|
||||
color: Styles.washedStone, size: 20),
|
||||
onPressed: () {
|
||||
setState(
|
||||
() => hasFamilyCode = false,
|
||||
);
|
||||
},
|
||||
text: 'Cancel',
|
||||
),
|
||||
],
|
||||
)
|
||||
: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: UiButton(
|
||||
width: screen.width * 0.5 > 400
|
||||
? 400
|
||||
: screen.width * 0.5,
|
||||
text: 'JOIN FAMILY',
|
||||
// color: Styles.flounderBlue,
|
||||
onPressed: () {
|
||||
setState(
|
||||
() => hasFamilyCode = true,
|
||||
);
|
||||
},
|
||||
),
|
||||
)),
|
||||
const Spacer(),
|
||||
UiButton(
|
||||
showLoading: true,
|
||||
onPressed: () => signup(),
|
||||
text: 'Submit',
|
||||
color: Styles.seaweedGreen,
|
||||
),
|
||||
const Spacer(),
|
||||
],
|
||||
),
|
||||
widget.exitNav,
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget generateTextField({
|
||||
required String text,
|
||||
String? label,
|
||||
required int index,
|
||||
required TextEditingController controller,
|
||||
isPassword = false,
|
||||
required Size size,
|
||||
required String? Function(String?) validatorFunc,
|
||||
}) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
text,
|
||||
style: TextStyle(fontSize: size.width < 350 ? 16 : 20),
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
Container(
|
||||
decoration: Styles.boxLavenderBubble,
|
||||
width: size.width * 0.5 > 300 ? 300 : size.width * 0.5,
|
||||
child: TextFormField(
|
||||
validator: validatorFunc,
|
||||
controller: controller,
|
||||
decoration: Styles.inputLavenderBubble(labelText: label),
|
||||
obscureText: isPassword,
|
||||
focusNode: focusNodes[index],
|
||||
style: const TextStyle(fontSize: 16),
|
||||
onFieldSubmitted: (_) {
|
||||
if (index != focusNodes.length - 1) {
|
||||
focusNodes[index + 1].requestFocus();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: size.width * 0.05,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future signup() async {
|
||||
try {
|
||||
if (widget.formKey.currentState != null &&
|
||||
!widget.formKey.currentState!.validate()) {
|
||||
return;
|
||||
}
|
||||
final data =
|
||||
await ref.read(apiProvider.notifier).post(path: 'auth/signup', data: {
|
||||
'name': nameCotroller.text,
|
||||
'username':
|
||||
usernameController.text.isEmpty ? null : usernameController.text,
|
||||
'email': emailController.text.isEmpty ? null : emailController.text,
|
||||
'password': passwordController.text,
|
||||
'family_code': familyCodeController.text.isEmpty
|
||||
? null
|
||||
: familyCodeController.text.toUpperCase(),
|
||||
'budget_name': 'Budget'
|
||||
});
|
||||
|
||||
final success = data?['success'] ?? false;
|
||||
showSnack(
|
||||
ref: ref,
|
||||
text: data?['message'] ?? success
|
||||
? 'Login successful'
|
||||
: 'Login unsuccessful',
|
||||
type: !success ? SnackType.error : SnackType.success);
|
||||
|
||||
printAmber(data);
|
||||
// final user = User.fromJson(data?['user']);
|
||||
// ref.read(tokenProvider.notifier).state =
|
||||
} catch (err) {
|
||||
printRed(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user