diff --git a/lib/views/login_view.dart b/lib/views/login_view.dart index 63a0106..d2f4d02 100644 --- a/lib/views/login_view.dart +++ b/lib/views/login_view.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:shlink_app/API/server_manager.dart'; import 'package:shlink_app/main.dart'; +import 'package:url_launcher/url_launcher.dart'; import '../globals.dart' as globals; class LoginView extends StatefulWidget { @@ -58,6 +59,7 @@ class _LoginViewState extends State { return Scaffold( extendBody: true, body: CustomScrollView( + physics: const NeverScrollableScrollPhysics(), slivers: [ const SliverAppBar.medium( title: Text("Add server", @@ -65,79 +67,107 @@ class _LoginViewState extends State { SliverFillRemaining( child: Padding( padding: const EdgeInsets.all(16), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, + child: Stack( children: [ - const Padding( - padding: EdgeInsets.only(bottom: 8), - child: Text( - "Server URL", - style: TextStyle(fontWeight: FontWeight.bold), - )), - Row( - children: [ - const Icon(Icons.dns_outlined), - const SizedBox(width: 8), - Expanded( - child: TextField( - controller: _serverUrlController, - keyboardType: TextInputType.url, - decoration: const InputDecoration( - border: OutlineInputBorder(), - labelText: "https://shlink.example.com"), - )) - ], - ), - const Padding( - padding: EdgeInsets.only(top: 8, bottom: 8), - child: Text("API Key", - style: TextStyle(fontWeight: FontWeight.bold)), - ), - Row( - children: [ - const Icon(Icons.key), - const SizedBox(width: 8), - Expanded( - child: TextField( - controller: _apiKeyController, - keyboardType: TextInputType.text, - obscureText: true, - decoration: const InputDecoration( - border: OutlineInputBorder(), labelText: "..."), - )) - ], - ), - Padding( - padding: const EdgeInsets.only(top: 16), - child: Row( + Align( + child: Column( mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, children: [ - FilledButton.tonal( - onPressed: () => {_connect()}, - child: _isLoggingIn - ? Container( + const Padding( + padding: EdgeInsets.only(bottom: 8), + child: Text( + "Server URL", + style: TextStyle(fontWeight: FontWeight.bold), + )), + Row( + children: [ + const Icon(Icons.dns_outlined), + const SizedBox(width: 8), + Expanded( + child: TextField( + controller: _serverUrlController, + keyboardType: TextInputType.url, + decoration: const InputDecoration( + border: OutlineInputBorder(), + labelText: "https://shlink.example.com"), + )) + ], + ), + const Padding( + padding: EdgeInsets.only(top: 8, bottom: 8), + child: Text("API Key", + style: TextStyle(fontWeight: FontWeight.bold)), + ), + Row( + children: [ + const Icon(Icons.key), + const SizedBox(width: 8), + Expanded( + child: TextField( + controller: _apiKeyController, + keyboardType: TextInputType.text, + obscureText: true, + decoration: const InputDecoration( + border: OutlineInputBorder(), labelText: "..."), + )) + ], + ), + Padding( + padding: const EdgeInsets.only(top: 16), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + FilledButton.tonal( + onPressed: () => {_connect()}, + child: _isLoggingIn + ? Container( width: 34, height: 34, padding: const EdgeInsets.all(4), child: const CircularProgressIndicator(), ) - : const Text("Connect", - style: TextStyle(fontSize: 20)), - ) + : const Text("Connect", + style: TextStyle(fontSize: 20)), + ) + ], + ), + ), + Padding( + padding: const EdgeInsets.only(top: 16), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Flexible( + child: Text(_errorMessage, + style: TextStyle(color: Theme.of(context).colorScheme.onError), + textAlign: TextAlign.center)) + ], + ), + ), ], ), ), - Padding( - padding: const EdgeInsets.only(top: 16), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Flexible( - child: Text(_errorMessage, - style: TextStyle(color: Theme.of(context).colorScheme.onError), - textAlign: TextAlign.center)) - ], + Align( + alignment: Alignment.bottomCenter, + child: TextButton( + onPressed: () async { + final Uri url = Uri.parse('https://shlink.io/documentation/api-docs/authentication/'); + try { + if (!await launchUrl(url)) { + throw Exception(); + } + } catch (e) { + final snackBar = SnackBar( + content: Text("Unable to launch url. See Shlink docs for more information.", + style: TextStyle(color: Theme.of(context).colorScheme.onError)), + backgroundColor: Theme.of(context).colorScheme.error, + behavior: SnackBarBehavior.floating); + ScaffoldMessenger.of(context).showSnackBar( + snackBar); + } + }, + child: Text("How to create an API Key"), ), ) ],