mirror of
https://github.com/lollipopkit/flutter_server_box.git
synced 2025-12-17 07:14:28 +01:00
fmt
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
final _dockerImageReg = RegExp(r'(\S+) +(\S+) +(\S+) +(.+) +(\S+)');
|
final _dockerImageReg = RegExp(r'(\S+) +(\S+) +(\S+) +(.+) +(\S+)');
|
||||||
|
|
||||||
class DockerImage {
|
class DockerImage {
|
||||||
final String repo;
|
final String repo;
|
||||||
final String tag;
|
final String tag;
|
||||||
@@ -44,4 +45,3 @@ class DockerImage {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ class DockerProvider extends BusyProvider {
|
|||||||
final verRaw = await client!.run('docker version'.withLangExport).string;
|
final verRaw = await client!.run('docker version'.withLangExport).string;
|
||||||
if (verRaw.contains(_dockerNotFound)) {
|
if (verRaw.contains(_dockerNotFound)) {
|
||||||
error = DockerErr(type: DockerErrType.notInstalled);
|
error = DockerErr(type: DockerErrType.notInstalled);
|
||||||
notifyListeners();
|
setBusyState(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,6 +65,7 @@ class DockerProvider extends BusyProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
setBusyState();
|
||||||
final cmd = _wrap(_dockerPS);
|
final cmd = _wrap(_dockerPS);
|
||||||
|
|
||||||
// run docker ps
|
// run docker ps
|
||||||
@@ -97,7 +98,7 @@ class DockerProvider extends BusyProvider {
|
|||||||
error = DockerErr(type: DockerErrType.unknown, message: e.toString());
|
error = DockerErr(type: DockerErrType.unknown, message: e.toString());
|
||||||
rethrow;
|
rethrow;
|
||||||
} finally {
|
} finally {
|
||||||
notifyListeners();
|
setBusyState(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,7 +146,8 @@ class DockerProvider extends BusyProvider {
|
|||||||
|
|
||||||
if (code != 0) {
|
if (code != 0) {
|
||||||
setBusyState(false);
|
setBusyState(false);
|
||||||
return DockerErr(type: DockerErrType.unknown, message: errs.join('\n').trim());
|
return DockerErr(
|
||||||
|
type: DockerErrType.unknown, message: errs.join('\n').trim());
|
||||||
}
|
}
|
||||||
await refresh();
|
await refresh();
|
||||||
setBusyState(false);
|
setBusyState(false);
|
||||||
|
|||||||
@@ -59,23 +59,28 @@ class _AptManagePageState extends State<AptManagePage>
|
|||||||
}
|
}
|
||||||
|
|
||||||
_aptProvider.init(
|
_aptProvider.init(
|
||||||
si.client!,
|
si.client!,
|
||||||
si.status.sysVer.dist,
|
si.status.sysVer.dist,
|
||||||
() =>
|
() => scrollController.jumpTo(scrollController.position.maxScrollExtent),
|
||||||
scrollController.jumpTo(scrollController.position.maxScrollExtent),
|
() => scrollControllerUpdate
|
||||||
() => scrollControllerUpdate
|
.jumpTo(scrollController.position.maxScrollExtent),
|
||||||
.jumpTo(scrollController.position.maxScrollExtent),
|
onPwdRequest,
|
||||||
onPwdRequest,
|
widget.spi.user,
|
||||||
widget.spi.user);
|
);
|
||||||
_aptProvider.refreshInstalled();
|
_aptProvider.refreshInstalled();
|
||||||
}
|
}
|
||||||
|
|
||||||
void onSubmitted() {
|
void onSubmitted() {
|
||||||
if (textController.text == '') {
|
if (textController.text == '') {
|
||||||
showRoundDialog(context, s.attention, Text(s.fieldMustNotEmpty), [
|
showRoundDialog(
|
||||||
TextButton(
|
context,
|
||||||
onPressed: () => Navigator.of(context).pop(), child: Text(s.ok)),
|
s.attention,
|
||||||
]);
|
Text(s.fieldMustNotEmpty),
|
||||||
|
[
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(), child: Text(s.ok)),
|
||||||
|
],
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
@@ -84,31 +89,32 @@ class _AptManagePageState extends State<AptManagePage>
|
|||||||
Future<String> onPwdRequest() async {
|
Future<String> onPwdRequest() async {
|
||||||
if (!mounted) return '';
|
if (!mounted) return '';
|
||||||
await showRoundDialog(
|
await showRoundDialog(
|
||||||
context,
|
context,
|
||||||
widget.spi.user,
|
widget.spi.user,
|
||||||
TextField(
|
TextField(
|
||||||
controller: textController,
|
controller: textController,
|
||||||
keyboardType: TextInputType.visiblePassword,
|
keyboardType: TextInputType.visiblePassword,
|
||||||
obscureText: true,
|
obscureText: true,
|
||||||
onSubmitted: (_) => onSubmitted(),
|
onSubmitted: (_) => onSubmitted(),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: s.pwd,
|
labelText: s.pwd,
|
||||||
),
|
|
||||||
),
|
),
|
||||||
[
|
),
|
||||||
TextButton(
|
[
|
||||||
onPressed: () {
|
TextButton(
|
||||||
Navigator.of(context).pop();
|
onPressed: () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
Navigator.of(context).pop();
|
||||||
child: Text(s.cancel)),
|
},
|
||||||
TextButton(
|
child: Text(s.cancel)),
|
||||||
onPressed: () => onSubmitted(),
|
TextButton(
|
||||||
child: Text(
|
onPressed: () => onSubmitted(),
|
||||||
s.ok,
|
child: Text(
|
||||||
style: const TextStyle(color: Colors.red),
|
s.ok,
|
||||||
)),
|
style: const TextStyle(color: Colors.red),
|
||||||
]);
|
)),
|
||||||
|
],
|
||||||
|
);
|
||||||
return textController.text.trim();
|
return textController.text.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,11 +144,12 @@ class _AptManagePageState extends State<AptManagePage>
|
|||||||
padding: const EdgeInsets.all(17),
|
padding: const EdgeInsets.all(17),
|
||||||
child: RoundRectCard(
|
child: RoundRectCard(
|
||||||
SingleChildScrollView(
|
SingleChildScrollView(
|
||||||
padding: const EdgeInsets.all(17),
|
padding: const EdgeInsets.all(17),
|
||||||
child: Text(
|
child: Text(
|
||||||
apt.error!,
|
apt.error!,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
)),
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -154,17 +161,17 @@ class _AptManagePageState extends State<AptManagePage>
|
|||||||
}
|
}
|
||||||
if (apt.updateLog != null && apt.upgradeable == null) {
|
if (apt.updateLog != null && apt.upgradeable == null) {
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: _media.size.height -
|
height:
|
||||||
_media.padding.top -
|
_media.size.height - _media.padding.top - _media.padding.bottom,
|
||||||
_media.padding.bottom,
|
child: ConstrainedBox(
|
||||||
child: ConstrainedBox(
|
constraints: const BoxConstraints.expand(),
|
||||||
constraints: const BoxConstraints.expand(),
|
child: SingleChildScrollView(
|
||||||
child: SingleChildScrollView(
|
padding: const EdgeInsets.all(18),
|
||||||
padding: const EdgeInsets.all(18),
|
controller: scrollControllerUpdate,
|
||||||
controller: scrollControllerUpdate,
|
child: Text(apt.updateLog!),
|
||||||
child: Text(apt.updateLog!),
|
),
|
||||||
),
|
),
|
||||||
));
|
);
|
||||||
}
|
}
|
||||||
return ListView(
|
return ListView(
|
||||||
padding: const EdgeInsets.all(13),
|
padding: const EdgeInsets.all(13),
|
||||||
@@ -216,23 +223,25 @@ class _AptManagePageState extends State<AptManagePage>
|
|||||||
SizedBox(
|
SizedBox(
|
||||||
height: _media.size.height * 0.73,
|
height: _media.size.height * 0.73,
|
||||||
child: ListView(
|
child: ListView(
|
||||||
controller: scrollController,
|
controller: scrollController,
|
||||||
children: apt.upgradeable!
|
children: apt.upgradeable!
|
||||||
.map((e) => _buildUpdateItem(e, apt))
|
.map((e) => _buildUpdateItem(e, apt))
|
||||||
.toList()),
|
.toList(),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
: [
|
: [
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: _media.size.height * 0.7,
|
height: _media.size.height * 0.7,
|
||||||
child: ConstrainedBox(
|
child: ConstrainedBox(
|
||||||
constraints: const BoxConstraints.expand(),
|
constraints: const BoxConstraints.expand(),
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
padding: const EdgeInsets.all(18),
|
padding: const EdgeInsets.all(18),
|
||||||
controller: scrollController,
|
controller: scrollController,
|
||||||
child: Text(apt.upgradeLog!),
|
child: Text(apt.upgradeLog!),
|
||||||
),
|
),
|
||||||
))
|
),
|
||||||
|
)
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -54,8 +54,12 @@ class BackupPage extends StatelessWidget {
|
|||||||
const SizedBox(height: 7),
|
const SizedBox(height: 7),
|
||||||
const Divider(),
|
const Divider(),
|
||||||
const SizedBox(height: 7),
|
const SizedBox(height: 7),
|
||||||
_buildCard(s.backup, Icons.file_upload, media,
|
_buildCard(
|
||||||
() => _showExportDialog(context, s))
|
s.backup,
|
||||||
|
Icons.file_upload,
|
||||||
|
media,
|
||||||
|
() => _showExportDialog(context, s),
|
||||||
|
)
|
||||||
],
|
],
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
@@ -91,60 +95,66 @@ class BackupPage extends StatelessWidget {
|
|||||||
|
|
||||||
Future<void> _showExportDialog(BuildContext context, S s) async {
|
Future<void> _showExportDialog(BuildContext context, S s) async {
|
||||||
final exportFieldController = TextEditingController()
|
final exportFieldController = TextEditingController()
|
||||||
..text = _diyEncrtpt(json.encode(Backup(
|
..text = _diyEncrtpt(
|
||||||
backupFormatVersion,
|
json.encode(
|
||||||
DateTime.now().toString().split('.').first,
|
Backup(
|
||||||
server.fetch(),
|
backupFormatVersion,
|
||||||
snippet.fetch(),
|
DateTime.now().toString().split('.').first,
|
||||||
privateKey.fetch(),
|
server.fetch(),
|
||||||
setting.primaryColor.fetch() ?? Colors.pinkAccent.value,
|
snippet.fetch(),
|
||||||
setting.serverStatusUpdateInterval.fetch() ?? 2,
|
privateKey.fetch(),
|
||||||
setting.launchPage.fetch() ?? 0,
|
setting.primaryColor.fetch() ?? Colors.pinkAccent.value,
|
||||||
)));
|
setting.serverStatusUpdateInterval.fetch() ?? 2,
|
||||||
await showRoundDialog(
|
setting.launchPage.fetch() ?? 0,
|
||||||
context,
|
|
||||||
s.export,
|
|
||||||
TextField(
|
|
||||||
decoration: const InputDecoration(
|
|
||||||
labelText: 'JSON',
|
|
||||||
),
|
),
|
||||||
maxLines: 7,
|
|
||||||
controller: exportFieldController,
|
|
||||||
),
|
),
|
||||||
[
|
);
|
||||||
TextButton(
|
await showRoundDialog(
|
||||||
child: Text(s.copy),
|
context,
|
||||||
onPressed: () {
|
s.export,
|
||||||
Clipboard.setData(
|
TextField(
|
||||||
ClipboardData(text: exportFieldController.text));
|
decoration: const InputDecoration(
|
||||||
Navigator.pop(context);
|
labelText: 'JSON',
|
||||||
},
|
),
|
||||||
),
|
maxLines: 7,
|
||||||
]);
|
controller: exportFieldController,
|
||||||
|
),
|
||||||
|
[
|
||||||
|
TextButton(
|
||||||
|
child: Text(s.copy),
|
||||||
|
onPressed: () {
|
||||||
|
Clipboard.setData(ClipboardData(text: exportFieldController.text));
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _showImportDialog(BuildContext context, S s) async {
|
Future<void> _showImportDialog(BuildContext context, S s) async {
|
||||||
final importFieldController = TextEditingController();
|
final importFieldController = TextEditingController();
|
||||||
await showRoundDialog(
|
await showRoundDialog(
|
||||||
context,
|
context,
|
||||||
s.import,
|
s.import,
|
||||||
TextField(
|
TextField(
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
labelText: 'JSON',
|
labelText: 'JSON',
|
||||||
),
|
|
||||||
maxLines: 3,
|
|
||||||
controller: importFieldController,
|
|
||||||
),
|
),
|
||||||
[
|
maxLines: 3,
|
||||||
TextButton(
|
controller: importFieldController,
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
),
|
||||||
child: Text(s.cancel)),
|
[
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () async =>
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
await _import(importFieldController.text.trim(), context, s),
|
child: Text(s.cancel),
|
||||||
child: const Text('GO'),
|
),
|
||||||
)
|
TextButton(
|
||||||
]);
|
onPressed: () async =>
|
||||||
|
await _import(importFieldController.text.trim(), context, s),
|
||||||
|
child: const Text('GO'),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _import(String text, BuildContext context, S s) async {
|
Future<void> _import(String text, BuildContext context, S s) async {
|
||||||
@@ -165,32 +175,36 @@ class BackupPage extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await showRoundDialog(
|
await showRoundDialog(
|
||||||
context, s.attention, Text(s.restoreSureWithDate(backup.date)), [
|
context,
|
||||||
TextButton(
|
s.attention,
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
Text(s.restoreSureWithDate(backup.date)),
|
||||||
child: Text(s.cancel),
|
[
|
||||||
),
|
TextButton(
|
||||||
TextButton(
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
onPressed: () async {
|
child: Text(s.cancel),
|
||||||
for (final s in backup.snippets) {
|
),
|
||||||
snippet.put(s);
|
TextButton(
|
||||||
}
|
onPressed: () async {
|
||||||
for (final s in backup.spis) {
|
for (final s in backup.snippets) {
|
||||||
server.put(s);
|
snippet.put(s);
|
||||||
}
|
}
|
||||||
for (final s in backup.keys) {
|
for (final s in backup.spis) {
|
||||||
privateKey.put(s);
|
server.put(s);
|
||||||
}
|
}
|
||||||
setting.primaryColor.put(backup.primaryColor);
|
for (final s in backup.keys) {
|
||||||
setting.serverStatusUpdateInterval
|
privateKey.put(s);
|
||||||
.put(backup.serverStatusUpdateInterval);
|
}
|
||||||
setting.launchPage.put(backup.launchPage);
|
setting.primaryColor.put(backup.primaryColor);
|
||||||
Navigator.of(context).pop();
|
setting.serverStatusUpdateInterval
|
||||||
showSnackBar(context, Text(s.restoreSuccess));
|
.put(backup.serverStatusUpdateInterval);
|
||||||
},
|
setting.launchPage.put(backup.launchPage);
|
||||||
child: Text(s.ok),
|
Navigator.of(context).pop();
|
||||||
),
|
showSnackBar(context, Text(s.restoreSuccess));
|
||||||
]);
|
},
|
||||||
|
child: Text(s.ok),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
showSnackBar(context, Text(s.invalidJson));
|
showSnackBar(context, Text(s.invalidJson));
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -45,14 +45,17 @@ class _ConvertPageState extends State<ConvertPage>
|
|||||||
super.build(context);
|
super.build(context);
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 7),
|
padding: const EdgeInsets.symmetric(horizontal: 7),
|
||||||
controller: ScrollController(),
|
controller: ScrollController(),
|
||||||
child: Column(children: [
|
child: Column(
|
||||||
|
children: [
|
||||||
const SizedBox(height: 13),
|
const SizedBox(height: 13),
|
||||||
_buildInputTop(),
|
_buildInputTop(),
|
||||||
_buildTypeOption(),
|
_buildTypeOption(),
|
||||||
_buildResult(),
|
_buildResult(),
|
||||||
])),
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
floatingActionButton: FloatingActionButton(
|
floatingActionButton: FloatingActionButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
try {
|
try {
|
||||||
@@ -119,7 +122,8 @@ class _ConvertPageState extends State<ConvertPage>
|
|||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
foregroundColor: MaterialStateProperty.all(primaryColor)),
|
foregroundColor: MaterialStateProperty.all(primaryColor),
|
||||||
|
),
|
||||||
child: Icon(Icons.copy, semanticLabel: s.copy),
|
child: Icon(Icons.copy, semanticLabel: s.copy),
|
||||||
onPressed: () => Clipboard.setData(
|
onPressed: () => Clipboard.setData(
|
||||||
ClipboardData(
|
ClipboardData(
|
||||||
@@ -136,13 +140,15 @@ class _ConvertPageState extends State<ConvertPage>
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Text(typeOption[_typeOptionIndex],
|
Text(
|
||||||
textScaleFactor: 1.0,
|
typeOption[_typeOptionIndex],
|
||||||
textAlign: TextAlign.left,
|
textScaleFactor: 1.0,
|
||||||
style: TextStyle(
|
textAlign: TextAlign.left,
|
||||||
fontSize: 16.0,
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.w500,
|
fontSize: 16.0,
|
||||||
color: primaryColor)),
|
fontWeight: FontWeight.w500,
|
||||||
|
color: primaryColor),
|
||||||
|
),
|
||||||
Text(
|
Text(
|
||||||
s.currentMode,
|
s.currentMode,
|
||||||
textScaleFactor: 1.0,
|
textScaleFactor: 1.0,
|
||||||
@@ -153,15 +159,17 @@ class _ConvertPageState extends State<ConvertPage>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
children: typeOption
|
children: typeOption
|
||||||
.map((e) => ListTile(
|
.map(
|
||||||
title: Text(
|
(e) => ListTile(
|
||||||
e,
|
title: Text(
|
||||||
style: TextStyle(
|
e,
|
||||||
color: _theme.textTheme.bodyText2?.color?.withAlpha(177),
|
style: TextStyle(
|
||||||
),
|
color: _theme.textTheme.bodyText2?.color?.withAlpha(177),
|
||||||
),
|
),
|
||||||
trailing: _buildRadio(typeOption.indexOf(e)),
|
),
|
||||||
))
|
trailing: _buildRadio(typeOption.indexOf(e)),
|
||||||
|
),
|
||||||
|
)
|
||||||
.toList(),
|
.toList(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -13,8 +13,10 @@ class _DebugPageState extends State<DebugPage> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar:
|
appBar: AppBar(
|
||||||
AppBar(title: const Text('App log'), backgroundColor: Colors.black),
|
title: const Text('App log'),
|
||||||
|
backgroundColor: Colors.black,
|
||||||
|
),
|
||||||
body: _buildTerminal(context),
|
body: _buildTerminal(context),
|
||||||
backgroundColor: Colors.black,
|
backgroundColor: Colors.black,
|
||||||
);
|
);
|
||||||
@@ -31,12 +33,15 @@ class _DebugPageState extends State<DebugPage> {
|
|||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
child: Consumer<DebugProvider>(builder: (_, debug, __) {
|
child: Consumer<DebugProvider>(
|
||||||
return Column(
|
builder: (_, debug, __) {
|
||||||
|
return Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: debug.widgets);
|
children: debug.widgets,
|
||||||
}),
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -66,8 +66,9 @@ class _DockerManagePageState extends State<DockerManagePage> {
|
|||||||
title: TwoLineText(up: 'Docker', down: widget.spi.name),
|
title: TwoLineText(up: 'Docker', down: widget.spi.name),
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () => docker.refresh(),
|
onPressed: () => docker.refresh(),
|
||||||
icon: const Icon(Icons.refresh))
|
icon: const Icon(Icons.refresh),
|
||||||
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: _buildMain(docker),
|
body: _buildMain(docker),
|
||||||
@@ -124,8 +125,13 @@ class _DockerManagePageState extends State<DockerManagePage> {
|
|||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
await _showAddCmdPreview(_buildAddCmd(imageCtrl.text.trim(),
|
await _showAddCmdPreview(
|
||||||
nameCtrl.text.trim(), argsCtrl.text.trim()));
|
_buildAddCmd(
|
||||||
|
imageCtrl.text.trim(),
|
||||||
|
nameCtrl.text.trim(),
|
||||||
|
argsCtrl.text.trim(),
|
||||||
|
),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
child: Text(s.ok),
|
child: Text(s.ok),
|
||||||
)
|
)
|
||||||
@@ -191,31 +197,34 @@ class _DockerManagePageState extends State<DockerManagePage> {
|
|||||||
Future<String> onPwdRequest() async {
|
Future<String> onPwdRequest() async {
|
||||||
if (!mounted) return '';
|
if (!mounted) return '';
|
||||||
await showRoundDialog(
|
await showRoundDialog(
|
||||||
context,
|
context,
|
||||||
widget.spi.user,
|
widget.spi.user,
|
||||||
TextField(
|
TextField(
|
||||||
controller: textController,
|
controller: textController,
|
||||||
keyboardType: TextInputType.visiblePassword,
|
keyboardType: TextInputType.visiblePassword,
|
||||||
obscureText: true,
|
obscureText: true,
|
||||||
onSubmitted: (_) => onSubmitted(),
|
onSubmitted: (_) => onSubmitted(),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: s.pwd,
|
labelText: s.pwd,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
[
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
child: Text(s.cancel),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => onSubmitted(),
|
||||||
|
child: Text(
|
||||||
|
s.ok,
|
||||||
|
style: const TextStyle(color: Colors.red),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
[
|
],
|
||||||
TextButton(
|
);
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
},
|
|
||||||
child: Text(s.cancel)),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () => onSubmitted(),
|
|
||||||
child: Text(
|
|
||||||
s.ok,
|
|
||||||
style: const TextStyle(color: Colors.red),
|
|
||||||
)),
|
|
||||||
]);
|
|
||||||
return textController.text.trim();
|
return textController.text.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,30 +273,29 @@ class _DockerManagePageState extends State<DockerManagePage> {
|
|||||||
return const SizedBox();
|
return const SizedBox();
|
||||||
}
|
}
|
||||||
return ExpansionTile(
|
return ExpansionTile(
|
||||||
title: Text(s.imagesList),
|
title: Text(s.imagesList),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
s.dockerImagesFmt(docker.images!.length),
|
s.dockerImagesFmt(docker.images!.length),
|
||||||
style: greyTextStyle,
|
style: greyTextStyle,
|
||||||
),
|
),
|
||||||
children: docker.images!
|
children: docker.images!
|
||||||
.map(
|
.map(
|
||||||
(e) => ListTile(
|
(e) => ListTile(
|
||||||
title: Text(e.repo),
|
title: Text(e.repo),
|
||||||
subtitle: Text('${e.tag} - ${e.size}'),
|
subtitle: Text('${e.tag} - ${e.size}'),
|
||||||
trailing: IconButton(
|
trailing: IconButton(
|
||||||
icon: const Icon(Icons.delete),
|
icon: const Icon(Icons.delete),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
final result = await _docker.run('docker rmi ${e.id} -f');
|
final result = await _docker.run('docker rmi ${e.id} -f');
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
showSnackBar(
|
showSnackBar(
|
||||||
context, Text(getErrMsg(result) ?? s.unknownError));
|
context, Text(getErrMsg(result) ?? s.unknownError));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
)
|
||||||
)
|
.toList());
|
||||||
.toList(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildLoading(DockerProvider docker) {
|
Widget _buildLoading(DockerProvider docker) {
|
||||||
@@ -318,8 +326,9 @@ class _DockerManagePageState extends State<DockerManagePage> {
|
|||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => _showEditHostDialog(docker),
|
onPressed: () => _showEditHostDialog(docker),
|
||||||
child: Text(s.dockerEditHost))
|
child: Text(s.dockerEditHost),
|
||||||
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -342,8 +351,9 @@ class _DockerManagePageState extends State<DockerManagePage> {
|
|||||||
),
|
),
|
||||||
[
|
[
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
child: Text(s.cancel)),
|
child: Text(s.cancel),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -399,14 +409,16 @@ class _DockerManagePageState extends State<DockerManagePage> {
|
|||||||
return ExpansionTile(
|
return ExpansionTile(
|
||||||
title: Text(s.containerStatus),
|
title: Text(s.containerStatus),
|
||||||
subtitle: Text(_buildSubtitle(running), style: greyTextStyle),
|
subtitle: Text(_buildSubtitle(running), style: greyTextStyle),
|
||||||
children: running.map((item) {
|
children: running.map(
|
||||||
return ListTile(
|
(item) {
|
||||||
title: Text(item.image),
|
return ListTile(
|
||||||
subtitle: Text(item.status),
|
title: Text(item.image),
|
||||||
trailing:
|
subtitle: Text(item.status),
|
||||||
_buildMoreBtn(item.running, item.containerId, docker.isBusy),
|
trailing:
|
||||||
);
|
_buildMoreBtn(item.running, item.containerId, docker.isBusy),
|
||||||
}).toList(),
|
);
|
||||||
|
},
|
||||||
|
).toList(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -134,12 +134,15 @@ class _MyHomePageState extends State<MyHomePage>
|
|||||||
height: 50,
|
height: 50,
|
||||||
width: isSelected ? width : width - 17,
|
width: isSelected ? width : width - 17,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: isSelected
|
color: isSelected
|
||||||
? isDarkMode
|
? isDarkMode
|
||||||
? Colors.white12
|
? Colors.white12
|
||||||
: Colors.black.withOpacity(0.07)
|
: Colors.black.withOpacity(0.07)
|
||||||
: Colors.transparent,
|
: Colors.transparent,
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(50))),
|
borderRadius: const BorderRadius.all(
|
||||||
|
Radius.circular(50),
|
||||||
|
),
|
||||||
|
),
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
icon: Icon(item.icon),
|
icon: Icon(item.icon),
|
||||||
tooltip: tabTitleName(context, idx),
|
tooltip: tabTitleName(context, idx),
|
||||||
@@ -158,18 +161,21 @@ class _MyHomePageState extends State<MyHomePage>
|
|||||||
|
|
||||||
Widget _buildBottom(BuildContext context) {
|
Widget _buildBottom(BuildContext context) {
|
||||||
return SafeArea(
|
return SafeArea(
|
||||||
child: Container(
|
child: Container(
|
||||||
height: 56,
|
height: 56,
|
||||||
padding: const EdgeInsets.only(left: 8, top: 4, bottom: 4, right: 8),
|
padding: const EdgeInsets.only(left: 8, top: 4, bottom: 4, right: 8),
|
||||||
width: _width,
|
width: _width,
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
children: tabItems.map((item) {
|
children: tabItems.map(
|
||||||
int itemIndex = tabItems.indexOf(item);
|
(item) {
|
||||||
return _buildItem(itemIndex, item, _selectIndex == itemIndex);
|
int itemIndex = tabItems.indexOf(item);
|
||||||
}).toList(),
|
return _buildItem(itemIndex, item, _selectIndex == itemIndex);
|
||||||
|
},
|
||||||
|
).toList(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
));
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildDrawer() {
|
Widget _buildDrawer() {
|
||||||
@@ -217,18 +223,25 @@ class _MyHomePageState extends State<MyHomePage>
|
|||||||
leading: const Icon(Icons.info),
|
leading: const Icon(Icons.info),
|
||||||
title: Text(s.feedback),
|
title: Text(s.feedback),
|
||||||
onTap: () => showRoundDialog(
|
onTap: () => showRoundDialog(
|
||||||
context, s.feedback, Text(s.feedbackOnGithub), [
|
context,
|
||||||
TextButton(
|
s.feedback,
|
||||||
|
Text(s.feedbackOnGithub),
|
||||||
|
[
|
||||||
|
TextButton(
|
||||||
onPressed: () => Clipboard.setData(
|
onPressed: () => Clipboard.setData(
|
||||||
const ClipboardData(text: issueUrl)),
|
const ClipboardData(text: issueUrl)),
|
||||||
child: Text(s.copy)),
|
child: Text(s.copy),
|
||||||
TextButton(
|
),
|
||||||
|
TextButton(
|
||||||
onPressed: () => openUrl(issueUrl),
|
onPressed: () => openUrl(issueUrl),
|
||||||
child: Text(s.feedback)),
|
child: Text(s.feedback),
|
||||||
TextButton(
|
),
|
||||||
|
TextButton(
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
child: Text(s.close))
|
child: Text(s.close),
|
||||||
]),
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.snippet_folder),
|
leading: const Icon(Icons.snippet_folder),
|
||||||
@@ -271,10 +284,11 @@ class _MyHomePageState extends State<MyHomePage>
|
|||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
children: [
|
children: [
|
||||||
ConstrainedBox(
|
ConstrainedBox(
|
||||||
constraints: const BoxConstraints(maxHeight: 53, maxWidth: 53),
|
constraints: const BoxConstraints(maxHeight: 53, maxWidth: 53),
|
||||||
child: Container(
|
child: Container(
|
||||||
color: primaryColor,
|
color: primaryColor,
|
||||||
)),
|
),
|
||||||
|
),
|
||||||
ConstrainedBox(
|
ConstrainedBox(
|
||||||
constraints: const BoxConstraints(maxHeight: 83, maxWidth: 83),
|
constraints: const BoxConstraints(maxHeight: 83, maxWidth: 83),
|
||||||
child: appIcon,
|
child: appIcon,
|
||||||
|
|||||||
@@ -51,8 +51,9 @@ class _PingPageState extends State<PingPage>
|
|||||||
super.build(context);
|
super.build(context);
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 7),
|
padding: const EdgeInsets.symmetric(horizontal: 7),
|
||||||
child: Column(children: [
|
child: Column(
|
||||||
|
children: [
|
||||||
const SizedBox(height: 13),
|
const SizedBox(height: 13),
|
||||||
buildInput(context, _textEditingController,
|
buildInput(context, _textEditingController,
|
||||||
hint: s.inputDomainHere,
|
hint: s.inputDomainHere,
|
||||||
@@ -69,7 +70,9 @@ class _PingPageState extends State<PingPage>
|
|||||||
return _buildResultItem(result);
|
return _buildResultItem(result);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
])),
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
floatingActionButton: FloatingActionButton(
|
floatingActionButton: FloatingActionButton(
|
||||||
heroTag: 'ping fab',
|
heroTag: 'ping fab',
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
@@ -87,19 +90,30 @@ class _PingPageState extends State<PingPage>
|
|||||||
Widget _buildResultItem(PingResult result) {
|
Widget _buildResultItem(PingResult result) {
|
||||||
final unknown = s.unknown;
|
final unknown = s.unknown;
|
||||||
final ms = s.ms;
|
final ms = s.ms;
|
||||||
return RoundRectCard(ListTile(
|
return RoundRectCard(
|
||||||
contentPadding: const EdgeInsets.symmetric(vertical: 7, horizontal: 17),
|
ListTile(
|
||||||
title: Text(result.serverName,
|
contentPadding: const EdgeInsets.symmetric(vertical: 7, horizontal: 17),
|
||||||
|
title: Text(
|
||||||
|
result.serverName,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 18, fontWeight: FontWeight.bold, color: primaryColor)),
|
fontSize: 18,
|
||||||
subtitle: Text(
|
fontWeight: FontWeight.bold,
|
||||||
_buildPingSummary(result, unknown, ms),
|
color: primaryColor,
|
||||||
style: summaryTextStyle,
|
),
|
||||||
),
|
),
|
||||||
trailing: Text(
|
subtitle: Text(
|
||||||
|
_buildPingSummary(result, unknown, ms),
|
||||||
|
style: summaryTextStyle,
|
||||||
|
),
|
||||||
|
trailing: Text(
|
||||||
'${s.pingAvg}${result.statistic?.avg?.toStringAsFixed(2) ?? s.unknown} $ms',
|
'${s.pingAvg}${result.statistic?.avg?.toStringAsFixed(2) ?? s.unknown} $ms',
|
||||||
style: TextStyle(fontSize: 14, color: primaryColor)),
|
style: TextStyle(
|
||||||
));
|
fontSize: 14,
|
||||||
|
color: primaryColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
String _buildPingSummary(PingResult result, String unknown, String ms) {
|
String _buildPingSummary(PingResult result, String unknown, String ms) {
|
||||||
|
|||||||
@@ -51,17 +51,20 @@ class _PrivateKeyEditPageState extends State<PrivateKeyEditPage>
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(title: Text(s.edit, style: textSize18), actions: [
|
appBar: AppBar(
|
||||||
widget.info != null
|
title: Text(s.edit, style: textSize18),
|
||||||
? IconButton(
|
actions: [
|
||||||
tooltip: s.delete,
|
widget.info != null
|
||||||
onPressed: () {
|
? IconButton(
|
||||||
_provider.delInfo(widget.info!);
|
tooltip: s.delete,
|
||||||
Navigator.of(context).pop();
|
onPressed: () {
|
||||||
},
|
_provider.delInfo(widget.info!);
|
||||||
icon: const Icon(Icons.delete))
|
Navigator.of(context).pop();
|
||||||
: const SizedBox()
|
},
|
||||||
]),
|
icon: const Icon(Icons.delete))
|
||||||
|
: const SizedBox()
|
||||||
|
],
|
||||||
|
),
|
||||||
body: ListView(
|
body: ListView(
|
||||||
padding: const EdgeInsets.all(13),
|
padding: const EdgeInsets.all(13),
|
||||||
children: [
|
children: [
|
||||||
|
|||||||
@@ -51,19 +51,22 @@ class _PrivateKeyListState extends State<StoredPrivateKeysPage> {
|
|||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => AppRoute(
|
onPressed: () => AppRoute(
|
||||||
PrivateKeyEditPage(info: key.infos[idx]),
|
PrivateKeyEditPage(info: key.infos[idx]),
|
||||||
'private key edit page')
|
'private key edit page')
|
||||||
.go(context),
|
.go(context),
|
||||||
child: Text(
|
child: Text(
|
||||||
s.edit,
|
s.edit,
|
||||||
style: _textStyle,
|
style: _textStyle,
|
||||||
))
|
),
|
||||||
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
})
|
})
|
||||||
: Center(child: Text(s.noSavedPrivateKey));
|
: Center(
|
||||||
|
child: Text(s.noSavedPrivateKey),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
floatingActionButton: FloatingActionButton(
|
floatingActionButton: FloatingActionButton(
|
||||||
|
|||||||
@@ -37,8 +37,9 @@ class _ServerDetailPageState extends State<ServerDetailPage>
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Consumer<ServerProvider>(builder: (_, provider, __) {
|
return Consumer<ServerProvider>(builder: (_, provider, __) {
|
||||||
return _buildMainPage(
|
return _buildMainPage(
|
||||||
provider.servers
|
provider.servers.firstWhere(
|
||||||
.firstWhere((e) => '${e.info.ip}:${e.info.port}' == widget.id),
|
(e) => '${e.info.ip}:${e.info.port}' == widget.id,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -211,8 +212,9 @@ class _ServerDetailPageState extends State<ServerDetailPage>
|
|||||||
_buildMemExplain(
|
_buildMemExplain(
|
||||||
(ss.memory.cache * mb).convertBytes, pColor.withAlpha(77)),
|
(ss.memory.cache * mb).convertBytes, pColor.withAlpha(77)),
|
||||||
_buildMemExplain(
|
_buildMemExplain(
|
||||||
((ss.memory.total - ss.memory.used) * mb).convertBytes,
|
((ss.memory.total - ss.memory.used) * mb).convertBytes,
|
||||||
progressColor.resolve(context))
|
progressColor.resolve(context),
|
||||||
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
@@ -363,11 +365,12 @@ class _ServerDetailPageState extends State<ServerDetailPage>
|
|||||||
textScaleFactor: 1.0),
|
textScaleFactor: 1.0),
|
||||||
),
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: _media.size.width / 4,
|
width: _media.size.width / 4,
|
||||||
child: Text(ns.speedOut(device: device),
|
child: Text(ns.speedOut(device: device),
|
||||||
style: textSize11,
|
style: textSize11,
|
||||||
textAlign: TextAlign.right,
|
textAlign: TextAlign.right,
|
||||||
textScaleFactor: 1.0))
|
textScaleFactor: 1.0),
|
||||||
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -63,30 +63,40 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(title: Text(s.edit, style: textSize18), actions: [
|
appBar: AppBar(
|
||||||
widget.spi != null
|
title: Text(s.edit, style: textSize18),
|
||||||
? IconButton(
|
actions: [
|
||||||
onPressed: () {
|
widget.spi != null
|
||||||
showRoundDialog(context, s.attention,
|
? IconButton(
|
||||||
Text(s.sureToDeleteServer(widget.spi!.name)), [
|
onPressed: () {
|
||||||
TextButton(
|
showRoundDialog(
|
||||||
onPressed: () {
|
context,
|
||||||
_serverProvider.delServer(widget.spi!);
|
s.attention,
|
||||||
Navigator.of(context).pop();
|
Text(s.sureToDeleteServer(widget.spi!.name)),
|
||||||
Navigator.of(context).pop();
|
[
|
||||||
},
|
TextButton(
|
||||||
child: Text(
|
onPressed: () {
|
||||||
s.ok,
|
_serverProvider.delServer(widget.spi!);
|
||||||
style: const TextStyle(color: Colors.red),
|
Navigator.of(context).pop();
|
||||||
)),
|
Navigator.of(context).pop();
|
||||||
TextButton(
|
},
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
child: Text(
|
||||||
child: Text(s.cancel))
|
s.ok,
|
||||||
]);
|
style: const TextStyle(color: Colors.red),
|
||||||
},
|
),
|
||||||
icon: const Icon(Icons.delete))
|
),
|
||||||
: const SizedBox()
|
TextButton(
|
||||||
]),
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
child: Text(s.cancel),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.delete),
|
||||||
|
)
|
||||||
|
: const SizedBox()
|
||||||
|
],
|
||||||
|
),
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
padding: const EdgeInsets.all(17),
|
padding: const EdgeInsets.all(17),
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -148,42 +158,47 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
|||||||
)
|
)
|
||||||
: const SizedBox(),
|
: const SizedBox(),
|
||||||
usePublicKey
|
usePublicKey
|
||||||
? Consumer<PrivateKeyProvider>(builder: (_, key, __) {
|
? Consumer<PrivateKeyProvider>(
|
||||||
for (var item in key.infos) {
|
builder: (_, key, __) {
|
||||||
if (item.id == widget.spi?.pubKeyId) {
|
for (var item in key.infos) {
|
||||||
_pubKeyIndex ??= key.infos.indexOf(item);
|
if (item.id == widget.spi?.pubKeyId) {
|
||||||
|
_pubKeyIndex ??= key.infos.indexOf(item);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
final tiles = key.infos
|
||||||
final tiles = key.infos
|
.map(
|
||||||
.map(
|
(e) => ListTile(
|
||||||
(e) => ListTile(
|
contentPadding: EdgeInsets.zero,
|
||||||
contentPadding: EdgeInsets.zero,
|
title: Text(e.id, textAlign: TextAlign.start),
|
||||||
title: Text(e.id, textAlign: TextAlign.start),
|
trailing: _buildRadio(key.infos.indexOf(e), e)),
|
||||||
trailing: _buildRadio(key.infos.indexOf(e), e)),
|
)
|
||||||
)
|
.toList();
|
||||||
.toList();
|
tiles.add(
|
||||||
tiles.add(ListTile(
|
ListTile(
|
||||||
title: Text(s.addPrivateKey),
|
title: Text(s.addPrivateKey),
|
||||||
contentPadding: EdgeInsets.zero,
|
contentPadding: EdgeInsets.zero,
|
||||||
trailing: IconButton(
|
trailing: IconButton(
|
||||||
icon: const Icon(Icons.add),
|
icon: const Icon(Icons.add),
|
||||||
onPressed: () => AppRoute(const PrivateKeyEditPage(),
|
onPressed: () => AppRoute(
|
||||||
'private key edit page')
|
const PrivateKeyEditPage(),
|
||||||
.go(context),
|
'private key edit page')
|
||||||
),
|
.go(context),
|
||||||
));
|
),
|
||||||
return ExpansionTile(
|
),
|
||||||
textColor: primaryColor,
|
);
|
||||||
iconColor: primaryColor,
|
return ExpansionTile(
|
||||||
tilePadding: EdgeInsets.zero,
|
textColor: primaryColor,
|
||||||
childrenPadding: EdgeInsets.zero,
|
iconColor: primaryColor,
|
||||||
title: Text(
|
tilePadding: EdgeInsets.zero,
|
||||||
s.choosePrivateKey,
|
childrenPadding: EdgeInsets.zero,
|
||||||
style: const TextStyle(fontSize: 14),
|
title: Text(
|
||||||
),
|
s.choosePrivateKey,
|
||||||
children: tiles,
|
style: const TextStyle(fontSize: 14),
|
||||||
);
|
),
|
||||||
})
|
children: tiles,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)
|
||||||
: const SizedBox()
|
: const SizedBox()
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -197,18 +212,19 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
|||||||
}
|
}
|
||||||
if (!usePublicKey && passwordController.text == '') {
|
if (!usePublicKey && passwordController.text == '') {
|
||||||
final cancel = await showRoundDialog<bool>(
|
final cancel = await showRoundDialog<bool>(
|
||||||
context,
|
context,
|
||||||
s.attention,
|
s.attention,
|
||||||
Text(s.sureNoPwd),
|
Text(s.sureNoPwd),
|
||||||
[
|
[
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => Navigator.of(context).pop(false),
|
onPressed: () => Navigator.of(context).pop(false),
|
||||||
child: Text(s.ok)),
|
child: Text(s.ok)),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => Navigator.of(context).pop(true),
|
onPressed: () => Navigator.of(context).pop(true),
|
||||||
child: Text(s.cancel))
|
child: Text(s.cancel))
|
||||||
],
|
],
|
||||||
barrierDismiss: false);
|
barrierDismiss: false,
|
||||||
|
);
|
||||||
if (cancel ?? true) {
|
if (cancel ?? true) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -230,12 +246,13 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
|||||||
|
|
||||||
final authorization = passwordController.text;
|
final authorization = passwordController.text;
|
||||||
final spi = ServerPrivateInfo(
|
final spi = ServerPrivateInfo(
|
||||||
name: nameController.text,
|
name: nameController.text,
|
||||||
ip: ipController.text,
|
ip: ipController.text,
|
||||||
port: int.parse(portController.text),
|
port: int.parse(portController.text),
|
||||||
user: usernameController.text,
|
user: usernameController.text,
|
||||||
pwd: authorization,
|
pwd: authorization,
|
||||||
pubKeyId: usePublicKey ? _keyInfo!.id : null);
|
pubKeyId: usePublicKey ? _keyInfo!.id : null,
|
||||||
|
);
|
||||||
|
|
||||||
if (widget.spi == null) {
|
if (widget.spi == null) {
|
||||||
_serverProvider.addServer(spi);
|
_serverProvider.addServer(spi);
|
||||||
|
|||||||
@@ -55,30 +55,32 @@ class _ServerPageState extends State<ServerPage>
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
super.build(context);
|
super.build(context);
|
||||||
final child = Consumer<ServerProvider>(builder: (_, pro, __) {
|
final child = Consumer<ServerProvider>(
|
||||||
if (pro.servers.isEmpty) {
|
builder: (_, pro, __) {
|
||||||
return Center(
|
if (pro.servers.isEmpty) {
|
||||||
child: Text(
|
return Center(
|
||||||
s.serverTabEmpty,
|
child: Text(
|
||||||
textAlign: TextAlign.center,
|
s.serverTabEmpty,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return ListView.separated(
|
||||||
|
padding: const EdgeInsets.all(7),
|
||||||
|
controller: ScrollController(),
|
||||||
|
itemBuilder: (ctx, idx) {
|
||||||
|
if (idx == pro.servers.length) {
|
||||||
|
return SizedBox(height: _media.padding.bottom);
|
||||||
|
}
|
||||||
|
return _buildEachServerCard(pro.servers[idx]);
|
||||||
|
},
|
||||||
|
itemCount: pro.servers.length + 1,
|
||||||
|
separatorBuilder: (_, __) => const SizedBox(
|
||||||
|
height: 3,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
return ListView.separated(
|
);
|
||||||
padding: const EdgeInsets.all(7),
|
|
||||||
controller: ScrollController(),
|
|
||||||
itemBuilder: (ctx, idx) {
|
|
||||||
if (idx == pro.servers.length) {
|
|
||||||
return SizedBox(height: _media.padding.bottom);
|
|
||||||
}
|
|
||||||
return _buildEachServerCard(pro.servers[idx]);
|
|
||||||
},
|
|
||||||
itemCount: pro.servers.length + 1,
|
|
||||||
separatorBuilder: (_, __) => const SizedBox(
|
|
||||||
height: 3,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: child,
|
body: child,
|
||||||
floatingActionButton: FloatingActionButton(
|
floatingActionButton: FloatingActionButton(
|
||||||
@@ -102,9 +104,10 @@ class _ServerPageState extends State<ServerPage>
|
|||||||
'Edit server info page')
|
'Edit server info page')
|
||||||
.go(context),
|
.go(context),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(13),
|
padding: const EdgeInsets.all(13),
|
||||||
child: _buildRealServerCard(
|
child: _buildRealServerCard(
|
||||||
si.status, si.info.name, si.connectionState, si.info)),
|
si.status, si.info.name, si.connectionState, si.info),
|
||||||
|
),
|
||||||
onTap: () => AppRoute(ServerDetailPage('${si.info.ip}:${si.info.port}'),
|
onTap: () => AppRoute(ServerDetailPage('${si.info.ip}:${si.info.port}'),
|
||||||
'server detail page')
|
'server detail page')
|
||||||
.go(context),
|
.go(context),
|
||||||
|
|||||||
@@ -71,18 +71,19 @@ class _SettingPageState extends State<SettingPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildCheckUpdate() {
|
Widget _buildCheckUpdate() {
|
||||||
return Consumer<AppProvider>(builder: (_, app, __) {
|
return Consumer<AppProvider>(
|
||||||
String display;
|
builder: (_, app, __) {
|
||||||
if (app.newestBuild != null) {
|
String display;
|
||||||
if (app.newestBuild! > BuildData.build) {
|
if (app.newestBuild != null) {
|
||||||
display = s.versionHaveUpdate(app.newestBuild!);
|
if (app.newestBuild! > BuildData.build) {
|
||||||
|
display = s.versionHaveUpdate(app.newestBuild!);
|
||||||
|
} else {
|
||||||
|
display = s.versionUpdated(BuildData.build);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
display = s.versionUpdated(BuildData.build);
|
display = s.versionUnknownUpdate(BuildData.build);
|
||||||
}
|
}
|
||||||
} else {
|
return ListTile(
|
||||||
display = s.versionUnknownUpdate(BuildData.build);
|
|
||||||
}
|
|
||||||
return ListTile(
|
|
||||||
contentPadding: roundRectCardPadding,
|
contentPadding: roundRectCardPadding,
|
||||||
trailing: const Icon(Icons.keyboard_arrow_right),
|
trailing: const Icon(Icons.keyboard_arrow_right),
|
||||||
title: Text(
|
title: Text(
|
||||||
@@ -90,8 +91,10 @@ class _SettingPageState extends State<SettingPage> {
|
|||||||
style: textSize13,
|
style: textSize13,
|
||||||
textAlign: TextAlign.start,
|
textAlign: TextAlign.start,
|
||||||
),
|
),
|
||||||
onTap: () => doUpdate(context, force: true));
|
onTap: () => doUpdate(context, force: true),
|
||||||
});
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildUpdateInterval() {
|
Widget _buildUpdateInterval() {
|
||||||
@@ -147,24 +150,22 @@ class _SettingPageState extends State<SettingPage> {
|
|||||||
|
|
||||||
Widget _buildAppColorPreview() {
|
Widget _buildAppColorPreview() {
|
||||||
return ExpansionTile(
|
return ExpansionTile(
|
||||||
textColor: priColor,
|
textColor: priColor,
|
||||||
tilePadding: roundRectCardPadding,
|
tilePadding: roundRectCardPadding,
|
||||||
childrenPadding: roundRectCardPadding,
|
childrenPadding: roundRectCardPadding,
|
||||||
trailing: ClipOval(
|
trailing: ClipOval(
|
||||||
child: Container(
|
child: Container(
|
||||||
color: priColor,
|
color: priColor,
|
||||||
height: 27,
|
height: 27,
|
||||||
width: 27,
|
width: 27,
|
||||||
),
|
|
||||||
),
|
),
|
||||||
title: Text(
|
),
|
||||||
s.appPrimaryColor,
|
title: Text(
|
||||||
style: textSize13,
|
s.appPrimaryColor,
|
||||||
),
|
style: textSize13,
|
||||||
children: [
|
),
|
||||||
_buildAppColorPicker(priColor),
|
children: [_buildAppColorPicker(priColor), _buildColorPickerConfirmBtn()],
|
||||||
_buildColorPickerConfirmBtn()
|
);
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildAppColorPicker(Color selected) {
|
Widget _buildAppColorPicker(Color selected) {
|
||||||
@@ -204,16 +205,18 @@ class _SettingPageState extends State<SettingPage> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
children: tabs
|
children: tabs
|
||||||
.map((e) => ListTile(
|
.map(
|
||||||
contentPadding: EdgeInsets.zero,
|
(e) => ListTile(
|
||||||
title: Text(
|
contentPadding: EdgeInsets.zero,
|
||||||
tabTitleName(context, tabs.indexOf(e)),
|
title: Text(
|
||||||
style: TextStyle(
|
tabTitleName(context, tabs.indexOf(e)),
|
||||||
fontSize: 14,
|
style: TextStyle(
|
||||||
color: _theme.textTheme.bodyText2!.color!.withAlpha(177)),
|
fontSize: 14,
|
||||||
),
|
color: _theme.textTheme.bodyText2!.color!.withAlpha(177)),
|
||||||
trailing: _buildRadio(tabs.indexOf(e)),
|
),
|
||||||
))
|
trailing: _buildRadio(tabs.indexOf(e)),
|
||||||
|
),
|
||||||
|
)
|
||||||
.toList(),
|
.toList(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -141,18 +141,21 @@ class _SFTPDownloadedPageState extends State<SFTPDownloadedPage> {
|
|||||||
void showFileActionDialog(FileSystemEntity file) {
|
void showFileActionDialog(FileSystemEntity file) {
|
||||||
final fileName = file.path.split('/').last;
|
final fileName = file.path.split('/').last;
|
||||||
showRoundDialog(
|
showRoundDialog(
|
||||||
context,
|
context,
|
||||||
s.choose,
|
s.choose,
|
||||||
Column(
|
Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.delete),
|
leading: const Icon(Icons.delete),
|
||||||
title: Text(s.delete),
|
title: Text(s.delete),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
showRoundDialog(
|
showRoundDialog(
|
||||||
context, s.sureDelete(fileName), const SizedBox(), [
|
context,
|
||||||
|
s.sureDelete(fileName),
|
||||||
|
const SizedBox(),
|
||||||
|
[
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
child: Text(s.cancel)),
|
child: Text(s.cancel)),
|
||||||
@@ -164,21 +167,24 @@ class _SFTPDownloadedPageState extends State<SFTPDownloadedPage> {
|
|||||||
},
|
},
|
||||||
child: Text(s.ok),
|
child: Text(s.ok),
|
||||||
),
|
),
|
||||||
]);
|
],
|
||||||
},
|
);
|
||||||
),
|
},
|
||||||
ListTile(
|
),
|
||||||
leading: const Icon(Icons.open_in_new),
|
ListTile(
|
||||||
title: Text(s.open),
|
leading: const Icon(Icons.open_in_new),
|
||||||
onTap: () {
|
title: Text(s.open),
|
||||||
shareFiles(context, [file.absolute.path]);
|
onTap: () {
|
||||||
}),
|
shareFiles(context, [file.absolute.path]);
|
||||||
],
|
},
|
||||||
),
|
),
|
||||||
[
|
],
|
||||||
TextButton(
|
),
|
||||||
onPressed: (() => Navigator.of(context).pop()),
|
[
|
||||||
child: Text(s.close))
|
TextButton(
|
||||||
]);
|
onPressed: (() => Navigator.of(context).pop()),
|
||||||
|
child: Text(s.close))
|
||||||
|
],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,16 +58,18 @@ class _SFTPDownloadingPageState extends State<SFTPDownloadingPage> {
|
|||||||
|
|
||||||
Widget _wrapInCard(SftpDownloadStatus status, String? subtitle,
|
Widget _wrapInCard(SftpDownloadStatus status, String? subtitle,
|
||||||
{Widget? trailing}) {
|
{Widget? trailing}) {
|
||||||
return RoundRectCard(ListTile(
|
return RoundRectCard(
|
||||||
title: Text(status.fileName),
|
ListTile(
|
||||||
subtitle: subtitle == null
|
title: Text(status.fileName),
|
||||||
? null
|
subtitle: subtitle == null
|
||||||
: Text(
|
? null
|
||||||
subtitle,
|
: Text(
|
||||||
style: grey,
|
subtitle,
|
||||||
),
|
style: grey,
|
||||||
trailing: trailing,
|
),
|
||||||
));
|
trailing: trailing,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildItem(SftpDownloadStatus status) {
|
Widget _buildItem(SftpDownloadStatus status) {
|
||||||
@@ -77,11 +79,14 @@ class _SFTPDownloadingPageState extends State<SFTPDownloadingPage> {
|
|||||||
switch (status.status) {
|
switch (status.status) {
|
||||||
case SftpWorkerStatus.finished:
|
case SftpWorkerStatus.finished:
|
||||||
final time = status.spentTime.toString();
|
final time = status.spentTime.toString();
|
||||||
return _wrapInCard(status,
|
return _wrapInCard(
|
||||||
'${s.downloadFinished} ${s.spentTime(time == 'null' ? s.unknown : (time.substring(0, time.length - 7)))}',
|
status,
|
||||||
trailing: IconButton(
|
'${s.downloadFinished} ${s.spentTime(time == 'null' ? s.unknown : (time.substring(0, time.length - 7)))}',
|
||||||
onPressed: () => shareFiles(context, [status.item.localPath]),
|
trailing: IconButton(
|
||||||
icon: const Icon(Icons.open_in_new)));
|
onPressed: () => shareFiles(context, [status.item.localPath]),
|
||||||
|
icon: const Icon(Icons.open_in_new),
|
||||||
|
),
|
||||||
|
);
|
||||||
case SftpWorkerStatus.downloading:
|
case SftpWorkerStatus.downloading:
|
||||||
return _wrapInCard(
|
return _wrapInCard(
|
||||||
status,
|
status,
|
||||||
@@ -94,11 +99,14 @@ class _SFTPDownloadingPageState extends State<SFTPDownloadingPage> {
|
|||||||
case SftpWorkerStatus.sshConnectted:
|
case SftpWorkerStatus.sshConnectted:
|
||||||
return _wrapInCard(status, s.sftpSSHConnected, trailing: loadingIcon);
|
return _wrapInCard(status, s.sftpSSHConnected, trailing: loadingIcon);
|
||||||
default:
|
default:
|
||||||
return _wrapInCard(status, s.unknown,
|
return _wrapInCard(
|
||||||
trailing: const Icon(
|
status,
|
||||||
Icons.error,
|
s.unknown,
|
||||||
size: 40,
|
trailing: const Icon(
|
||||||
));
|
Icons.error,
|
||||||
|
size: 40,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,34 +55,36 @@ class _SFTPPageState extends State<SFTPPage> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
title: TwoLineText(up: 'SFTP', down: widget.spi.name),
|
title: TwoLineText(up: 'SFTP', down: widget.spi.name),
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: (() => showRoundDialog(
|
onPressed: (() => showRoundDialog(
|
||||||
context,
|
context,
|
||||||
s.choose,
|
s.choose,
|
||||||
Column(
|
Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.folder),
|
leading: const Icon(Icons.folder),
|
||||||
title: Text(s.createFolder),
|
title: Text(s.createFolder),
|
||||||
onTap: () => mkdir(context)),
|
onTap: () => mkdir(context)),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.insert_drive_file),
|
leading: const Icon(Icons.insert_drive_file),
|
||||||
title: Text(s.createFile),
|
title: Text(s.createFile),
|
||||||
onTap: () => newFile(context)),
|
onTap: () => newFile(context)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
[
|
[
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
child: Text(s.close))
|
child: Text(s.close))
|
||||||
])),
|
],
|
||||||
icon: const Icon(Icons.add),
|
)),
|
||||||
)
|
icon: const Icon(Icons.add),
|
||||||
]),
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
body: _buildFileView(),
|
body: _buildFileView(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -121,85 +123,90 @@ class _SFTPPageState extends State<SFTPPage> {
|
|||||||
return centerCircleLoading;
|
return centerCircleLoading;
|
||||||
} else {
|
} else {
|
||||||
return RefreshIndicator(
|
return RefreshIndicator(
|
||||||
child: FadeIn(
|
child: FadeIn(
|
||||||
key: Key(_status.spi!.name + _status.path!.path),
|
key: Key(_status.spi!.name + _status.path!.path),
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
itemCount: _status.files!.length + 1,
|
itemCount: _status.files!.length + 1,
|
||||||
controller: _scrollController,
|
controller: _scrollController,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
if (index == 0) {
|
if (index == 0) {
|
||||||
return _buildDestSelector();
|
return _buildDestSelector();
|
||||||
}
|
}
|
||||||
final file = _status.files![index - 1];
|
final file = _status.files![index - 1];
|
||||||
final isDir = file.attr.isDirectory;
|
final isDir = file.attr.isDirectory;
|
||||||
return ListTile(
|
return ListTile(
|
||||||
leading: Icon(isDir ? Icons.folder : Icons.insert_drive_file),
|
leading: Icon(isDir ? Icons.folder : Icons.insert_drive_file),
|
||||||
title: Text(file.filename),
|
title: Text(file.filename),
|
||||||
trailing: Text(
|
trailing: Text(
|
||||||
DateTime.fromMillisecondsSinceEpoch(
|
DateTime.fromMillisecondsSinceEpoch(
|
||||||
(file.attr.modifyTime ?? 0) * 1000)
|
(file.attr.modifyTime ?? 0) * 1000)
|
||||||
.toString()
|
.toString()
|
||||||
.replaceFirst('.000', ''),
|
.replaceFirst('.000', ''),
|
||||||
style: const TextStyle(color: Colors.grey),
|
style: const TextStyle(color: Colors.grey),
|
||||||
),
|
),
|
||||||
subtitle:
|
subtitle:
|
||||||
isDir ? null : Text((file.attr.size ?? 0).convertBytes),
|
isDir ? null : Text((file.attr.size ?? 0).convertBytes),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (isDir) {
|
if (isDir) {
|
||||||
_status.path?.update(file.filename);
|
_status.path?.update(file.filename);
|
||||||
listDir(path: _status.path?.path);
|
listDir(path: _status.path?.path);
|
||||||
} else {
|
} else {
|
||||||
onItemPress(context, file, true);
|
onItemPress(context, file, true);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLongPress: () => onItemPress(context, file, false),
|
onLongPress: () => onItemPress(context, file, false),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
|
||||||
),
|
),
|
||||||
onRefresh: () => listDir(path: _status.path?.path));
|
),
|
||||||
|
onRefresh: () => listDir(path: _status.path?.path),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onItemPress(BuildContext context, SftpName file, bool showDownload) {
|
void onItemPress(BuildContext context, SftpName file, bool showDownload) {
|
||||||
showRoundDialog(
|
showRoundDialog(
|
||||||
context,
|
context,
|
||||||
s.choose,
|
s.choose,
|
||||||
Column(
|
Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.delete),
|
leading: const Icon(Icons.delete),
|
||||||
title: Text(s.delete),
|
title: Text(s.delete),
|
||||||
onTap: () => delete(context, file),
|
onTap: () => delete(context, file),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.edit),
|
leading: const Icon(Icons.edit),
|
||||||
title: Text(s.rename),
|
title: Text(s.rename),
|
||||||
onTap: () => rename(context, file),
|
onTap: () => rename(context, file),
|
||||||
),
|
),
|
||||||
showDownload
|
showDownload
|
||||||
? ListTile(
|
? ListTile(
|
||||||
leading: const Icon(Icons.download),
|
leading: const Icon(Icons.download),
|
||||||
title: Text(s.download),
|
title: Text(s.download),
|
||||||
onTap: () => download(context, file),
|
onTap: () => download(context, file),
|
||||||
)
|
)
|
||||||
: const SizedBox()
|
: const SizedBox()
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
[
|
[
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
onPressed: () => Navigator.of(context).pop(), child: Text(s.cancel))
|
||||||
child: Text(s.cancel))
|
],
|
||||||
]);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void download(BuildContext context, SftpName name) {
|
void download(BuildContext context, SftpName name) {
|
||||||
showRoundDialog(context, s.download,
|
showRoundDialog(
|
||||||
Text('${s.dl2Local(name.filename)}\n${s.keepForeground}'), [
|
context,
|
||||||
TextButton(
|
s.download,
|
||||||
onPressed: () => Navigator.of(context).pop(), child: Text(s.cancel)),
|
Text('${s.dl2Local(name.filename)}\n${s.keepForeground}'),
|
||||||
TextButton(
|
[
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
child: Text(s.cancel)),
|
||||||
|
TextButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
final prePath = _status.path!.path;
|
final prePath = _status.path!.path;
|
||||||
@@ -207,36 +214,56 @@ class _SFTPPageState extends State<SFTPPage> {
|
|||||||
prePath + (prePath.endsWith('/') ? '' : '/') + name.filename;
|
prePath + (prePath.endsWith('/') ? '' : '/') + name.filename;
|
||||||
final local = '${(await sftpDownloadDir).path}$remotePath';
|
final local = '${(await sftpDownloadDir).path}$remotePath';
|
||||||
final pubKeyId = _status.spi!.pubKeyId;
|
final pubKeyId = _status.spi!.pubKeyId;
|
||||||
|
|
||||||
locator<SftpDownloadProvider>().add(
|
locator<SftpDownloadProvider>().add(
|
||||||
DownloadItem(_status.spi!, remotePath, local),
|
DownloadItem(
|
||||||
key: pubKeyId == null
|
_status.spi!,
|
||||||
? null
|
remotePath,
|
||||||
: locator<PrivateKeyStore>().get(pubKeyId).privateKey);
|
local,
|
||||||
|
),
|
||||||
|
key: pubKeyId == null
|
||||||
|
? null
|
||||||
|
: locator<PrivateKeyStore>().get(pubKeyId).privateKey,
|
||||||
|
);
|
||||||
|
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
showRoundDialog(context, s.goSftpDlPage, const SizedBox(), [
|
showRoundDialog(
|
||||||
TextButton(
|
context,
|
||||||
|
s.goSftpDlPage,
|
||||||
|
const SizedBox(),
|
||||||
|
[
|
||||||
|
TextButton(
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
child: Text(s.cancel)),
|
child: Text(s.cancel),
|
||||||
TextButton(
|
),
|
||||||
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
AppRoute(const SFTPDownloadingPage(), 'sftp downloading')
|
AppRoute(const SFTPDownloadingPage(), 'sftp downloading')
|
||||||
.go(context);
|
.go(context);
|
||||||
},
|
},
|
||||||
child: Text(s.ok))
|
child: Text(s.ok),
|
||||||
]);
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
},
|
},
|
||||||
child: Text(s.download))
|
child: Text(s.download),
|
||||||
]);
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void delete(BuildContext context, SftpName file) {
|
void delete(BuildContext context, SftpName file) {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
showRoundDialog(context, s.attention, Text(s.sureDelete(file.filename)), [
|
showRoundDialog(
|
||||||
TextButton(
|
context,
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
s.attention,
|
||||||
child: const Text('Cancel')),
|
Text(s.sureDelete(file.filename)),
|
||||||
TextButton(
|
[
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
child: const Text('Cancel')),
|
||||||
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
_status.client!.remove(file.filename);
|
_status.client!.remove(file.filename);
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
@@ -245,126 +272,149 @@ class _SFTPPageState extends State<SFTPPage> {
|
|||||||
child: Text(
|
child: Text(
|
||||||
s.delete,
|
s.delete,
|
||||||
style: const TextStyle(color: Colors.red),
|
style: const TextStyle(color: Colors.red),
|
||||||
)),
|
),
|
||||||
]);
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mkdir(BuildContext context) {
|
void mkdir(BuildContext context) {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
final textController = TextEditingController();
|
final textController = TextEditingController();
|
||||||
showRoundDialog(
|
showRoundDialog(
|
||||||
context,
|
context,
|
||||||
s.createFolder,
|
s.createFolder,
|
||||||
TextField(
|
TextField(
|
||||||
controller: textController,
|
controller: textController,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: s.name,
|
labelText: s.name,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
[
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
child: Text(s.cancel),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
if (textController.text == '') {
|
||||||
|
showRoundDialog(
|
||||||
|
context,
|
||||||
|
s.attention,
|
||||||
|
Text(s.fieldMustNotEmpty),
|
||||||
|
[
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
child: Text(s.ok)),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_status.client!
|
||||||
|
.mkdir('${_status.path!.path}/${textController.text}');
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
listDir();
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
s.ok,
|
||||||
|
style: const TextStyle(color: Colors.red),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
[
|
],
|
||||||
TextButton(
|
);
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
|
||||||
child: Text(s.cancel)),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () {
|
|
||||||
if (textController.text == '') {
|
|
||||||
showRoundDialog(
|
|
||||||
context, s.attention, Text(s.fieldMustNotEmpty), [
|
|
||||||
TextButton(
|
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
|
||||||
child: Text(s.ok)),
|
|
||||||
]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_status.client!
|
|
||||||
.mkdir('${_status.path!.path}/${textController.text}');
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
listDir();
|
|
||||||
},
|
|
||||||
child: Text(
|
|
||||||
s.ok,
|
|
||||||
style: const TextStyle(color: Colors.red),
|
|
||||||
)),
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void newFile(BuildContext context) {
|
void newFile(BuildContext context) {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
final textController = TextEditingController();
|
final textController = TextEditingController();
|
||||||
showRoundDialog(
|
showRoundDialog(
|
||||||
context,
|
context,
|
||||||
s.createFile,
|
s.createFile,
|
||||||
TextField(
|
TextField(
|
||||||
controller: textController,
|
controller: textController,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: s.name,
|
labelText: s.name,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
[
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
child: Text(s.cancel),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () async {
|
||||||
|
if (textController.text == '') {
|
||||||
|
showRoundDialog(
|
||||||
|
context,
|
||||||
|
s.attention,
|
||||||
|
Text(s.fieldMustNotEmpty),
|
||||||
|
[
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
child: Text(s.ok),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
(await _status.client!
|
||||||
|
.open('${_status.path!.path}/${textController.text}'))
|
||||||
|
.writeBytes(Uint8List(0));
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
listDir();
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
s.ok,
|
||||||
|
style: const TextStyle(color: Colors.red),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
[
|
],
|
||||||
TextButton(
|
);
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
|
||||||
child: Text(s.cancel)),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () async {
|
|
||||||
if (textController.text == '') {
|
|
||||||
showRoundDialog(
|
|
||||||
context, s.attention, Text(s.fieldMustNotEmpty), [
|
|
||||||
TextButton(
|
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
|
||||||
child: Text(s.ok)),
|
|
||||||
]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
(await _status.client!
|
|
||||||
.open('${_status.path!.path}/${textController.text}'))
|
|
||||||
.writeBytes(Uint8List(0));
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
listDir();
|
|
||||||
},
|
|
||||||
child: Text(
|
|
||||||
s.ok,
|
|
||||||
style: const TextStyle(color: Colors.red),
|
|
||||||
)),
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void rename(BuildContext context, SftpName file) {
|
void rename(BuildContext context, SftpName file) {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
final textController = TextEditingController();
|
final textController = TextEditingController();
|
||||||
showRoundDialog(
|
showRoundDialog(
|
||||||
context,
|
context,
|
||||||
s.rename,
|
s.rename,
|
||||||
TextField(
|
TextField(
|
||||||
controller: textController,
|
controller: textController,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: s.name,
|
labelText: s.name,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
[
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
child: Text(s.cancel)),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () async {
|
||||||
|
if (textController.text == '') {
|
||||||
|
showRoundDialog(
|
||||||
|
context,
|
||||||
|
s.attention,
|
||||||
|
Text(s.fieldMustNotEmpty),
|
||||||
|
[
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
child: Text(s.ok),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await _status.client!.rename(file.filename, textController.text);
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
listDir();
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
s.rename,
|
||||||
|
style: const TextStyle(color: Colors.red),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
[
|
],
|
||||||
TextButton(
|
);
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
|
||||||
child: Text(s.cancel)),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () async {
|
|
||||||
if (textController.text == '') {
|
|
||||||
showRoundDialog(
|
|
||||||
context, s.attention, Text(s.fieldMustNotEmpty), [
|
|
||||||
TextButton(
|
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
|
||||||
child: Text(s.ok)),
|
|
||||||
]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await _status.client!
|
|
||||||
.rename(file.filename, textController.text);
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
listDir();
|
|
||||||
},
|
|
||||||
child: Text(
|
|
||||||
s.rename,
|
|
||||||
style: const TextStyle(color: Colors.red),
|
|
||||||
)),
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> listDir({String? path, SSHClient? client}) async {
|
Future<void> listDir({String? path, SSHClient? client}) async {
|
||||||
@@ -388,10 +438,17 @@ class _SFTPPageState extends State<SFTPPage> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await showRoundDialog(context, s.error, Text(e.toString()), [
|
await showRoundDialog(
|
||||||
TextButton(
|
context,
|
||||||
onPressed: () => Navigator.of(context).pop(), child: Text(s.ok))
|
s.error,
|
||||||
]);
|
Text(e.toString()),
|
||||||
|
[
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
child: Text(s.ok),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
if (_status.path!.undo()) {
|
if (_status.path!.undo()) {
|
||||||
await listDir();
|
await listDir();
|
||||||
}
|
}
|
||||||
@@ -420,11 +477,12 @@ class _SFTPPageState extends State<SFTPPage> {
|
|||||||
_status.selected = true;
|
_status.selected = true;
|
||||||
_status.path = AbsolutePath('/');
|
_status.path = AbsolutePath('/');
|
||||||
listDir(
|
listDir(
|
||||||
client: locator<ServerProvider>()
|
client: locator<ServerProvider>()
|
||||||
.servers
|
.servers
|
||||||
.firstWhere((s) => s.info == spi)
|
.firstWhere((s) => s.info == spi)
|
||||||
.client,
|
.client,
|
||||||
path: '/');
|
path: '/',
|
||||||
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,17 +41,20 @@ class _SnippetEditPageState extends State<SnippetEditPage>
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(title: Text(s.edit, style: textSize18), actions: [
|
appBar: AppBar(
|
||||||
widget.snippet != null
|
title: Text(s.edit, style: textSize18),
|
||||||
? IconButton(
|
actions: [
|
||||||
onPressed: () {
|
widget.snippet != null
|
||||||
_provider.del(widget.snippet!);
|
? IconButton(
|
||||||
Navigator.of(context).pop();
|
onPressed: () {
|
||||||
},
|
_provider.del(widget.snippet!);
|
||||||
tooltip: s.delete,
|
Navigator.of(context).pop();
|
||||||
icon: const Icon(Icons.delete))
|
},
|
||||||
: const SizedBox()
|
tooltip: s.delete,
|
||||||
]),
|
icon: const Icon(Icons.delete))
|
||||||
|
: const SizedBox()
|
||||||
|
],
|
||||||
|
),
|
||||||
body: ListView(
|
body: ListView(
|
||||||
padding: const EdgeInsets.all(13),
|
padding: const EdgeInsets.all(13),
|
||||||
children: [
|
children: [
|
||||||
|
|||||||
@@ -59,107 +59,135 @@ class _SnippetListPageState extends State<SnippetListPage> {
|
|||||||
itemCount: key.snippets.length,
|
itemCount: key.snippets.length,
|
||||||
itemExtent: 57,
|
itemExtent: 57,
|
||||||
itemBuilder: (context, idx) {
|
itemBuilder: (context, idx) {
|
||||||
return RoundRectCard(Padding(
|
return RoundRectCard(
|
||||||
padding: roundRectCardPadding,
|
Padding(
|
||||||
child: Row(
|
padding: roundRectCardPadding,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
Text(
|
children: [
|
||||||
key.snippets[idx].name,
|
Text(
|
||||||
textAlign: TextAlign.center,
|
key.snippets[idx].name,
|
||||||
),
|
textAlign: TextAlign.center,
|
||||||
Row(children: [
|
),
|
||||||
TextButton(
|
Row(
|
||||||
onPressed: () => AppRoute(
|
children: [
|
||||||
SnippetEditPage(
|
TextButton(
|
||||||
snippet: key.snippets[idx]),
|
onPressed: () => AppRoute(
|
||||||
'snippet edit page')
|
SnippetEditPage(
|
||||||
.go(context),
|
snippet: key.snippets[idx]),
|
||||||
child: Text(
|
'snippet edit page')
|
||||||
s.edit,
|
.go(context),
|
||||||
style: _textStyle,
|
child: Text(
|
||||||
)),
|
s.edit,
|
||||||
TextButton(
|
style: _textStyle,
|
||||||
onPressed: () {
|
),
|
||||||
final snippet = key.snippets[idx];
|
),
|
||||||
if (widget.spi == null) {
|
TextButton(
|
||||||
_showRunDialog(snippet);
|
onPressed: () {
|
||||||
return;
|
final snippet = key.snippets[idx];
|
||||||
}
|
if (widget.spi == null) {
|
||||||
run(context, snippet);
|
_showRunDialog(snippet);
|
||||||
},
|
return;
|
||||||
child: Text(
|
}
|
||||||
s.run,
|
run(context, snippet);
|
||||||
style: _textStyle,
|
},
|
||||||
))
|
child: Text(
|
||||||
])
|
s.run,
|
||||||
],
|
style: _textStyle,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
));
|
);
|
||||||
})
|
},
|
||||||
: Center(child: Text(s.noSavedSnippet));
|
)
|
||||||
|
: Center(
|
||||||
|
child: Text(s.noSavedSnippet),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showRunDialog(Snippet snippet) {
|
void _showRunDialog(Snippet snippet) {
|
||||||
showRoundDialog(context, s.chooseDestination,
|
showRoundDialog(
|
||||||
Consumer<ServerProvider>(builder: (_, provider, __) {
|
context,
|
||||||
if (provider.servers.isEmpty) {
|
s.chooseDestination,
|
||||||
return Text(s.noServerAvailable);
|
Consumer<ServerProvider>(
|
||||||
}
|
builder: (_, provider, __) {
|
||||||
_selectedIndex = provider.servers.first.info;
|
if (provider.servers.isEmpty) {
|
||||||
return SizedBox(
|
return Text(s.noServerAvailable);
|
||||||
height: 111,
|
}
|
||||||
child: Stack(children: [
|
_selectedIndex = provider.servers.first.info;
|
||||||
Positioned(
|
return SizedBox(
|
||||||
top: 36,
|
height: 111,
|
||||||
bottom: 36,
|
child: Stack(
|
||||||
left: 0,
|
children: [
|
||||||
right: 0,
|
Positioned(
|
||||||
child: Container(
|
top: 36,
|
||||||
height: 37,
|
bottom: 36,
|
||||||
decoration: const BoxDecoration(
|
left: 0,
|
||||||
borderRadius: BorderRadius.all(Radius.circular(7)),
|
right: 0,
|
||||||
color: Colors.black12,
|
child: Container(
|
||||||
|
height: 37,
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(7)),
|
||||||
|
color: Colors.black12,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
ListWheelScrollView.useDelegate(
|
||||||
|
itemExtent: 37,
|
||||||
|
diameterRatio: 1.2,
|
||||||
|
controller: FixedExtentScrollController(initialItem: 0),
|
||||||
|
onSelectedItemChanged: (idx) =>
|
||||||
|
_selectedIndex = provider.servers[idx].info,
|
||||||
|
physics: const FixedExtentScrollPhysics(),
|
||||||
|
childDelegate: ListWheelChildBuilderDelegate(
|
||||||
|
builder: (context, index) => Center(
|
||||||
|
child: Text(
|
||||||
|
provider.servers[index].info.name,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
childCount: provider.servers.length),
|
||||||
|
)
|
||||||
|
],
|
||||||
),
|
),
|
||||||
ListWheelScrollView.useDelegate(
|
);
|
||||||
itemExtent: 37,
|
},
|
||||||
diameterRatio: 1.2,
|
),
|
||||||
controller: FixedExtentScrollController(initialItem: 0),
|
[
|
||||||
onSelectedItemChanged: (idx) =>
|
TextButton(
|
||||||
_selectedIndex = provider.servers[idx].info,
|
onPressed: () async => run(context, snippet),
|
||||||
physics: const FixedExtentScrollPhysics(),
|
child: Text(s.run),
|
||||||
childDelegate: ListWheelChildBuilderDelegate(
|
),
|
||||||
builder: (context, index) => Center(
|
TextButton(
|
||||||
child: Text(
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
provider.servers[index].info.name,
|
child: Text(s.cancel),
|
||||||
textAlign: TextAlign.center,
|
),
|
||||||
),
|
],
|
||||||
),
|
);
|
||||||
childCount: provider.servers.length),
|
|
||||||
)
|
|
||||||
]));
|
|
||||||
}), [
|
|
||||||
TextButton(
|
|
||||||
onPressed: () async => run(context, snippet), child: Text(s.run)),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () => Navigator.of(context).pop(), child: Text(s.cancel)),
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> run(BuildContext context, Snippet snippet) async {
|
Future<void> run(BuildContext context, Snippet snippet) async {
|
||||||
final result = await locator<ServerProvider>()
|
final result = await locator<ServerProvider>()
|
||||||
.runSnippet(widget.spi ?? _selectedIndex, snippet);
|
.runSnippet(widget.spi ?? _selectedIndex, snippet);
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
showRoundDialog(context, s.result,
|
showRoundDialog(
|
||||||
Text(result, style: const TextStyle(fontSize: 13)), [
|
context,
|
||||||
TextButton(
|
s.result,
|
||||||
onPressed: () => Navigator.of(context).pop(), child: Text(s.close))
|
Text(result, style: const TextStyle(fontSize: 13)),
|
||||||
]);
|
[
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
child: Text(s.close),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user