|
|
- import 'dart:convert';
- import 'dart:typed_data';
-
- import 'package:Capsule/components/flash_message.dart';
- import 'package:Capsule/models/my_profile.dart';
- import 'package:flutter/material.dart';
- import 'package:http/http.dart' as http;
-
- import '/utils/encryption/aes_helper.dart';
- import '/utils/encryption/crypto_utils.dart';
-
-
- class Signup extends StatelessWidget {
- const Signup({Key? key}) : super(key: key);
-
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- appBar: AppBar(
- title: null,
- automaticallyImplyLeading: true,
- //`true` if you want Flutter to automatically add Back Button when needed,
- //or `false` if you want to force your own back button every where
- leading: IconButton(icon: const Icon(Icons.arrow_back),
- onPressed:() => {
- Navigator.pop(context)
- }
- ),
- backgroundColor: Colors.transparent,
- shadowColor: Colors.transparent,
- ),
- body: const SafeArea(
- child: SignupWidget(),
- )
- );
- }
- }
-
- class SignupResponse {
- final String status;
- final String message;
-
- const SignupResponse({
- required this.status,
- required this.message,
- });
-
- factory SignupResponse.fromJson(Map<String, dynamic> json) {
- return SignupResponse(
- status: json['status'],
- message: json['message'],
- );
- }
- }
-
- class SignupWidget extends StatefulWidget {
- const SignupWidget({Key? key}) : super(key: key);
-
- @override
- State<SignupWidget> createState() => _SignupWidgetState();
- }
-
- class _SignupWidgetState extends State<SignupWidget> {
- final _formKey = GlobalKey<FormState>();
-
- final TextEditingController _usernameController = TextEditingController();
- final TextEditingController _passwordController = TextEditingController();
- final TextEditingController _passwordConfirmController = TextEditingController();
- final TextEditingController _serverUrlController = TextEditingController();
-
- bool showUrlInput = false;
-
- final OutlineInputBorder inputBorderStyle = OutlineInputBorder(
- borderRadius: BorderRadius.circular(5),
- borderSide: const BorderSide(
- color: Colors.transparent,
- )
- );
-
- final TextStyle inputTextStyle = const TextStyle(
- fontSize: 18,
- );
-
- @override
- Widget build(BuildContext context) {
-
- final ButtonStyle buttonStyle = ElevatedButton.styleFrom(
- primary: Theme.of(context).colorScheme.surface,
- onPrimary: Theme.of(context).colorScheme.onSurface,
- minimumSize: const Size.fromHeight(50),
- padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
- textStyle: TextStyle(
- fontSize: 20,
- fontWeight: FontWeight.bold,
- color: Theme.of(context).colorScheme.error,
- ),
- );
-
- return Center(
- child: SingleChildScrollView(
- child: Form(
- key: _formKey,
- child: Center(
- child: Padding(
- padding: const EdgeInsets.only(
- left: 20,
- right: 20,
- top: 0,
- bottom: 100,
- ),
- child: Column(
- mainAxisAlignment: MainAxisAlignment.center,
- crossAxisAlignment: CrossAxisAlignment.center,
- children: [
- Text(
- 'Sign Up',
- style: TextStyle(
- fontSize: 35,
- color: Theme.of(context).colorScheme.onBackground,
- ),
- ),
- const SizedBox(height: 30),
- input(
- _usernameController,
- 'Username',
- false,
- (value) {
- if (value == null || value.isEmpty) {
- return 'Create a username';
- }
- return null;
- },
- ),
- const SizedBox(height: 10),
- input(
- _passwordController,
- 'Password',
- true,
- (value) {
- if (value == null || value.isEmpty) {
- return 'Enter a password';
- }
- return null;
- },
- ),
- const SizedBox(height: 10),
- input(
- _passwordConfirmController,
- 'Confirm Password',
- true,
- (value) {
- if (value == null || value.isEmpty) {
- return 'Confirm your password';
- }
- if (value != _passwordController.text) {
- return 'Passwords do not match';
- }
- return null;
- },
- ),
- const SizedBox(height: 15),
- serverUrl(),
- const SizedBox(height: 15),
- ElevatedButton(
- style: buttonStyle,
- onPressed: () {
- if (!_formKey.currentState!.validate()) {
- return;
- }
-
- ScaffoldMessenger.of(context).showSnackBar(
- const SnackBar(content: Text('Processing Data')),
- );
-
- signUp()
- .then((dynamic) {
- Navigator.of(context).popUntil((route) => route.isFirst);
- }).catchError((error) {
- showMessage('Failed to signup to Capsule, please try again later', context);
- });
- },
- child: const Text('Submit'),
- ),
- ],
- )
- )
- )
- )
- )
- );
- }
-
- Widget input(
- TextEditingController textController,
- String hintText,
- bool password,
- String? Function(dynamic) validationFunction,
- ) {
- return TextFormField(
- controller: textController,
- obscureText: password,
- enableSuggestions: false,
- autocorrect: false,
- decoration: InputDecoration(
- hintText: hintText,
- enabledBorder: inputBorderStyle,
- focusedBorder: inputBorderStyle,
- ),
- style: inputTextStyle,
- validator: validationFunction,
- );
- }
-
- Widget serverUrl() {
- if (!showUrlInput) {
- return
- Padding(
- padding: const EdgeInsets.only(top: 0, bottom: 10),
- child: Row(
- children: [
- SizedBox(
- height: 10,
- child: IconButton(
- onPressed: () {
- setState(() {
- showUrlInput = true;
- });
- },
- icon: Icon(
- Icons.edit,
- color: Theme.of(context).disabledColor,
- ),
- splashRadius: 2,
- padding: const EdgeInsets.all(2),
- iconSize: 15,
- ),
- ),
- const SizedBox(width: 2),
- Column(
- children: [
- const SizedBox(height: 10),
- Text(
- 'Server URL - $defaultServerUrl',
- style: TextStyle(
- color: Theme.of(context).disabledColor,
- fontSize: 12,
- ),
- ),
- ],
- ),
- ],
- ),
- );
- }
-
- if (_serverUrlController.text == '') {
- _serverUrlController.text = defaultServerUrl;
- }
-
- return input(
- _serverUrlController,
- 'Server URL',
- false,
- (dynamic) {
- return null;
- },
- );
- }
-
- Future<SignupResponse> signUp() async {
- await MyProfile.setServerUrl(_serverUrlController.text);
-
- var keyPair = CryptoUtils.generateRSAKeyPair();
-
- var rsaPubPem = CryptoUtils.encodeRSAPublicKeyToPem(keyPair.publicKey);
- var rsaPrivPem = CryptoUtils.encodeRSAPrivateKeyToPem(keyPair.privateKey);
-
- String encRsaPriv = AesHelper.aesEncrypt(
- _passwordController.text,
- Uint8List.fromList(rsaPrivPem.codeUnits),
- );
-
- final resp = await http.post(
- await MyProfile.getServerUrl('api/v1/signup'),
- headers: <String, String>{
- 'Content-Type': 'application/json; charset=UTF-8',
- },
- body: jsonEncode(<String, String>{
- 'username': _usernameController.text,
- 'password': _passwordController.text,
- 'confirm_password': _passwordConfirmController.text,
- 'asymmetric_public_key': rsaPubPem,
- 'asymmetric_private_key': encRsaPriv,
- }),
- );
-
- SignupResponse response = SignupResponse.fromJson(jsonDecode(resp.body));
-
- if (resp.statusCode != 201) {
- throw Exception(response.message);
- }
-
- return response;
- }
- }
|