migrate: flutter 3.32

This commit is contained in:
lollipopkit🏳️‍⚧️
2025-05-25 17:05:46 +08:00
parent 7e16d2f159
commit 9547d92ac5
25 changed files with 1380 additions and 825 deletions

View File

@@ -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(

View File

@@ -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(

View File

@@ -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: [

View File

@@ -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(),
);
}),
);
}

View File

@@ -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),
));
);
}
}
}

View File

@@ -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);
},
);
}

View File

@@ -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 {

View File

@@ -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),
)
],