fix: updated login screen glitches
All checks were successful
deploy / deploy (push) Successful in 4m2s

This commit is contained in:
Aswin B. S 2026-11-05 01:39:06 +05:30
parent 0b86be6796
commit 78c77f708c
7 changed files with 94 additions and 74 deletions

View File

@ -1 +0,0 @@
API_BASE_URL=http://127.0.0.1:8080

5
.vscode/launch.json vendored
View File

@ -2,12 +2,11 @@
"version": "0.2.0",
"configurations": [
{
"name": "SRS (local API)",
"name": "SRS",
"request": "launch",
"type": "dart",
"program": "lib/main.dart",
"deviceId": "chrome",
"args": ["--web-port", "5000", "--dart-define-from-file=.env"]
"deviceId": "chrome"
}
]
}

View File

@ -1,11 +1,5 @@
import 'package:flutter/foundation.dart';
class AppConfig {
const AppConfig._();
static const apiBaseUrl = String.fromEnvironment('API_BASE_URL');
static bool get unconfigured => kDebugMode && apiBaseUrl.isEmpty;
static const setupHint =
'No API_BASE_URL set. For local development run with '
'--dart-define-from-file=.env (copy .env.example to .env first).';
static const apiBaseUrl = String.fromEnvironment('API_BASE_URL');
}

View File

@ -5,7 +5,6 @@ import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';
import 'api.dart';
import 'config.dart';
import 'format.dart';
import 'models.dart';
@ -20,9 +19,8 @@ class HttpApi implements Api {
_client = client ?? http.Client();
static const _tokenKey = 'token';
static String get _unreachable => AppConfig.unconfigured
? 'Unable to reach the server. ${AppConfig.setupHint}'
: 'Unable to reach the server. Check your connection and try again.';
static const _unreachable =
'Unable to reach the server. Check your connection and try again.';
final String _base;
final SharedPreferences _prefs;

View File

@ -1,4 +1,4 @@
import 'package:flutter/material.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../core/api.dart';
@ -16,16 +16,34 @@ class LoginScreen extends ConsumerStatefulWidget {
class _LoginScreenState extends ConsumerState<LoginScreen> {
final _user = TextEditingController();
final _pass = TextEditingController();
final _scroll = ScrollController();
bool _busy = false;
bool _keyboard = false;
String? _error;
@override
void dispose() {
_user.dispose();
_pass.dispose();
_scroll.dispose();
super.dispose();
}
void _followKeyboard(BuildContext context) {
final open = MediaQuery.viewInsetsOf(context).bottom > 0;
if (open && !_keyboard) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (!_scroll.hasClients) return;
_scroll.animateTo(
_scroll.position.maxScrollExtent,
duration: const Duration(milliseconds: 250),
curve: Curves.easeOutCubic,
);
});
}
_keyboard = open;
}
Future<void> _submit() async {
if (_busy) return;
if (_user.text.trim().isEmpty || _pass.text.isEmpty) {
@ -51,74 +69,82 @@ class _LoginScreenState extends ConsumerState<LoginScreen> {
@override
Widget build(BuildContext context) {
final screen = MediaQuery.sizeOf(context);
final landscape = screen.width > screen.height && screen.width >= 600;
if (screen.width >= 900 || landscape) {
return Scaffold(
backgroundColor: C.navy,
body: LayoutBuilder(
builder: (context, c) {
final landscape = c.maxWidth > c.maxHeight && c.maxWidth >= 600;
if (c.maxWidth >= 900 || landscape) {
return Row(
body: Row(
children: [
Expanded(child: _BrandPanel(large: c.maxHeight >= 560)),
Expanded(child: _BrandPanel(large: screen.height >= 560)),
Container(
width: c.maxWidth >= 1280
width: screen.width >= 1280
? 560
: (c.maxWidth >= 900 ? 480 : 420),
: (screen.width >= 900 ? 480 : 420),
color: C.bg,
child: Center(
child: SingleChildScrollView(
padding: const EdgeInsets.all(S.s36),
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 380),
child: _form(),
child: _form(autofocus: true),
),
),
),
),
],
),
);
}
final hero = (c.maxHeight * 0.36).clamp(230.0, 340.0);
return Column(
children: [
SizedBox(
final hero = (screen.height * 0.36).clamp(230.0, 340.0);
_followKeyboard(context);
return Scaffold(
backgroundColor: C.bg,
body: CustomScrollView(
controller: _scroll,
physics: const ClampingScrollPhysics(),
slivers: [
SliverToBoxAdapter(
child: ColoredBox(
color: C.navy,
child: SizedBox(
height: hero,
width: double.infinity,
child: const _BrandPanel(large: false),
),
Expanded(
),
),
SliverFillRemaining(
hasScrollBody: false,
child: ColoredBox(
color: C.navy,
child: Container(
width: double.infinity,
decoration: const BoxDecoration(
color: C.bg,
borderRadius: BorderRadius.vertical(
top: Radius.circular(R.hero),
),
),
child: SingleChildScrollView(
padding: const EdgeInsets.fromLTRB(
S.s24,
S.s28,
S.s24,
S.s24,
),
child: Center(
alignment: Alignment.topCenter,
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 420),
child: _form(),
),
child: _form(autofocus: false),
),
),
),
),
],
);
},
),
);
}
Widget _form() => AutofillGroup(
Widget _form({required bool autofocus}) => AutofillGroup(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -132,7 +158,7 @@ class _LoginScreenState extends ConsumerState<LoginScreen> {
hint: 'Enter username',
error: _error != null,
enabled: !_busy,
autofocus: true,
autofocus: autofocus,
autofill: const [AutofillHints.username],
onChanged: (_) => setState(() => _error = null),
),

View File

@ -11,7 +11,6 @@ import 'core/providers.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
if (AppConfig.unconfigured) debugPrint('SRS: ${AppConfig.setupHint}');
final Api api = HttpApi(
baseUrl: AppConfig.apiBaseUrl,
prefs: await SharedPreferences.getInstance(),

5
web_dev_config.yaml Normal file
View File

@ -0,0 +1,5 @@
server:
port: 5000
proxy:
- target: "http://127.0.0.1:8080/"
prefix: "/api/"