mirror of
https://github.com/lollipopkit/flutter_server_box.git
synced 2025-12-17 07:14:28 +01:00
#54 snippet group
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
PODS:
|
PODS:
|
||||||
- countly_flutter (22.09.0):
|
- countly_flutter (23.6.0):
|
||||||
- Flutter
|
- Flutter
|
||||||
- file_picker (0.0.1):
|
- file_picker (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
@@ -55,15 +55,15 @@ EXTERNAL SOURCES:
|
|||||||
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
countly_flutter: 135f1a4930f8e26ba223a14201d3f265ea7b4c83
|
countly_flutter: 4eeee607183664b871589250a0bd049cfd2697eb
|
||||||
file_picker: 1d63c4949e05e386da864365f8c13e1e64787675
|
file_picker: 1d63c4949e05e386da864365f8c13e1e64787675
|
||||||
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
|
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
|
||||||
flutter_native_splash: 52501b97d1c0a5f898d687f1646226c1f93c56ef
|
flutter_native_splash: 52501b97d1c0a5f898d687f1646226c1f93c56ef
|
||||||
flutter_volume_controller: e4d5832f08008180f76e30faf671ffd5a425e529
|
flutter_volume_controller: e4d5832f08008180f76e30faf671ffd5a425e529
|
||||||
path_provider_foundation: c68054786f1b4f3343858c1e1d0caaded73f0be9
|
path_provider_foundation: eaf5b3e458fc0e5fbb9940fb09980e853fe058b8
|
||||||
plain_notification_token: b36467dc91939a7b6754267c701bbaca14996ee1
|
plain_notification_token: b36467dc91939a7b6754267c701bbaca14996ee1
|
||||||
r_upgrade: 44d715c61914cce3d01ea225abffe894fd51c114
|
r_upgrade: 44d715c61914cce3d01ea225abffe894fd51c114
|
||||||
share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68
|
share_plus: 599aa54e4ea31d4b4c0e9c911bcc26c55e791028
|
||||||
url_launcher_ios: 08a3dfac5fb39e8759aeb0abbd5d9480f30fc8b4
|
url_launcher_ios: 08a3dfac5fb39e8759aeb0abbd5d9480f30fc8b4
|
||||||
|
|
||||||
PODFILE CHECKSUM: 7fb15c416f8685fca4966867a8da218ec592ec2e
|
PODFILE CHECKSUM: 7fb15c416f8685fca4966867a8da218ec592ec2e
|
||||||
|
|||||||
@@ -1,9 +1,15 @@
|
|||||||
import 'package:toolbox/core/persistant_store.dart';
|
import 'package:toolbox/core/persistant_store.dart';
|
||||||
|
|
||||||
typedef Order<T> = List<T>;
|
typedef Order<T> = List<T>;
|
||||||
|
typedef _OnMove<T> = void Function(Order<T>);
|
||||||
|
|
||||||
extension OrderX<T> on Order<T> {
|
extension OrderX<T> on Order<T> {
|
||||||
void move(int oldIndex, int newIndex, StoreProperty<List<T>> property) {
|
void move(
|
||||||
|
int oldIndex,
|
||||||
|
int newIndex, {
|
||||||
|
StoreProperty<List<T>>? property,
|
||||||
|
_OnMove<T>? onMove,
|
||||||
|
}) {
|
||||||
if (oldIndex == newIndex) return;
|
if (oldIndex == newIndex) return;
|
||||||
if (oldIndex < newIndex) {
|
if (oldIndex < newIndex) {
|
||||||
newIndex -= 1;
|
newIndex -= 1;
|
||||||
@@ -11,7 +17,8 @@ extension OrderX<T> on Order<T> {
|
|||||||
final item = this[oldIndex];
|
final item = this[oldIndex];
|
||||||
removeAt(oldIndex);
|
removeAt(oldIndex);
|
||||||
insert(newIndex, item);
|
insert(newIndex, item);
|
||||||
property.put(this);
|
property?.put(this);
|
||||||
|
onMove?.call(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void update(T id, T newId) {
|
void update(T id, T newId) {
|
||||||
@@ -24,11 +31,16 @@ extension OrderX<T> on Order<T> {
|
|||||||
return indexOf(id);
|
return indexOf(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void moveById(T oid, T nid, StoreProperty<List<T>> property) {
|
void moveById(
|
||||||
|
T oid,
|
||||||
|
T nid, {
|
||||||
|
StoreProperty<List<T>>? property,
|
||||||
|
_OnMove<T>? onMove,
|
||||||
|
}) {
|
||||||
final index = indexOf(oid);
|
final index = indexOf(oid);
|
||||||
if (index == -1) return;
|
if (index == -1) return;
|
||||||
final newIndex = indexOf(nid);
|
final newIndex = indexOf(nid);
|
||||||
if (newIndex == -1) return;
|
if (newIndex == -1) return;
|
||||||
move(index, newIndex, property);
|
move(index, newIndex, property: property, onMove: onMove);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,16 +8,20 @@ class Snippet {
|
|||||||
late String name;
|
late String name;
|
||||||
@HiveField(1)
|
@HiveField(1)
|
||||||
late String script;
|
late String script;
|
||||||
Snippet(this.name, this.script);
|
@HiveField(2)
|
||||||
|
late List<String>? tags;
|
||||||
|
Snippet(this.name, this.script, this.tags);
|
||||||
|
|
||||||
Snippet.fromJson(Map<String, dynamic> json) {
|
Snippet.fromJson(Map<String, dynamic> json) {
|
||||||
name = json['name'].toString();
|
name = json['name'].toString();
|
||||||
script = json['script'].toString();
|
script = json['script'].toString();
|
||||||
|
tags = json['tags']?.cast<String>();
|
||||||
}
|
}
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
final data = <String, dynamic>{};
|
final data = <String, dynamic>{};
|
||||||
data['name'] = name;
|
data['name'] = name;
|
||||||
data['script'] = script;
|
data['script'] = script;
|
||||||
|
data['tags'] = tags;
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,17 +19,20 @@ class SnippetAdapter extends TypeAdapter<Snippet> {
|
|||||||
return Snippet(
|
return Snippet(
|
||||||
fields[0] as String,
|
fields[0] as String,
|
||||||
fields[1] as String,
|
fields[1] as String,
|
||||||
|
(fields[2] as List?)?.cast<String>(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void write(BinaryWriter writer, Snippet obj) {
|
void write(BinaryWriter writer, Snippet obj) {
|
||||||
writer
|
writer
|
||||||
..writeByte(2)
|
..writeByte(3)
|
||||||
..writeByte(0)
|
..writeByte(0)
|
||||||
..write(obj.name)
|
..write(obj.name)
|
||||||
..writeByte(1)
|
..writeByte(1)
|
||||||
..write(obj.script);
|
..write(obj.script)
|
||||||
|
..writeByte(2)
|
||||||
|
..write(obj.tags);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -5,25 +5,44 @@ import 'package:toolbox/data/model/server/snippet.dart';
|
|||||||
import 'package:toolbox/data/store/snippet.dart';
|
import 'package:toolbox/data/store/snippet.dart';
|
||||||
import 'package:toolbox/locator.dart';
|
import 'package:toolbox/locator.dart';
|
||||||
|
|
||||||
|
import '../../core/extension/order.dart';
|
||||||
|
|
||||||
class SnippetProvider extends BusyProvider {
|
class SnippetProvider extends BusyProvider {
|
||||||
List<Snippet> get snippets => _snippets;
|
late Order<Snippet> _snippets;
|
||||||
|
Order<Snippet> get snippets => _snippets;
|
||||||
|
|
||||||
|
final _tags = <String>[];
|
||||||
|
List<String> get tags => _tags;
|
||||||
|
|
||||||
final _store = locator<SnippetStore>();
|
final _store = locator<SnippetStore>();
|
||||||
late List<Snippet> _snippets;
|
|
||||||
|
|
||||||
void loadData() {
|
void loadData() {
|
||||||
_snippets = _store.fetch();
|
_snippets = _store.fetch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _updateTags() {
|
||||||
|
_tags.clear();
|
||||||
|
for (final s in _snippets) {
|
||||||
|
if (s.tags?.isEmpty ?? true) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
_tags.addAll(s.tags!);
|
||||||
|
}
|
||||||
|
_tags.toSet().toList();
|
||||||
|
}
|
||||||
|
|
||||||
void add(Snippet snippet) {
|
void add(Snippet snippet) {
|
||||||
_snippets.add(snippet);
|
_snippets.add(snippet);
|
||||||
_store.put(snippet);
|
_store.put(snippet);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
_updateTags();
|
||||||
}
|
}
|
||||||
|
|
||||||
void del(Snippet snippet) {
|
void del(Snippet snippet) {
|
||||||
_snippets.remove(snippet);
|
_snippets.remove(snippet);
|
||||||
_store.delete(snippet);
|
_store.delete(snippet);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
_updateTags();
|
||||||
}
|
}
|
||||||
|
|
||||||
void update(Snippet old, Snippet newOne) {
|
void update(Snippet old, Snippet newOne) {
|
||||||
@@ -32,6 +51,21 @@ class SnippetProvider extends BusyProvider {
|
|||||||
_snippets.remove(old);
|
_snippets.remove(old);
|
||||||
_snippets.add(newOne);
|
_snippets.add(newOne);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
_updateTags();
|
||||||
|
}
|
||||||
|
|
||||||
|
void renameTag(String old, String newOne) {
|
||||||
|
for (final s in _snippets) {
|
||||||
|
if (s.tags?.contains(old) ?? false) {
|
||||||
|
s.tags?.remove(old);
|
||||||
|
s.tags?.add(newOne);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (final s in _snippets) {
|
||||||
|
_store.put(s);
|
||||||
|
}
|
||||||
|
notifyListeners();
|
||||||
|
_updateTags();
|
||||||
}
|
}
|
||||||
|
|
||||||
String get export => json.encode(snippets);
|
String get export => json.encode(snippets);
|
||||||
|
|||||||
@@ -46,6 +46,11 @@ class SettingStore extends PersistentStore {
|
|||||||
StoreProperty<List<String>> get serverOrder =>
|
StoreProperty<List<String>> get serverOrder =>
|
||||||
property('serverOrder', defaultValue: null);
|
property('serverOrder', defaultValue: null);
|
||||||
|
|
||||||
|
StoreProperty<List<String>> get snippetOrder => property(
|
||||||
|
'snippetOrder',
|
||||||
|
defaultValue: null,
|
||||||
|
);
|
||||||
|
|
||||||
// Server details page cards order
|
// Server details page cards order
|
||||||
StoreProperty<List<String>> get detailCardOrder =>
|
StoreProperty<List<String>> get detailCardOrder =>
|
||||||
property('detailCardPrder', defaultValue: defaultDetailCardOrder);
|
property('detailCardPrder', defaultValue: defaultDetailCardOrder);
|
||||||
|
|||||||
@@ -74,7 +74,11 @@ class _ServerDetailPageState extends State<ServerDetailPage>
|
|||||||
left: 13, right: 13, top: 13, bottom: _media.padding.bottom),
|
left: 13, right: 13, top: 13, bottom: _media.padding.bottom),
|
||||||
onReorder: (int oldIndex, int newIndex) {
|
onReorder: (int oldIndex, int newIndex) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_cardsOrder.move(oldIndex, newIndex, _setting.detailCardOrder);
|
_cardsOrder.move(
|
||||||
|
oldIndex,
|
||||||
|
newIndex,
|
||||||
|
property: _setting.detailCardOrder,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
footer: height13,
|
footer: height13,
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import 'package:toolbox/core/extension/navigator.dart';
|
|||||||
import 'package:toolbox/core/extension/order.dart';
|
import 'package:toolbox/core/extension/order.dart';
|
||||||
import 'package:toolbox/core/utils/misc.dart';
|
import 'package:toolbox/core/utils/misc.dart';
|
||||||
import 'package:toolbox/view/page/process.dart';
|
import 'package:toolbox/view/page/process.dart';
|
||||||
|
import 'package:toolbox/view/widget/tag_switcher.dart';
|
||||||
|
|
||||||
import '../../../core/route.dart';
|
import '../../../core/route.dart';
|
||||||
import '../../../core/utils/ui.dart';
|
import '../../../core/utils/ui.dart';
|
||||||
@@ -103,13 +104,21 @@ class _ServerPageState extends State<ServerPage>
|
|||||||
(pro.servers[e]?.spi.tags?.contains(_tag) ?? false))
|
(pro.servers[e]?.spi.tags?.contains(_tag) ?? false))
|
||||||
.toList();
|
.toList();
|
||||||
return ReorderableListView.builder(
|
return ReorderableListView.builder(
|
||||||
header: _buildTagsSwitcher(pro.tags),
|
header: TagSwitcher(
|
||||||
|
tags: pro.tags,
|
||||||
|
width: _media.size.width,
|
||||||
|
onTagChanged: (p0) => setState(() {
|
||||||
|
_tag = p0;
|
||||||
|
}),
|
||||||
|
initTag: _tag,
|
||||||
|
all: _s.all,
|
||||||
|
),
|
||||||
padding: const EdgeInsets.fromLTRB(7, 10, 7, 7),
|
padding: const EdgeInsets.fromLTRB(7, 10, 7, 7),
|
||||||
onReorder: (oldIndex, newIndex) => setState(() {
|
onReorder: (oldIndex, newIndex) => setState(() {
|
||||||
pro.serverOrder.moveById(
|
pro.serverOrder.moveById(
|
||||||
filtered[oldIndex],
|
filtered[oldIndex],
|
||||||
filtered[newIndex],
|
filtered[newIndex],
|
||||||
_settingStore.serverOrder,
|
property: _settingStore.serverOrder,
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
itemBuilder: (_, index) => _buildEachServerCard(
|
itemBuilder: (_, index) => _buildEachServerCard(
|
||||||
@@ -122,51 +131,6 @@ class _ServerPageState extends State<ServerPage>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildTagsSwitcher(List<String> tags) {
|
|
||||||
if (tags.isEmpty) return nil;
|
|
||||||
final items = <String?>[null, ...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 _buildEachServerCard(Server? si) {
|
Widget _buildEachServerCard(Server? si) {
|
||||||
if (si == null) {
|
if (si == null) {
|
||||||
return nil;
|
return nil;
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import 'package:after_layout/after_layout.dart';
|
import 'package:after_layout/after_layout.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||||
import 'package:nil/nil.dart';
|
|
||||||
import 'package:toolbox/core/extension/navigator.dart';
|
import 'package:toolbox/core/extension/navigator.dart';
|
||||||
import 'package:toolbox/view/widget/input_field.dart';
|
import 'package:toolbox/view/widget/input_field.dart';
|
||||||
|
|
||||||
@@ -10,6 +9,7 @@ import '../../../data/model/server/snippet.dart';
|
|||||||
import '../../../data/provider/snippet.dart';
|
import '../../../data/provider/snippet.dart';
|
||||||
import '../../../data/res/ui.dart';
|
import '../../../data/res/ui.dart';
|
||||||
import '../../../locator.dart';
|
import '../../../locator.dart';
|
||||||
|
import '../../widget/tag_editor.dart';
|
||||||
|
|
||||||
class SnippetEditPage extends StatefulWidget {
|
class SnippetEditPage extends StatefulWidget {
|
||||||
const SnippetEditPage({Key? key, this.snippet}) : super(key: key);
|
const SnippetEditPage({Key? key, this.snippet}) : super(key: key);
|
||||||
@@ -29,6 +29,8 @@ class _SnippetEditPageState extends State<SnippetEditPage>
|
|||||||
late SnippetProvider _provider;
|
late SnippetProvider _provider;
|
||||||
late S _s;
|
late S _s;
|
||||||
|
|
||||||
|
List<String> _tags = [];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@@ -46,24 +48,29 @@ class _SnippetEditPageState extends State<SnippetEditPage>
|
|||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(_s.edit, style: textSize18),
|
title: Text(_s.edit, style: textSize18),
|
||||||
actions: [
|
actions: _buildAppBarActions(),
|
||||||
widget.snippet != null
|
|
||||||
? IconButton(
|
|
||||||
onPressed: () {
|
|
||||||
_provider.del(widget.snippet!);
|
|
||||||
context.pop();
|
|
||||||
},
|
|
||||||
tooltip: _s.delete,
|
|
||||||
icon: const Icon(Icons.delete),
|
|
||||||
)
|
|
||||||
: nil
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
body: _buildBody(),
|
body: _buildBody(),
|
||||||
floatingActionButton: _buildFAB(),
|
floatingActionButton: _buildFAB(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<Widget>? _buildAppBarActions() {
|
||||||
|
if (widget.snippet == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
_provider.del(widget.snippet!);
|
||||||
|
context.pop();
|
||||||
|
},
|
||||||
|
tooltip: _s.delete,
|
||||||
|
icon: const Icon(Icons.delete),
|
||||||
|
)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildFAB() {
|
Widget _buildFAB() {
|
||||||
return FloatingActionButton(
|
return FloatingActionButton(
|
||||||
heroTag: 'snippet',
|
heroTag: 'snippet',
|
||||||
@@ -75,7 +82,7 @@ class _SnippetEditPageState extends State<SnippetEditPage>
|
|||||||
showSnackBar(context, Text(_s.fieldMustNotEmpty));
|
showSnackBar(context, Text(_s.fieldMustNotEmpty));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final snippet = Snippet(name, script);
|
final snippet = Snippet(name, script, _tags);
|
||||||
if (widget.snippet != null) {
|
if (widget.snippet != null) {
|
||||||
_provider.update(widget.snippet!, snippet);
|
_provider.update(widget.snippet!, snippet);
|
||||||
} else {
|
} else {
|
||||||
@@ -106,6 +113,15 @@ class _SnippetEditPageState extends State<SnippetEditPage>
|
|||||||
label: _s.snippet,
|
label: _s.snippet,
|
||||||
icon: Icons.code,
|
icon: Icons.code,
|
||||||
),
|
),
|
||||||
|
TagEditor(
|
||||||
|
tags: _tags,
|
||||||
|
onChanged: (p0) => setState(() {
|
||||||
|
_tags = p0;
|
||||||
|
}),
|
||||||
|
s: _s,
|
||||||
|
tagSuggestions: [..._provider.tags],
|
||||||
|
onRenameTag: _provider.renameTag,
|
||||||
|
)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:toolbox/core/extension/order.dart';
|
||||||
|
import 'package:toolbox/view/widget/tag_switcher.dart';
|
||||||
|
|
||||||
|
import '../../../data/store/setting.dart';
|
||||||
|
import '../../../locator.dart';
|
||||||
import '/core/route.dart';
|
import '/core/route.dart';
|
||||||
import '/data/provider/snippet.dart';
|
import '/data/provider/snippet.dart';
|
||||||
import 'edit.dart';
|
import 'edit.dart';
|
||||||
@@ -16,11 +20,17 @@ class SnippetListPage extends StatefulWidget {
|
|||||||
|
|
||||||
class _SnippetListPageState extends State<SnippetListPage> {
|
class _SnippetListPageState extends State<SnippetListPage> {
|
||||||
late S _s;
|
late S _s;
|
||||||
|
late MediaQueryData _media;
|
||||||
|
|
||||||
|
final _settingStore = locator<SettingStore>();
|
||||||
|
|
||||||
|
String? _tag;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didChangeDependencies() {
|
void didChangeDependencies() {
|
||||||
super.didChangeDependencies();
|
super.didChangeDependencies();
|
||||||
_s = S.of(context)!;
|
_s = S.of(context)!;
|
||||||
|
_media = MediaQuery.of(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -47,26 +57,48 @@ class _SnippetListPageState extends State<SnippetListPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ListView.builder(
|
final filtered = provider.snippets
|
||||||
|
.where((e) => _tag == null || (e.tags?.contains(_tag) ?? false))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
return ReorderableListView.builder(
|
||||||
padding: const EdgeInsets.all(13),
|
padding: const EdgeInsets.all(13),
|
||||||
itemCount: provider.snippets.length,
|
itemCount: filtered.length,
|
||||||
|
onReorder: (oldIdx, newIdx) => setState(() {
|
||||||
|
provider.snippets.moveById(
|
||||||
|
filtered[oldIdx],
|
||||||
|
filtered[newIdx],
|
||||||
|
onMove: (p0) {
|
||||||
|
_settingStore.snippetOrder.put(p0.map((e) => e.name).toList());
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
header: TagSwitcher(
|
||||||
|
tags: provider.tags,
|
||||||
|
onTagChanged: (tag) => setState(() => _tag = tag),
|
||||||
|
initTag: _tag,
|
||||||
|
all: _s.all,
|
||||||
|
width: _media.size.width,
|
||||||
|
),
|
||||||
itemBuilder: (context, idx) {
|
itemBuilder: (context, idx) {
|
||||||
|
final snippet = filtered[idx];
|
||||||
return RoundRectCard(
|
return RoundRectCard(
|
||||||
ListTile(
|
ListTile(
|
||||||
contentPadding: const EdgeInsets.only(left: 23, right: 17),
|
contentPadding: const EdgeInsets.only(left: 23, right: 17),
|
||||||
title: Text(
|
title: Text(
|
||||||
provider.snippets[idx].name,
|
snippet.name,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
),
|
),
|
||||||
trailing: IconButton(
|
trailing: IconButton(
|
||||||
onPressed: () => AppRoute(
|
onPressed: () => AppRoute(
|
||||||
SnippetEditPage(snippet: provider.snippets[idx]),
|
SnippetEditPage(snippet: snippet),
|
||||||
'snippet edit page')
|
'snippet edit page',
|
||||||
.go(context),
|
).go(context),
|
||||||
icon: const Icon(Icons.edit),
|
icon: const Icon(Icons.edit),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
key: ValueKey(snippet.name),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ class _SSHVirtKeySettingPageState extends State<SSHVirtKeySettingPage> {
|
|||||||
showSnackBar(context, Text(_s.disabled));
|
showSnackBar(context, Text(_s.disabled));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
keys.moveById(keys[o], keys[n], _setting.sshVirtKeys);
|
keys.moveById(keys[o], keys[n], property: _setting.sshVirtKeys);
|
||||||
setState(() {});
|
setState(() {});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
67
lib/view/widget/tag_switcher.dart
Normal file
67
lib/view/widget/tag_switcher.dart
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:nil/nil.dart';
|
||||||
|
|
||||||
|
import '../../data/res/color.dart';
|
||||||
|
|
||||||
|
class TagSwitcher extends StatelessWidget {
|
||||||
|
final List<String> tags;
|
||||||
|
final double width;
|
||||||
|
final void Function(String?) onTagChanged;
|
||||||
|
final String? initTag;
|
||||||
|
final String all;
|
||||||
|
|
||||||
|
const TagSwitcher({
|
||||||
|
Key? key,
|
||||||
|
required this.tags,
|
||||||
|
required this.width,
|
||||||
|
required this.onTagChanged,
|
||||||
|
required this.all,
|
||||||
|
this.initTag,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return _buildTagsSwitcher(tags);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildTagsSwitcher(List<String> tags) {
|
||||||
|
if (tags.isEmpty) return nil;
|
||||||
|
final items = <String?>[null, ...tags];
|
||||||
|
return Container(
|
||||||
|
height: 37,
|
||||||
|
width: 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: () => onTagChanged(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 ? all : '#$tag',
|
||||||
|
style: TextStyle(
|
||||||
|
color: initTag == tag ? null : Colors.grey,
|
||||||
|
fontSize: 15,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user