diff --git a/lib/client.dart b/lib/client.dart index 18f6679..380bcb8 100644 --- a/lib/client.dart +++ b/lib/client.dart @@ -20,8 +20,8 @@ class ApiClient { } } - Map headers({bool forGet = false}) { - if (token == null) { + Map headers({bool forGet = false, bool forLogin = false}) { + if (token == null && !forLogin) { throw Exception('Not logged in'); } @@ -61,11 +61,11 @@ class ApiClient { return _jsonDecode(response); } - Future> post(String path, Map body) async { + Future> post(String path, Map body, {bool forLogin = false}) async { final response = await httpClient.post( Uri.parse('$baseUrl$path'), body: jsonEncode(body), - headers: headers(), + headers: headers(forLogin: forLogin), ); if (response.statusCode != 200) { @@ -198,4 +198,17 @@ class ApiClient { }; await patch("/entry/$id", entry); } + + Future register(String username, String password) async { + try { + await post("/user", { + "username": username, + "password": password, + }, + forLogin: true, + ); + } catch (e) { + throw Exception("Failed to register"); + } + } } diff --git a/lib/screens/login.dart b/lib/screens/login.dart index 0a4d6c2..9436721 100644 --- a/lib/screens/login.dart +++ b/lib/screens/login.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:fooder_web/screens/based.dart'; import 'package:fooder_web/screens/main.dart'; +import 'package:fooder_web/screens/register.dart'; class LoginScreen extends BasedScreen { @@ -99,24 +100,41 @@ class _LoginScreen extends State { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - TextFormField( + TextFormField( decoration: const InputDecoration( labelText: 'Username', ), controller: usernameController, - ), - TextFormField( + ), + TextFormField( obscureText: true, decoration: const InputDecoration( labelText: 'Password', ), controller: passwordController, onFieldSubmitted: (_) => _login() - ), - FilledButton( - onPressed: _login, - child: const Text('Login'), - ) + ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 10), + child: FilledButton( + onPressed: _login, + child: const Text('Login'), + ), + ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 10), + child: TextButton( + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => RegisterScreen(apiClient: widget.apiClient), + ), + ); + }, + child: const Text('Don\'t have an account? Register here!'), + ), + ), ], ), ), diff --git a/lib/screens/register.dart b/lib/screens/register.dart new file mode 100644 index 0000000..afe9669 --- /dev/null +++ b/lib/screens/register.dart @@ -0,0 +1,117 @@ +import 'package:flutter/material.dart'; +import 'package:fooder_web/screens/based.dart'; + + +class RegisterScreen extends BasedScreen { + const RegisterScreen({super.key, required super.apiClient}); + + @override + State createState() => _RegisterScreen(); +} + + +class _RegisterScreen extends State { + final usernameController = TextEditingController(); + final passwordController = TextEditingController(); + final passwordConfirmController = TextEditingController(); + + @override + void dispose() { + usernameController.dispose(); + passwordController.dispose(); + passwordConfirmController.dispose(); + super.dispose(); + } + + void showError(String message) + { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(message, textAlign: TextAlign.center), + backgroundColor: Theme.of(context).colorScheme.error, + ), + ); + } + + void showText(String text) + { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(text, textAlign: TextAlign.center), + backgroundColor: Theme.of(context).colorScheme.primary, + ), + ); + } + + void popMeDady() { + Navigator.pop(context); + } + + // login client when button pressed + Future _register() async { + if (passwordController.text != passwordConfirmController.text) { + showError("Passwords don't match"); + return; + } + + try { + await widget.apiClient.register( + usernameController.text, + passwordController.text, + ); + showText("Created account. You can now log in."); + popMeDady(); + } on Exception catch (e) { + showError(e.toString()); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + backgroundColor: Theme.of(context).colorScheme.inversePrimary, + title: const Text("FOODER"), + ), + body: Center( + child: Container( + constraints: const BoxConstraints(maxWidth: 600), + padding: const EdgeInsets.all(10), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + TextFormField( + decoration: const InputDecoration( + labelText: 'Username', + ), + controller: usernameController, + ), + TextFormField( + obscureText: true, + decoration: const InputDecoration( + labelText: 'Password', + ), + controller: passwordController, + ), + TextFormField( + obscureText: true, + decoration: const InputDecoration( + labelText: 'Confirm password', + ), + controller: passwordConfirmController, + onFieldSubmitted: (_) => _register() + ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 10), + child: FilledButton( + onPressed: _register, + child: const Text('Register'), + ) + ), + ], + ), + ), + ), + ); + } +}