mirror of
https://github.com/lollipopkit/flutter_server_box.git
synced 2025-12-17 07:14:28 +01:00
new: fullscreen mode
This commit is contained in:
@@ -75,7 +75,13 @@ class MyApp extends StatelessWidget {
|
||||
themeMode: themeMode,
|
||||
theme: light,
|
||||
darkTheme: tMode < 3 ? dark : _getAmoledTheme(dark),
|
||||
home: const HomePage(),
|
||||
home: Stores.setting.fullScreen.fetch()
|
||||
? OrientationBuilder(
|
||||
builder: (_, ori) {
|
||||
return HomePage(landscape: ori == Orientation.landscape);
|
||||
},
|
||||
)
|
||||
: const HomePage(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,15 +111,15 @@ class SettingStore extends PersistentStore {
|
||||
Defaults.editorDarkTheme,
|
||||
);
|
||||
|
||||
// late final fullScreen = property(
|
||||
// 'fullScreen',
|
||||
// false,
|
||||
// );
|
||||
late final fullScreen = property(
|
||||
'fullScreen',
|
||||
false,
|
||||
);
|
||||
|
||||
// late final fullScreenJitter = property(
|
||||
// 'fullScreenJitter',
|
||||
// true,
|
||||
// );
|
||||
late final fullScreenJitter = property(
|
||||
'fullScreenJitter',
|
||||
true,
|
||||
);
|
||||
|
||||
// late final fullScreenRotateQuarter = property(
|
||||
// 'fullScreenRotateQuarter',
|
||||
|
||||
@@ -109,6 +109,7 @@
|
||||
"fullScreen": "Vollbildmodus",
|
||||
"fullScreenJitter": "Jitter im Vollbildmodus",
|
||||
"fullScreenJitterHelp": "Einbrennen des Bildschirms verhindern",
|
||||
"fullScreenTip": "Soll der Vollbildmodus aktiviert werden, wenn das Gerät in den Quermodus gedreht wird? Diese Option gilt nur für die Server-Registerkarte.",
|
||||
"getPushTokenFailed": "Push-Token kann nicht abgerufen werden",
|
||||
"gettingToken": "Getting token...",
|
||||
"goBackQ": "Zurückkommen?",
|
||||
|
||||
@@ -109,6 +109,7 @@
|
||||
"fullScreen": "Full screen mode",
|
||||
"fullScreenJitter": "Full screen jitter",
|
||||
"fullScreenJitterHelp": "To avoid screen burn-in",
|
||||
"fullScreenTip": "Should full-screen mode be enabled when the device is rotated to landscape mode? This option only applies to the server tab.",
|
||||
"getPushTokenFailed": "Can't fetch push token",
|
||||
"gettingToken": "Getting token...",
|
||||
"goBackQ": "Go back?",
|
||||
|
||||
@@ -109,6 +109,7 @@
|
||||
"fullScreen": "Modo pantalla completa",
|
||||
"fullScreenJitter": "Temblores en modo pantalla completa",
|
||||
"fullScreenJitterHelp": "Prevención de quemaduras de pantalla",
|
||||
"fullScreenTip": "¿Debe habilitarse el modo de pantalla completa cuando el dispositivo se rote al modo horizontal? Esta opción solo se aplica a la pestaña del servidor.",
|
||||
"getPushTokenFailed": "No se pudo obtener el token de notificación",
|
||||
"gettingToken": "Obteniendo Token...",
|
||||
"goBackQ": "¿Regresar?",
|
||||
|
||||
@@ -109,6 +109,7 @@
|
||||
"fullScreen": "Mode plein écran",
|
||||
"fullScreenJitter": "Tremblement plein écran",
|
||||
"fullScreenJitterHelp": "Pour éviter la rémanence d'écran",
|
||||
"fullScreenTip": "Le mode plein écran doit-il être activé lorsque l'appareil est basculé en mode paysage ? Cette option s'applique uniquement à l'onglet du serveur.",
|
||||
"getPushTokenFailed": "Impossible de récupérer le jeton d'identification",
|
||||
"gettingToken": "Récupération du jeton...",
|
||||
"goBackQ": "Revenir en arrière ?",
|
||||
|
||||
@@ -109,6 +109,7 @@
|
||||
"fullScreen": "Mode Layar Penuh",
|
||||
"fullScreenJitter": "Jitter layar penuh",
|
||||
"fullScreenJitterHelp": "Untuk menghindari pembakaran layar",
|
||||
"fullScreenTip": "Apakah mode layar penuh diaktifkan ketika perangkat diputar ke modus lanskap? Opsi ini hanya berlaku untuk tab server.",
|
||||
"getPushTokenFailed": "Tidak bisa mengambil token dorong",
|
||||
"gettingToken": "Mendapatkan token ...",
|
||||
"goBackQ": "Datang kembali?",
|
||||
|
||||
@@ -109,6 +109,7 @@
|
||||
"fullScreen": "フルスクリーンモード",
|
||||
"fullScreenJitter": "フルスクリーンモードのジッター",
|
||||
"fullScreenJitterHelp": "焼き付き防止",
|
||||
"fullScreenTip": "デバイスが横向きに回転したときにフルスクリーンモードを有効にしますか?このオプションはサーバータブにのみ適用されます。",
|
||||
"getPushTokenFailed": "プッシュトークンを取得できませんでした",
|
||||
"gettingToken": "トークンを取得しています...",
|
||||
"goBackQ": "戻りますか?",
|
||||
|
||||
@@ -109,6 +109,7 @@
|
||||
"fullScreen": "Modo tela cheia",
|
||||
"fullScreenJitter": "Tremulação em tela cheia",
|
||||
"fullScreenJitterHelp": "Prevenir burn-in de tela",
|
||||
"fullScreenTip": "Deve ser ativado o modo de tela cheia quando o dispositivo é girado para o modo paisagem? Esta opção aplica-se apenas à aba do servidor.",
|
||||
"getPushTokenFailed": "Não foi possível obter token de notificação",
|
||||
"gettingToken": "Obtendo Token...",
|
||||
"goBackQ": "Voltar?",
|
||||
|
||||
@@ -109,6 +109,7 @@
|
||||
"fullScreen": "полноэкранный режим",
|
||||
"fullScreenJitter": "дрожание в полноэкранном режиме",
|
||||
"fullScreenJitterHelp": "предотвращение выгорания экрана",
|
||||
"fullScreenTip": "Следует ли включить полноэкранный режим, когда устройство поворачивается в альбомный режим? Эта опция применяется только к вкладке сервера.",
|
||||
"getPushTokenFailed": "Не удалось получить токен уведомлений",
|
||||
"gettingToken": "Получение токена...",
|
||||
"goBackQ": "Вернуться?",
|
||||
|
||||
@@ -109,6 +109,7 @@
|
||||
"fullScreen": "全屏模式",
|
||||
"fullScreenJitter": "全屏模式抖动",
|
||||
"fullScreenJitterHelp": "防止烧屏",
|
||||
"fullScreenTip": "当设备旋转为横屏时,是否开启全屏模式。此选项仅作用于服务器Tab页。",
|
||||
"getPushTokenFailed": "未能获取到推送token",
|
||||
"gettingToken": "正在获取Token...",
|
||||
"goBackQ": "返回?",
|
||||
|
||||
@@ -109,6 +109,7 @@
|
||||
"fullScreen": "全屏模式",
|
||||
"fullScreenJitter": "全屏模式抖動",
|
||||
"fullScreenJitterHelp": "防止燒屏",
|
||||
"fullScreenTip": "當設備旋轉為橫屏時,是否開啟全屏模式?此選項僅適用於伺服器選項卡。",
|
||||
"getPushTokenFailed": "未能獲取到推送token",
|
||||
"gettingToken": "正在獲取Token...",
|
||||
"goBackQ": "返回?",
|
||||
|
||||
@@ -29,13 +29,14 @@ import 'package:toolbox/data/res/ui.dart';
|
||||
import 'package:toolbox/data/res/url.dart';
|
||||
import 'package:toolbox/view/widget/appbar.dart';
|
||||
import 'package:toolbox/view/widget/cardx.dart';
|
||||
import 'package:toolbox/view/widget/count_down_btn.dart';
|
||||
import 'package:toolbox/view/widget/markdown.dart';
|
||||
|
||||
part 'appbar.dart';
|
||||
|
||||
class HomePage extends StatefulWidget {
|
||||
const HomePage({super.key});
|
||||
final bool landscape;
|
||||
|
||||
const HomePage({super.key, this.landscape = false});
|
||||
|
||||
@override
|
||||
State<HomePage> createState() => _HomePageState();
|
||||
@@ -119,7 +120,9 @@ class _HomePageState extends State<HomePage>
|
||||
|
||||
return Scaffold(
|
||||
drawer: _buildDrawer(),
|
||||
appBar: _AppBar(
|
||||
appBar: widget.landscape
|
||||
? null
|
||||
: _AppBar(
|
||||
selectIndex: _selectIndex,
|
||||
centerTitle: false,
|
||||
title: const Text(BuildData.name),
|
||||
@@ -141,7 +144,9 @@ class _HomePageState extends State<HomePage>
|
||||
}
|
||||
},
|
||||
),
|
||||
bottomNavigationBar: ListenableBuilder(
|
||||
bottomNavigationBar: widget.landscape
|
||||
? null
|
||||
: ListenableBuilder(
|
||||
listenable: _selectIndex,
|
||||
builder: (_, __) => _buildBottomBar(),
|
||||
),
|
||||
@@ -321,7 +326,6 @@ ${GithubIds.participants.map((e) => '[$e](${e.url})').join(' ')}
|
||||
BioAuth.go();
|
||||
|
||||
_reqNotiPerm();
|
||||
context.showRoundDialog(child: CountDownBtn(onTap: context.pop));
|
||||
|
||||
if (Stores.setting.autoCheckAppUpdate.fetch()) {
|
||||
doUpdate(context);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import 'dart:async';
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:after_layout/after_layout.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
@@ -5,6 +8,7 @@ import 'package:provider/provider.dart';
|
||||
import 'package:toolbox/core/extension/context/common.dart';
|
||||
import 'package:toolbox/core/extension/context/dialog.dart';
|
||||
import 'package:toolbox/core/extension/context/locale.dart';
|
||||
import 'package:toolbox/core/extension/listx.dart';
|
||||
import 'package:toolbox/core/extension/media_queryx.dart';
|
||||
import 'package:toolbox/core/extension/numx.dart';
|
||||
import 'package:toolbox/core/extension/ssh_client.dart';
|
||||
@@ -38,17 +42,36 @@ class _ServerPageState extends State<ServerPage>
|
||||
late MediaQueryData _media;
|
||||
|
||||
late double _textFactorDouble;
|
||||
double _offset = 1;
|
||||
late TextScaler _textFactor;
|
||||
|
||||
final _cardsStatus = <String, _CardNotifier>{};
|
||||
|
||||
Timer? _timer;
|
||||
|
||||
String? _tag;
|
||||
bool _useDoubleColumn = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (!Stores.setting.fullScreenJitter.fetch()) return;
|
||||
_timer = Timer.periodic(const Duration(seconds: 30), (_) {
|
||||
if (mounted) {
|
||||
_updateOffset();
|
||||
setState(() {});
|
||||
} else {
|
||||
_timer?.cancel();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
_media = MediaQuery.of(context);
|
||||
_updateOffset();
|
||||
_updateTextScaler();
|
||||
_useDoubleColumn = _media.useDoubleColumn &&
|
||||
Stores.setting.doubleColumnServersPage.fetch();
|
||||
}
|
||||
@@ -56,13 +79,22 @@ class _ServerPageState extends State<ServerPage>
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
super.build(context);
|
||||
return OrientationBuilder(builder: (_, orientation) {
|
||||
if (orientation == Orientation.landscape) {
|
||||
final useFullScreen = Stores.setting.fullScreen.fetch();
|
||||
if (useFullScreen) return _buildLandscape();
|
||||
}
|
||||
return _buildPortrait();
|
||||
});
|
||||
}
|
||||
|
||||
Widget _buildPortrait() {
|
||||
return Scaffold(
|
||||
appBar: _buildTagsSwitcher(Pros.server),
|
||||
body: ListenableBuilder(
|
||||
listenable: Stores.setting.textFactor.listenable(),
|
||||
builder: (_, __) {
|
||||
_textFactorDouble = Stores.setting.textFactor.fetch();
|
||||
_textFactor = TextScaler.linear(_textFactorDouble);
|
||||
_updateTextScaler();
|
||||
return _buildBody();
|
||||
},
|
||||
),
|
||||
@@ -74,6 +106,75 @@ class _ServerPageState extends State<ServerPage>
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLandscape() {
|
||||
final offset = Offset(_offset, _offset);
|
||||
return Padding(
|
||||
// Avoid display cutout
|
||||
padding: EdgeInsets.all(_offset.abs()),
|
||||
child: Transform.translate(
|
||||
offset: offset,
|
||||
child: Stack(
|
||||
children: [
|
||||
_buildLandscapeBody(),
|
||||
Positioned(
|
||||
top: 0,
|
||||
left: 0,
|
||||
child: IconButton(
|
||||
onPressed: () => AppRoute.settings().go(context),
|
||||
icon: const Icon(Icons.settings, color: Colors.grey),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLandscapeBody() {
|
||||
return Consumer<ServerProvider>(builder: (_, pro, __) {
|
||||
if (pro.serverOrder.isEmpty) {
|
||||
return Center(
|
||||
child: Text(
|
||||
l10n.serverTabEmpty,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return PageView.builder(
|
||||
itemCount: pro.serverOrder.length,
|
||||
itemBuilder: (_, idx) {
|
||||
final id = pro.serverOrder[idx];
|
||||
final srv = pro.pick(id: id);
|
||||
if (srv == null) return UIs.placeholder;
|
||||
|
||||
final title = _buildServerCardTitle(srv);
|
||||
final List<Widget> children = [
|
||||
title,
|
||||
..._buildNormalCard(srv.status, srv.spi).joinWith(SizedBox(
|
||||
height: _media.size.height / 10,
|
||||
))
|
||||
];
|
||||
|
||||
return Padding(
|
||||
padding: _media.padding,
|
||||
child: ListenableBuilder(
|
||||
listenable: _getCardNoti(id),
|
||||
builder: (_, __) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: children,
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Widget _buildBody() {
|
||||
final child = Consumer<ServerProvider>(
|
||||
builder: (_, pro, __) {
|
||||
@@ -560,6 +661,19 @@ class _ServerPageState extends State<ServerPage>
|
||||
id,
|
||||
() => _CardNotifier(const _CardStatus()),
|
||||
);
|
||||
|
||||
void _updateOffset() {
|
||||
if (!Stores.setting.fullScreenJitter.fetch()) return;
|
||||
final x = _media.size.height * 0.03;
|
||||
final r = math.Random().nextDouble();
|
||||
final n = math.Random().nextBool() ? 1 : -1;
|
||||
_offset = x * r * n;
|
||||
}
|
||||
|
||||
void _updateTextScaler() {
|
||||
_textFactorDouble = Stores.setting.textFactor.fetch();
|
||||
_textFactor = TextScaler.linear(_textFactorDouble);
|
||||
}
|
||||
}
|
||||
|
||||
typedef _CardNotifier = ValueNotifier<_CardStatus>;
|
||||
|
||||
@@ -91,9 +91,9 @@ class _SettingPageState extends State<SettingPage> {
|
||||
_buildEditor(),
|
||||
|
||||
/// Fullscreen Mode is designed for old mobile phone which can be
|
||||
/// used as a status screen, so it's only available on mobile phone.
|
||||
// if (!isDesktop) _buildTitle(l10n.fullScreen),
|
||||
// if (!isDesktop) _buildFullScreen(),
|
||||
/// used as a status screen.
|
||||
if (isMobile) _buildTitle(l10n.fullScreen),
|
||||
if (isMobile) _buildFullScreen(),
|
||||
const SizedBox(height: 37),
|
||||
],
|
||||
),
|
||||
@@ -131,15 +131,15 @@ class _SettingPageState extends State<SettingPage> {
|
||||
);
|
||||
}
|
||||
|
||||
// Widget _buildFullScreen() {
|
||||
// return Column(
|
||||
// children: [
|
||||
// _buildFullScreenSwitch(),
|
||||
// _buildFullScreenJitter(),
|
||||
Widget _buildFullScreen() {
|
||||
return Column(
|
||||
children: [
|
||||
_buildFullScreenSwitch(),
|
||||
_buildFullScreenJitter(),
|
||||
// _buildFulScreenRotateQuarter(),
|
||||
// ].map((e) => CardX(child: e)).toList(),
|
||||
// );
|
||||
// }
|
||||
].map((e) => CardX(child: e)).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildServer() {
|
||||
return Column(
|
||||
@@ -599,23 +599,29 @@ class _SettingPageState extends State<SettingPage> {
|
||||
);
|
||||
}
|
||||
|
||||
// Widget _buildFullScreenSwitch() {
|
||||
// return ListTile(
|
||||
// title: Text(l10n.fullScreen),
|
||||
// trailing: StoreSwitch(
|
||||
// prop: _setting.fullScreen,
|
||||
// callback: (_) => RebuildNodes.app.rebuild(),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
Widget _buildFullScreenSwitch() {
|
||||
return ListTile(
|
||||
title: Text(l10n.fullScreen),
|
||||
subtitle: Text(l10n.fullScreenTip, style: UIs.textGrey),
|
||||
trailing: StoreSwitch(
|
||||
prop: _setting.fullScreen,
|
||||
callback: (_) => RebuildNodes.app.rebuild(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Widget _buildFullScreenJitter() {
|
||||
// return ListTile(
|
||||
// title: Text(l10n.fullScreenJitter),
|
||||
// subtitle: Text(l10n.fullScreenJitterHelp, style: UIs.textGrey),
|
||||
// trailing: StoreSwitch(prop: _setting.fullScreenJitter),
|
||||
// );
|
||||
// }
|
||||
Widget _buildFullScreenJitter() {
|
||||
return ListTile(
|
||||
title: Text(l10n.fullScreenJitter),
|
||||
subtitle: Text(l10n.fullScreenJitterHelp, style: UIs.textGrey),
|
||||
trailing: StoreSwitch(
|
||||
prop: _setting.fullScreenJitter,
|
||||
callback: (_) {
|
||||
context.showSnackBar(l10n.needRestart);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Widget _buildFulScreenRotateQuarter() {
|
||||
// final degrees = List.generate(4, (idx) => '${idx * 90}°').toList();
|
||||
|
||||
Reference in New Issue
Block a user