diff --git a/lib/screens/account/preferences/notifications.dart b/lib/screens/account/preferences/notifications.dart index 0c62d49..87206f5 100644 --- a/lib/screens/account/preferences/notifications.dart +++ b/lib/screens/account/preferences/notifications.dart @@ -1,11 +1,11 @@ import 'package:flutter/material.dart'; -import 'package:flutter_animate/flutter_animate.dart'; import 'package:get/get.dart'; import 'package:get/get_connect/http/src/exceptions/exceptions.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:solian/exceptions/request.dart'; import 'package:solian/exts.dart'; import 'package:solian/providers/auth.dart'; +import 'package:solian/widgets/loading_indicator.dart'; class NotificationPreferencesScreen extends StatefulWidget { const NotificationPreferencesScreen({super.key}); @@ -76,7 +76,7 @@ class _NotificationPreferencesScreenState Widget build(BuildContext context) { return Column( children: [ - if (_isBusy) const LinearProgressIndicator().animate().scaleX(), + LoadingIndicator(isActive: _isBusy), ListTile( tileColor: Theme.of(context).colorScheme.surfaceContainer, contentPadding: const EdgeInsets.symmetric(horizontal: 24), diff --git a/lib/screens/account/preferences/security.dart b/lib/screens/account/preferences/security.dart index 6ac3b05..c0fbf95 100644 --- a/lib/screens/account/preferences/security.dart +++ b/lib/screens/account/preferences/security.dart @@ -1,11 +1,11 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_animate/flutter_animate.dart'; import 'package:get/get.dart'; import 'package:get/get_connect/http/src/exceptions/exceptions.dart'; import 'package:solian/exceptions/request.dart'; import 'package:solian/exts.dart'; import 'package:solian/providers/auth.dart'; +import 'package:solian/widgets/loading_indicator.dart'; class AuthPreferencesScreen extends StatefulWidget { const AuthPreferencesScreen({super.key}); @@ -67,7 +67,7 @@ class _AuthPreferencesScreenState extends State { Widget build(BuildContext context) { return Column( children: [ - if (_isBusy) const LinearProgressIndicator().animate().scaleX(), + LoadingIndicator(isActive: _isBusy), ListTile( tileColor: Theme.of(context).colorScheme.surfaceContainer, contentPadding: const EdgeInsets.symmetric(horizontal: 24), diff --git a/lib/screens/account/profile_edit.dart b/lib/screens/account/profile_edit.dart index d4b4765..dbd915d 100644 --- a/lib/screens/account/profile_edit.dart +++ b/lib/screens/account/profile_edit.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter_animate/flutter_animate.dart'; import 'package:gap/gap.dart'; import 'package:get/get.dart'; import 'package:image_cropper/image_cropper.dart'; @@ -12,6 +11,7 @@ import 'package:solian/providers/auth.dart'; import 'package:solian/providers/content/attachment.dart'; import 'package:solian/services.dart'; import 'package:solian/widgets/account/account_avatar.dart'; +import 'package:solian/widgets/loading_indicator.dart'; class PersonalizeScreen extends StatefulWidget { const PersonalizeScreen({super.key}); @@ -188,7 +188,7 @@ class _PersonalizeScreenState extends State { return ListView( children: [ - if (_isBusy) const LinearProgressIndicator().animate().scaleX(), + LoadingIndicator(isActive: _isBusy), const Gap(24), Stack( children: [ diff --git a/lib/screens/channel/channel_organize.dart b/lib/screens/channel/channel_organize.dart index dad97d5..a580aa5 100644 --- a/lib/screens/channel/channel_organize.dart +++ b/lib/screens/channel/channel_organize.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter_animate/flutter_animate.dart'; import 'package:get/get.dart'; import 'package:solian/exts.dart'; import 'package:solian/models/channel.dart'; @@ -9,6 +8,7 @@ import 'package:solian/providers/content/channel.dart'; import 'package:solian/router.dart'; import 'package:solian/theme.dart'; import 'package:solian/widgets/app_bar_title.dart'; +import 'package:solian/widgets/loading_indicator.dart'; import 'package:solian/widgets/root_container.dart'; import 'package:uuid/uuid.dart'; @@ -132,7 +132,7 @@ class _ChannelOrganizeScreenState extends State { top: false, child: Column( children: [ - if (_isBusy) const LinearProgressIndicator().animate().scaleX(), + LoadingIndicator(isActive: _isBusy), if (widget.edit != null) MaterialBanner( leading: const Icon(Icons.edit), diff --git a/lib/screens/chat.dart b/lib/screens/chat.dart index e1e497c..49199c4 100644 --- a/lib/screens/chat.dart +++ b/lib/screens/chat.dart @@ -288,7 +288,7 @@ class _ChatListState extends State { return Column( children: [ const ChatCallCurrentIndicator(), - if (_isBusy) const LoadingIndicator(), + LoadingIndicator(isActive: _isBusy), Expanded( child: TabBarView( children: [ diff --git a/lib/screens/posts/post_editor.dart b/lib/screens/posts/post_editor.dart index 0e00902..63d7c02 100644 --- a/lib/screens/posts/post_editor.dart +++ b/lib/screens/posts/post_editor.dart @@ -16,6 +16,7 @@ import 'package:solian/router.dart'; import 'package:solian/theme.dart'; import 'package:solian/widgets/app_bar_leading.dart'; import 'package:solian/widgets/app_bar_title.dart'; +import 'package:solian/widgets/loading_indicator.dart'; import 'package:solian/widgets/markdown_text_content.dart'; import 'package:solian/widgets/posts/post_item.dart'; import 'package:badges/badges.dart' as badges; @@ -274,7 +275,7 @@ class _PostPublishScreenState extends State { ), ], ), - if (_isBusy) const LinearProgressIndicator().animate().scaleX(), + LoadingIndicator(isActive: _isBusy), Expanded( child: DefaultTabController( length: 2, diff --git a/lib/screens/posts/post_search.dart b/lib/screens/posts/post_search.dart index 82662d9..92feef9 100644 --- a/lib/screens/posts/post_search.dart +++ b/lib/screens/posts/post_search.dart @@ -135,7 +135,7 @@ class _PostSearchScreenState extends State { }, ), ), - if (_isBusy) const LoadingIndicator(), + LoadingIndicator(isActive: _isBusy), Expanded( child: RefreshIndicator( onRefresh: () => Future.sync(() => _pagingController.refresh()), diff --git a/lib/screens/realms.dart b/lib/screens/realms.dart index 83ddaef..146e66c 100644 --- a/lib/screens/realms.dart +++ b/lib/screens/realms.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter_animate/flutter_animate.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; import 'package:solian/models/realm.dart'; @@ -15,6 +14,7 @@ import 'package:solian/widgets/app_bar_leading.dart'; import 'package:solian/widgets/app_bar_title.dart'; import 'package:solian/widgets/auto_cache_image.dart'; import 'package:solian/widgets/current_state_action.dart'; +import 'package:solian/widgets/loading_indicator.dart'; import 'package:solian/widgets/root_container.dart'; import 'package:solian/widgets/sized_container.dart'; @@ -93,7 +93,7 @@ class _RealmListScreenState extends State { return Column( children: [ - if (_isBusy) const LinearProgressIndicator().animate().scaleX(), + LoadingIndicator(isActive: _isBusy), Expanded( child: CenteredContainer( child: RefreshIndicator( diff --git a/lib/screens/realms/realm_organize.dart b/lib/screens/realms/realm_organize.dart index 132b9ec..37c70ee 100644 --- a/lib/screens/realms/realm_organize.dart +++ b/lib/screens/realms/realm_organize.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter_animate/flutter_animate.dart'; import 'package:get/get.dart'; import 'package:image_cropper/image_cropper.dart'; import 'package:image_picker/image_picker.dart'; @@ -13,6 +12,7 @@ import 'package:solian/router.dart'; import 'package:solian/theme.dart'; import 'package:solian/widgets/app_bar_leading.dart'; import 'package:solian/widgets/app_bar_title.dart'; +import 'package:solian/widgets/loading_indicator.dart'; import 'package:solian/widgets/root_container.dart'; import 'package:uuid/uuid.dart'; @@ -208,7 +208,7 @@ class _RealmOrganizeScreenState extends State { top: false, child: Column( children: [ - if (_isBusy) const LinearProgressIndicator().animate().scaleX(), + LoadingIndicator(isActive: _isBusy), if (widget.edit != null) MaterialBanner( leading: const Icon(Icons.edit), diff --git a/lib/widgets/attachments/attachment_editor.dart b/lib/widgets/attachments/attachment_editor.dart index 2fa3ea5..c21f696 100644 --- a/lib/widgets/attachments/attachment_editor.dart +++ b/lib/widgets/attachments/attachment_editor.dart @@ -21,6 +21,7 @@ import 'package:solian/providers/content/attachment.dart'; import 'package:solian/widgets/attachments/attachment_attr_editor.dart'; import 'package:solian/widgets/attachments/attachment_editor_thumbnail.dart'; import 'package:solian/widgets/attachments/attachment_fullscreen.dart'; +import 'package:solian/widgets/loading_indicator.dart'; class AttachmentEditorPopup extends StatefulWidget { final String pool; @@ -660,7 +661,7 @@ class _AttachmentEditorPopupState extends State { ), ], ).paddingOnly(left: 24, right: 24, top: 32, bottom: 16), - if (_isBusy) const LinearProgressIndicator().animate().scaleX(), + LoadingIndicator(isActive: _isBusy), Expanded( child: CustomScrollView( slivers: [ diff --git a/lib/widgets/channel/channel_member.dart b/lib/widgets/channel/channel_member.dart index 0753c1b..b175da1 100644 --- a/lib/widgets/channel/channel_member.dart +++ b/lib/widgets/channel/channel_member.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter_animate/flutter_animate.dart'; import 'package:get/get.dart'; import 'package:solian/exts.dart'; import 'package:solian/models/channel.dart'; @@ -8,6 +7,7 @@ import 'package:solian/services.dart'; import 'package:solian/widgets/account/account_avatar.dart'; import 'package:solian/widgets/account/account_profile_popup.dart'; import 'package:solian/widgets/account/relative_select.dart'; +import 'package:solian/widgets/loading_indicator.dart'; class ChannelMemberListPopup extends StatefulWidget { final Channel channel; @@ -131,7 +131,7 @@ class _ChannelMemberListPopupState extends State { 'channelMembers'.tr, style: Theme.of(context).textTheme.headlineSmall, ).paddingOnly(left: 24, right: 24, top: 32, bottom: 16), - if (_isBusy) const LinearProgressIndicator().animate().scaleX(), + LoadingIndicator(isActive: _isBusy), ListTile( tileColor: Theme.of(context).colorScheme.surfaceContainerHigh, contentPadding: const EdgeInsets.symmetric(horizontal: 20), diff --git a/lib/widgets/chat/chat_event_action.dart b/lib/widgets/chat/chat_event_action.dart index c01087d..cb1baab 100644 --- a/lib/widgets/chat/chat_event_action.dart +++ b/lib/widgets/chat/chat_event_action.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter_animate/flutter_animate.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; import 'package:solian/models/channel.dart'; @@ -7,6 +6,7 @@ import 'package:solian/models/event.dart'; import 'package:solian/models/realm.dart'; import 'package:solian/providers/auth.dart'; import 'package:solian/widgets/chat/chat_event_deletion.dart'; +import 'package:solian/widgets/loading_indicator.dart'; class ChatEventAction extends StatefulWidget { final Channel channel; @@ -73,7 +73,7 @@ class _ChatEventActionState extends State { ), ], ).paddingOnly(left: 24, right: 24, top: 32, bottom: 16), - if (_isBusy) const LinearProgressIndicator().animate().scaleX(), + LoadingIndicator(isActive: _isBusy), Expanded( child: ListView( children: [ diff --git a/lib/widgets/loading_indicator.dart b/lib/widgets/loading_indicator.dart index ea74fd9..ea137c9 100644 --- a/lib/widgets/loading_indicator.dart +++ b/lib/widgets/loading_indicator.dart @@ -1,27 +1,83 @@ import 'package:flutter/material.dart'; -import 'package:gap/gap.dart'; import 'package:get/get.dart'; +import 'package:gap/gap.dart'; -class LoadingIndicator extends StatelessWidget { - const LoadingIndicator({super.key}); +class LoadingIndicator extends StatefulWidget { + final bool isActive; + + const LoadingIndicator({super.key, this.isActive = true}); + + @override + State createState() => _LoadingIndicatorState(); +} + +class _LoadingIndicatorState extends State + with SingleTickerProviderStateMixin { + late AnimationController _controller; + late Animation _animation; + + @override + void initState() { + super.initState(); + + _controller = AnimationController( + vsync: this, + duration: const Duration(milliseconds: 300), + ); + + _animation = CurvedAnimation( + parent: _controller, + curve: Curves.easeInOut, + ); + + if (widget.isActive) { + _controller.forward(); + } else { + _controller.reverse(); + } + } + + @override + void didUpdateWidget(covariant LoadingIndicator oldWidget) { + super.didUpdateWidget(oldWidget); + + if (widget.isActive != oldWidget.isActive) { + if (widget.isActive) { + _controller.forward(); + } else { + _controller.reverse(); + } + } + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } @override Widget build(BuildContext context) { - return Container( - padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 24), - color: Theme.of(context).colorScheme.surfaceContainerLow.withOpacity(0.5), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const SizedBox( - height: 16, - width: 16, - child: CircularProgressIndicator(strokeWidth: 2.5), - ), - const Gap(8), - Text('loading'.tr) - ], + return SizeTransition( + sizeFactor: _animation, + axisAlignment: -1, // Align animation from the top + child: Container( + padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 24), + color: + Theme.of(context).colorScheme.surfaceContainerLow.withOpacity(0.5), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const SizedBox( + height: 16, + width: 16, + child: CircularProgressIndicator(strokeWidth: 2.5), + ), + const Gap(8), + Text('loading'.tr), + ], + ), ), ); } diff --git a/lib/widgets/posts/post_action.dart b/lib/widgets/posts/post_action.dart index e6eb651..9bae3e2 100644 --- a/lib/widgets/posts/post_action.dart +++ b/lib/widgets/posts/post_action.dart @@ -3,7 +3,6 @@ import 'dart:math'; import 'package:file_saver/file_saver.dart'; import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_animate/flutter_animate.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; import 'package:screenshot/screenshot.dart'; @@ -14,6 +13,7 @@ import 'package:solian/platform.dart'; import 'package:solian/providers/auth.dart'; import 'package:solian/router.dart'; import 'package:solian/screens/posts/post_editor.dart'; +import 'package:solian/widgets/loading_indicator.dart'; import 'package:solian/widgets/posts/post_share.dart'; import 'package:solian/widgets/reports/abuse_report.dart'; @@ -182,7 +182,7 @@ class _PostActionState extends State { ), ], ).paddingOnly(left: 24, right: 24, top: 32, bottom: 16), - if (_isBusy) const LinearProgressIndicator().animate().scaleX(), + LoadingIndicator(isActive: _isBusy), Expanded( child: ListView( children: [ diff --git a/lib/widgets/realms/realm_member.dart b/lib/widgets/realms/realm_member.dart index 386307f..03d6fe4 100644 --- a/lib/widgets/realms/realm_member.dart +++ b/lib/widgets/realms/realm_member.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter_animate/flutter_animate.dart'; import 'package:get/get.dart'; import 'package:solian/exts.dart'; import 'package:solian/models/realm.dart'; @@ -8,6 +7,7 @@ import 'package:solian/services.dart'; import 'package:solian/widgets/account/account_avatar.dart'; import 'package:solian/widgets/account/account_profile_popup.dart'; import 'package:solian/widgets/account/relative_select.dart'; +import 'package:solian/widgets/loading_indicator.dart'; class RealmMemberListPopup extends StatefulWidget { final Realm realm; @@ -128,7 +128,7 @@ class _RealmMemberListPopupState extends State { 'realmMembers'.tr, style: Theme.of(context).textTheme.headlineSmall, ).paddingOnly(left: 24, right: 24, top: 32, bottom: 16), - if (_isBusy) const LinearProgressIndicator().animate().scaleX(), + LoadingIndicator(isActive: _isBusy), ListTile( tileColor: Theme.of(context).colorScheme.surfaceContainerHigh, contentPadding: const EdgeInsets.symmetric(horizontal: 20), diff --git a/pubspec.yaml b/pubspec.yaml index 49064da..8b21825 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: solian description: "The Solar Network App" publish_to: "none" -version: 1.3.8+12 +version: 1.3.8+13 environment: sdk: ">=3.3.4 <4.0.0"