mirror of
https://github.com/lollipopkit/flutter_server_box.git
synced 2025-12-17 07:14:28 +01:00
new: full screen
This commit is contained in:
@@ -480,6 +480,12 @@ abstract class S {
|
|||||||
/// **'Found {count} update'**
|
/// **'Found {count} update'**
|
||||||
String foundNUpdate(Object count);
|
String foundNUpdate(Object count);
|
||||||
|
|
||||||
|
/// No description provided for @fullScreen.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Full screen mode'**
|
||||||
|
String get fullScreen;
|
||||||
|
|
||||||
/// No description provided for @getPushTokenFailed.
|
/// No description provided for @getPushTokenFailed.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
|
|||||||
@@ -212,6 +212,9 @@ class SDe extends S {
|
|||||||
return 'Update $count gefunden';
|
return 'Update $count gefunden';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get fullScreen => 'Full screen mode';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get getPushTokenFailed => 'Push-Token kann nicht abgerufen werden';
|
String get getPushTokenFailed => 'Push-Token kann nicht abgerufen werden';
|
||||||
|
|
||||||
|
|||||||
@@ -212,6 +212,9 @@ class SEn extends S {
|
|||||||
return 'Found $count update';
|
return 'Found $count update';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get fullScreen => 'Full screen mode';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get getPushTokenFailed => 'Can\'t fetch push token';
|
String get getPushTokenFailed => 'Can\'t fetch push token';
|
||||||
|
|
||||||
|
|||||||
@@ -212,6 +212,9 @@ class SZh extends S {
|
|||||||
return '找到 $count 个更新';
|
return '找到 $count 个更新';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get fullScreen => '全屏模式';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get getPushTokenFailed => '未能获取到推送token';
|
String get getPushTokenFailed => '未能获取到推送token';
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||||
import 'package:toolbox/core/extension/locale.dart';
|
import 'package:toolbox/core/extension/locale.dart';
|
||||||
|
import 'package:toolbox/view/page/full_screen.dart';
|
||||||
|
|
||||||
import 'core/utils/ui.dart';
|
import 'core/utils/ui.dart';
|
||||||
import 'data/res/build_data.dart';
|
import 'data/res/build_data.dart';
|
||||||
@@ -18,6 +19,7 @@ class MyApp extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
setTransparentNavigationBar(context);
|
setTransparentNavigationBar(context);
|
||||||
primaryColor = Color(_setting.primaryColor.fetch()!);
|
primaryColor = Color(_setting.primaryColor.fetch()!);
|
||||||
|
final fullScreen = _setting.fullScreen.fetch()!;
|
||||||
|
|
||||||
return ValueListenableBuilder<int>(
|
return ValueListenableBuilder<int>(
|
||||||
valueListenable: _setting.themeMode.listenable(),
|
valueListenable: _setting.themeMode.listenable(),
|
||||||
@@ -75,7 +77,7 @@ class MyApp extends StatelessWidget {
|
|||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
home: const HomePage(),
|
home: fullScreen ? const FullScreenPage() : const HomePage(),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ Future<T?> showRoundDialog<T>({
|
|||||||
Widget buildSwitch(
|
Widget buildSwitch(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
StoreProperty<bool> prop, {
|
StoreProperty<bool> prop, {
|
||||||
Function(bool)? func,
|
void Function(bool)? func,
|
||||||
}) {
|
}) {
|
||||||
return ValueListenableBuilder(
|
return ValueListenableBuilder(
|
||||||
valueListenable: prop.listenable(),
|
valueListenable: prop.listenable(),
|
||||||
@@ -167,3 +167,8 @@ void showSnippetDialog(
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void hideStatusBar() {
|
||||||
|
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky,
|
||||||
|
overlays: []);
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import '../res/default.dart';
|
|||||||
class SettingStore extends PersistentStore {
|
class SettingStore extends PersistentStore {
|
||||||
StoreProperty<int> get primaryColor => property(
|
StoreProperty<int> get primaryColor => property(
|
||||||
'primaryColor',
|
'primaryColor',
|
||||||
defaultValue: defaultPrimaryColor.value,
|
defaultValue: 4287106639,
|
||||||
);
|
);
|
||||||
|
|
||||||
StoreProperty<int> get serverStatusUpdateInterval => property(
|
StoreProperty<int> get serverStatusUpdateInterval => property(
|
||||||
@@ -66,4 +66,7 @@ class SettingStore extends PersistentStore {
|
|||||||
// Editor theme
|
// Editor theme
|
||||||
StoreProperty<String> get editorTheme =>
|
StoreProperty<String> get editorTheme =>
|
||||||
property('editorTheme', defaultValue: defaultEditorTheme);
|
property('editorTheme', defaultValue: defaultEditorTheme);
|
||||||
|
|
||||||
|
StoreProperty<bool> get fullScreen =>
|
||||||
|
property('fullScreen', defaultValue: false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,6 +64,7 @@
|
|||||||
"font": "Font",
|
"font": "Font",
|
||||||
"fontSize": "Font size",
|
"fontSize": "Font size",
|
||||||
"foundNUpdate": "Found {count} update",
|
"foundNUpdate": "Found {count} update",
|
||||||
|
"fullScreen": "Full screen mode",
|
||||||
"getPushTokenFailed": "Can't fetch push token",
|
"getPushTokenFailed": "Can't fetch push token",
|
||||||
"gettingToken": "Getting token...",
|
"gettingToken": "Getting token...",
|
||||||
"goto": "Go to",
|
"goto": "Go to",
|
||||||
|
|||||||
@@ -64,6 +64,7 @@
|
|||||||
"font": "字体",
|
"font": "字体",
|
||||||
"fontSize": "字体大小",
|
"fontSize": "字体大小",
|
||||||
"foundNUpdate": "找到 {count} 个更新",
|
"foundNUpdate": "找到 {count} 个更新",
|
||||||
|
"fullScreen": "全屏模式",
|
||||||
"getPushTokenFailed": "未能获取到推送token",
|
"getPushTokenFailed": "未能获取到推送token",
|
||||||
"gettingToken": "正在获取Token...",
|
"gettingToken": "正在获取Token...",
|
||||||
"goto": "前往",
|
"goto": "前往",
|
||||||
|
|||||||
327
lib/view/page/full_screen.dart
Normal file
327
lib/view/page/full_screen.dart
Normal file
@@ -0,0 +1,327 @@
|
|||||||
|
import 'package:after_layout/after_layout.dart';
|
||||||
|
import 'package:circle_chart/circle_chart.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||||
|
import 'package:get_it/get_it.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:toolbox/core/route.dart';
|
||||||
|
import 'package:toolbox/data/provider/server.dart';
|
||||||
|
import 'package:toolbox/data/res/ui.dart';
|
||||||
|
import 'package:toolbox/locator.dart';
|
||||||
|
|
||||||
|
import '../../core/analysis.dart';
|
||||||
|
import '../../core/update.dart';
|
||||||
|
import '../../core/utils/ui.dart';
|
||||||
|
import '../../data/model/server/server.dart';
|
||||||
|
import '../../data/model/server/server_private_info.dart';
|
||||||
|
import '../../data/model/server/server_status.dart';
|
||||||
|
import '../../data/res/color.dart';
|
||||||
|
import 'server/detail.dart';
|
||||||
|
import 'server/edit.dart';
|
||||||
|
import 'setting.dart';
|
||||||
|
|
||||||
|
class FullScreenPage extends StatefulWidget {
|
||||||
|
const FullScreenPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_FullScreenPageState createState() => _FullScreenPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _FullScreenPageState extends State<FullScreenPage>
|
||||||
|
with AfterLayoutMixin, AutomaticKeepAliveClientMixin {
|
||||||
|
late S _s;
|
||||||
|
late MediaQueryData _media;
|
||||||
|
late ThemeData _theme;
|
||||||
|
|
||||||
|
final _pageController = PageController(initialPage: 0);
|
||||||
|
final _serverProvider = locator<ServerProvider>();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
hideStatusBar();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didChangeDependencies() {
|
||||||
|
super.didChangeDependencies();
|
||||||
|
_s = S.of(context)!;
|
||||||
|
_media = MediaQuery.of(context);
|
||||||
|
_theme = Theme.of(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
super.build(context);
|
||||||
|
return Scaffold(
|
||||||
|
body: SafeArea(
|
||||||
|
child: RotatedBox(
|
||||||
|
quarterTurns: 3,
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
_buildMain(),
|
||||||
|
Positioned(
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
child: _buildSettingBtn(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildSettingBtn() {
|
||||||
|
return IconButton(
|
||||||
|
onPressed: () => AppRoute(
|
||||||
|
const SettingPage(),
|
||||||
|
'Setting',
|
||||||
|
).go(context),
|
||||||
|
icon: const Icon(Icons.settings, color: Colors.grey));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildMain() {
|
||||||
|
return Consumer<ServerProvider>(builder: (_, pro, __) {
|
||||||
|
if (pro.serverOrder.isEmpty) {
|
||||||
|
return Center(
|
||||||
|
child: TextButton(
|
||||||
|
onPressed: () => AppRoute(
|
||||||
|
const ServerEditPage(),
|
||||||
|
'Add server info page',
|
||||||
|
).go(context),
|
||||||
|
child: Text(
|
||||||
|
_s.addAServer,
|
||||||
|
style: const TextStyle(fontSize: 27),
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return PageView.builder(
|
||||||
|
controller: _pageController,
|
||||||
|
itemCount: pro.servers.length,
|
||||||
|
itemBuilder: (_, idx) {
|
||||||
|
final id = pro.serverOrder[idx];
|
||||||
|
final s = pro.servers[id];
|
||||||
|
if (s == null) {
|
||||||
|
return placeholder;
|
||||||
|
}
|
||||||
|
return _buildRealServerCard(s.status, s.state, s.spi);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildRealServerCard(
|
||||||
|
ServerStatus ss,
|
||||||
|
ServerState cs,
|
||||||
|
ServerPrivateInfo spi,
|
||||||
|
) {
|
||||||
|
final rootDisk = ss.disk.firstWhere((element) => element.loc == '/');
|
||||||
|
|
||||||
|
return InkWell(
|
||||||
|
onTap: () => AppRoute(
|
||||||
|
ServerDetailPage(spi.id),
|
||||||
|
'server detail page',
|
||||||
|
).go(context),
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
Positioned(top: 0, left: 0, right: 0, child: _buildServerCardTitle(ss, cs, spi)),
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
SizedBox(height: _media.size.width * 0.1),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
|
children: [
|
||||||
|
_buildPercentCircle(ss.cpu.usedPercent()),
|
||||||
|
_buildPercentCircle(ss.mem.usedPercent * 100),
|
||||||
|
_buildIOData(
|
||||||
|
'Conn:\n${ss.tcp.maxConn}', 'Fail:\n${ss.tcp.fail}'),
|
||||||
|
_buildIOData(
|
||||||
|
'Total:\n${rootDisk.size}',
|
||||||
|
'Used:\n${rootDisk.usedPercent}%',
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(height: _media.size.width * 0.1),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
|
children: [
|
||||||
|
_buildExplainText('CPU'),
|
||||||
|
_buildExplainText('Mem'),
|
||||||
|
_buildExplainText('Net'),
|
||||||
|
_buildExplainText('Disk'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildServerCardTitle(
|
||||||
|
ServerStatus ss,
|
||||||
|
ServerState cs,
|
||||||
|
ServerPrivateInfo spi,
|
||||||
|
) {
|
||||||
|
return Padding(
|
||||||
|
padding: EdgeInsets.symmetric(vertical: _media.size.width * 0.05),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
spi.name,
|
||||||
|
style:
|
||||||
|
const TextStyle(fontWeight: FontWeight.bold, fontSize: 19),
|
||||||
|
textScaleFactor: 1.0,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
const Icon(
|
||||||
|
Icons.keyboard_arrow_right,
|
||||||
|
size: 21,
|
||||||
|
color: Colors.grey,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
height13,
|
||||||
|
_buildTopRightText(ss, cs),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildTopRightText(ServerStatus ss, ServerState cs) {
|
||||||
|
final topRightStr = _getTopRightStr(
|
||||||
|
cs,
|
||||||
|
ss.temps.first,
|
||||||
|
ss.uptime,
|
||||||
|
ss.failedInfo,
|
||||||
|
);
|
||||||
|
return Text(
|
||||||
|
topRightStr,
|
||||||
|
style: textSize12Grey,
|
||||||
|
textScaleFactor: 1.0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildExplainText(String text) {
|
||||||
|
return SizedBox(
|
||||||
|
width: _media.size.height * 0.2,
|
||||||
|
child: Text(
|
||||||
|
text,
|
||||||
|
style: const TextStyle(fontSize: 13),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
textScaleFactor: 1.0,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String _getTopRightStr(
|
||||||
|
ServerState cs,
|
||||||
|
double? temp,
|
||||||
|
String upTime,
|
||||||
|
String? failedInfo,
|
||||||
|
) {
|
||||||
|
switch (cs) {
|
||||||
|
case ServerState.disconnected:
|
||||||
|
return _s.disconnected;
|
||||||
|
case ServerState.connected:
|
||||||
|
final tempStr = temp == null ? '' : '${temp.toStringAsFixed(1)}°C';
|
||||||
|
final items = [tempStr, upTime];
|
||||||
|
final str = items.where((element) => element.isNotEmpty).join(' | ');
|
||||||
|
if (str.isEmpty) return _s.serverTabLoading;
|
||||||
|
return str;
|
||||||
|
case ServerState.connecting:
|
||||||
|
return _s.serverTabConnecting;
|
||||||
|
case ServerState.failed:
|
||||||
|
if (failedInfo == null) {
|
||||||
|
return _s.serverTabFailed;
|
||||||
|
}
|
||||||
|
if (failedInfo.contains('encypted')) {
|
||||||
|
return _s.serverTabPlzSave;
|
||||||
|
}
|
||||||
|
return failedInfo;
|
||||||
|
default:
|
||||||
|
return _s.serverTabUnkown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildIOData(String up, String down) {
|
||||||
|
final statusTextStyle = TextStyle(
|
||||||
|
fontSize: 13, color: _theme.textTheme.bodyLarge!.color!.withAlpha(177));
|
||||||
|
return SizedBox(
|
||||||
|
width: _media.size.height * 0.23,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 5),
|
||||||
|
Text(
|
||||||
|
up,
|
||||||
|
style: statusTextStyle,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
textScaleFactor: 1.0,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 3),
|
||||||
|
Text(
|
||||||
|
down,
|
||||||
|
style: statusTextStyle,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
textScaleFactor: 1.0,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildPercentCircle(double percent) {
|
||||||
|
if (percent <= 0) percent = 0.01;
|
||||||
|
if (percent >= 100) percent = 99.9;
|
||||||
|
return SizedBox(
|
||||||
|
width: _media.size.height * 0.23,
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
Center(
|
||||||
|
child: CircleChart(
|
||||||
|
progressColor: primaryColor,
|
||||||
|
progressNumber: percent,
|
||||||
|
animationDuration: const Duration(milliseconds: 377),
|
||||||
|
maxNumber: 100,
|
||||||
|
width: _media.size.width * 0.22,
|
||||||
|
height: _media.size.width * 0.22,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Positioned.fill(
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
'${percent.toStringAsFixed(1)}%',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: const TextStyle(fontSize: 15),
|
||||||
|
textScaleFactor: 1.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get wantKeepAlive => true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> afterFirstLayout(BuildContext context) async {
|
||||||
|
await GetIt.I.allReady();
|
||||||
|
await _serverProvider.loadLocalData();
|
||||||
|
await _serverProvider.refreshData();
|
||||||
|
await doUpdate(context);
|
||||||
|
if (!Analysis.enabled) {
|
||||||
|
await Analysis.init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -112,7 +112,6 @@ class _ServerPageState extends State<ServerPage>
|
|||||||
}),
|
}),
|
||||||
itemBuilder: (_, index) => _buildEachServerCard(
|
itemBuilder: (_, index) => _buildEachServerCard(
|
||||||
pro.servers[filtered[index]],
|
pro.servers[filtered[index]],
|
||||||
index,
|
|
||||||
),
|
),
|
||||||
itemCount: filtered.length,
|
itemCount: filtered.length,
|
||||||
);
|
);
|
||||||
@@ -166,7 +165,7 @@ class _ServerPageState extends State<ServerPage>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildEachServerCard(Server? si, int index) {
|
Widget _buildEachServerCard(Server? si) {
|
||||||
if (si == null) {
|
if (si == null) {
|
||||||
return placeholder;
|
return placeholder;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,6 +127,7 @@ class _SettingPageState extends State<SettingPage> {
|
|||||||
_buildAppColor(),
|
_buildAppColor(),
|
||||||
_buildLaunchPage(),
|
_buildLaunchPage(),
|
||||||
_buildCheckUpdate(),
|
_buildCheckUpdate(),
|
||||||
|
_buildFullScreen(),
|
||||||
];
|
];
|
||||||
if (isIOS) {
|
if (isIOS) {
|
||||||
children.add(_buildPushToken());
|
children.add(_buildPushToken());
|
||||||
@@ -660,4 +661,15 @@ class _SettingPageState extends State<SettingPage> {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildFullScreen() {
|
||||||
|
return ListTile(
|
||||||
|
title: Text(_s.fullScreen),
|
||||||
|
trailing: buildSwitch(
|
||||||
|
context,
|
||||||
|
_setting.fullScreen,
|
||||||
|
func: (_) => _showRestartSnackbar(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user