|
|
@ -20,18 +20,23 @@ class LoginPage extends StatefulWidget { |
|
|
|
class _LoginPageState extends State<LoginPage> { |
|
|
|
class _LoginPageState extends State<LoginPage> { |
|
|
|
final _formKey = GlobalKey<FormState>(); |
|
|
|
final _formKey = GlobalKey<FormState>(); |
|
|
|
final _focusNode = FocusNode(); |
|
|
|
final _focusNode = FocusNode(); |
|
|
|
|
|
|
|
final _controller = TextEditingController(); |
|
|
|
@override |
|
|
|
|
|
|
|
void initState() { |
|
|
|
|
|
|
|
super.initState(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@override |
|
|
|
@override |
|
|
|
void dispose() { |
|
|
|
void dispose() { |
|
|
|
_focusNode.dispose(); |
|
|
|
_focusNode.dispose(); |
|
|
|
|
|
|
|
_controller.dispose(); |
|
|
|
super.dispose(); |
|
|
|
super.dispose(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void login(final String url) { |
|
|
|
|
|
|
|
if (_formKey.currentState!.validate()) { |
|
|
|
|
|
|
|
LoginCheckServerStatusRoute(serverUrl: url).go(context); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
_focusNode.requestFocus(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@override |
|
|
|
@override |
|
|
|
Widget build(final BuildContext context) { |
|
|
|
Widget build(final BuildContext context) { |
|
|
|
final branding = Branding.of(context); |
|
|
|
final branding = Branding.of(context); |
|
|
@ -39,77 +44,76 @@ class _LoginPageState extends State<LoginPage> { |
|
|
|
|
|
|
|
|
|
|
|
return Scaffold( |
|
|
|
return Scaffold( |
|
|
|
resizeToAvoidBottomInset: true, |
|
|
|
resizeToAvoidBottomInset: true, |
|
|
|
appBar: AppBar( |
|
|
|
appBar: Navigator.canPop(context) |
|
|
|
leading: Navigator.canPop(context) ? const CloseButton() : null, |
|
|
|
? AppBar( |
|
|
|
), |
|
|
|
leading: const CloseButton(), |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
: null, |
|
|
|
body: Center( |
|
|
|
body: Center( |
|
|
|
child: ConstrainedBox( |
|
|
|
child: ConstrainedBox( |
|
|
|
constraints: NeonDialogTheme.of(context).constraints, |
|
|
|
constraints: NeonDialogTheme.of(context).constraints, |
|
|
|
child: Scrollbar( |
|
|
|
child: Scrollbar( |
|
|
|
child: SingleChildScrollView( |
|
|
|
child: SingleChildScrollView( |
|
|
|
padding: const EdgeInsets.symmetric(vertical: 40, horizontal: 20), |
|
|
|
padding: const EdgeInsets.all(10), |
|
|
|
primary: true, |
|
|
|
primary: true, |
|
|
|
child: Column( |
|
|
|
child: Column( |
|
|
|
children: [ |
|
|
|
children: [ |
|
|
|
branding.logo, |
|
|
|
ExcludeSemantics( |
|
|
|
|
|
|
|
child: branding.logo, |
|
|
|
|
|
|
|
), |
|
|
|
Text( |
|
|
|
Text( |
|
|
|
branding.name, |
|
|
|
branding.name, |
|
|
|
style: Theme.of(context).textTheme.titleLarge, |
|
|
|
style: Theme.of(context).textTheme.titleLarge, |
|
|
|
), |
|
|
|
), |
|
|
|
const SizedBox( |
|
|
|
|
|
|
|
height: 20, |
|
|
|
|
|
|
|
), |
|
|
|
|
|
|
|
if (branding.showLoginWithNextcloud) ...[ |
|
|
|
if (branding.showLoginWithNextcloud) ...[ |
|
|
|
Text(AppLocalizations.of(context).loginWorksWith), |
|
|
|
|
|
|
|
const SizedBox( |
|
|
|
const SizedBox( |
|
|
|
height: 20, |
|
|
|
height: 10, |
|
|
|
), |
|
|
|
), |
|
|
|
], |
|
|
|
Text(AppLocalizations.of(context).loginWorksWith), |
|
|
|
const NextcloudLogo(), |
|
|
|
|
|
|
|
const SizedBox( |
|
|
|
const SizedBox( |
|
|
|
height: 40, |
|
|
|
height: 10, |
|
|
|
), |
|
|
|
|
|
|
|
if (platform.canUseCamera) ...[ |
|
|
|
|
|
|
|
ExcludeSemantics( |
|
|
|
|
|
|
|
child: Center( |
|
|
|
|
|
|
|
child: Text(AppLocalizations.of(context).loginUsingQrcode), |
|
|
|
|
|
|
|
), |
|
|
|
|
|
|
|
), |
|
|
|
|
|
|
|
IconButton( |
|
|
|
|
|
|
|
tooltip: AppLocalizations.of(context).loginUsingQrcode, |
|
|
|
|
|
|
|
icon: const Icon( |
|
|
|
|
|
|
|
Icons.qr_code_scanner, |
|
|
|
|
|
|
|
size: 50, |
|
|
|
|
|
|
|
), |
|
|
|
), |
|
|
|
onPressed: () => const LoginQrcodeRoute().go(context), |
|
|
|
Semantics( |
|
|
|
|
|
|
|
label: AppLocalizations.of(context).nextcloud, |
|
|
|
|
|
|
|
child: const NextcloudLogo(), |
|
|
|
), |
|
|
|
), |
|
|
|
|
|
|
|
], |
|
|
|
const SizedBox( |
|
|
|
const SizedBox( |
|
|
|
height: 20, |
|
|
|
height: 50, |
|
|
|
), |
|
|
|
|
|
|
|
ExcludeSemantics( |
|
|
|
|
|
|
|
child: Center( |
|
|
|
|
|
|
|
child: Text(AppLocalizations.of(context).loginUsingServerAddress), |
|
|
|
|
|
|
|
), |
|
|
|
|
|
|
|
), |
|
|
|
), |
|
|
|
], |
|
|
|
|
|
|
|
Form( |
|
|
|
Form( |
|
|
|
key: _formKey, |
|
|
|
key: _formKey, |
|
|
|
child: TextFormField( |
|
|
|
child: TextFormField( |
|
|
|
focusNode: _focusNode, |
|
|
|
focusNode: _focusNode, |
|
|
|
decoration: const InputDecoration( |
|
|
|
controller: _controller, |
|
|
|
|
|
|
|
decoration: InputDecoration( |
|
|
|
hintText: 'https://...', |
|
|
|
hintText: 'https://...', |
|
|
|
|
|
|
|
labelText: AppLocalizations.of(context).loginUsingServerAddress, |
|
|
|
|
|
|
|
suffixIcon: IconButton( |
|
|
|
|
|
|
|
icon: const Icon(Icons.arrow_forward), |
|
|
|
|
|
|
|
onPressed: () { |
|
|
|
|
|
|
|
login(_controller.text); |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
), |
|
|
|
), |
|
|
|
), |
|
|
|
keyboardType: TextInputType.url, |
|
|
|
keyboardType: TextInputType.url, |
|
|
|
validator: (final input) => validateHttpUrl(context, input), |
|
|
|
validator: (final input) => validateHttpUrl(context, input), |
|
|
|
onFieldSubmitted: (final input) { |
|
|
|
onFieldSubmitted: login, |
|
|
|
if (_formKey.currentState!.validate()) { |
|
|
|
), |
|
|
|
LoginCheckServerStatusRoute(serverUrl: input).go(context); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
_focusNode.requestFocus(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
), |
|
|
|
), |
|
|
|
|
|
|
|
if (platform.canUseCamera || true) ...[ |
|
|
|
|
|
|
|
const SizedBox( |
|
|
|
|
|
|
|
height: 50, |
|
|
|
), |
|
|
|
), |
|
|
|
|
|
|
|
IconButton( |
|
|
|
|
|
|
|
tooltip: AppLocalizations.of(context).loginUsingQrcode, |
|
|
|
|
|
|
|
icon: const Icon( |
|
|
|
|
|
|
|
Icons.qr_code_scanner_rounded, |
|
|
|
|
|
|
|
size: 60, |
|
|
|
|
|
|
|
), |
|
|
|
|
|
|
|
onPressed: () => const LoginQrcodeRoute().go(context), |
|
|
|
|
|
|
|
), |
|
|
|
|
|
|
|
], |
|
|
|
], |
|
|
|
], |
|
|
|
), |
|
|
|
), |
|
|
|
), |
|
|
|
), |
|
|
|