revert: remove custom macOS window frame (#2416)

This commit is contained in:
Tien Do Nam
2026-07-26 15:06:46 +02:00
parent 8b51680c3c
commit db7e961407
35 changed files with 833 additions and 1036 deletions
+3 -3
View File
@@ -46,9 +46,9 @@
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.EnableCutout"
android:value="true"
/>
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
@@ -5,7 +5,6 @@
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
@@ -15,6 +14,5 @@
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
</style>
</resources>
@@ -5,7 +5,6 @@
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
@@ -15,6 +14,5 @@
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
</style>
</resources>
+6 -8
View File
@@ -1,7 +1,7 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:bitsdojo_window/bitsdojo_window.dart';
import 'package:dart_mappable/dart_mappable.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
@@ -137,13 +137,11 @@ Future<RefenaContainer> preInit(List<String> args) async {
startHidden = await isLaunchedAsLoginItem() && await getLaunchAtLoginMinimized();
}
doWhenWindowReady(() {
if (startHidden) {
unawaited(hideToTray());
} else {
unawaited(showFromTray());
}
});
if (startHidden) {
unawaited(hideToTray());
} else {
unawaited(showFromTray());
}
if (defaultTargetPlatform == TargetPlatform.macOS) {
await setupStatusBar();
+3 -2
View File
@@ -3,7 +3,6 @@ import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:localsend_app/gen/strings.g.dart';
import 'package:localsend_app/pages/debug/debug_page.dart';
import 'package:localsend_app/widget/custom_basic_appbar.dart';
import 'package:localsend_app/widget/local_send_logo.dart';
import 'package:localsend_app/widget/responsive_list_view.dart';
import 'package:routerino/routerino.dart';
@@ -24,7 +23,9 @@ class AboutPage extends StatelessWidget {
Widget build(BuildContext context) {
final primaryColor = Theme.of(context).colorScheme.primary;
return Scaffold(
appBar: basicLocalSendAppbar(t.aboutPage.title),
appBar: AppBar(
title: Text(t.aboutPage.title),
),
body: ResponsiveListView(
padding: const EdgeInsets.symmetric(horizontal: 15),
children: [
+5 -4
View File
@@ -4,7 +4,6 @@ import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:localsend_app/gen/assets.gen.dart';
import 'package:localsend_app/gen/strings.g.dart';
import 'package:localsend_app/util/ui/nav_bar_padding.dart';
import 'package:localsend_app/widget/custom_basic_appbar.dart';
class ChangelogPage extends StatelessWidget {
const ChangelogPage();
@@ -12,7 +11,9 @@ class ChangelogPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: basicLocalSendAppbar(t.changelogPage.title),
appBar: AppBar(
title: Text(t.changelogPage.title),
),
body: FutureBuilder(
future: rootBundle.loadString(Assets.changelog), // ignore: discarded_futures
builder: (context, data) {
@@ -21,8 +22,8 @@ class ChangelogPage extends StatelessWidget {
}
return Markdown(
padding: EdgeInsets.only(
left: 15 + MediaQuery.of(context).padding.left,
right: 15 + MediaQuery.of(context).padding.right,
left: 15,
right: 15,
top: 15,
bottom: 15 + getNavBarPadding(context),
),
+3 -2
View File
@@ -8,7 +8,6 @@ import 'package:localsend_app/pages/debug/security_debug_page.dart';
import 'package:localsend_app/provider/app_arguments_provider.dart';
import 'package:localsend_app/provider/persistence_provider.dart';
import 'package:localsend_app/util/shared_preferences/shared_preferences_file.dart';
import 'package:localsend_app/widget/custom_basic_appbar.dart';
import 'package:localsend_app/widget/debug_entry.dart';
import 'package:refena_flutter/refena_flutter.dart';
import 'package:routerino/routerino.dart';
@@ -24,7 +23,9 @@ class DebugPage extends StatelessWidget {
final store = SharedPreferencesStorePlatform.instance;
return Scaffold(
appBar: basicLocalSendAppbar('Debugging'),
appBar: AppBar(
title: const Text('Debugging'),
),
body: ListView(
padding: const EdgeInsets.only(left: 20, right: 20, top: 10, bottom: 30),
children: [
@@ -3,7 +3,6 @@ import 'package:intl/intl.dart';
import 'package:localsend_app/provider/logging/discovery_logs_provider.dart';
import 'package:localsend_app/provider/network/nearby_devices_provider.dart';
import 'package:localsend_app/widget/copyable_text.dart';
import 'package:localsend_app/widget/custom_basic_appbar.dart';
import 'package:localsend_app/widget/responsive_list_view.dart';
import 'package:refena_flutter/refena_flutter.dart';
@@ -17,7 +16,9 @@ class DiscoveryDebugPage extends StatelessWidget {
final ref = context.ref;
final logs = ref.watch(discoveryLoggerProvider);
return Scaffold(
appBar: basicLocalSendAppbar('Discovery Debugging'),
appBar: AppBar(
title: const Text('Discovery Debugging'),
),
body: ResponsiveListView(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 30),
children: [
+3 -2
View File
@@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:localsend_app/provider/logging/http_logs_provider.dart';
import 'package:localsend_app/widget/copyable_text.dart';
import 'package:localsend_app/widget/custom_basic_appbar.dart';
import 'package:localsend_app/widget/responsive_list_view.dart';
import 'package:refena_flutter/refena_flutter.dart';
@@ -15,7 +14,9 @@ class HttpLogsPage extends StatelessWidget {
Widget build(BuildContext context) {
final logs = context.ref.watch(httpLogsProvider);
return Scaffold(
appBar: basicLocalSendAppbar('HTTP Logs'),
appBar: AppBar(
title: const Text('HTTP Logs'),
),
body: ResponsiveListView(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 30),
children: [
+3 -2
View File
@@ -1,6 +1,5 @@
import 'package:flutter/material.dart';
import 'package:localsend_app/provider/security_provider.dart';
import 'package:localsend_app/widget/custom_basic_appbar.dart';
import 'package:localsend_app/widget/debug_entry.dart';
import 'package:localsend_app/widget/responsive_list_view.dart';
import 'package:refena_flutter/refena_flutter.dart';
@@ -12,7 +11,9 @@ class SecurityDebugPage extends StatelessWidget {
Widget build(BuildContext context) {
final securityContext = context.ref.watch(securityProvider);
return Scaffold(
appBar: basicLocalSendAppbar('Security Debugging'),
appBar: AppBar(
title: const Text('Security Debugging'),
),
body: ResponsiveListView(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 30),
maxWidth: 700,
+3 -2
View File
@@ -5,7 +5,6 @@ import 'package:localsend_app/pages/donation/donation_page_vm.dart';
// [FOSS_REMOVE_START]
import 'package:localsend_app/provider/purchase_provider.dart';
// [FOSS_REMOVE_END]
import 'package:localsend_app/widget/custom_basic_appbar.dart';
import 'package:localsend_app/widget/responsive_list_view.dart';
import 'package:refena_flutter/refena_flutter.dart';
import 'package:url_launcher/url_launcher.dart';
@@ -22,7 +21,9 @@ class DonationPage extends StatelessWidget {
// [FOSS_REMOVE_END]
builder: (context, vm) {
return Scaffold(
appBar: basicLocalSendAppbar(t.donationPage.title),
appBar: AppBar(
title: Text(t.donationPage.title),
),
body: Stack(
children: [
ResponsiveListView(
+54 -73
View File
@@ -1,6 +1,5 @@
import 'dart:io';
import 'package:bitsdojo_window/bitsdojo_window.dart';
import 'package:desktop_drop/desktop_drop.dart';
import 'package:flutter/material.dart';
import 'package:localsend_app/config/init.dart';
@@ -12,15 +11,13 @@ import 'package:localsend_app/pages/tabs/send_tab.dart';
import 'package:localsend_app/pages/tabs/settings_tab.dart';
import 'package:localsend_app/provider/selection/selected_sending_files_provider.dart';
import 'package:localsend_app/util/native/cross_file_converters.dart';
import 'package:localsend_app/util/native/platform_check.dart';
import 'package:localsend_app/widget/responsive_builder.dart';
import 'package:refena_flutter/refena_flutter.dart';
enum HomeTab {
receive(Icons.wifi),
send(Icons.send),
settings(Icons.settings)
;
settings(Icons.settings);
const HomeTab(this.icon);
@@ -107,78 +104,62 @@ class _HomePageState extends State<HomePage> with Refena {
body: Row(
children: [
if (!sizingInformation.isMobile)
Stack(
children: [
NavigationRail(
selectedIndex: vm.currentTab.index,
onDestinationSelected: (index) => vm.changeTab(HomeTab.values[index]),
extended: sizingInformation.isDesktop,
backgroundColor: Theme.of(context).cardColorWithElevation,
leading: sizingInformation.isDesktop
? Column(
children: [
checkPlatform([TargetPlatform.macOS])
? // considered adding some extra space so it looks more natural
SizedBox(height: 40)
: SizedBox(height: 20),
const Text(
'LocalSend',
style: TextStyle(fontSize: 32, fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
),
SizedBox(height: 20),
],
)
: checkPlatform([TargetPlatform.macOS])
? SizedBox(
height: 20,
)
: null,
destinations: HomeTab.values.map((tab) {
return NavigationRailDestination(
icon: Icon(tab.icon),
label: Text(tab.label),
);
}).toList(),
),
// makes the top draggable
Positioned(
top: 0,
left: 0,
right: 0,
height: 40,
child: MoveWindow(),
),
],
NavigationRail(
selectedIndex: vm.currentTab.index,
onDestinationSelected: (index) => vm.changeTab(HomeTab.values[index]),
extended: sizingInformation.isDesktop,
backgroundColor: Theme.of(context).cardColorWithElevation,
leading: sizingInformation.isDesktop
? const Column(
children: [
SizedBox(height: 20),
Text(
'LocalSend',
style: TextStyle(fontSize: 32, fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
),
SizedBox(height: 20),
],
)
: null,
destinations: HomeTab.values.map((tab) {
return NavigationRailDestination(
icon: Icon(tab.icon),
label: Text(tab.label),
);
}).toList(),
),
Expanded(
child: Stack(
children: [
PageView(
controller: vm.controller,
physics: const NeverScrollableScrollPhysics(),
children: const [
SafeArea(child: ReceiveTab()),
SafeArea(child: SendTab()),
SettingsTab(),
],
),
if (_dragAndDropIndicator)
Container(
width: double.infinity,
decoration: BoxDecoration(
color: Theme.of(context).scaffoldBackgroundColor,
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.file_download, size: 128),
const SizedBox(height: 30),
Text(t.sendTab.placeItems, style: Theme.of(context).textTheme.titleLarge),
],
),
child: SafeArea(
left: sizingInformation.isMobile,
child: Stack(
children: [
PageView(
controller: vm.controller,
physics: const NeverScrollableScrollPhysics(),
children: const [
ReceiveTab(),
SendTab(),
SettingsTab(),
],
),
],
if (_dragAndDropIndicator)
Container(
width: double.infinity,
decoration: BoxDecoration(
color: Theme.of(context).scaffoldBackgroundColor,
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.file_download, size: 128),
const SizedBox(height: 30),
Text(t.sendTab.placeItems, style: Theme.of(context).textTheme.titleLarge),
],
),
),
],
),
),
),
],
+3 -2
View File
@@ -1,7 +1,6 @@
import 'package:flutter/material.dart';
import 'package:localsend_app/gen/strings.g.dart';
import 'package:localsend_app/provider/settings_provider.dart';
import 'package:localsend_app/widget/custom_basic_appbar.dart';
import 'package:localsend_app/widget/responsive_list_view.dart';
import 'package:refena_flutter/refena_flutter.dart';
@@ -28,7 +27,9 @@ class _LanguagePageState extends State<LanguagePage> {
final t = Translations.of(context);
final activeLocale = context.ref.watch(settingsProvider.select((s) => s.locale));
return Scaffold(
appBar: basicLocalSendAppbar(t.sendTab.selection.title),
appBar: AppBar(
title: Text(t.settingsTab.general.language),
),
body: ResponsiveListView(
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 20),
children: [
+5 -12
View File
@@ -1,7 +1,6 @@
import 'dart:async';
import 'dart:typed_data';
import 'package:bitsdojo_window/bitsdojo_window.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:localsend_app/config/theme.dart';
@@ -17,7 +16,6 @@ import 'package:localsend_app/util/native/platform_check.dart';
import 'package:localsend_app/util/native/taskbar_helper.dart';
import 'package:localsend_app/util/notification_strings.dart';
import 'package:localsend_app/util/ui/nav_bar_padding.dart';
import 'package:localsend_app/widget/custom_basic_appbar.dart';
import 'package:localsend_app/widget/custom_progress_bar.dart';
import 'package:localsend_app/widget/dialogs/cancel_session_dialog.dart';
import 'package:localsend_app/widget/dialogs/error_dialog.dart';
@@ -246,7 +244,11 @@ class _ProgressPageState extends State<ProgressPage> with Refena {
},
canPop: false,
child: Scaffold(
appBar: widget.showAppBar ? basicLocalSendAppbar(title) : null,
appBar: widget.showAppBar
? AppBar(
title: Text(title),
)
: null,
body: Stack(
children: [
ListView.builder(
@@ -532,15 +534,6 @@ class _ProgressPageState extends State<ProgressPage> with Refena {
),
),
),
checkPlatform([TargetPlatform.macOS])
? Positioned(
top: 0,
left: 0,
right: 0,
height: 40,
child: MoveWindow(),
)
: SizedBox(),
],
),
),
+4 -2
View File
@@ -11,7 +11,6 @@ import 'package:localsend_app/util/native/directories.dart';
import 'package:localsend_app/util/native/open_file.dart';
import 'package:localsend_app/util/native/open_folder.dart';
import 'package:localsend_app/util/native/platform_check.dart';
import 'package:localsend_app/widget/custom_basic_appbar.dart';
import 'package:localsend_app/widget/dialogs/file_info_dialog.dart';
import 'package:localsend_app/widget/dialogs/history_clear_dialog.dart';
import 'package:localsend_app/widget/file_thumbnail.dart';
@@ -64,8 +63,11 @@ class ReceiveHistoryPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final entries = context.watch(receiveHistoryProvider);
return Scaffold(
appBar: basicLocalSendAppbar(t.receiveHistoryPage.title),
appBar: AppBar(
title: Text(t.receiveHistoryPage.title),
),
body: ResponsiveListView(
padding: const EdgeInsets.symmetric(vertical: 20),
children: [
+3 -2
View File
@@ -5,7 +5,6 @@ import 'package:localsend_app/gen/strings.g.dart';
import 'package:localsend_app/provider/selection/selected_sending_files_provider.dart';
import 'package:localsend_app/util/native/open_file.dart';
import 'package:localsend_app/util/ui/nav_bar_padding.dart';
import 'package:localsend_app/widget/custom_basic_appbar.dart';
import 'package:localsend_app/widget/dialogs/message_input_dialog.dart';
import 'package:localsend_app/widget/file_thumbnail.dart';
import 'package:localsend_app/widget/responsive_list_view.dart';
@@ -23,7 +22,9 @@ class SelectedFilesPage extends StatelessWidget {
final selectedFiles = ref.watch(selectedSendingFilesProvider);
return Scaffold(
appBar: basicLocalSendAppbar(t.sendTab.selection.title),
appBar: AppBar(
title: Text(t.sendTab.selection.title),
),
body: ResponsiveListView.single(
padding: const EdgeInsets.symmetric(horizontal: 15),
tabletPadding: const EdgeInsets.symmetric(horizontal: 15),
+1 -2
View File
@@ -10,7 +10,6 @@ import 'package:localsend_app/util/favorites.dart';
import 'package:localsend_app/util/native/taskbar_helper.dart';
import 'package:localsend_app/widget/animations/initial_fade_transition.dart';
import 'package:localsend_app/widget/animations/initial_slide_transition.dart';
import 'package:localsend_app/widget/custom_basic_appbar.dart';
import 'package:localsend_app/widget/dialogs/error_dialog.dart';
import 'package:localsend_app/widget/list_tile/device_list_tile.dart';
import 'package:localsend_app/widget/responsive_list_view.dart';
@@ -90,7 +89,7 @@ class _SendPageState extends State<SendPage> with Refena {
},
canPop: true,
child: Scaffold(
appBar: widget.showAppBar ? basicLocalSendAppbar('') : null,
appBar: widget.showAppBar ? AppBar() : null,
body: SafeArea(
child: Center(
child: ConstrainedBox(
@@ -4,7 +4,6 @@ import 'package:flutter/material.dart';
import 'package:local_hero/local_hero.dart';
import 'package:localsend_app/gen/strings.g.dart';
import 'package:localsend_app/provider/settings_provider.dart';
import 'package:localsend_app/widget/custom_basic_appbar.dart';
import 'package:localsend_app/widget/dialogs/text_field_tv.dart';
import 'package:localsend_app/widget/labeled_checkbox.dart';
import 'package:localsend_app/widget/responsive_list_view.dart';
@@ -44,7 +43,9 @@ class _NetworkInterfacesPageState extends State<NetworkInterfacesPage> {
? context.notifier(settingsProvider).setNetworkWhitelist
: context.notifier(settingsProvider).setNetworkBlacklist;
return Scaffold(
appBar: basicLocalSendAppbar(t.networkInterfacesPage.title),
appBar: AppBar(
title: Text(t.networkInterfacesPage.title),
),
body: LocalHeroScope(
duration: const Duration(milliseconds: 200),
curve: Curves.easeInOut,
-5
View File
@@ -1,4 +1,3 @@
import 'package:bitsdojo_window/bitsdojo_window.dart';
import 'package:flutter/material.dart';
import 'package:localsend_app/gen/strings.g.dart';
import 'package:localsend_app/pages/home_page.dart';
@@ -7,7 +6,6 @@ import 'package:localsend_app/pages/receive_history_page.dart';
import 'package:localsend_app/pages/tabs/receive_tab_vm.dart';
import 'package:localsend_app/provider/animation_provider.dart';
import 'package:localsend_app/util/ip_helper.dart';
import 'package:localsend_app/util/native/platform_check.dart';
import 'package:localsend_app/widget/animations/initial_fade_transition.dart';
import 'package:localsend_app/widget/column_list_view.dart';
import 'package:localsend_app/widget/custom_icon_button.dart';
@@ -32,9 +30,6 @@ class ReceiveTab extends StatelessWidget {
return Stack(
children: [
checkPlatform([TargetPlatform.macOS])
? SizedBox(height: 50, child: MoveWindow())
: SizedBox(height: 0, width: 0), // makes the top part that's not occupied by another widget draggable
Center(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: ResponsiveListView.defaultMaxWidth),
+204 -216
View File
@@ -1,4 +1,3 @@
import 'package:bitsdojo_window/bitsdojo_window.dart';
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:localsend_app/config/theme.dart';
@@ -50,233 +49,222 @@ class SendTab extends StatelessWidget {
final sizingInformation = SizingInformation(MediaQuery.sizeOf(context).width);
final buttonWidth = sizingInformation.isDesktop ? BigButton.desktopWidth : BigButton.mobileWidth;
final ref = context.ref;
return Stack(
return ResponsiveListView(
padding: EdgeInsets.zero,
children: [
ResponsiveListView(
padding: EdgeInsets.zero,
children: [
const SizedBox(height: 20),
if (vm.selectedFiles.isEmpty) ...[
Padding(
padding: const EdgeInsets.symmetric(horizontal: _horizontalPadding),
child: Text(
t.sendTab.selection.title,
style: Theme.of(context).textTheme.titleMedium,
),
),
ResponsiveWrapView(
outerHorizontalPadding: 15,
outerVerticalPadding: 10,
childPadding: 10,
minChildWidth: buttonWidth,
children: _options.map((option) {
return BigButton(
icon: option.icon,
label: option.label,
filled: false,
onTap: () async => ref.global.dispatchAsync(
PickFileAction(
option: option,
context: context,
),
),
);
}).toList(),
),
] else ...[
Card(
margin: const EdgeInsets.only(bottom: 10, left: _horizontalPadding, right: _horizontalPadding),
child: Padding(
padding: const EdgeInsetsDirectional.only(start: 15, top: 5, bottom: 15),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
t.sendTab.selection.title,
style: Theme.of(context).textTheme.titleMedium,
),
const Spacer(),
CustomIconButton(
onPressed: () => ref.redux(selectedSendingFilesProvider).dispatch(ClearSelectionAction()),
child: Icon(Icons.close, color: Theme.of(context).colorScheme.secondary),
),
const SizedBox(width: 5),
],
),
const SizedBox(height: 5),
Text(t.sendTab.selection.files(files: vm.selectedFiles.length)),
Text(t.sendTab.selection.size(size: vm.selectedFiles.fold(0, (prev, curr) => prev + curr.size).asReadableFileSize)),
const SizedBox(height: 10),
SizedBox(
height: defaultThumbnailSize,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: vm.selectedFiles.length,
itemBuilder: (context, index) {
final file = vm.selectedFiles[index];
return Padding(
padding: const EdgeInsets.only(right: 10),
child: SmartFileThumbnail.fromCrossFile(file),
);
},
),
),
const SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
style: TextButton.styleFrom(
foregroundColor: Theme.of(context).colorScheme.onSurface,
),
onPressed: () async {
await context.push(() => const SelectedFilesPage());
},
child: Text(t.general.edit),
),
const SizedBox(width: 15),
ElevatedButton.icon(
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).colorScheme.primary,
foregroundColor: Theme.of(context).colorScheme.onPrimary,
),
onPressed: () async {
if (_options.length == 1) {
// open directly
await ref.global.dispatchAsync(
PickFileAction(
option: _options.first,
context: context,
),
);
return;
}
await AddFileDialog.open(
context: context,
options: _options,
);
},
icon: const Icon(Icons.add),
label: Text(t.general.add),
),
const SizedBox(width: 15),
],
),
],
),
),
),
],
Row(
children: [
const SizedBox(width: _horizontalPadding),
Flexible(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: Text(t.sendTab.nearbyDevices, style: Theme.of(context).textTheme.titleMedium),
),
),
const SizedBox(width: 10),
_ScanButton(
ips: vm.localIps,
),
Tooltip(
message: t.sendTab.manualSending,
child: CustomIconButton(
onPressed: () async => vm.onTapAddress(context),
child: const Icon(Icons.ads_click),
),
),
Tooltip(
message: t.dialogs.favoriteDialog.title,
child: CustomIconButton(
onPressed: () async => await vm.onTapFavorite(context),
child: const Icon(Icons.favorite),
),
),
_SendModeButton(
onSelect: (mode) async => vm.onTapSendMode(context, mode),
),
],
const SizedBox(height: 20),
if (vm.selectedFiles.isEmpty) ...[
Padding(
padding: const EdgeInsets.symmetric(horizontal: _horizontalPadding),
child: Text(
t.sendTab.selection.title,
style: Theme.of(context).textTheme.titleMedium,
),
if (vm.nearbyDevices.isEmpty)
const Padding(
padding: EdgeInsets.only(bottom: 10, left: _horizontalPadding, right: _horizontalPadding),
child: Opacity(
opacity: 0.3,
child: DevicePlaceholderListTile(),
),
),
...vm.nearbyDevices.map((device) {
final favoriteEntry = vm.favoriteDevices.findDevice(device);
return Padding(
padding: const EdgeInsets.only(bottom: 10, left: _horizontalPadding, right: _horizontalPadding),
child: Hero(
tag: 'device-${device.ip}',
child: vm.sendMode == SendMode.multiple
? _MultiSendDeviceListTile(
device: device,
isFavorite: favoriteEntry != null,
nameOverride: favoriteEntry?.alias,
vm: vm,
)
: DeviceListTile(
device: device,
isFavorite: favoriteEntry != null,
nameOverride: favoriteEntry?.alias,
onFavoriteTap: () async => await vm.onToggleFavorite(context, device),
onTap: () async => await vm.onTapDevice(context, device),
),
),
ResponsiveWrapView(
outerHorizontalPadding: 15,
outerVerticalPadding: 10,
childPadding: 10,
minChildWidth: buttonWidth,
children: _options.map((option) {
return BigButton(
icon: option.icon,
label: option.label,
filled: false,
onTap: () async => ref.global.dispatchAsync(
PickFileAction(
option: option,
context: context,
),
),
);
}),
const SizedBox(height: 10),
Center(
child: TextButton(
onPressed: () async {
await context.push(() => const TroubleshootPage());
},
child: Text(t.troubleshootPage.title),
),
),
const SizedBox(height: 20),
Padding(
padding: const EdgeInsets.symmetric(horizontal: _horizontalPadding),
child: Consumer(
builder: (context, ref) {
final animations = ref.watch(animationProvider);
return OpacitySlideshow(
durationMillis: 6000,
running: animations,
}).toList(),
),
] else ...[
Card(
margin: const EdgeInsets.only(bottom: 10, left: _horizontalPadding, right: _horizontalPadding),
child: Padding(
padding: const EdgeInsetsDirectional.only(start: 15, top: 5, bottom: 15),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
t.sendTab.help,
style: const TextStyle(color: Colors.grey),
textAlign: TextAlign.center,
t.sendTab.selection.title,
style: Theme.of(context).textTheme.titleMedium,
),
if (checkPlatformCanReceiveShareIntent())
Text(
t.sendTab.shareIntentInfo,
style: const TextStyle(color: Colors.grey),
textAlign: TextAlign.center,
),
const Spacer(),
CustomIconButton(
onPressed: () => ref.redux(selectedSendingFilesProvider).dispatch(ClearSelectionAction()),
child: Icon(Icons.close, color: Theme.of(context).colorScheme.secondary),
),
const SizedBox(width: 5),
],
);
},
),
const SizedBox(height: 5),
Text(t.sendTab.selection.files(files: vm.selectedFiles.length)),
Text(t.sendTab.selection.size(size: vm.selectedFiles.fold(0, (prev, curr) => prev + curr.size).asReadableFileSize)),
const SizedBox(height: 10),
SizedBox(
height: defaultThumbnailSize,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: vm.selectedFiles.length,
itemBuilder: (context, index) {
final file = vm.selectedFiles[index];
return Padding(
padding: const EdgeInsets.only(right: 10),
child: SmartFileThumbnail.fromCrossFile(file),
);
},
),
),
const SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
style: TextButton.styleFrom(
foregroundColor: Theme.of(context).colorScheme.onSurface,
),
onPressed: () async {
await context.push(() => const SelectedFilesPage());
},
child: Text(t.general.edit),
),
const SizedBox(width: 15),
ElevatedButton.icon(
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).colorScheme.primary,
foregroundColor: Theme.of(context).colorScheme.onPrimary,
),
onPressed: () async {
if (_options.length == 1) {
// open directly
await ref.global.dispatchAsync(
PickFileAction(
option: _options.first,
context: context,
),
);
return;
}
await AddFileDialog.open(
context: context,
options: _options,
);
},
icon: const Icon(Icons.add),
label: Text(t.general.add),
),
const SizedBox(width: 15),
],
),
],
),
),
const SizedBox(height: 50),
),
],
Row(
children: [
const SizedBox(width: _horizontalPadding),
Flexible(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: Text(t.sendTab.nearbyDevices, style: Theme.of(context).textTheme.titleMedium),
),
),
const SizedBox(width: 10),
_ScanButton(
ips: vm.localIps,
),
Tooltip(
message: t.sendTab.manualSending,
child: CustomIconButton(
onPressed: () async => vm.onTapAddress(context),
child: const Icon(Icons.ads_click),
),
),
Tooltip(
message: t.dialogs.favoriteDialog.title,
child: CustomIconButton(
onPressed: () async => await vm.onTapFavorite(context),
child: const Icon(Icons.favorite),
),
),
_SendModeButton(
onSelect: (mode) async => vm.onTapSendMode(context, mode),
),
],
),
// make the top draggable on Desktop
checkPlatform([TargetPlatform.macOS])
? SizedBox(height: 50, child: MoveWindow())
: SizedBox(
height: 0,
width: 0,
),
if (vm.nearbyDevices.isEmpty)
const Padding(
padding: EdgeInsets.only(bottom: 10, left: _horizontalPadding, right: _horizontalPadding),
child: Opacity(
opacity: 0.3,
child: DevicePlaceholderListTile(),
),
),
...vm.nearbyDevices.map((device) {
final favoriteEntry = vm.favoriteDevices.findDevice(device);
return Padding(
padding: const EdgeInsets.only(bottom: 10, left: _horizontalPadding, right: _horizontalPadding),
child: Hero(
tag: 'device-${device.ip}',
child: vm.sendMode == SendMode.multiple
? _MultiSendDeviceListTile(
device: device,
isFavorite: favoriteEntry != null,
nameOverride: favoriteEntry?.alias,
vm: vm,
)
: DeviceListTile(
device: device,
isFavorite: favoriteEntry != null,
nameOverride: favoriteEntry?.alias,
onFavoriteTap: () async => await vm.onToggleFavorite(context, device),
onTap: () async => await vm.onTapDevice(context, device),
),
),
);
}),
const SizedBox(height: 10),
Center(
child: TextButton(
onPressed: () async {
await context.push(() => const TroubleshootPage());
},
child: Text(t.troubleshootPage.title),
),
),
const SizedBox(height: 20),
Padding(
padding: const EdgeInsets.symmetric(horizontal: _horizontalPadding),
child: Consumer(
builder: (context, ref) {
final animations = ref.watch(animationProvider);
return OpacitySlideshow(
durationMillis: 6000,
running: animations,
children: [
Text(
t.sendTab.help,
style: const TextStyle(color: Colors.grey),
textAlign: TextAlign.center,
),
if (checkPlatformCanReceiveShareIntent())
Text(
t.sendTab.shareIntentInfo,
style: const TextStyle(color: Colors.grey),
textAlign: TextAlign.center,
),
],
);
},
),
),
const SizedBox(height: 50),
],
);
},
File diff suppressed because it is too large Load Diff
+3 -2
View File
@@ -7,7 +7,6 @@ import 'package:localsend_app/provider/settings_provider.dart';
import 'package:localsend_app/util/native/cmd_helper.dart';
import 'package:localsend_app/util/native/macos_channel.dart' as macos_channel;
import 'package:localsend_app/util/native/platform_check.dart';
import 'package:localsend_app/widget/custom_basic_appbar.dart';
import 'package:localsend_app/widget/custom_icon_button.dart';
import 'package:localsend_app/widget/dialogs/not_available_on_platform_dialog.dart';
import 'package:localsend_app/widget/responsive_list_view.dart';
@@ -20,7 +19,9 @@ class TroubleshootPage extends StatelessWidget {
Widget build(BuildContext context) {
final settings = context.ref.watch(settingsProvider);
return Scaffold(
appBar: basicLocalSendAppbar(t.troubleshootPage.title),
appBar: AppBar(
title: Text(t.troubleshootPage.title),
),
body: ResponsiveListView(
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 30),
children: [
+3 -2
View File
@@ -8,7 +8,6 @@ import 'package:localsend_app/provider/network/server/server_provider.dart';
import 'package:localsend_app/provider/settings_provider.dart';
import 'package:localsend_app/util/native/platform_check.dart';
import 'package:localsend_app/util/ui/snackbar.dart';
import 'package:localsend_app/widget/custom_basic_appbar.dart';
import 'package:localsend_app/widget/dialogs/pin_dialog.dart';
import 'package:localsend_app/widget/dialogs/qr_dialog.dart';
import 'package:localsend_app/widget/dialogs/zoom_dialog.dart';
@@ -98,7 +97,9 @@ class _WebSendPageState extends State<WebSendPage> with Refena {
},
canPop: false,
child: Scaffold(
appBar: basicLocalSendAppbar(t.webSharePage.title),
appBar: AppBar(
title: Text(t.webSharePage.title),
),
body: Builder(
builder: (context) {
if (_stateEnum != _ServerState.running) {
-2
View File
@@ -1,6 +1,5 @@
import 'dart:io';
import 'package:bitsdojo_window/bitsdojo_window.dart';
import 'package:flutter/foundation.dart';
import 'package:localsend_app/gen/assets.gen.dart';
import 'package:localsend_app/gen/strings.g.dart';
@@ -86,7 +85,6 @@ Future<void> showFromTray() async {
// This will crash on Windows
// https://github.com/localsend/localsend/issues/32
await windowManager.setSkipTaskbar(false);
appWindow.show();
}
// Enable animations
-75
View File
@@ -1,75 +0,0 @@
import 'dart:io';
import 'dart:ui';
import 'package:bitsdojo_window/bitsdojo_window.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:localsend_app/util/native/platform_check.dart';
class CustomBackButton extends StatelessWidget {
final Color? color;
const CustomBackButton({super.key, this.color});
@override
Widget build(BuildContext context) {
final isRtl = Directionality.of(context) == TextDirection.rtl;
return IconButton(
icon: Icon(
isRtl ? Icons.arrow_forward_ios_rounded : Icons.arrow_back_ios_new_rounded,
color: color ?? IconTheme.of(context).color,
),
tooltip: MaterialLocalizations.of(context).backButtonTooltip,
onPressed: () async {
await Navigator.maybePop(context);
},
);
}
}
PreferredSizeWidget basicLocalSendAppbar(String title) {
// Creates a very simple new appBar to support bitsdojo_windows on mac and make them draggable
// if you want have more items on here for a specific page, make sure to add it here as an option
// so that mac users can still appreciate this near native new design
return checkPlatform([TargetPlatform.macOS])
? PreferredSize(
preferredSize: const Size.fromHeight(kToolbarHeight),
child: ClipRRect(
child: BackdropFilter(
filter: ImageFilter.blur(
sigmaX: 20.0,
sigmaY: 20.0,
),
child: MoveWindow(
child: Container(
color: Colors.transparent,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// Padding space for macOS traffic lights
if (!kIsWeb && Platform.isMacOS) const SizedBox(width: 60),
// Originally leading Icon
CustomBackButton(),
// Center Title
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Center(
child: FittedBox(
fit: BoxFit.scaleDown,
child: Text(title, style: TextStyle(fontSize: 100, fontWeight: FontWeight.normal)),
),
),
),
),
// For true centering of the icon since it shifted
const SizedBox(width: 60),
],
),
),
),
),
),
)
: AppBar(title: Text(title));
}
@@ -6,7 +6,6 @@
#include "generated_plugin_registrant.h"
#include <bitsdojo_window_linux/bitsdojo_window_plugin.h>
#include <desktop_drop/desktop_drop_plugin.h>
#include <dynamic_color/dynamic_color_plugin.h>
#include <file_selector_linux/file_selector_plugin.h>
@@ -21,9 +20,6 @@
#include <yaru_window_linux/yaru_window_linux_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) bitsdojo_window_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "BitsdojoWindowPlugin");
bitsdojo_window_plugin_register_with_registrar(bitsdojo_window_linux_registrar);
g_autoptr(FlPluginRegistrar) desktop_drop_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "DesktopDropPlugin");
desktop_drop_plugin_register_with_registrar(desktop_drop_registrar);
@@ -3,7 +3,6 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
bitsdojo_window_linux
desktop_drop
dynamic_color
file_selector_linux
@@ -5,7 +5,6 @@
import FlutterMacOS
import Foundation
import bitsdojo_window_macos
import connectivity_plus
import desktop_drop
import device_info_plus
@@ -29,7 +28,6 @@ import wakelock_plus
import window_manager
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
BitsdojoWindowPlugin.register(with: registry.registrar(forPlugin: "BitsdojoWindowPlugin"))
ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))
DesktopDropPlugin.register(with: registry.registrar(forPlugin: "DesktopDropPlugin"))
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
-6
View File
@@ -1,6 +1,4 @@
PODS:
- bitsdojo_window_macos (0.0.1):
- FlutterMacOS
- connectivity_plus (0.0.1):
- FlutterMacOS
- Defaults (4.2.2)
@@ -54,7 +52,6 @@ PODS:
- FlutterMacOS
DEPENDENCIES:
- bitsdojo_window_macos (from `Flutter/ephemeral/.symlinks/plugins/bitsdojo_window_macos/macos`)
- connectivity_plus (from `Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos`)
- Defaults (~> 4.2)
- desktop_drop (from `Flutter/ephemeral/.symlinks/plugins/desktop_drop/macos`)
@@ -85,8 +82,6 @@ SPEC REPOS:
- Defaults
EXTERNAL SOURCES:
bitsdojo_window_macos:
:path: Flutter/ephemeral/.symlinks/plugins/bitsdojo_window_macos/macos
connectivity_plus:
:path: Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos
desktop_drop:
@@ -135,7 +130,6 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral/.symlinks/plugins/window_manager/macos
SPEC CHECKSUMS:
bitsdojo_window_macos: 7959fb0ca65a3ccda30095c181ecb856fae48ea9
connectivity_plus: 4adf20a405e25b42b9c9f87feff8f4b6fde18a4e
Defaults: d785e56c0fb8890dc40351603f05c8e1df1bdd45
desktop_drop: 10a3e6a7fa9dbe350541f2574092fecfa345a07b
-1
View File
@@ -3,7 +3,6 @@ import FlutterMacOS
import Defaults
import DockProgress
import LaunchAtLogin
import bitsdojo_window_macos
enum DockIcon: CaseIterable {
case regular
+7 -8
View File
@@ -1,13 +1,8 @@
import Cocoa
import FlutterMacOS
import window_manager
import bitsdojo_window_macos // used to make custom window bars on macOS (or any desktop operating system for that matter)
class MainFlutterWindow: BitsdojoWindow {
// just following intructions from https://pub.dev/packages/bitsdojo_window
override func bitsdojo_window_configure() -> UInt {
return BDW_CUSTOM_FRAME | BDW_HIDE_ON_STARTUP
}
class MainFlutterWindow: NSWindow {
override func awakeFromNib() {
let flutterViewController = FlutterViewController.init()
let windowFrame = self.frame
@@ -15,9 +10,13 @@ class MainFlutterWindow: BitsdojoWindow {
self.setFrame(windowFrame, display: true)
RegisterGeneratedPlugins(registry: flutterViewController)
// window_manager: start window hidden
hiddenWindowAtLaunch()
super.awakeFromNib()
}
// window_manager: start hidden
override public func order(_ place: NSWindow.OrderingMode, relativeTo otherWin: Int) {
super.order(place, relativeTo: otherWin)
hiddenWindowAtLaunch()
}
}
-40
View File
@@ -81,46 +81,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "5.8.2"
bitsdojo_window:
dependency: "direct main"
description:
name: bitsdojo_window
sha256: "88ef7765dafe52d97d7a3684960fb5d003e3151e662c18645c1641c22b873195"
url: "https://pub.dev"
source: hosted
version: "0.1.6"
bitsdojo_window_linux:
dependency: transitive
description:
name: bitsdojo_window_linux
sha256: "9519c0614f98be733e0b1b7cb15b827007886f6fe36a4fb62cf3d35b9dd578ab"
url: "https://pub.dev"
source: hosted
version: "0.1.4"
bitsdojo_window_macos:
dependency: transitive
description:
name: bitsdojo_window_macos
sha256: f7c5be82e74568c68c5b8449e2c5d8fd12ec195ecd70745a7b9c0f802bb0268f
url: "https://pub.dev"
source: hosted
version: "0.1.4"
bitsdojo_window_platform_interface:
dependency: transitive
description:
name: bitsdojo_window_platform_interface
sha256: "65daa015a0c6dba749bdd35a0f092e7a8ba8b0766aa0480eb3ef808086f6e27c"
url: "https://pub.dev"
source: hosted
version: "0.1.2"
bitsdojo_window_windows:
dependency: transitive
description:
name: bitsdojo_window_windows
sha256: fa982cf61ede53f483e50b257344a1c250af231a3cdc93a7064dd6dc0d720b68
url: "https://pub.dev"
source: hosted
version: "0.1.6"
boolean_selector:
dependency: transitive
description:
-1
View File
@@ -11,7 +11,6 @@ environment:
dependencies:
basic_utils: 5.8.2
bitsdojo_window: ^0.1.6
collection: ^1.17.2 # allow newer versions, so it can compile with newer Flutter versions
connectivity_plus: 7.2.0
convert: 3.1.2
@@ -6,7 +6,6 @@
#include "generated_plugin_registrant.h"
#include <bitsdojo_window_windows/bitsdojo_window_plugin.h>
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
#include <desktop_drop/desktop_drop_plugin.h>
#include <dynamic_color/dynamic_color_plugin_c_api.h>
@@ -23,8 +22,6 @@
#include <windows_taskbar/windows_taskbar_plugin.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
BitsdojoWindowPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("BitsdojoWindowPlugin"));
ConnectivityPlusWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
DesktopDropPluginRegisterWithRegistrar(
@@ -3,7 +3,6 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
bitsdojo_window_windows
connectivity_plus
desktop_drop
dynamic_color