mirror of
https://github.com/lollipopkit/flutter_server_box.git
synced 2025-12-17 07:14:28 +01:00
migrate: flutter 3.32
This commit is contained in:
@@ -90,7 +90,7 @@ final class _BackupPageState extends State<BackupPage>
|
||||
trailing: const Icon(Icons.save),
|
||||
onTap: () async {
|
||||
final path = await Backup.backup();
|
||||
await Pfs.share(path: path);
|
||||
await Pfs.sharePaths(paths: [path]);
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
|
||||
@@ -121,7 +121,7 @@ Widget _buildLineChart(
|
||||
lineTouchData: LineTouchData(
|
||||
touchTooltipData: LineTouchTooltipData(
|
||||
tooltipPadding: const EdgeInsets.all(5),
|
||||
tooltipRoundedRadius: 8,
|
||||
tooltipBorderRadius: BorderRadius.circular(8),
|
||||
getTooltipItems: (List<LineBarSpot> touchedSpots) {
|
||||
return touchedSpots.map((e) {
|
||||
return LineTooltipItem(
|
||||
|
||||
@@ -113,9 +113,7 @@ class _ServerDetailPageState extends State<ServerDetailPage> with SingleTickerPr
|
||||
|
||||
return Scaffold(
|
||||
appBar: _buildAppBar(si),
|
||||
body: AutoMultiList(
|
||||
children: children,
|
||||
),
|
||||
body: SafeArea(child: AutoMultiList(children: children)),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -716,7 +714,7 @@ class _ServerDetailPageState extends State<ServerDetailPage> with SingleTickerPr
|
||||
|
||||
Widget _buildTemperatureItem(String key, double? val) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 17, vertical: 5),
|
||||
padding: const EdgeInsets.only(left: 3, right: 17, top: 5, bottom: 5),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
|
||||
@@ -61,7 +61,6 @@ class _ServerPageState extends State<ServerPage> with AutomaticKeepAliveClientMi
|
||||
|
||||
final _scrollController = ScrollController();
|
||||
final _autoHideCtrl = AutoHideController();
|
||||
final _splitViewCtrl = SplitViewController();
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
@@ -168,10 +167,10 @@ class _ServerPageState extends State<ServerPage> with AutomaticKeepAliveClientMi
|
||||
|
||||
// Calculate number of columns based on available width
|
||||
final columnsCount = math.max(1, (_media.size.width / UIs.columnWidth).floor());
|
||||
|
||||
|
||||
// Calculate number of rows needed
|
||||
final rowCount = (filtered.length + columnsCount - 1) ~/ columnsCount;
|
||||
|
||||
|
||||
return ListView.builder(
|
||||
controller: _scrollController,
|
||||
padding: padding,
|
||||
@@ -179,27 +178,24 @@ class _ServerPageState extends State<ServerPage> with AutomaticKeepAliveClientMi
|
||||
itemBuilder: (_, rowIndex) {
|
||||
// Bottom space
|
||||
if (rowIndex == rowCount) return UIs.height77;
|
||||
|
||||
|
||||
// Create a row of server cards
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4.0),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: List.generate(columnsCount, (colIndex) {
|
||||
final index = rowIndex * columnsCount + colIndex;
|
||||
if (index >= filtered.length) return Expanded(child: Container());
|
||||
|
||||
final vnode = ServerProvider.pick(id: filtered[index]);
|
||||
if (vnode == null) return Expanded(child: UIs.placeholder);
|
||||
|
||||
return Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4.0),
|
||||
child: vnode.listenVal(_buildEachServerCard),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: List.generate(columnsCount, (colIndex) {
|
||||
final index = rowIndex * columnsCount + colIndex;
|
||||
if (index >= filtered.length) return Expanded(child: Container());
|
||||
|
||||
final vnode = ServerProvider.pick(id: filtered[index]);
|
||||
if (vnode == null) return Expanded(child: UIs.placeholder);
|
||||
|
||||
return Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4.0),
|
||||
child: vnode.listenVal(_buildEachServerCard),
|
||||
),
|
||||
);
|
||||
}),
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -307,16 +303,18 @@ class _ServerPageState extends State<ServerPage> with AutomaticKeepAliveClientMi
|
||||
)
|
||||
];
|
||||
|
||||
final width = (_media.size.width - _cardPad) / children.length;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 9),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: children.map((e) {
|
||||
if (width == 0) return e;
|
||||
return SizedBox(width: width, child: e);
|
||||
}).toList(),
|
||||
),
|
||||
child: LayoutBuilder(builder: (_, cons) {
|
||||
final width = (cons.maxWidth - _cardPad) / children.length;
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: children.map((e) {
|
||||
if (width == 0) return e;
|
||||
return SizedBox(width: width, child: e);
|
||||
}).toList(),
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -33,10 +33,10 @@ extension _Actions on _ServerPageState {
|
||||
flip: !cardStatus.value.flip,
|
||||
);
|
||||
} else {
|
||||
_splitViewCtrl.replace(ServerEditPage(
|
||||
key: ValueKey(srv.spi.id),
|
||||
ServerEditPage.route.go(
|
||||
context,
|
||||
args: SpiRequiredArgs(srv.spi),
|
||||
));
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,15 +100,15 @@ extension _Operation on _ServerPageState {
|
||||
|
||||
void _onTapEdit(Server srv) {
|
||||
if (srv.canViewDetails) {
|
||||
_splitViewCtrl.replace(ServerDetailPage(
|
||||
key: ValueKey(srv.spi.id),
|
||||
args: SpiRequiredArgs(srv.spi),
|
||||
));
|
||||
ServerDetailPage.route.go(
|
||||
context,
|
||||
SpiRequiredArgs(srv.spi),
|
||||
);
|
||||
} else {
|
||||
_splitViewCtrl.replace(ServerEditPage(
|
||||
key: ValueKey(srv.spi.id),
|
||||
ServerEditPage.route.go(
|
||||
context,
|
||||
args: SpiRequiredArgs(srv.spi),
|
||||
));
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import 'package:fl_lib/fl_lib.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:server_box/data/res/store.dart';
|
||||
import 'package:flutter_reorderable_grid_view/widgets/widgets.dart';
|
||||
|
||||
import 'package:server_box/data/model/server/snippet.dart';
|
||||
import 'package:server_box/data/provider/snippet.dart';
|
||||
@@ -23,6 +21,9 @@ class _SnippetListPageState extends State<SnippetListPage> with AutomaticKeepAli
|
||||
final _tag = ''.vn;
|
||||
final _splitViewCtrl = SplitViewController();
|
||||
|
||||
static final _desiredItemHeight = isDesktop ? 113 : 97;
|
||||
static final _childAspectRatio = UIs.columnWidth / _desiredItemHeight;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
@@ -87,43 +88,16 @@ class _SnippetListPageState extends State<SnippetListPage> with AutomaticKeepAli
|
||||
? snippets
|
||||
: snippets.where((e) => e.tags?.contains(tag) ?? false).toList();
|
||||
|
||||
final generatedChildren = List.generate(
|
||||
filtered.length,
|
||||
(idx) {
|
||||
final snippet = filtered.elementAtOrNull(idx);
|
||||
if (snippet == null) return UIs.placeholder;
|
||||
return Container(
|
||||
key: ValueKey(snippet.name),
|
||||
child: _buildSnippetItem(snippet),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
return ReorderableBuilder(
|
||||
children: generatedChildren,
|
||||
onReorder: (ReorderedListFunction reorderedListFunction) {
|
||||
setState(() {
|
||||
final newFiltered = reorderedListFunction(filtered) as List<Snippet>;
|
||||
snippets.moveByItem(
|
||||
0,
|
||||
0,
|
||||
filtered: filtered,
|
||||
onMove: (p0) {
|
||||
Stores.setting.snippetOrder.put(newFiltered.map((e) => e.name).toList());
|
||||
},
|
||||
);
|
||||
SnippetProvider.snippets.notify();
|
||||
});
|
||||
},
|
||||
builder: (children) {
|
||||
return GridView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 9),
|
||||
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
|
||||
maxCrossAxisExtent: 330,
|
||||
childAspectRatio: 3.4,
|
||||
),
|
||||
children: children,
|
||||
);
|
||||
return GridView.builder(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 9),
|
||||
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
|
||||
maxCrossAxisExtent: UIs.columnWidth,
|
||||
childAspectRatio: _childAspectRatio,
|
||||
),
|
||||
itemCount: filtered.length,
|
||||
itemBuilder: (context, index) {
|
||||
final snippet = filtered[index];
|
||||
return _buildSnippetItem(snippet);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -95,8 +95,8 @@ class _LocalFilePageState extends State<LocalFilePage> with AutomaticKeepAliveCl
|
||||
return FutureWidget(
|
||||
future: getEntities(),
|
||||
loading: UIs.placeholder,
|
||||
success: (items_) {
|
||||
final items = items_ ?? [];
|
||||
success: (items) {
|
||||
items ??= [];
|
||||
final len = _path.canBack ? items.length + 1 : items.length;
|
||||
return ListView.builder(
|
||||
itemCount: len,
|
||||
@@ -115,37 +115,17 @@ class _LocalFilePageState extends State<LocalFilePage> with AutomaticKeepAliveCl
|
||||
|
||||
if (_path.canBack) index--;
|
||||
|
||||
final item = items[index];
|
||||
final item = items![index];
|
||||
final file = item.$1;
|
||||
final fileName = file.path.split('/').last;
|
||||
final stat = item.$2;
|
||||
final isDir = stat.type == FileSystemEntityType.directory;
|
||||
|
||||
return CardX(
|
||||
child: ListTile(
|
||||
leading: isDir ? const Icon(Icons.folder_open) : const Icon(Icons.insert_drive_file),
|
||||
title: Text(fileName),
|
||||
subtitle: isDir ? null : Text(stat.size.bytes2Str, style: UIs.textGrey),
|
||||
trailing: Text(
|
||||
stat.modified.ymdhms(),
|
||||
style: UIs.textGrey,
|
||||
),
|
||||
onLongPress: () {
|
||||
if (isDir) {
|
||||
_showDirActionDialog(file);
|
||||
return;
|
||||
}
|
||||
_showFileActionDialog(file);
|
||||
},
|
||||
onTap: () {
|
||||
if (!isDir) {
|
||||
_showFileActionDialog(file);
|
||||
return;
|
||||
}
|
||||
_path.update(fileName);
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
return _buildItem(
|
||||
file: file,
|
||||
fileName: fileName,
|
||||
stat: stat,
|
||||
isDir: isDir,
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -153,6 +133,66 @@ class _LocalFilePageState extends State<LocalFilePage> with AutomaticKeepAliveCl
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildItem({
|
||||
required FileSystemEntity file,
|
||||
required String fileName,
|
||||
required FileStat stat,
|
||||
required bool isDir,
|
||||
}) {
|
||||
return CardX(
|
||||
child: ListTile(
|
||||
leading: isDir ? const Icon(Icons.folder_open) : const Icon(Icons.insert_drive_file),
|
||||
title: Text(fileName),
|
||||
subtitle: isDir ? null : Text(stat.size.bytes2Str, style: UIs.textGrey),
|
||||
trailing: Text(
|
||||
stat.modified.ymdhms(),
|
||||
style: UIs.textGrey,
|
||||
),
|
||||
onLongPress: () {
|
||||
if (isDir) {
|
||||
_showDirActionDialog(file);
|
||||
return;
|
||||
}
|
||||
_showFileActionDialog(file);
|
||||
},
|
||||
onTap: () {
|
||||
if (!isDir) {
|
||||
_showFileActionDialog(file);
|
||||
return;
|
||||
}
|
||||
_path.update(fileName);
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMissionBtn() {
|
||||
return IconButton(
|
||||
icon: const Icon(Icons.downloading),
|
||||
onPressed: () => SftpMissionPage.route.go(context),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSortBtn() {
|
||||
return _sortType.listenVal(
|
||||
(value) {
|
||||
return PopupMenuButton<_SortType>(
|
||||
icon: const Icon(Icons.sort),
|
||||
itemBuilder: (_) => _SortType.values.map((e) => e.menuItem).toList(),
|
||||
onSelected: (value) {
|
||||
_sortType.value = value;
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool get wantKeepAlive => true;
|
||||
}
|
||||
|
||||
extension _Actions on _LocalFilePageState {
|
||||
Future<void> _showDirActionDialog(FileSystemEntity file) async {
|
||||
context.showRoundDialog(
|
||||
child: Column(
|
||||
@@ -180,7 +220,7 @@ class _LocalFilePageState extends State<LocalFilePage> with AutomaticKeepAliveCl
|
||||
}
|
||||
|
||||
Future<void> _showFileActionDialog(FileSystemEntity file) async {
|
||||
final fileName = file.path.split('/').last;
|
||||
final fileName = file.path.split('/').lastOrNull ?? '';
|
||||
if (isPickFile) {
|
||||
context.showRoundDialog(
|
||||
title: libL10n.file,
|
||||
@@ -198,35 +238,12 @@ class _LocalFilePageState extends State<LocalFilePage> with AutomaticKeepAliveCl
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Btn.tile(
|
||||
icon: const Icon(Icons.edit),
|
||||
text: libL10n.edit,
|
||||
onTap: () async {
|
||||
context.pop();
|
||||
final stat = await file.stat();
|
||||
if (stat.size > Miscs.editorMaxSize) {
|
||||
context.showRoundDialog(
|
||||
title: libL10n.attention,
|
||||
child: Text(l10n.fileTooLarge(fileName, stat.size, '1m')),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
await EditorPage.route.go(
|
||||
context,
|
||||
args: EditorPageArgs(
|
||||
path: file.absolute.path,
|
||||
onSave: (_) {
|
||||
context.showSnackBar(l10n.saved);
|
||||
setState(() {});
|
||||
},
|
||||
closeAfterSave: SettingStore.instance.closeAfterSave.fetch(),
|
||||
softWrap: SettingStore.instance.editorSoftWrap.fetch(),
|
||||
enableHighlight: SettingStore.instance.editorHighlight.fetch(),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
if (isMobile)
|
||||
Btn.tile(
|
||||
icon: const Icon(Icons.edit),
|
||||
text: libL10n.edit,
|
||||
onTap: () => _onTapEdit(file, fileName),
|
||||
),
|
||||
Btn.tile(
|
||||
icon: const Icon(Icons.abc),
|
||||
text: libL10n.rename,
|
||||
@@ -246,42 +263,13 @@ class _LocalFilePageState extends State<LocalFilePage> with AutomaticKeepAliveCl
|
||||
Btn.tile(
|
||||
icon: const Icon(Icons.upload),
|
||||
text: l10n.upload,
|
||||
onTap: () async {
|
||||
context.pop();
|
||||
|
||||
final spi = await context.showPickSingleDialog<Spi>(
|
||||
title: libL10n.select,
|
||||
items: ServerProvider.serverOrder.value
|
||||
.map((e) => ServerProvider.pick(id: e)?.value.spi)
|
||||
.whereType<Spi>()
|
||||
.toList(),
|
||||
display: (e) => e.name,
|
||||
);
|
||||
if (spi == null) return;
|
||||
|
||||
final args = SftpPageArgs(
|
||||
spi: spi,
|
||||
isSelect: true,
|
||||
);
|
||||
final remotePath = await SftpPage.route.go(context, args);
|
||||
if (remotePath == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
SftpProvider.add(SftpReq(
|
||||
spi,
|
||||
'$remotePath/$fileName',
|
||||
file.absolute.path,
|
||||
SftpReqType.upload,
|
||||
));
|
||||
context.showSnackBar(l10n.added2List);
|
||||
},
|
||||
onTap: () => _onTapUpload(file, fileName),
|
||||
),
|
||||
Btn.tile(
|
||||
icon: const Icon(Icons.open_in_new),
|
||||
text: libL10n.open,
|
||||
onTap: () {
|
||||
Pfs.share(path: file.absolute.path);
|
||||
Pfs.sharePaths(paths: [file.absolute.path]);
|
||||
},
|
||||
),
|
||||
],
|
||||
@@ -303,7 +291,7 @@ class _LocalFilePageState extends State<LocalFilePage> with AutomaticKeepAliveCl
|
||||
final newPath = '${file.parent.path}${Pfs.seperator}$newName';
|
||||
await context.showLoadingDialog(fn: () => file.rename(newPath));
|
||||
|
||||
setState(() {});
|
||||
setStateSafe(() {});
|
||||
}
|
||||
|
||||
context.showRoundDialog(
|
||||
@@ -335,35 +323,70 @@ class _LocalFilePageState extends State<LocalFilePage> with AutomaticKeepAliveCl
|
||||
context.showSnackBar('${libL10n.fail}:\n$e');
|
||||
return;
|
||||
}
|
||||
setState(() {});
|
||||
setStateSafe(() {});
|
||||
},
|
||||
).toList,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildMissionBtn() {
|
||||
return IconButton(
|
||||
icon: const Icon(Icons.downloading),
|
||||
onPressed: () => SftpMissionPage.route.go(context),
|
||||
extension _OnTapFile on _LocalFilePageState {
|
||||
void _onTapEdit(FileSystemEntity file, String fileName) async {
|
||||
context.pop();
|
||||
final stat = await file.stat();
|
||||
if (stat.size > Miscs.editorMaxSize) {
|
||||
context.showRoundDialog(
|
||||
title: libL10n.attention,
|
||||
child: Text(l10n.fileTooLarge(fileName, stat.size, '1m')),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
await EditorPage.route.go(
|
||||
context,
|
||||
args: EditorPageArgs(
|
||||
path: file.absolute.path,
|
||||
onSave: (_) {
|
||||
context.showSnackBar(l10n.saved);
|
||||
setStateSafe(() {});
|
||||
},
|
||||
closeAfterSave: SettingStore.instance.closeAfterSave.fetch(),
|
||||
softWrap: SettingStore.instance.editorSoftWrap.fetch(),
|
||||
enableHighlight: SettingStore.instance.editorHighlight.fetch(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSortBtn() {
|
||||
return _sortType.listenVal(
|
||||
(value) {
|
||||
return PopupMenuButton<_SortType>(
|
||||
icon: const Icon(Icons.sort),
|
||||
itemBuilder: (_) => _SortType.values.map((e) => e.menuItem).toList(),
|
||||
onSelected: (value) {
|
||||
_sortType.value = value;
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
void _onTapUpload(FileSystemEntity file, String fileName) async {
|
||||
context.pop();
|
||||
|
||||
@override
|
||||
bool get wantKeepAlive => true;
|
||||
final spi = await context.showPickSingleDialog<Spi>(
|
||||
title: libL10n.select,
|
||||
items: ServerProvider.serverOrder.value
|
||||
.map((e) => ServerProvider.pick(id: e)?.value.spi)
|
||||
.whereType<Spi>()
|
||||
.toList(),
|
||||
display: (e) => e.name,
|
||||
);
|
||||
if (spi == null) return;
|
||||
|
||||
final args = SftpPageArgs(
|
||||
spi: spi,
|
||||
isSelect: true,
|
||||
);
|
||||
final remotePath = await SftpPage.route.go(context, args);
|
||||
if (remotePath == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
SftpProvider.add(SftpReq(
|
||||
spi,
|
||||
'$remotePath/$fileName',
|
||||
file.absolute.path,
|
||||
SftpReqType.upload,
|
||||
));
|
||||
context.showSnackBar(l10n.added2List);
|
||||
}
|
||||
}
|
||||
|
||||
enum _SortType {
|
||||
|
||||
@@ -128,7 +128,7 @@ class _SftpMissionPageState extends State<SftpMissionPage> {
|
||||
icon: const Icon(Icons.file_open),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () => Pfs.share(path: status.req.localPath),
|
||||
onPressed: () => Pfs.sharePaths(paths: [status.req.localPath]),
|
||||
icon: const Icon(Icons.open_in_new),
|
||||
)
|
||||
],
|
||||
|
||||
@@ -52,7 +52,7 @@ class ServerFuncBtns extends StatelessWidget {
|
||||
if (btns.isEmpty) return UIs.placeholder;
|
||||
|
||||
return SizedBox(
|
||||
height: 70,
|
||||
height: 74,
|
||||
child: ListView.builder(
|
||||
itemCount: btns.length,
|
||||
scrollDirection: Axis.horizontal,
|
||||
|
||||
Reference in New Issue
Block a user