You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
decky/lib/screens/login_screen.dart

168 lines
5.3 KiB

import 'package:flutter/material.dart';
import '../routes.dart';
import '../services/api_auth_service.dart';
class LoginScreen extends StatefulWidget {
const LoginScreen({super.key});
@override
State<LoginScreen> createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
final _formKey = GlobalKey<FormState>();
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
bool _loading = false;
String? _errorMessage;
bool _isRegister = false;
final _displayNameController = TextEditingController();
@override
void dispose() {
_emailController.dispose();
_passwordController.dispose();
_displayNameController.dispose();
super.dispose();
}
Future<void> _submit() async {
setState(() {
_errorMessage = null;
_loading = true;
});
String? err;
if (_isRegister) {
err = await ApiAuthService.instance.register(
_emailController.text,
_passwordController.text,
_displayNameController.text,
);
} else {
err = await ApiAuthService.instance.login(
_emailController.text,
_passwordController.text,
);
}
if (!mounted) return;
setState(() {
_loading = false;
_errorMessage = err;
});
if (err == null) {
Navigator.pushReplacementNamed(context, Routes.deckList);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_isRegister ? 'Create account' : 'Log in'),
),
body: SafeArea(
child: SingleChildScrollView(
padding: const EdgeInsets.all(24),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SizedBox(height: 24),
if (_isRegister) ...[
TextFormField(
controller: _displayNameController,
decoration: const InputDecoration(
labelText: 'Display name (optional)',
border: OutlineInputBorder(),
),
textInputAction: TextInputAction.next,
),
const SizedBox(height: 16),
],
TextFormField(
controller: _emailController,
decoration: InputDecoration(
labelText: _isRegister ? 'Email' : 'Email or username',
border: const OutlineInputBorder(),
),
keyboardType: _isRegister ? TextInputType.emailAddress : TextInputType.text,
textInputAction: TextInputAction.next,
validator: (v) {
if (v == null || v.trim().isEmpty) {
return _isRegister ? 'Enter your email.' : 'Enter your email or username.';
}
return null;
},
),
const SizedBox(height: 16),
TextFormField(
controller: _passwordController,
decoration: const InputDecoration(
labelText: 'Password',
border: OutlineInputBorder(),
),
obscureText: true,
textInputAction: TextInputAction.done,
onFieldSubmitted: (_) => _submit(),
validator: (v) {
if (v == null || v.isEmpty) return 'Enter your password.';
return null;
},
),
if (_errorMessage != null) ...[
const SizedBox(height: 16),
Text(
_errorMessage!,
style: TextStyle(color: Theme.of(context).colorScheme.error),
),
],
const SizedBox(height: 24),
FilledButton(
onPressed: _loading
? null
: () {
if (_formKey.currentState?.validate() ?? false) {
_submit();
}
},
style: FilledButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 16),
),
child: _loading
? const SizedBox(
height: 24,
width: 24,
child: CircularProgressIndicator(strokeWidth: 2),
)
: Text(_isRegister ? 'Create account' : 'Log in'),
),
const SizedBox(height: 16),
TextButton(
onPressed: _loading
? null
: () {
setState(() {
_isRegister = !_isRegister;
_errorMessage = null;
});
},
child: Text(
_isRegister
? 'Already have an account? Log in'
: 'No account? Create one',
),
),
],
),
),
),
),
);
}
}

Powered by TurnKey Linux.