From fdc0676e6b57d7e8a40630f5b5c30cf2f130ee49 Mon Sep 17 00:00:00 2001 From: Tien Do Nam Date: Mon, 3 Aug 2026 13:23:11 +0200 Subject: [PATCH] refactor: make ReceiveTab stateful --- app/lib/pages/tabs/receive_tab.dart | 77 ++++++++++++++++++------ app/lib/pages/tabs/receive_tab_vm.dart | 82 -------------------------- 2 files changed, 60 insertions(+), 99 deletions(-) delete mode 100644 app/lib/pages/tabs/receive_tab_vm.dart diff --git a/app/lib/pages/tabs/receive_tab.dart b/app/lib/pages/tabs/receive_tab.dart index d2ebb129..9ef48c73 100644 --- a/app/lib/pages/tabs/receive_tab.dart +++ b/app/lib/pages/tabs/receive_tab.dart @@ -1,11 +1,14 @@ import 'package:flutter/material.dart'; import 'package:localsend_app/gen/strings.g.dart'; +import 'package:localsend_app/model/state/server/server_state.dart'; import 'package:localsend_app/pages/home_page.dart'; import 'package:localsend_app/pages/home_page_controller.dart'; import 'package:localsend_app/pages/receive_history_page.dart'; -import 'package:localsend_app/pages/tabs/receive_tab_vm.dart'; import 'package:localsend_app/pages/web_share_page.dart'; import 'package:localsend_app/provider/animation_provider.dart'; +import 'package:localsend_app/provider/local_ip_provider.dart'; +import 'package:localsend_app/provider/network/server/server_provider.dart'; +import 'package:localsend_app/provider/settings_provider.dart'; import 'package:localsend_app/util/ip_helper.dart'; import 'package:localsend_app/widget/animations/initial_fade_transition.dart'; import 'package:localsend_app/widget/column_list_view.dart'; @@ -13,16 +16,46 @@ import 'package:localsend_app/widget/custom_icon_button.dart'; import 'package:localsend_app/widget/local_send_logo.dart'; import 'package:localsend_app/widget/responsive_list_view.dart'; import 'package:localsend_app/widget/rotating_widget.dart'; +import 'package:localsend_isolates/util/sleep.dart'; import 'package:refena_flutter/addons.dart'; import 'package:refena_flutter/refena_flutter.dart'; import 'package:routerino/routerino.dart'; -class ReceiveTab extends StatelessWidget { +class ReceiveTab extends StatefulWidget { const ReceiveTab(); + @override + State createState() => _ReceiveTabState(); +} + +class _ReceiveTabState extends State { + /// Whether the advanced network info is shown + bool _showAdvanced = false; + + /// Whether the history button is shown + /// This extra boolean is needed to delay the animation + bool _showHistoryButton = true; + + Future _toggleAdvanced() async { + if (_showAdvanced) { + setState(() => _showAdvanced = false); + await sleepAsync(200); + if (mounted) { + setState(() => _showHistoryButton = true); + } + } else { + setState(() { + _showAdvanced = true; + _showHistoryButton = false; + }); + } + } + @override Widget build(BuildContext context) { - final vm = context.watch(receiveTabVmProvider); + final alias = context.watch(settingsProvider.select((s) => s.alias)); + final serverState = context.watch(serverProvider); + final localIps = context.watch(localIpProvider.select((s) => s.localIps)); return Stack( children: [ @@ -47,7 +80,7 @@ class ReceiveTab extends StatelessWidget { final activeTab = ref.watch(homePageControllerProvider.select((state) => state.currentTab)); return RotatingWidget( duration: const Duration(seconds: 15), - spinning: vm.serverState != null && animations && activeTab == HomeTab.receive, + spinning: serverState != null && animations && activeTab == HomeTab.receive, child: const LocalSendLogo(withText: false), ); }, @@ -55,13 +88,13 @@ class ReceiveTab extends StatelessWidget { ), FittedBox( fit: BoxFit.scaleDown, - child: Text(vm.serverState?.alias ?? vm.aliasSettings, style: const TextStyle(fontSize: 48)), + child: Text(serverState?.alias ?? alias, style: const TextStyle(fontSize: 48)), ), InitialFadeTransition( duration: const Duration(milliseconds: 300), delay: const Duration(milliseconds: 500), child: Text( - vm.serverState == null ? t.general.offline : vm.localIps.map((ip) => '#${ip.visualId}').toSet().join(' '), + serverState == null ? t.general.offline : localIps.map((ip) => '#${ip.visualId}').toSet().join(' '), style: const TextStyle(fontSize: 24), textAlign: TextAlign.center, ), @@ -87,11 +120,15 @@ class ReceiveTab extends StatelessWidget { ), ), ), - _InfoBox(vm), + _InfoBox( + serverState: serverState, + localIps: localIps, + showAdvanced: _showAdvanced, + ), _CornerButtons( - showAdvanced: vm.showAdvanced, - showHistoryButton: vm.showHistoryButton, - toggleAdvanced: vm.toggleAdvanced, + showAdvanced: _showAdvanced, + showHistoryButton: _showHistoryButton, + toggleAdvanced: _toggleAdvanced, ), ], ); @@ -142,14 +179,20 @@ class _CornerButtons extends StatelessWidget { } class _InfoBox extends StatelessWidget { - final ReceiveTabVm vm; + final ServerState? serverState; + final List localIps; + final bool showAdvanced; - const _InfoBox(this.vm); + const _InfoBox({ + required this.serverState, + required this.localIps, + required this.showAdvanced, + }); @override Widget build(BuildContext context) { return AnimatedCrossFade( - crossFadeState: vm.showAdvanced ? CrossFadeState.showSecond : CrossFadeState.showFirst, + crossFadeState: showAdvanced ? CrossFadeState.showSecond : CrossFadeState.showFirst, duration: const Duration(milliseconds: 200), firstChild: Container(), secondChild: Align( @@ -172,7 +215,7 @@ class _InfoBox extends StatelessWidget { const SizedBox(width: 10), Padding( padding: const EdgeInsets.only(right: 30), - child: SelectableText(vm.serverState?.alias ?? '-'), + child: SelectableText(serverState?.alias ?? '-'), ), ], ), @@ -183,8 +226,8 @@ class _InfoBox extends StatelessWidget { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (vm.localIps.isEmpty) Text(t.general.unknown), - ...vm.localIps.map((ip) => SelectableText(ip)), + if (localIps.isEmpty) Text(t.general.unknown), + ...localIps.map((ip) => SelectableText(ip)), ], ), ], @@ -193,7 +236,7 @@ class _InfoBox extends StatelessWidget { children: [ Text(t.receiveTab.infoBox.port), const SizedBox(width: 10), - SelectableText(vm.serverState?.port.toString() ?? '-'), + SelectableText(serverState?.port.toString() ?? '-'), ], ), ], diff --git a/app/lib/pages/tabs/receive_tab_vm.dart b/app/lib/pages/tabs/receive_tab_vm.dart deleted file mode 100644 index d1686456..00000000 --- a/app/lib/pages/tabs/receive_tab_vm.dart +++ /dev/null @@ -1,82 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:localsend_app/model/state/server/server_state.dart'; -import 'package:localsend_app/provider/local_ip_provider.dart'; -import 'package:localsend_app/provider/network/server/server_provider.dart'; -import 'package:localsend_app/provider/settings_provider.dart'; -import 'package:localsend_app/widget/dialogs/quick_save_from_favorites_notice.dart'; -import 'package:localsend_app/widget/dialogs/quick_save_notice.dart'; -import 'package:localsend_isolates/util/sleep.dart'; -import 'package:refena_flutter/refena_flutter.dart'; - -/// Whether the advanced network info is shown -final _showAdvancedProvider = StateProvider((ref) => false, debugLabel: '_showAdvancedProvider'); - -/// Whether the history button is shown -/// This extra boolean is needed to delay the animation -final _showHistoryButtonProvider = StateProvider((ref) => true, debugLabel: '_showHistoryButtonProvider'); - -class ReceiveTabVm { - final String aliasSettings; - final bool quickSaveSettings; - final bool quickSaveFromFavoritesSettings; - final ServerState? serverState; - final List localIps; - final bool showAdvanced; - final bool showHistoryButton; - final Future Function() toggleAdvanced; - final Future Function(BuildContext context, bool enable) onSetQuickSave; - final Future Function(BuildContext context, bool enable) onSetQuickSaveFromFavorites; - - const ReceiveTabVm({ - required this.aliasSettings, - required this.quickSaveSettings, - required this.quickSaveFromFavoritesSettings, - required this.serverState, - required this.localIps, - required this.showAdvanced, - required this.showHistoryButton, - required this.toggleAdvanced, - required this.onSetQuickSave, - required this.onSetQuickSaveFromFavorites, - }); -} - -final receiveTabVmProvider = ViewProvider((ref) { - final (alias, quickSave, quickSaveFromFavorites) = ref.watch(settingsProvider.select((s) => (s.alias, s.quickSave, s.quickSaveFromFavorites))); - final networkInfo = ref.watch(localIpProvider).localIps; - final serverState = ref.watch(serverProvider); - final showAdvanced = ref.watch(_showAdvancedProvider); - final showHistoryButton = ref.watch(_showHistoryButtonProvider); - - return ReceiveTabVm( - aliasSettings: alias, - quickSaveSettings: quickSave, - quickSaveFromFavoritesSettings: quickSaveFromFavorites, - serverState: serverState, - localIps: networkInfo, - showAdvanced: showAdvanced, - showHistoryButton: showHistoryButton, - toggleAdvanced: () async { - if (showAdvanced) { - ref.notifier(_showAdvancedProvider).setState((_) => false); - await sleepAsync(200); - ref.notifier(_showHistoryButtonProvider).setState((_) => true); - } else { - ref.notifier(_showAdvancedProvider).setState((_) => true); - ref.notifier(_showHistoryButtonProvider).setState((_) => false); - } - }, - onSetQuickSave: (context, enable) async { - await ref.notifier(settingsProvider).setQuickSave(enable); - if (enable && context.mounted) { - await QuickSaveNotice.open(context); - } - }, - onSetQuickSaveFromFavorites: (context, enable) async { - await ref.notifier(settingsProvider).setQuickSaveFromFavorites(enable); - if (enable && context.mounted) { - await QuickSaveFromFavoritesNotice.open(context); - } - }, - ); -});