feat: no titlebar on macOS

Fixes #136
This commit is contained in:
lollipopkit
2023-08-18 20:01:18 +08:00
parent fe51669369
commit b5c705a1fe
28 changed files with 290 additions and 204 deletions

View File

@@ -28,8 +28,7 @@ class MyApp extends StatelessWidget {
// Issue #57 // Issue #57
// if not [ok] -> [AMOLED] mode, use [ThemeMode.dark] // if not [ok] -> [AMOLED] mode, use [ThemeMode.dark]
final themeMode = isAMOLED ? ThemeMode.values[tMode] : ThemeMode.dark; final themeMode = isAMOLED ? ThemeMode.values[tMode] : ThemeMode.dark;
final localeStr = _setting.locale.fetch(); final locale = _setting.locale.fetch()?.toLocale;
final locale = localeStr?.toLocale;
final darkTheme = ThemeData( final darkTheme = ThemeData(
useMaterial3: true, useMaterial3: true,
brightness: Brightness.dark, brightness: Brightness.dark,
@@ -47,39 +46,25 @@ class MyApp extends StatelessWidget {
useMaterial3: true, useMaterial3: true,
colorSchemeSeed: primaryColor, colorSchemeSeed: primaryColor,
), ),
darkTheme: isAMOLED darkTheme: isAMOLED ? darkTheme : _getAmoledTheme(darkTheme),
? darkTheme
: darkTheme.copyWith(
scaffoldBackgroundColor: Colors.black,
dialogBackgroundColor: Colors.black,
drawerTheme: const DrawerThemeData(
backgroundColor: Colors.black,
),
appBarTheme: const AppBarTheme(
backgroundColor: Colors.black,
),
dialogTheme: const DialogTheme(
backgroundColor: Colors.black,
),
bottomSheetTheme: const BottomSheetThemeData(
backgroundColor: Colors.black,
),
listTileTheme: const ListTileThemeData(
tileColor: Colors.black12,
),
cardTheme: const CardTheme(
color: Colors.black12,
),
navigationBarTheme: const NavigationBarThemeData(
backgroundColor: Colors.black,
),
popupMenuTheme: const PopupMenuThemeData(
color: Colors.black,
),
),
home: fullScreen ? const FullScreenPage() : const HomePage(), home: fullScreen ? const FullScreenPage() : const HomePage(),
); );
}, },
); );
} }
} }
ThemeData _getAmoledTheme(ThemeData darkTheme) => darkTheme.copyWith(
scaffoldBackgroundColor: Colors.black,
dialogBackgroundColor: Colors.black,
drawerTheme: const DrawerThemeData(backgroundColor: Colors.black),
appBarTheme: const AppBarTheme(backgroundColor: Colors.black),
dialogTheme: const DialogTheme(backgroundColor: Colors.black),
bottomSheetTheme:
const BottomSheetThemeData(backgroundColor: Colors.black),
listTileTheme: const ListTileThemeData(tileColor: Colors.black12),
cardTheme: const CardTheme(color: Colors.black12),
navigationBarTheme:
const NavigationBarThemeData(backgroundColor: Colors.black),
popupMenuTheme: const PopupMenuThemeData(color: Colors.black),
);

View File

@@ -18,11 +18,11 @@ import 'data/store/snippet.dart';
GetIt locator = GetIt.instance; GetIt locator = GetIt.instance;
void setupLocatorForServices() { void _setupLocatorForServices() {
locator.registerLazySingleton(() => AppService()); locator.registerLazySingleton(() => AppService());
} }
void setupLocatorForProviders() { void _setupLocatorForProviders() {
locator.registerSingleton(AppProvider()); locator.registerSingleton(AppProvider());
locator.registerSingleton(PkgProvider()); locator.registerSingleton(PkgProvider());
locator.registerSingleton(DebugProvider()); locator.registerSingleton(DebugProvider());
@@ -34,7 +34,7 @@ void setupLocatorForProviders() {
locator.registerSingleton(SftpProvider()); locator.registerSingleton(SftpProvider());
} }
Future<void> setupLocatorForStores() async { Future<void> _setupLocatorForStores() async {
final setting = SettingStore(); final setting = SettingStore();
await setting.init(boxName: 'setting'); await setting.init(boxName: 'setting');
locator.registerSingleton(setting); locator.registerSingleton(setting);
@@ -57,7 +57,7 @@ Future<void> setupLocatorForStores() async {
} }
Future<void> setupLocator() async { Future<void> setupLocator() async {
await setupLocatorForStores(); await _setupLocatorForStores();
setupLocatorForProviders(); _setupLocatorForProviders();
setupLocatorForServices(); _setupLocatorForServices();
} }

View File

@@ -3,17 +3,20 @@ import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hive_flutter/hive_flutter.dart'; import 'package:hive_flutter/hive_flutter.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:macos_window_utils/window_manipulator.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:toolbox/data/model/app/net_view.dart'; import 'package:toolbox/view/widget/custom_appbar.dart';
import 'package:toolbox/data/model/ssh/virtual_key.dart';
import 'app.dart'; import 'app.dart';
import 'core/analysis.dart'; import 'core/analysis.dart';
import 'core/utils/platform.dart';
import 'core/utils/ui.dart'; import 'core/utils/ui.dart';
import 'data/model/app/net_view.dart';
import 'data/model/server/private_key_info.dart'; import 'data/model/server/private_key_info.dart';
import 'data/model/server/server_private_info.dart'; import 'data/model/server/server_private_info.dart';
import 'data/model/server/snippet.dart'; import 'data/model/server/snippet.dart';
import 'data/model/ssh/virtual_key.dart';
import 'data/provider/app.dart'; import 'data/provider/app.dart';
import 'data/provider/debug.dart'; import 'data/provider/debug.dart';
import 'data/provider/docker.dart'; import 'data/provider/docker.dart';
@@ -27,69 +30,10 @@ import 'data/store/setting.dart';
import 'locator.dart'; import 'locator.dart';
import 'view/widget/rebuild.dart'; import 'view/widget/rebuild.dart';
late final DebugProvider _debug; DebugProvider? _debug;
Future<void> initApp() async {
await initHive();
await setupLocator();
_debug = locator<DebugProvider>();
locator<SnippetProvider>().loadData();
locator<PrivateKeyProvider>().loadData();
final settings = locator<SettingStore>();
await loadFontFile(settings.fontPath.fetch());
SharedPreferences.setPrefix('');
Logger.root.level = Level.ALL;
Logger.root.onRecord.listen((record) {
var str = '[${record.loggerName}][${record.level.name}]: ${record.message}';
if (record.error != null) {
str += '\n${record.error}';
_debug.addMultiline(record.error.toString(), Colors.red);
}
if (record.stackTrace != null) {
str += '\n${record.stackTrace}';
_debug.addMultiline(record.stackTrace.toString(), Colors.white);
}
// ignore: avoid_print
print(str);
});
}
Future<void> initHive() async {
await Hive.initFlutter();
// 以 typeId 为顺序
Hive.registerAdapter(PrivateKeyInfoAdapter());
Hive.registerAdapter(SnippetAdapter());
Hive.registerAdapter(ServerPrivateInfoAdapter());
Hive.registerAdapter(VirtKeyAdapter());
Hive.registerAdapter(NetViewTypeAdapter());
}
void runInZone(void Function() body) {
final zoneSpec = ZoneSpecification(
print: (Zone self, ZoneDelegate parent, Zone zone, String line) {
parent.print(zone, line);
// This is a hack to avoid
// `setState() or markNeedsBuild() called during build`
// error.
Future.delayed(const Duration(milliseconds: 1), () {
_debug.addText(line);
});
},
);
runZonedGuarded(
body,
(obj, trace) => Analysis.recordException(trace),
zoneSpecification: zoneSpec,
);
}
Future<void> main() async { Future<void> main() async {
runInZone(() async { _runInZone(() async {
await initApp(); await initApp();
runApp( runApp(
MultiProvider( MultiProvider(
@@ -111,3 +55,86 @@ Future<void> main() async {
); );
}); });
} }
void _runInZone(void Function() body) {
final zoneSpec = ZoneSpecification(
print: (Zone self, ZoneDelegate parent, Zone zone, String line) {
parent.print(zone, line);
// This is a hack to avoid
// `setState() or markNeedsBuild() called during build`
// error.
Future.delayed(const Duration(milliseconds: 1), () {
_debug?.addText(line);
});
},
);
runZonedGuarded(
body,
(obj, trace) => Analysis.recordException(trace),
zoneSpecification: zoneSpec,
);
}
Future<void> initApp() async {
await _initMacOSWindow();
// Base of all data.
await _initHive();
await setupLocator();
// Setup [DebugProvider] first to catch all logs.
_debug = locator<DebugProvider>();
_setupLogger();
_setupProviders();
// Load font
final settings = locator<SettingStore>();
loadFontFile(settings.fontPath.fetch());
// SharedPreferences is only used on Android for saving home widgets settings.
if (!isAndroid) return;
SharedPreferences.setPrefix('');
}
void _setupProviders() {
locator<SnippetProvider>().loadData();
locator<PrivateKeyProvider>().loadData();
}
Future<void> _initHive() async {
await Hive.initFlutter();
// 以 typeId 为顺序
Hive.registerAdapter(PrivateKeyInfoAdapter());
Hive.registerAdapter(SnippetAdapter());
Hive.registerAdapter(ServerPrivateInfoAdapter());
Hive.registerAdapter(VirtKeyAdapter());
Hive.registerAdapter(NetViewTypeAdapter());
}
void _setupLogger() {
Logger.root.level = Level.ALL;
Logger.root.onRecord.listen((record) {
var str = '[${record.loggerName}][${record.level.name}]: ${record.message}';
if (record.error != null) {
str += '\n${record.error}';
_debug?.addMultiline(record.error.toString(), Colors.red);
}
if (record.stackTrace != null) {
str += '\n${record.stackTrace}';
_debug?.addMultiline(record.stackTrace.toString(), Colors.white);
}
// ignore: avoid_print
print(str);
});
}
Future<void> _initMacOSWindow() async {
if (!isMacOS) return;
WidgetsFlutterBinding.ensureInitialized();
await WindowManipulator.initialize();
WindowManipulator.makeTitlebarTransparent();
WindowManipulator.enableFullSizeContentView();
WindowManipulator.hideTitle();
await CustomAppBar.updateTitlebarHeight();
}

View File

@@ -19,6 +19,7 @@ import '../../data/store/private_key.dart';
import '../../data/store/server.dart'; import '../../data/store/server.dart';
import '../../data/store/snippet.dart'; import '../../data/store/snippet.dart';
import '../../locator.dart'; import '../../locator.dart';
import '../widget/custom_appbar.dart';
const backupFormatVersion = 1; const backupFormatVersion = 1;
@@ -34,7 +35,7 @@ class BackupPage extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final s = S.of(context)!; final s = S.of(context)!;
return Scaffold( return Scaffold(
appBar: AppBar( appBar: CustomAppBar(
title: Text(s.backupAndRestore, style: textSize18), title: Text(s.backupAndRestore, style: textSize18),
), ),
body: _buildBody(context, s), body: _buildBody(context, s),

View File

@@ -7,6 +7,7 @@ import 'package:toolbox/data/res/ui.dart';
import 'package:toolbox/view/widget/value_notifier.dart'; import 'package:toolbox/view/widget/value_notifier.dart';
import '../../core/utils/ui.dart'; import '../../core/utils/ui.dart';
import '../widget/custom_appbar.dart';
import '../widget/input_field.dart'; import '../widget/input_field.dart';
import '../widget/popup_menu.dart'; import '../widget/popup_menu.dart';
import '../widget/round_rect_card.dart'; import '../widget/round_rect_card.dart';
@@ -52,7 +53,7 @@ class _ConvertPageState extends State<ConvertPage>
Widget build(BuildContext context) { Widget build(BuildContext context) {
super.build(context); super.build(context);
return Scaffold( return Scaffold(
appBar: AppBar( appBar: CustomAppBar(
title: Text(_s.convert), title: Text(_s.convert),
), ),
body: SingleChildScrollView( body: SingleChildScrollView(

View File

@@ -3,6 +3,8 @@ import 'package:provider/provider.dart';
import 'package:toolbox/core/extension/navigator.dart'; import 'package:toolbox/core/extension/navigator.dart';
import 'package:toolbox/data/provider/debug.dart'; import 'package:toolbox/data/provider/debug.dart';
import '../widget/custom_appbar.dart';
class DebugPage extends StatefulWidget { class DebugPage extends StatefulWidget {
const DebugPage({Key? key}) : super(key: key); const DebugPage({Key? key}) : super(key: key);
@@ -14,7 +16,7 @@ class _DebugPageState extends State<DebugPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: CustomAppBar(
leading: IconButton( leading: IconButton(
onPressed: () => context.pop(), onPressed: () => context.pop(),
icon: const Icon(Icons.arrow_back, color: Colors.white), icon: const Icon(Icons.arrow_back, color: Colors.white),

View File

@@ -18,6 +18,7 @@ import '../../data/res/ui.dart';
import '../../data/res/url.dart'; import '../../data/res/url.dart';
import '../../data/store/docker.dart'; import '../../data/store/docker.dart';
import '../../locator.dart'; import '../../locator.dart';
import '../widget/custom_appbar.dart';
import '../widget/popup_menu.dart'; import '../widget/popup_menu.dart';
import '../widget/round_rect_card.dart'; import '../widget/round_rect_card.dart';
import '../widget/two_line_text.dart'; import '../widget/two_line_text.dart';
@@ -64,7 +65,7 @@ class _DockerManagePageState extends State<DockerManagePage> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Consumer<DockerProvider>(builder: (_, ___, __) { return Consumer<DockerProvider>(builder: (_, ___, __) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: CustomAppBar(
centerTitle: true, centerTitle: true,
title: TwoLineText(up: 'Docker', down: widget.spi.name), title: TwoLineText(up: 'Docker', down: widget.spi.name),
actions: [ actions: [

View File

@@ -15,6 +15,7 @@ import 'package:toolbox/data/res/highlight.dart';
import 'package:toolbox/data/store/setting.dart'; import 'package:toolbox/data/store/setting.dart';
import 'package:toolbox/locator.dart'; import 'package:toolbox/locator.dart';
import '../widget/custom_appbar.dart';
import '../widget/two_line_text.dart'; import '../widget/two_line_text.dart';
class EditorPage extends StatefulWidget { class EditorPage extends StatefulWidget {
@@ -68,14 +69,20 @@ class _EditorPageState extends State<EditorPage> with AfterLayoutMixin {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
backgroundColor: () { backgroundColor: _codeTheme?['root']?.backgroundColor,
if (_codeTheme != null) { appBar: _buildAppBar(),
return _codeTheme!['root']!.backgroundColor; body: _buildBody(),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.done),
onPressed: () {
context.pop(_controller.text);
},
),
);
} }
return null;
}(), PreferredSizeWidget _buildAppBar() {
appBar: AppBar( return CustomAppBar(
centerTitle: true,
title: TwoLineText(up: getFileName(widget.path) ?? '', down: _s.editor), title: TwoLineText(up: getFileName(widget.path) ?? '', down: _s.editor),
actions: [ actions: [
PopupMenuButton<String>( PopupMenuButton<String>(
@@ -95,9 +102,12 @@ class _EditorPageState extends State<EditorPage> with AfterLayoutMixin {
}, },
) )
], ],
), );
body: Visibility( }
visible: (_codeTheme != null),
Widget _buildBody() {
return Visibility(
visible: _codeTheme != null,
replacement: const Center( replacement: const Center(
child: CircularProgressIndicator(), child: CircularProgressIndicator(),
), ),
@@ -116,13 +126,6 @@ class _EditorPageState extends State<EditorPage> with AfterLayoutMixin {
), ),
), ),
), ),
),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.done),
onPressed: () {
context.pop(_controller.text);
},
),
); );
} }

View File

@@ -20,6 +20,7 @@ import '../../data/res/ui.dart';
import '../../data/res/url.dart'; import '../../data/res/url.dart';
import '../../data/store/setting.dart'; import '../../data/store/setting.dart';
import '../../locator.dart'; import '../../locator.dart';
import '../widget/custom_appbar.dart';
import '../widget/url_text.dart'; import '../widget/url_text.dart';
import 'backup.dart'; import 'backup.dart';
import 'convert.dart'; import 'convert.dart';
@@ -109,31 +110,10 @@ class _HomePageState extends State<HomePage>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
super.build(context); super.build(context);
final actions = <Widget>[
IconButton(
icon: const Icon(Icons.developer_mode, size: 23),
tooltip: _s.debug,
onPressed: () => AppRoute(
const DebugPage(),
'Debug Page',
).go(context),
),
];
if (isDesktop && _selectIndex.value == AppTab.server.index) {
actions.add(
IconButton(
icon: const Icon(Icons.refresh, size: 23),
tooltip: 'Refresh',
onPressed: () => _serverProvider.refreshData(),
),
);
}
return Scaffold( return Scaffold(
drawer: _buildDrawer(), drawer: _buildDrawer(),
appBar: AppBar( appBar: _buildAppBar(),
title: const Text(BuildData.name),
actions: actions,
),
body: PageView.builder( body: PageView.builder(
controller: _pageController, controller: _pageController,
itemCount: AppTab.values.length, itemCount: AppTab.values.length,
@@ -151,9 +131,44 @@ class _HomePageState extends State<HomePage>
); );
} }
PreferredSizeWidget _buildAppBar() {
final actions = <Widget>[
IconButton(
icon: const Icon(Icons.developer_mode, size: 23),
tooltip: _s.debug,
onPressed: () => AppRoute(
const DebugPage(),
'Debug Page',
).go(context),
),
];
if (isDesktop && _selectIndex.value == AppTab.server.index) {
actions.add(
ValueBuilder(
listenable: _selectIndex,
build: () {
if (_selectIndex.value != AppTab.server.index) {
return const SizedBox();
}
return IconButton(
icon: const Icon(Icons.refresh, size: 23),
tooltip: 'Refresh',
onPressed: () => _serverProvider.refreshData(),
);
},
),
);
}
return CustomAppBar(
title: const Text(BuildData.name),
actions: actions,
);
}
Widget _buildBottomBar() { Widget _buildBottomBar() {
return NavigationBar( return NavigationBar(
selectedIndex: _selectIndex.value, selectedIndex: _selectIndex.value,
height: kBottomNavigationBarHeight * 1.2,
animationDuration: const Duration(milliseconds: 250), animationDuration: const Duration(milliseconds: 250),
onDestinationSelected: (int index) { onDestinationSelected: (int index) {
if (_selectIndex.value == index) return; if (_selectIndex.value == index) return;

View File

@@ -12,6 +12,7 @@ import '../../data/provider/pkg.dart';
import '../../data/provider/server.dart'; import '../../data/provider/server.dart';
import '../../data/res/ui.dart'; import '../../data/res/ui.dart';
import '../../locator.dart'; import '../../locator.dart';
import '../widget/custom_appbar.dart';
import '../widget/round_rect_card.dart'; import '../widget/round_rect_card.dart';
import '../widget/two_line_text.dart'; import '../widget/two_line_text.dart';
@@ -72,7 +73,7 @@ class _PkgManagePageState extends State<PkgManagePage>
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Consumer<PkgProvider>(builder: (_, pkg, __) { return Consumer<PkgProvider>(builder: (_, pkg, __) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: CustomAppBar(
centerTitle: true, centerTitle: true,
title: TwoLineText(up: _s.pkg, down: widget.spi.name), title: TwoLineText(up: _s.pkg, down: widget.spi.name),
), ),

View File

@@ -17,6 +17,7 @@ import '../../../data/model/server/private_key_info.dart';
import '../../../data/provider/private_key.dart'; import '../../../data/provider/private_key.dart';
import '../../../data/res/ui.dart'; import '../../../data/res/ui.dart';
import '../../../locator.dart'; import '../../../locator.dart';
import '../../widget/custom_appbar.dart';
const _format = 'text/plain'; const _format = 'text/plain';
@@ -89,7 +90,7 @@ class _PrivateKeyEditPageState extends State<PrivateKeyEditPage>
}, },
icon: const Icon(Icons.delete)) icon: const Icon(Icons.delete))
]; ];
return AppBar( return CustomAppBar(
title: Text(_s.edit, style: textSize18), title: Text(_s.edit, style: textSize18),
actions: actions, actions: actions,
); );

View File

@@ -14,6 +14,7 @@ import '../../../core/utils/platform.dart';
import '../../../data/model/server/private_key_info.dart'; import '../../../data/model/server/private_key_info.dart';
import '../../../data/provider/private_key.dart'; import '../../../data/provider/private_key.dart';
import '../../../data/res/ui.dart'; import '../../../data/res/ui.dart';
import '../../widget/custom_appbar.dart';
import 'edit.dart'; import 'edit.dart';
import '../../../view/widget/round_rect_card.dart'; import '../../../view/widget/round_rect_card.dart';
@@ -37,7 +38,7 @@ class _PrivateKeyListState extends State<PrivateKeysListPage>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: CustomAppBar(
title: Text(_s.privateKey, style: textSize18), title: Text(_s.privateKey, style: textSize18),
), ),
body: _buildBody(), body: _buildBody(),

View File

@@ -15,6 +15,7 @@ import 'package:toolbox/view/widget/two_line_text.dart';
import '../../data/provider/server.dart'; import '../../data/provider/server.dart';
import '../../locator.dart'; import '../../locator.dart';
import '../widget/custom_appbar.dart';
class ProcessPage extends StatefulWidget { class ProcessPage extends StatefulWidget {
final ServerPrivateInfo spi; final ServerPrivateInfo spi;
@@ -124,7 +125,7 @@ class _ProcessPageState extends State<ProcessPage> {
); );
} }
return Scaffold( return Scaffold(
appBar: AppBar( appBar: CustomAppBar(
centerTitle: true, centerTitle: true,
title: TwoLineText(up: widget.spi.name, down: _s.process), title: TwoLineText(up: widget.spi.name, down: _s.process),
actions: actions, actions: actions,

View File

@@ -14,6 +14,7 @@ import '../../../data/res/default.dart';
import '../../../data/res/ui.dart'; import '../../../data/res/ui.dart';
import '../../../data/store/setting.dart'; import '../../../data/store/setting.dart';
import '../../../locator.dart'; import '../../../locator.dart';
import '../../widget/custom_appbar.dart';
import '../../widget/round_rect_card.dart'; import '../../widget/round_rect_card.dart';
class ServerDetailPage extends StatefulWidget { class ServerDetailPage extends StatefulWidget {
@@ -75,7 +76,7 @@ class _ServerDetailPageState extends State<ServerDetailPage>
Widget _buildMainPage(Server si) { Widget _buildMainPage(Server si) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: CustomAppBar(
title: Text(si.spi.name, style: textSize18), title: Text(si.spi.name, style: textSize18),
), ),
body: ReorderableListView.builder( body: ReorderableListView.builder(

View File

@@ -15,6 +15,7 @@ import '../../../data/provider/server.dart';
import '../../../data/res/ui.dart'; import '../../../data/res/ui.dart';
import '../../../data/store/private_key.dart'; import '../../../data/store/private_key.dart';
import '../../../locator.dart'; import '../../../locator.dart';
import '../../widget/custom_appbar.dart';
import '../../widget/tag/editor.dart'; import '../../widget/tag/editor.dart';
import '../private_key/edit.dart'; import '../private_key/edit.dart';
@@ -116,7 +117,7 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
icon: const Icon(Icons.delete), icon: const Icon(Icons.delete),
); );
final actions = widget.spi != null ? [delBtn] : null; final actions = widget.spi != null ? [delBtn] : null;
return AppBar( return CustomAppBar(
title: Text(_s.edit, style: textSize18), title: Text(_s.edit, style: textSize18),
actions: actions, actions: actions,
); );

View File

@@ -29,6 +29,7 @@ import '../../data/res/ui.dart';
import '../../data/store/server.dart'; import '../../data/store/server.dart';
import '../../data/store/setting.dart'; import '../../data/store/setting.dart';
import '../../locator.dart'; import '../../locator.dart';
import '../widget/custom_appbar.dart';
import '../widget/future_widget.dart'; import '../widget/future_widget.dart';
import '../widget/round_rect_card.dart'; import '../widget/round_rect_card.dart';
@@ -100,7 +101,7 @@ class _SettingPageState extends State<SettingPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: CustomAppBar(
title: Text(_s.setting), title: Text(_s.setting),
), ),
body: ListView( body: ListView(

View File

@@ -9,6 +9,7 @@ import '../../../data/model/server/snippet.dart';
import '../../../data/provider/snippet.dart'; import '../../../data/provider/snippet.dart';
import '../../../data/res/ui.dart'; import '../../../data/res/ui.dart';
import '../../../locator.dart'; import '../../../locator.dart';
import '../../widget/custom_appbar.dart';
import '../../widget/tag/editor.dart'; import '../../widget/tag/editor.dart';
class SnippetEditPage extends StatefulWidget { class SnippetEditPage extends StatefulWidget {
@@ -54,7 +55,7 @@ class _SnippetEditPageState extends State<SnippetEditPage>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: CustomAppBar(
title: Text(_s.edit, style: textSize18), title: Text(_s.edit, style: textSize18),
actions: _buildAppBarActions(), actions: _buildAppBarActions(),
), ),

View File

@@ -9,6 +9,8 @@ import 'package:toolbox/data/store/setting.dart';
import 'package:toolbox/locator.dart'; import 'package:toolbox/locator.dart';
import 'package:toolbox/view/widget/round_rect_card.dart'; import 'package:toolbox/view/widget/round_rect_card.dart';
import '../../widget/custom_appbar.dart';
class SSHVirtKeySettingPage extends StatefulWidget { class SSHVirtKeySettingPage extends StatefulWidget {
const SSHVirtKeySettingPage({Key? key}) : super(key: key); const SSHVirtKeySettingPage({Key? key}) : super(key: key);
@@ -29,7 +31,7 @@ class _SSHVirtKeySettingPageState extends State<SSHVirtKeySettingPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: CustomAppBar(
title: Text(_s.editVirtKeys), title: Text(_s.editVirtKeys),
), ),
body: _buildBody(), body: _buildBody(),

View File

@@ -22,6 +22,7 @@ import '../../../core/utils/ui.dart';
import '../../../data/model/app/path_with_prefix.dart'; import '../../../data/model/app/path_with_prefix.dart';
import '../../../data/res/path.dart'; import '../../../data/res/path.dart';
import '../../../data/res/ui.dart'; import '../../../data/res/ui.dart';
import '../../widget/custom_appbar.dart';
import '../../widget/fade_in.dart'; import '../../widget/fade_in.dart';
import 'sftp_mission.dart'; import 'sftp_mission.dart';
@@ -64,7 +65,7 @@ class _LocalStoragePageState extends State<LocalStoragePage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: CustomAppBar(
leading: IconButton( leading: IconButton(
icon: const BackButtonIcon(), icon: const BackButtonIcon(),
onPressed: () { onPressed: () {

View File

@@ -26,6 +26,7 @@ import '../../../data/provider/sftp.dart';
import '../../../data/res/path.dart'; import '../../../data/res/path.dart';
import '../../../data/res/ui.dart'; import '../../../data/res/ui.dart';
import '../../../locator.dart'; import '../../../locator.dart';
import '../../widget/custom_appbar.dart';
import '../../widget/fade_in.dart'; import '../../widget/fade_in.dart';
import '../../widget/input_field.dart'; import '../../widget/input_field.dart';
import '../../widget/two_line_text.dart'; import '../../widget/two_line_text.dart';
@@ -74,7 +75,7 @@ class _SftpPageState extends State<SftpPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: CustomAppBar(
leading: IconButton( leading: IconButton(
icon: const BackButtonIcon(), icon: const BackButtonIcon(),
onPressed: () { onPressed: () {

View File

@@ -13,6 +13,7 @@ import '../../../core/utils/ui.dart';
import '../../../data/model/sftp/req.dart'; import '../../../data/model/sftp/req.dart';
import '../../../data/provider/sftp.dart'; import '../../../data/provider/sftp.dart';
import '../../../data/res/ui.dart'; import '../../../data/res/ui.dart';
import '../../widget/custom_appbar.dart';
import '../../widget/round_rect_card.dart'; import '../../widget/round_rect_card.dart';
class SftpMissionPage extends StatefulWidget { class SftpMissionPage extends StatefulWidget {
@@ -34,11 +35,8 @@ class _SftpMissionPageState extends State<SftpMissionPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: CustomAppBar(
title: Text( title: Text(_s.mission, style: textSize18),
_s.mission,
style: textSize18,
),
), ),
body: _buildBody(), body: _buildBody(),
); );

View File

@@ -0,0 +1,22 @@
import 'package:flutter/material.dart';
import 'package:macos_window_utils/window_manipulator.dart';
double? _titlebarHeight;
class CustomAppBar extends AppBar implements PreferredSizeWidget {
CustomAppBar({
super.key,
super.title,
super.actions,
super.centerTitle,
super.leading,
super.backgroundColor,
}) : super(toolbarHeight: (_titlebarHeight ?? 0) + kToolbarHeight);
static Future<void> updateTitlebarHeight() async {
final newTitlebarHeight = await WindowManipulator.getTitlebarHeight();
if (_titlebarHeight != newTitlebarHeight) {
_titlebarHeight = newTitlebarHeight;
}
}
}

View File

@@ -5,12 +5,14 @@
import FlutterMacOS import FlutterMacOS
import Foundation import Foundation
import macos_window_utils
import path_provider_foundation import path_provider_foundation
import share_plus import share_plus
import shared_preferences_foundation import shared_preferences_foundation
import url_launcher_macos import url_launcher_macos
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
MacOSWindowUtilsPlugin.register(with: registry.registrar(forPlugin: "MacOSWindowUtilsPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))

View File

@@ -1,4 +1,4 @@
platform :osx, '10.14' platform :osx, '10.15'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency. # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true' ENV['COCOAPODS_DISABLE_STATS'] = 'true'

View File

@@ -1,5 +1,7 @@
PODS: PODS:
- FlutterMacOS (1.0.0) - FlutterMacOS (1.0.0)
- macos_window_utils (1.0.0):
- FlutterMacOS
- path_provider_foundation (0.0.1): - path_provider_foundation (0.0.1):
- Flutter - Flutter
- FlutterMacOS - FlutterMacOS
@@ -13,6 +15,7 @@ PODS:
DEPENDENCIES: DEPENDENCIES:
- FlutterMacOS (from `Flutter/ephemeral`) - FlutterMacOS (from `Flutter/ephemeral`)
- macos_window_utils (from `Flutter/ephemeral/.symlinks/plugins/macos_window_utils/macos`)
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
- share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`) - share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`)
- shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`) - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`)
@@ -21,6 +24,8 @@ DEPENDENCIES:
EXTERNAL SOURCES: EXTERNAL SOURCES:
FlutterMacOS: FlutterMacOS:
:path: Flutter/ephemeral :path: Flutter/ephemeral
macos_window_utils:
:path: Flutter/ephemeral/.symlinks/plugins/macos_window_utils/macos
path_provider_foundation: path_provider_foundation:
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin
share_plus: share_plus:
@@ -32,11 +37,12 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS: SPEC CHECKSUMS:
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
path_provider_foundation: eaf5b3e458fc0e5fbb9940fb09980e853fe058b8 macos_window_utils: 933f91f64805e2eb91a5bd057cf97cd097276663
path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
share_plus: 76dd39142738f7a68dd57b05093b5e8193f220f7 share_plus: 76dd39142738f7a68dd57b05093b5e8193f220f7
shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126 shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126
url_launcher_macos: 5335912b679c073563f29d89d33d10d459f95451 url_launcher_macos: d2691c7dd33ed713bf3544850a623080ec693d95
PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367 PODFILE CHECKSUM: 9ebaf0ce3d369aaa26a9ea0e159195ed94724cf3
COCOAPODS: 1.12.1 COCOAPODS: 1.12.1

View File

@@ -195,7 +195,6 @@
15AF97DF993E8968098D6EBE /* Pods-RunnerTests.release.xcconfig */, 15AF97DF993E8968098D6EBE /* Pods-RunnerTests.release.xcconfig */,
7CFA7DE7FABA75685DFB6948 /* Pods-RunnerTests.profile.xcconfig */, 7CFA7DE7FABA75685DFB6948 /* Pods-RunnerTests.profile.xcconfig */,
); );
name = Pods;
path = Pods; path = Pods;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
@@ -475,9 +474,9 @@
baseConfigurationReference = C1C758C41C4E208965A68933 /* Pods-RunnerTests.debug.xcconfig */; baseConfigurationReference = C1C758C41C4E208965A68933 /* Pods-RunnerTests.debug.xcconfig */;
buildSettings = { buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 476; CURRENT_PROJECT_VERSION = 477;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0.476; MARKETING_VERSION = 1.0.477;
PRODUCT_BUNDLE_IDENTIFIER = tech.lolli.serverBox.RunnerTests; PRODUCT_BUNDLE_IDENTIFIER = tech.lolli.serverBox.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
@@ -490,9 +489,9 @@
baseConfigurationReference = 15AF97DF993E8968098D6EBE /* Pods-RunnerTests.release.xcconfig */; baseConfigurationReference = 15AF97DF993E8968098D6EBE /* Pods-RunnerTests.release.xcconfig */;
buildSettings = { buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 476; CURRENT_PROJECT_VERSION = 477;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0.476; MARKETING_VERSION = 1.0.477;
PRODUCT_BUNDLE_IDENTIFIER = tech.lolli.serverBox.RunnerTests; PRODUCT_BUNDLE_IDENTIFIER = tech.lolli.serverBox.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
@@ -505,9 +504,9 @@
baseConfigurationReference = 7CFA7DE7FABA75685DFB6948 /* Pods-RunnerTests.profile.xcconfig */; baseConfigurationReference = 7CFA7DE7FABA75685DFB6948 /* Pods-RunnerTests.profile.xcconfig */;
buildSettings = { buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 476; CURRENT_PROJECT_VERSION = 477;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0.476; MARKETING_VERSION = 1.0.477;
PRODUCT_BUNDLE_IDENTIFIER = tech.lolli.serverBox.RunnerTests; PRODUCT_BUNDLE_IDENTIFIER = tech.lolli.serverBox.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
@@ -575,6 +574,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 10.15;
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
}; };
@@ -701,6 +701,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 10.15;
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
@@ -721,6 +722,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 10.15;
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
}; };

View File

@@ -510,6 +510,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.2.0" version: "1.2.0"
macos_window_utils:
dependency: "direct main"
description:
name: macos_window_utils
sha256: "43a90473f8786f00f07203e6819dab67e032f8896dafa4a6f85fbc71fba32c0b"
url: "https://pub.dev"
source: hosted
version: "1.2.0"
matcher: matcher:
dependency: transitive dependency: transitive
description: description:

View File

@@ -45,6 +45,7 @@ dependencies:
code_text_field: ^1.1.0 code_text_field: ^1.1.0
shared_preferences: ^2.1.1 shared_preferences: ^2.1.1
crypto: ^3.0.3 crypto: ^3.0.3
macos_window_utils: ^1.2.0
dev_dependencies: dev_dependencies:
flutter_native_splash: ^2.1.6 flutter_native_splash: ^2.1.6