diff --git a/.dart_tool/flutter_gen/gen_l10n/l10n.dart b/.dart_tool/flutter_gen/gen_l10n/l10n.dart index c0cd1bae..bbb64771 100644 --- a/.dart_tool/flutter_gen/gen_l10n/l10n.dart +++ b/.dart_tool/flutter_gen/gen_l10n/l10n.dart @@ -108,24 +108,30 @@ abstract class S { /// **'Thanks to the following people who participated in.'** String get aboutThanks; + /// No description provided for @add. + /// + /// In en, this message translates to: + /// **'Add'** + String get add; + /// No description provided for @addAServer. /// /// In en, this message translates to: /// **'add a server'** String get addAServer; - /// No description provided for @addOne. - /// - /// In en, this message translates to: - /// **'Add one'** - String get addOne; - /// No description provided for @addPrivateKey. /// /// In en, this message translates to: /// **'Add private key'** String get addPrivateKey; + /// No description provided for @all. + /// + /// In en, this message translates to: + /// **'All'** + String get all; + /// No description provided for @alreadyLastDir. /// /// In en, this message translates to: diff --git a/.dart_tool/flutter_gen/gen_l10n/l10n_de.dart b/.dart_tool/flutter_gen/gen_l10n/l10n_de.dart index e44b2289..9d83dd47 100644 --- a/.dart_tool/flutter_gen/gen_l10n/l10n_de.dart +++ b/.dart_tool/flutter_gen/gen_l10n/l10n_de.dart @@ -10,14 +10,17 @@ class SDe extends S { @override String get aboutThanks => 'Vielen Dank an die folgenden Personen, die daran teilgenommen haben.\n'; + @override + String get add => 'Neu'; + @override String get addAServer => 'Server hinzufügen'; @override - String get addOne => 'Hinzufügen'; + String get addPrivateKey => 'Private key hinzufügen'; @override - String get addPrivateKey => 'Private key hinzufügen'; + String get all => 'Alle'; @override String get alreadyLastDir => 'Bereits im letzten Verzeichnis.'; diff --git a/.dart_tool/flutter_gen/gen_l10n/l10n_en.dart b/.dart_tool/flutter_gen/gen_l10n/l10n_en.dart index 30908d69..0d951e5c 100644 --- a/.dart_tool/flutter_gen/gen_l10n/l10n_en.dart +++ b/.dart_tool/flutter_gen/gen_l10n/l10n_en.dart @@ -10,14 +10,17 @@ class SEn extends S { @override String get aboutThanks => 'Thanks to the following people who participated in.'; + @override + String get add => 'Add'; + @override String get addAServer => 'add a server'; @override - String get addOne => 'Add one'; + String get addPrivateKey => 'Add private key'; @override - String get addPrivateKey => 'Add private key'; + String get all => 'All'; @override String get alreadyLastDir => 'Already in last directory.'; diff --git a/.dart_tool/flutter_gen/gen_l10n/l10n_zh.dart b/.dart_tool/flutter_gen/gen_l10n/l10n_zh.dart index daa8c02a..36164e9c 100644 --- a/.dart_tool/flutter_gen/gen_l10n/l10n_zh.dart +++ b/.dart_tool/flutter_gen/gen_l10n/l10n_zh.dart @@ -10,14 +10,17 @@ class SZh extends S { @override String get aboutThanks => '感谢以下参与的各位。'; + @override + String get add => '新增'; + @override String get addAServer => '添加服务器'; @override - String get addOne => '前去新增'; + String get addPrivateKey => '添加一个私钥'; @override - String get addPrivateKey => '添加一个私钥'; + String get all => '所有'; @override String get alreadyLastDir => '已经是最上层目录了'; @@ -614,14 +617,17 @@ class SZhTw extends SZh { @override String get aboutThanks => '感謝以下參與的各位。'; + @override + String get add => '新增'; + @override String get addAServer => '新增服務器'; @override - String get addOne => '前去新增'; + String get addPrivateKey => '新增一個私鑰'; @override - String get addPrivateKey => '新增一個私鑰'; + String get all => '所有'; @override String get alreadyLastDir => '已經是最上層目錄了'; diff --git a/lib/core/extension/order.dart b/lib/core/extension/order.dart index baa5a957..582c336a 100644 --- a/lib/core/extension/order.dart +++ b/lib/core/extension/order.dart @@ -19,4 +19,16 @@ extension StringOrderX on StringOrder { if (index == -1) return; this[index] = newId; } + + int index(String id) { + return indexOf(id); + } + + void moveById(String oid, String nid, StoreProperty property) { + final index = indexOf(oid); + if (index == -1) return; + final newIndex = indexOf(nid); + if (newIndex == -1) return; + move(index, newIndex, property); + } } diff --git a/lib/core/utils/ui.dart b/lib/core/utils/ui.dart index 630d8fc2..09719982 100644 --- a/lib/core/utils/ui.dart +++ b/lib/core/utils/ui.dart @@ -141,7 +141,7 @@ void showSnippetDialog( context.pop(); AppRoute(const SnippetEditPage(), 'edit snippet').go(context); }, - child: Text(s.addOne), + child: Text(s.add), ) ], ); diff --git a/lib/data/model/server/server_private_info.dart b/lib/data/model/server/server_private_info.dart index 55521f2f..049a97b3 100644 --- a/lib/data/model/server/server_private_info.dart +++ b/lib/data/model/server/server_private_info.dart @@ -16,6 +16,8 @@ class ServerPrivateInfo { late String pwd; @HiveField(5) String? pubKeyId; + @HiveField(6) + List? tags; late String id; @@ -26,6 +28,7 @@ class ServerPrivateInfo { required this.user, required this.pwd, this.pubKeyId, + this.tags, }) : id = '$user@$ip:$port'; ServerPrivateInfo.fromJson(Map json) { @@ -36,6 +39,7 @@ class ServerPrivateInfo { pwd = json["authorization"].toString(); pubKeyId = json["pubKeyId"]?.toString(); id = '$user@$ip:$port'; + tags = json["tags"]?.cast(); } Map toJson() { @@ -46,6 +50,11 @@ class ServerPrivateInfo { data["user"] = user; data["authorization"] = pwd; data["pubKeyId"] = pubKeyId; + data["tags"] = tags; return data; } + + bool shouldReconnect(ServerPrivateInfo old) { + return id != id || pwd != old.pwd || pubKeyId != old.pubKeyId; + } } diff --git a/lib/data/model/server/server_private_info.g.dart b/lib/data/model/server/server_private_info.g.dart index c25fa678..5ed90420 100644 --- a/lib/data/model/server/server_private_info.g.dart +++ b/lib/data/model/server/server_private_info.g.dart @@ -23,13 +23,14 @@ class ServerPrivateInfoAdapter extends TypeAdapter { user: fields[3] as String, pwd: fields[4] as String, pubKeyId: fields[5] as String?, + tags: (fields[6] as List?)?.cast(), ); } @override void write(BinaryWriter writer, ServerPrivateInfo obj) { writer - ..writeByte(6) + ..writeByte(7) ..writeByte(0) ..write(obj.name) ..writeByte(1) @@ -41,7 +42,9 @@ class ServerPrivateInfoAdapter extends TypeAdapter { ..writeByte(4) ..write(obj.pwd) ..writeByte(5) - ..write(obj.pubKeyId); + ..write(obj.pubKeyId) + ..writeByte(6) + ..write(obj.tags); } @override diff --git a/lib/data/model/server/snippet.dart b/lib/data/model/server/snippet.dart index 36c18573..20c504a4 100644 --- a/lib/data/model/server/snippet.dart +++ b/lib/data/model/server/snippet.dart @@ -8,20 +8,16 @@ class Snippet { late String name; @HiveField(1) late String script; - @HiveField(2) - List? tags; - Snippet(this.name, this.script, {this.tags}); + Snippet(this.name, this.script); Snippet.fromJson(Map json) { name = json['name'].toString(); script = json['script'].toString(); - tags = json['tags'].cast(); } Map toJson() { final data = {}; data['name'] = name; data['script'] = script; - data['tags'] = tags; return data; } } diff --git a/lib/data/model/server/snippet.g.dart b/lib/data/model/server/snippet.g.dart index 948c7e5e..acef2f9a 100644 --- a/lib/data/model/server/snippet.g.dart +++ b/lib/data/model/server/snippet.g.dart @@ -19,20 +19,17 @@ class SnippetAdapter extends TypeAdapter { return Snippet( fields[0] as String, fields[1] as String, - tags: (fields[2] as List?)?.cast(), ); } @override void write(BinaryWriter writer, Snippet obj) { writer - ..writeByte(3) + ..writeByte(2) ..writeByte(0) ..write(obj.name) ..writeByte(1) - ..write(obj.script) - ..writeByte(2) - ..write(obj.tags); + ..write(obj.script); } @override diff --git a/lib/data/provider/server.dart b/lib/data/provider/server.dart index e9c08fce..c2ba7dfa 100644 --- a/lib/data/provider/server.dart +++ b/lib/data/provider/server.dart @@ -23,7 +23,10 @@ typedef ServersMap = Map; class ServerProvider extends BusyProvider { final ServersMap _servers = {}; ServersMap get servers => _servers; - final StringOrder serverOrder = []; + final StringOrder _serverOrder = []; + StringOrder get serverOrder => _serverOrder; + final List _tags = []; + List get tags => _tags; final _limiter = TryLimiter(); @@ -42,23 +45,38 @@ class ServerProvider extends BusyProvider { } final serverOrder_ = _settingStore.serverOrder.fetch(); if (serverOrder_ != null) { - serverOrder.addAll(serverOrder_.toSet()); - if (serverOrder.length != infos.length) { + _serverOrder.addAll(serverOrder_.toSet()); + if (_serverOrder.length != infos.length) { final missed = infos .where( - (e) => !serverOrder.contains(e.id), + (e) => !_serverOrder.contains(e.id), ) .map((e) => e.id); - serverOrder.addAll(missed); + _serverOrder.addAll(missed); } } else { - serverOrder.addAll(_servers.keys); + _serverOrder.addAll(_servers.keys); } - _settingStore.serverOrder.put(serverOrder); + _settingStore.serverOrder.put(_serverOrder); + _updateTags(); setBusyState(false); notifyListeners(); } + void _updateTags() { + _tags.clear(); + for (final s in _servers.values) { + if (s.spi.tags == null) continue; + for (final t in s.spi.tags!) { + if (!_tags.contains(t)) { + _tags.add(t); + } + } + } + _tags.sort(); + notifyListeners(); + } + Server genServer(ServerPrivateInfo spi) { return Server(spi, initStatus, null, ServerState.disconnected); } @@ -81,8 +99,7 @@ class ServerProvider extends BusyProvider { } Future startAutoRefresh() async { - final duration = - locator().serverStatusUpdateInterval.fetch()!; + final duration = _settingStore.serverStatusUpdateInterval.fetch()!; if (duration == 0) return; stopAutoRefresh(); _timer = Timer.periodic(Duration(seconds: duration), (_) async { @@ -126,15 +143,17 @@ class ServerProvider extends BusyProvider { _servers[spi.id] = genServer(spi); notifyListeners(); _serverStore.put(spi); - serverOrder.add(spi.id); - _settingStore.serverOrder.put(serverOrder); + _serverOrder.add(spi.id); + _settingStore.serverOrder.put(_serverOrder); + _updateTags(); refreshData(spi: spi); } void delServer(String id) { _servers.remove(id); - serverOrder.remove(id); - _settingStore.serverOrder.put(serverOrder); + _serverOrder.remove(id); + _settingStore.serverOrder.put(_serverOrder); + _updateTags(); notifyListeners(); _serverStore.delete(id); } @@ -143,13 +162,27 @@ class ServerProvider extends BusyProvider { ServerPrivateInfo old, ServerPrivateInfo newSpi, ) async { - _servers.remove(old.id); - _serverStore.update(old, newSpi); - _servers[newSpi.id] = genServer(newSpi); - _servers[newSpi.id]?.client = await genClient(newSpi); - serverOrder.update(old.id, newSpi.id); - _settingStore.serverOrder.put(serverOrder); - await refreshData(spi: newSpi); + if (old != newSpi) { + _serverStore.update(old, newSpi); + _servers[old.id]?.spi = newSpi; + + if (newSpi.id != old.id) { + _servers[newSpi.id] = _servers[old.id]!; + _servers[newSpi.id]?.spi = newSpi; + _servers.remove(old.id); + _serverOrder.update(old.id, newSpi.id); + _settingStore.serverOrder.put(_serverOrder); + } + + // Only reconnect if neccessary + if (newSpi.shouldReconnect(old)) { + _servers[newSpi.id]?.client = await genClient(newSpi); + refreshData(spi: newSpi); + } + + // Only update if [spi.tags] changed + _updateTags(); + } } Future _getData(ServerPrivateInfo spi) async { diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index f325a368..5c5eb322 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -2,9 +2,10 @@ "@@locale": "de", "about": "Über", "aboutThanks": "Vielen Dank an die folgenden Personen, die daran teilgenommen haben.\n", + "add": "Neu", "addAServer": "Server hinzufügen", - "addOne": "Hinzufügen", "addPrivateKey": "Private key hinzufügen", + "all": "Alle", "alreadyLastDir": "Bereits im letzten Verzeichnis.", "appPrimaryColor": "Farbschema", "attention": "Achtung", diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index f8ea78f4..099c53a7 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -2,9 +2,10 @@ "@@locale": "en", "about": "About", "aboutThanks": "Thanks to the following people who participated in.", + "add": "Add", "addAServer": "add a server", - "addOne": "Add one", "addPrivateKey": "Add private key", + "all": "All", "alreadyLastDir": "Already in last directory.", "appPrimaryColor": "App primary color", "attention": "Attention", diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb index 2b9d2c2b..c39cd1e7 100644 --- a/lib/l10n/app_zh.arb +++ b/lib/l10n/app_zh.arb @@ -2,9 +2,10 @@ "@@locale": "zh", "about": "关于", "aboutThanks": "感谢以下参与的各位。", + "add": "新增", "addAServer": "添加服务器", - "addOne": "前去新增", "addPrivateKey": "添加一个私钥", + "all": "所有", "alreadyLastDir": "已经是最上层目录了", "appPrimaryColor": "App主要色", "attention": "注意", diff --git a/lib/l10n/app_zh_tw.arb b/lib/l10n/app_zh_tw.arb index 218ff1c3..eb55017b 100644 --- a/lib/l10n/app_zh_tw.arb +++ b/lib/l10n/app_zh_tw.arb @@ -2,9 +2,10 @@ "@@locale": "zh_TW", "about": "關於", "aboutThanks": "感謝以下參與的各位。", + "add": "新增", "addAServer": "新增服務器", - "addOne": "前去新增", "addPrivateKey": "新增一個私鑰", + "all": "所有", "alreadyLastDir": "已經是最上層目錄了", "appPrimaryColor": "主要色調", "attention": "注意", diff --git a/lib/view/page/convert.dart b/lib/view/page/convert.dart index 4ba39de2..f5ec4e0f 100644 --- a/lib/view/page/convert.dart +++ b/lib/view/page/convert.dart @@ -69,7 +69,6 @@ class _ConvertPageState extends State } }, tooltip: _s.convert, - heroTag: 'convert fab', child: const Icon(Icons.send), ), ); diff --git a/lib/view/page/ping.dart b/lib/view/page/ping.dart index 734f56ee..280ca519 100644 --- a/lib/view/page/ping.dart +++ b/lib/view/page/ping.dart @@ -76,7 +76,6 @@ class _PingPageState extends State ), ), floatingActionButton: FloatingActionButton( - heroTag: 'ping fab', onPressed: () { try { doPing(); diff --git a/lib/view/page/server/edit.dart b/lib/view/page/server/edit.dart index 37110d28..e2159621 100644 --- a/lib/view/page/server/edit.dart +++ b/lib/view/page/server/edit.dart @@ -14,6 +14,7 @@ import '../../../data/provider/server.dart'; import '../../../data/res/ui.dart'; import '../../../data/store/private_key.dart'; import '../../../locator.dart'; +import '../../widget/tag.dart'; import '../private_key/edit.dart'; class ServerEditPage extends StatefulWidget { @@ -43,6 +44,7 @@ class _ServerEditPageState extends State with AfterLayoutMixin { bool usePublicKey = false; int? _pubKeyIndex; PrivateKeyInfo? _keyInfo; + List _tags = []; @override void initState() { @@ -143,6 +145,13 @@ class _ServerEditPageState extends State with AfterLayoutMixin { icon: Icons.account_box, hint: 'root', ), + TagEditor( + tags: _tags, + onChanged: (p0) => setState(() { + _tags = p0; + }), + s: _s, + ), width7, Row( children: [ @@ -262,6 +271,7 @@ class _ServerEditPageState extends State with AfterLayoutMixin { user: _usernameController.text, pwd: authorization, pubKeyId: usePublicKey ? _keyInfo!.id : null, + tags: _tags, ); if (widget.spi == null) { @@ -300,6 +310,9 @@ class _ServerEditPageState extends State with AfterLayoutMixin { } else { usePublicKey = true; } + if (widget.spi?.tags != null) { + _tags = widget.spi!.tags!; + } setState(() {}); } } diff --git a/lib/view/page/server/tab.dart b/lib/view/page/server/tab.dart index 34ea5968..03dfd51b 100644 --- a/lib/view/page/server/tab.dart +++ b/lib/view/page/server/tab.dart @@ -7,6 +7,7 @@ import 'package:provider/provider.dart'; import 'package:toolbox/core/extension/navigator.dart'; import 'package:toolbox/core/extension/order.dart'; import 'package:toolbox/core/utils/misc.dart'; +import 'package:toolbox/view/widget/fade_in.dart'; import '../../../core/route.dart'; import '../../../core/utils/ui.dart'; @@ -45,6 +46,8 @@ class _ServerPageState extends State late SettingStore _settingStore; late S _s; + String? _tag; + @override void initState() { super.initState(); @@ -71,18 +74,65 @@ class _ServerPageState extends State 'Add server info page', ).go(context), tooltip: _s.addAServer, - heroTag: 'server page fab', child: const Icon(Icons.add), ), ); } + Widget _buildTagsSwitcher(ServerProvider pro) { + if (pro.tags.isEmpty) return placeholder; + final items = [null, ...pro.tags]; + return Container( + height: 37, + width: _media.size.width, + alignment: Alignment.center, + color: Colors.transparent, + child: ListView.builder( + scrollDirection: Axis.horizontal, + itemBuilder: (context, index) => _buildTagItem(items[index]), + itemCount: items.length, + ), + ); + } + + Widget _buildTagItem(String? tag) { + return Padding( + padding: const EdgeInsets.only(left: 4, right: 5, bottom: 9), + child: GestureDetector( + onTap: () { + setState(() { + _tag = tag; + }); + }, + child: Container( + decoration: BoxDecoration( + borderRadius: const BorderRadius.all(Radius.circular(20.0)), + color: primaryColor.withAlpha(20), + ), + padding: const EdgeInsets.symmetric(horizontal: 11, vertical: 2.7), + child: Center( + child: Text( + tag == null ? _s.all : '#$tag', + style: TextStyle( + color: _tag == tag ? null : _theme.disabledColor, + fontSize: 15, + fontWeight: FontWeight.w500, + ), + ), + )), + ), + ); + } + Widget _buildBody() { return RefreshIndicator( onRefresh: () async => await _serverProvider.refreshData(onlyFailed: true), child: Consumer( builder: (_, pro, __) { + if (!pro.tags.contains(_tag)) { + _tag = null; + } if (pro.serverOrder.isEmpty) { return Center( child: Text( @@ -91,21 +141,29 @@ class _ServerPageState extends State ), ); } - return ReorderableListView( - padding: const EdgeInsets.fromLTRB(7, 10, 7, 7), - physics: const AlwaysScrollableScrollPhysics(), - onReorder: (oldIndex, newIndex) => setState(() { - pro.serverOrder.move( - oldIndex, - newIndex, - _settingStore.serverOrder, - ); - }), - children: pro.serverOrder - .where((e) => pro.servers.containsKey(e)) - .map((e) => _buildEachServerCard(pro.servers[e])) - .toList(), - ); + final filtered = pro.serverOrder + .where((e) => pro.servers.containsKey(e)) + .where((e) => + _tag == null || + (pro.servers[e]?.spi.tags?.contains(_tag) ?? false)) + .toList(); + return FadeIn( + key: ValueKey(_tag), + child: ReorderableListView( + header: _buildTagsSwitcher(pro), + padding: const EdgeInsets.fromLTRB(7, 10, 7, 7), + physics: const AlwaysScrollableScrollPhysics(), + onReorder: (oldIndex, newIndex) => setState(() { + pro.serverOrder.moveById( + filtered[oldIndex], + filtered[newIndex], + _settingStore.serverOrder, + ); + }), + children: filtered + .map((e) => _buildEachServerCard(pro.servers[e])) + .toList(), + )); }, ), ); diff --git a/lib/view/page/sftp/view.dart b/lib/view/page/sftp/view.dart index 2f7f20e0..c725f355 100644 --- a/lib/view/page/sftp/view.dart +++ b/lib/view/page/sftp/view.dart @@ -217,7 +217,7 @@ class _SFTPPageState extends State { _onItemPress(context, file, true); } }, - onLongPress: () => _onItemPress(context, file, false), + onLongPress: () => _onItemPress(context, file, !isDir), ); }, ), diff --git a/lib/view/page/snippet/edit.dart b/lib/view/page/snippet/edit.dart index 406624a4..e32f1ba7 100644 --- a/lib/view/page/snippet/edit.dart +++ b/lib/view/page/snippet/edit.dart @@ -3,7 +3,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:toolbox/core/extension/navigator.dart'; import 'package:toolbox/view/widget/input_field.dart'; -import 'package:toolbox/view/widget/tag.dart'; import '../../../core/utils/ui.dart'; import '../../../data/model/server/snippet.dart'; @@ -29,8 +28,6 @@ class _SnippetEditPageState extends State late SnippetProvider _provider; late S _s; - var _tags = []; - @override void initState() { super.initState(); @@ -76,7 +73,7 @@ class _SnippetEditPageState extends State showSnackBar(context, Text(_s.fieldMustNotEmpty)); return; } - final snippet = Snippet(name, script, tags: _tags); + final snippet = Snippet(name, script); if (widget.snippet != null) { _provider.update(widget.snippet!, snippet); } else { @@ -107,13 +104,6 @@ class _SnippetEditPageState extends State label: _s.snippet, icon: Icons.code, ), - TagEditor( - tags: widget.snippet?.tags ?? [], - onChanged: (p0) => setState(() { - _tags = p0; - }), - s: _s.tag, - ) ], ); } @@ -123,7 +113,6 @@ class _SnippetEditPageState extends State if (widget.snippet != null) { _nameController.text = widget.snippet!.name; _scriptController.text = widget.snippet!.script; - _tags = widget.snippet!.tags ?? []; } } } diff --git a/lib/view/page/snippet/group_order.dart b/lib/view/page/snippet/group_order.dart deleted file mode 100644 index c04bb452..00000000 --- a/lib/view/page/snippet/group_order.dart +++ /dev/null @@ -1,5 +0,0 @@ -// import 'package:flutter/material.dart'; - -// class SnippetGroupOrderPage extends StatelessWidget { -// final -// } diff --git a/lib/view/widget/tag.dart b/lib/view/widget/tag.dart index 5e2aec1d..8fd40d3e 100644 --- a/lib/view/widget/tag.dart +++ b/lib/view/widget/tag.dart @@ -1,16 +1,22 @@ import 'package:flutter/material.dart'; import 'package:toolbox/view/widget/input_field.dart'; import 'package:toolbox/view/widget/round_rect_card.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; +import '../../core/utils/ui.dart'; import '../../data/res/color.dart'; class TagEditor extends StatelessWidget { final List tags; - final String s; + final S s; final void Function(List)? onChanged; - const TagEditor( - {super.key, required this.tags, this.onChanged, required this.s}); + const TagEditor({ + super.key, + required this.tags, + this.onChanged, + required this.s, + }); @override Widget build(BuildContext context) { @@ -38,7 +44,7 @@ class TagEditor extends StatelessWidget { List tags, Function(String) onTagDelete, ) { - if (tags.isEmpty) return Text(s); + if (tags.isEmpty) return Text(s.tag); return SingleChildScrollView( scrollDirection: Axis.horizontal, child: Row( @@ -88,28 +94,24 @@ class TagEditor extends StatelessWidget { void Function(List)? onChanged, ) { final textEditingController = TextEditingController(); - showDialog( + showRoundDialog( context: context, - builder: (context) { - return AlertDialog( - title: const Text('Add Tag'), - content: Input( - controller: textEditingController, - hint: 'Tag', - ), - actions: [ - TextButton( - onPressed: () { - final tag = textEditingController.text; - tags.add(tag.trim()); - onChanged?.call(tags); - Navigator.pop(context); - }, - child: const Text('Add'), - ), - ], - ); - }, + title: Text(s.add), + child: Input( + controller: textEditingController, + hint: s.tag, + ), + actions: [ + TextButton( + onPressed: () { + final tag = textEditingController.text; + tags.add(tag.trim()); + onChanged?.call(tags); + Navigator.pop(context); + }, + child: Text(s.add), + ), + ], ); } }