mirror of
https://github.com/localsend/localsend.git
synced 2026-06-23 04:10:07 +00:00
A clean UI revamp (#2416)
This commit is contained in:
@@ -41,9 +41,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.NormalTheme"
|
||||
android:resource="@style/NormalTheme"
|
||||
/>
|
||||
android:name="io.flutter.embedding.android.EnableCutout"
|
||||
android:value="true"
|
||||
/>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<!-- 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
|
||||
@@ -14,5 +15,6 @@
|
||||
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,6 +5,7 @@
|
||||
<!-- 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
|
||||
@@ -14,5 +15,6 @@
|
||||
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>
|
||||
|
||||
@@ -9,17 +9,25 @@ import Flutter
|
||||
) -> Bool {
|
||||
|
||||
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
|
||||
let channel = FlutterMethodChannel(name: "ios-delegate-channel",
|
||||
binaryMessenger: controller.engine.binaryMessenger)
|
||||
channel.setMethodCallHandler({
|
||||
(call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
|
||||
if call.method == "isReduceMotionEnabled" {
|
||||
result(UIAccessibility.isReduceMotionEnabled)
|
||||
} else {
|
||||
result(FlutterMethodNotImplemented)
|
||||
|
||||
if let engine = controller.engine {
|
||||
let channel = FlutterMethodChannel(
|
||||
name: "ios-delegate-channel",
|
||||
binaryMessenger: engine.binaryMessenger
|
||||
)
|
||||
channel.setMethodCallHandler { (call: FlutterMethodCall, result: @escaping FlutterResult) in
|
||||
if call.method == "isReduceMotionEnabled" {
|
||||
result(UIAccessibility.isReduceMotionEnabled)
|
||||
} else {
|
||||
result(FlutterMethodNotImplemented)
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
// I couldn't get the iOS build to run without this check
|
||||
print("Flutter engine is nil!")
|
||||
}
|
||||
|
||||
GeneratedPluginRegistrant.register(with: self)
|
||||
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:bitsdojo_window/bitsdojo_window.dart';
|
||||
import 'package:common/api_route_builder.dart';
|
||||
import 'package:common/constants.dart';
|
||||
import 'package:common/isolate.dart';
|
||||
@@ -114,12 +115,14 @@ Future<RefenaContainer> preInit(List<String> args) async {
|
||||
} else if (defaultTargetPlatform == TargetPlatform.macOS) {
|
||||
startHidden = await isLaunchedAsLoginItem() && await getLaunchAtLoginMinimized();
|
||||
}
|
||||
|
||||
if (startHidden) {
|
||||
unawaited(hideToTray());
|
||||
} else {
|
||||
unawaited(showFromTray());
|
||||
}
|
||||
|
||||
doWhenWindowReady(() {
|
||||
if (startHidden) {
|
||||
unawaited(hideToTray());
|
||||
} else {
|
||||
unawaited(showFromTray());
|
||||
}
|
||||
});
|
||||
|
||||
if (defaultTargetPlatform == TargetPlatform.macOS) {
|
||||
await setupStatusBar();
|
||||
|
||||
@@ -3,6 +3,7 @@ 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';
|
||||
@@ -23,9 +24,7 @@ class AboutPage extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
final primaryColor = Theme.of(context).colorScheme.primary;
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(t.aboutPage.title),
|
||||
),
|
||||
appBar: basicLocalSendAppbar(t.aboutPage.title),
|
||||
body: ResponsiveListView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
children: [
|
||||
|
||||
@@ -4,6 +4,7 @@ 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();
|
||||
@@ -11,9 +12,7 @@ class ChangelogPage extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(t.changelogPage.title),
|
||||
),
|
||||
appBar: basicLocalSendAppbar(t.changelogPage.title),
|
||||
body: FutureBuilder(
|
||||
future: rootBundle.loadString(Assets.changelog), // ignore: discarded_futures
|
||||
builder: (context, data) {
|
||||
@@ -22,8 +21,8 @@ class ChangelogPage extends StatelessWidget {
|
||||
}
|
||||
return Markdown(
|
||||
padding: EdgeInsets.only(
|
||||
left: 15,
|
||||
right: 15,
|
||||
left: 15 + MediaQuery.of(context).padding.left,
|
||||
right: 15 + MediaQuery.of(context).padding.right,
|
||||
top: 15,
|
||||
bottom: 15 + getNavBarPadding(context),
|
||||
),
|
||||
|
||||
@@ -8,6 +8,7 @@ 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';
|
||||
@@ -23,9 +24,7 @@ class DebugPage extends StatelessWidget {
|
||||
final store = SharedPreferencesStorePlatform.instance;
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Debugging'),
|
||||
),
|
||||
appBar: basicLocalSendAppbar('Debugging'),
|
||||
body: ListView(
|
||||
padding: const EdgeInsets.only(left: 20, right: 20, top: 10, bottom: 30),
|
||||
children: [
|
||||
|
||||
@@ -3,6 +3,7 @@ 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';
|
||||
|
||||
@@ -16,9 +17,7 @@ class DiscoveryDebugPage extends StatelessWidget {
|
||||
final ref = context.ref;
|
||||
final logs = ref.watch(discoveryLoggerProvider);
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Discovery Debugging'),
|
||||
),
|
||||
appBar: basicLocalSendAppbar('Discovery Debugging'),
|
||||
body: ResponsiveListView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 30),
|
||||
children: [
|
||||
|
||||
@@ -2,6 +2,7 @@ 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';
|
||||
|
||||
@@ -14,9 +15,7 @@ class HttpLogsPage extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
final logs = context.ref.watch(httpLogsProvider);
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('HTTP Logs'),
|
||||
),
|
||||
appBar: basicLocalSendAppbar('HTTP Logs'),
|
||||
body: ResponsiveListView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 30),
|
||||
children: [
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
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';
|
||||
@@ -11,9 +12,7 @@ class SecurityDebugPage extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
final securityContext = context.ref.watch(securityProvider);
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Security Debugging'),
|
||||
),
|
||||
appBar: basicLocalSendAppbar('Security Debugging'),
|
||||
body: ResponsiveListView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 30),
|
||||
maxWidth: 700,
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:localsend_app/model/state/purchase_state.dart';
|
||||
import 'package:localsend_app/pages/donation/donation_page_vm.dart';
|
||||
// [FOSS_REMOVE_START]
|
||||
import 'package:localsend_app/provider/purchase_provider.dart';
|
||||
import 'package:localsend_app/widget/custom_basic_appbar.dart';
|
||||
// [FOSS_REMOVE_END]
|
||||
import 'package:localsend_app/widget/responsive_list_view.dart';
|
||||
import 'package:refena_flutter/refena_flutter.dart';
|
||||
@@ -21,9 +22,7 @@ class DonationPage extends StatelessWidget {
|
||||
// [FOSS_REMOVE_END]
|
||||
builder: (context, vm) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(t.donationPage.title),
|
||||
),
|
||||
appBar: basicLocalSendAppbar(t.donationPage.title),
|
||||
body: Stack(
|
||||
children: [
|
||||
ResponsiveListView(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
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';
|
||||
@@ -11,6 +12,7 @@ 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';
|
||||
|
||||
@@ -100,62 +102,78 @@ class _HomePageState extends State<HomePage> with Refena {
|
||||
body: Row(
|
||||
children: [
|
||||
if (!sizingInformation.isMobile)
|
||||
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(),
|
||||
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(),
|
||||
),
|
||||
],
|
||||
),
|
||||
Expanded(
|
||||
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),
|
||||
],
|
||||
),
|
||||
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),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
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';
|
||||
|
||||
@@ -27,9 +28,7 @@ class _LanguagePageState extends State<LanguagePage> {
|
||||
final t = Translations.of(context);
|
||||
final activeLocale = context.ref.watch(settingsProvider.select((s) => s.locale));
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(t.settingsTab.general.language),
|
||||
),
|
||||
appBar: basicLocalSendAppbar(t.sendTab.selection.title),
|
||||
body: ResponsiveListView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 20),
|
||||
children: [
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:bitsdojo_window/bitsdojo_window.dart';
|
||||
import 'package:common/model/dto/file_dto.dart';
|
||||
import 'package:common/model/file_status.dart';
|
||||
import 'package:common/model/session_status.dart';
|
||||
@@ -19,6 +20,7 @@ import 'package:localsend_app/util/native/open_folder.dart';
|
||||
import 'package:localsend_app/util/native/platform_check.dart';
|
||||
import 'package:localsend_app/util/native/taskbar_helper.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';
|
||||
@@ -229,11 +231,7 @@ class _ProgressPageState extends State<ProgressPage> with Refena {
|
||||
},
|
||||
canPop: false,
|
||||
child: Scaffold(
|
||||
appBar: widget.showAppBar
|
||||
? AppBar(
|
||||
title: Text(title),
|
||||
)
|
||||
: null,
|
||||
appBar: widget.showAppBar ? basicLocalSendAppbar(title) : null,
|
||||
body: Stack(
|
||||
children: [
|
||||
ListView.builder(
|
||||
@@ -510,6 +508,15 @@ class _ProgressPageState extends State<ProgressPage> with Refena {
|
||||
),
|
||||
),
|
||||
),
|
||||
checkPlatform([TargetPlatform.macOS])
|
||||
? Positioned(
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
height: 40,
|
||||
child: MoveWindow(),
|
||||
)
|
||||
: SizedBox(),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -13,6 +13,7 @@ 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';
|
||||
@@ -61,11 +62,8 @@ class ReceiveHistoryPage extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final entries = context.watch(receiveHistoryProvider);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(t.receiveHistoryPage.title),
|
||||
),
|
||||
appBar: basicLocalSendAppbar(t.receiveHistoryPage.title),
|
||||
body: ResponsiveListView(
|
||||
padding: const EdgeInsets.symmetric(vertical: 20),
|
||||
children: [
|
||||
|
||||
@@ -7,6 +7,7 @@ import 'package:localsend_app/provider/selection/selected_sending_files_provider
|
||||
import 'package:localsend_app/util/file_size_helper.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';
|
||||
@@ -22,9 +23,7 @@ class SelectedFilesPage extends StatelessWidget {
|
||||
final selectedFiles = ref.watch(selectedSendingFilesProvider);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(t.sendTab.selection.title),
|
||||
),
|
||||
appBar: basicLocalSendAppbar(t.sendTab.selection.title),
|
||||
body: ResponsiveListView.single(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
tabletPadding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
|
||||
@@ -12,6 +12,7 @@ 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';
|
||||
@@ -86,7 +87,7 @@ class _SendPageState extends State<SendPage> with Refena {
|
||||
},
|
||||
canPop: true,
|
||||
child: Scaffold(
|
||||
appBar: widget.showAppBar ? AppBar() : null,
|
||||
appBar: widget.showAppBar ? basicLocalSendAppbar('') : null,
|
||||
body: SafeArea(
|
||||
child: Center(
|
||||
child: ConstrainedBox(
|
||||
|
||||
@@ -5,6 +5,7 @@ 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';
|
||||
@@ -43,9 +44,7 @@ class _NetworkInterfacesPageState extends State<NetworkInterfacesPage> {
|
||||
? context.notifier(settingsProvider).setNetworkWhitelist
|
||||
: context.notifier(settingsProvider).setNetworkBlacklist;
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(t.networkInterfacesPage.title),
|
||||
),
|
||||
appBar: basicLocalSendAppbar(t.networkInterfacesPage.title),
|
||||
body: LocalHeroScope(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
curve: Curves.easeInOut,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
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';
|
||||
@@ -6,6 +7,7 @@ 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';
|
||||
@@ -30,6 +32,9 @@ 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),
|
||||
|
||||
+208
-196
@@ -1,3 +1,4 @@
|
||||
import 'package:bitsdojo_window/bitsdojo_window.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:common/model/device.dart';
|
||||
import 'package:common/model/session_status.dart';
|
||||
@@ -49,210 +50,221 @@ class SendTab extends StatelessWidget {
|
||||
final sizingInformation = SizingInformation(MediaQuery.sizeOf(context).width);
|
||||
final buttonWidth = sizingInformation.isDesktop ? BigButton.desktopWidth : BigButton.mobileWidth;
|
||||
final ref = context.ref;
|
||||
return ResponsiveListView(
|
||||
padding: EdgeInsets.zero,
|
||||
return Stack(
|
||||
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,
|
||||
),
|
||||
),
|
||||
HorizontalClipListView(
|
||||
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(
|
||||
ResponsiveListView(
|
||||
padding: EdgeInsets.zero,
|
||||
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(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,
|
||||
),
|
||||
),
|
||||
HorizontalClipListView(
|
||||
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),
|
||||
),
|
||||
],
|
||||
),
|
||||
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(width: 10),
|
||||
_ScanButton(
|
||||
ips: vm.localIps,
|
||||
),
|
||||
Tooltip(
|
||||
message: t.sendTab.manualSending,
|
||||
child: CustomIconButton(
|
||||
onPressed: () async => vm.onTapAddress(context),
|
||||
child: const Icon(Icons.ads_click),
|
||||
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),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
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: 50),
|
||||
],
|
||||
),
|
||||
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),
|
||||
// make the top draggable on Desktop
|
||||
checkPlatform([TargetPlatform.macOS])
|
||||
? SizedBox(height: 50, child: MoveWindow())
|
||||
: SizedBox(
|
||||
height: 0,
|
||||
width: 0,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,6 +6,7 @@ import 'package:localsend_app/gen/strings.g.dart';
|
||||
import 'package:localsend_app/provider/settings_provider.dart';
|
||||
import 'package:localsend_app/util/native/cmd_helper.dart';
|
||||
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';
|
||||
@@ -18,9 +19,7 @@ class TroubleshootPage extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
final settings = context.ref.watch(settingsProvider);
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(t.troubleshootPage.title),
|
||||
),
|
||||
appBar: basicLocalSendAppbar(t.troubleshootPage.title),
|
||||
body: ResponsiveListView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 30),
|
||||
children: [
|
||||
|
||||
@@ -9,6 +9,7 @@ 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';
|
||||
@@ -99,9 +100,7 @@ class _WebSendPageState extends State<WebSendPage> with Refena {
|
||||
},
|
||||
canPop: false,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(t.webSharePage.title),
|
||||
),
|
||||
appBar: basicLocalSendAppbar(t.webSharePage.title),
|
||||
body: Builder(
|
||||
builder: (context) {
|
||||
if (_stateEnum != _ServerState.running) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
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';
|
||||
@@ -81,6 +82,7 @@ Future<void> showFromTray() async {
|
||||
// This will crash on Windows
|
||||
// https://github.com/localsend/localsend/issues/32
|
||||
await windowManager.setSkipTaskbar(false);
|
||||
appWindow.show();
|
||||
}
|
||||
|
||||
// Enable animations
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
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,6 +6,7 @@
|
||||
|
||||
#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>
|
||||
@@ -20,6 +21,9 @@
|
||||
#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,6 +3,7 @@
|
||||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
bitsdojo_window_linux
|
||||
desktop_drop
|
||||
dynamic_color
|
||||
file_selector_linux
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import FlutterMacOS
|
||||
import Foundation
|
||||
|
||||
import bitsdojo_window_macos
|
||||
import connectivity_plus
|
||||
import desktop_drop
|
||||
import device_info_plus
|
||||
@@ -28,6 +29,7 @@ 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"))
|
||||
|
||||
@@ -3,6 +3,7 @@ import FlutterMacOS
|
||||
import Defaults
|
||||
import DockProgress
|
||||
import LaunchAtLogin
|
||||
import bitsdojo_window_macos
|
||||
|
||||
enum DockIcon: CaseIterable {
|
||||
case regular
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
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: NSWindow {
|
||||
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
|
||||
}
|
||||
override func awakeFromNib() {
|
||||
let flutterViewController = FlutterViewController.init()
|
||||
let windowFrame = self.frame
|
||||
@@ -10,13 +15,9 @@ class MainFlutterWindow: NSWindow {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,6 +86,46 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.7.0"
|
||||
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:
|
||||
|
||||
@@ -11,6 +11,7 @@ environment:
|
||||
|
||||
dependencies:
|
||||
basic_utils: 5.7.0
|
||||
bitsdojo_window: ^0.1.6
|
||||
collection: ^1.17.2 # allow newer versions, so it can compile with newer Flutter versions
|
||||
common:
|
||||
path: ../common
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#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>
|
||||
@@ -22,6 +23,8 @@
|
||||
#include <windows_taskbar/windows_taskbar_plugin.h>
|
||||
|
||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
BitsdojoWindowPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("BitsdojoWindowPlugin"));
|
||||
ConnectivityPlusWindowsPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
|
||||
DesktopDropPluginRegisterWithRegistrar(
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
bitsdojo_window_windows
|
||||
connectivity_plus
|
||||
desktop_drop
|
||||
dynamic_color
|
||||
|
||||
Reference in New Issue
Block a user