diff --git a/lib/screens/login_screen.dart b/lib/screens/login_screen.dart index dac23b7..7771974 100644 --- a/lib/screens/login_screen.dart +++ b/lib/screens/login_screen.dart @@ -15,6 +15,8 @@ class _LoginPageState extends State { final TextEditingController usernameController = TextEditingController(); final TextEditingController passwordController = TextEditingController(); + bool _isLoading = false; + void _showMsg(String message, {String title = ''}) { Get ..closeAllSnackbars() @@ -31,37 +33,88 @@ class _LoginPageState extends State { @override Widget build(BuildContext context) { return Scaffold( - body: CustomScrollView( - slivers: [ - SliverAppBar(floating: false, pinned: true, title: Text('Login')), - SliverList( - delegate: SliverChildListDelegate([ - ListTile( - title: Text('Benutzername'), - subtitle: TextField(controller: usernameController), + body: Center( + child: SingleChildScrollView( + padding: const EdgeInsets.all(24), + child: Card( + elevation: 8, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + child: Padding( + padding: const EdgeInsets.all(24), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + children: [ + IconButton( + icon: const Icon(Icons.arrow_back), + tooltip: 'Zurück', + onPressed: () => Get.back(), + ), + const SizedBox(width: 8), + Text( + 'Login', + style: Theme.of(context).textTheme.headlineMedium, + ), + ], + ), + const SizedBox(height: 24), + TextField( + controller: usernameController, + decoration: const InputDecoration( + labelText: 'Benutzername', + prefixIcon: Icon(Icons.person), + border: OutlineInputBorder(), + ), + ), + const SizedBox(height: 16), + TextField( + controller: passwordController, + obscureText: true, + decoration: const InputDecoration( + labelText: 'Passwort', + prefixIcon: Icon(Icons.lock), + border: OutlineInputBorder(), + ), + ), + const SizedBox(height: 24), + SizedBox( + width: double.infinity, + child: ElevatedButton( + onPressed: _isLoading + ? null + : () async { + setState(() => _isLoading = true); + final success = await authController.login( + usernameController.text, + passwordController.text, + ); + setState(() => _isLoading = false); + if (!success) { + return _showMsg('Login fehlgeschlagen!'); + } + _showMsg('Erfolgreich eingeloggt.'); + Get.back(); + }, + child: _isLoading + ? const SizedBox( + width: 24, + height: 24, + child: CircularProgressIndicator( + strokeWidth: 2, + color: Colors.white, + ), + ) + : const Text('Login'), + ), + ), + ], ), - ListTile( - title: Text('Passwort'), - subtitle: TextField( - controller: passwordController, - obscureText: true, - ), - ), - ElevatedButton( - onPressed: () async { - final success = await authController.login( - usernameController.text, - passwordController.text, - ); - if (!success) { - _showMsg('Login fehlgeschlagen!'); - } - }, - child: Text('Login'), - ), - ]), + ), ), - ], + ), ), ); } diff --git a/lib/screens/media_grid.dart b/lib/screens/media_grid.dart index 7ca5660..f8c3c99 100644 --- a/lib/screens/media_grid.dart +++ b/lib/screens/media_grid.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -18,6 +20,7 @@ class MediaGrid extends StatefulWidget { class _MediaGridState extends State { final MediaController controller = Get.find(); final ScrollController _scrollController = ScrollController(); + Timer? _debounce; @override void initState() { @@ -31,12 +34,15 @@ class _MediaGridState extends State { _scrollController.addListener(() { if (_scrollController.position.extentAfter < 200 && !controller.isLoading.value) { - controller.loadMediaItems( - older: controller.mediaItems.isNotEmpty - ? controller.mediaItems.last.id - : null, - append: true, - ); + if (_debounce?.isActive ?? false) _debounce!.cancel(); + _debounce = Timer(const Duration(milliseconds: 300), () { + controller.loadMediaItems( + older: controller.mediaItems.isNotEmpty + ? controller.mediaItems.last.id + : null, + append: true, + ); + }); } }); } diff --git a/lib/widgets/end_drawer.dart b/lib/widgets/end_drawer.dart index f0580f6..3f07e44 100644 --- a/lib/widgets/end_drawer.dart +++ b/lib/widgets/end_drawer.dart @@ -89,7 +89,7 @@ class EndDrawer extends StatelessWidget { ElevatedButton( onPressed: () { Navigator.pop(context); - Get.bottomSheet(LoginPage(), isDismissible: false); + Get.to(() => LoginPage()); }, child: const Text('Login'), ), diff --git a/lib/widgets/media_tile.dart b/lib/widgets/media_tile.dart index 8526985..b6c3a72 100644 --- a/lib/widgets/media_tile.dart +++ b/lib/widgets/media_tile.dart @@ -12,34 +12,37 @@ class MediaTile extends StatelessWidget { @override Widget build(BuildContext context) { - return InkWell( - onTap: () { - Get.toNamed('/${item.id}'); - }, - child: Stack( - fit: StackFit.expand, - children: [ - Hero( - tag: 'media-${item.id}', - child: CachedNetworkImage( - imageUrl: item.thumbnailUrl, - fit: BoxFit.cover, - errorWidget: (context, url, error) => const Icon(Icons.error), + return RepaintBoundary( + child: InkWell( + onTap: () { + Get.toNamed('/${item.id}'); + }, + child: Stack( + fit: StackFit.expand, + children: [ + Hero( + tag: 'media-${item.id}', + child: CachedNetworkImage( + imageUrl: item.thumbnailUrl, + fit: BoxFit.cover, + placeholder: (content, url) => Container(color: Colors.grey[900]), + errorWidget: (context, url, error) => const Icon(Icons.error), + ), ), - ), - Align( - alignment: Alignment.bottomRight, - child: Icon( - Icons.square, - color: switch (item.mode) { - 1 => Colors.green, - 2 => Colors.red, - _ => Colors.yellow, - }, - size: 15.0, + Align( + alignment: Alignment.bottomRight, + child: Icon( + Icons.square, + color: switch (item.mode) { + 1 => Colors.green, + 2 => Colors.red, + _ => Colors.yellow, + }, + size: 15.0, + ), ), - ), - ], + ], + ), ), ); } diff --git a/pubspec.yaml b/pubspec.yaml index c8c7ccd..60a4750 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 1.3.3+59 +version: 1.3.4+60 environment: sdk: ^3.9.0-100.2.beta