Created home top items reorder screen

This commit is contained in:
Juan Gilsanz Polo 2023-09-09 23:30:53 +02:00
parent d967399b64
commit 520a14fcda
8 changed files with 313 additions and 4 deletions

View file

@ -655,5 +655,10 @@
"quickFilters": "Quick filters",
"searchDomainInternet": "Search domain on the Internet",
"hideServerAddress": "Hide server address",
"hideServerAddressDescription": "Hides the server address on the home screen"
"hideServerAddressDescription": "Hides the server address on the home screen",
"topItemsOrder": "Top items order",
"topItemsOrderDescription": "Order the home screen top items lists",
"topItemsReorderInfo": "Hold and swipe an item to reorder it.",
"discardChanges": "Discard changes",
"discardChangesDescription": "Are you sure you want to discard the changes?"
}

View file

@ -655,5 +655,10 @@
"quickFilters": "Filtros rápidos",
"searchDomainInternet": "Buscar dominio en internet",
"hideServerAddress": "Ocultar dirección del servidor",
"hideServerAddressDescription": "Oculta la dirección del servidor en la pantalla de inicio"
"hideServerAddressDescription": "Oculta la dirección del servidor en la pantalla de inicio",
"topItemsOrder": "Orden de los top",
"topItemsOrderDescription": "Ordena las listas de top de elementos en la pantalla de inicio",
"topItemsReorderInfo": "Mantén presionado y desliza un elemento para reordenarlo.",
"discardChanges": "Descartar cambios",
"discardChangesDescription": "¿Estás seguro de que deseas descartar los cambios realizados?"
}

View file

@ -374,7 +374,7 @@ class AppConfigProvider with ChangeNotifier {
final updated = await updateConfigQuery(
db: _dbInstance!,
column: 'homeTopItemsOrder',
value: jsonEncode(order)
value: jsonEncode(List<String>.from(order.map((e) => e.name)))
);
if (updated == true) {
_homeTopItemsOrder = order;

View file

@ -7,6 +7,8 @@ import 'package:provider/provider.dart';
import 'package:store_checker/store_checker.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:adguard_home_manager/screens/settings/general_settings/reorderable_top_items_home.dart';
import 'package:adguard_home_manager/widgets/custom_list_tile.dart';
import 'package:adguard_home_manager/widgets/section_label.dart';
@ -181,6 +183,14 @@ class _GeneralSettingsState extends State<GeneralSettings> {
right: 10
)
),
CustomListTile(
icon: Icons.reorder_rounded,
title: AppLocalizations.of(context)!.topItemsOrder,
subtitle: AppLocalizations.of(context)!.topItemsOrderDescription,
onTap: () => Navigator.push(context, MaterialPageRoute(
builder: (context) => const ReorderableTopItemsHome()
)),
),
SectionLabel(label: AppLocalizations.of(context)!.logs),
CustomListTile(
icon: Icons.timer_rounded,

View file

@ -0,0 +1,280 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:flutter_reorderable_list/flutter_reorderable_list.dart' as reorderable_list_library;
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:adguard_home_manager/widgets/custom_list_tile.dart';
import 'package:adguard_home_manager/constants/enums.dart';
import 'package:adguard_home_manager/providers/app_config_provider.dart';
class ItemData {
final HomeTopItems title;
final Key key;
const ItemData({
required this.title,
required this.key
});
}
enum DraggingMode {
iOS,
android,
}
class ReorderableTopItemsHome extends StatefulWidget {
const ReorderableTopItemsHome({Key? key}) : super(key: key);
@override
State<ReorderableTopItemsHome> createState() => _ReorderableTopItemsHomeState();
}
class _ReorderableTopItemsHomeState extends State<ReorderableTopItemsHome> {
List<HomeTopItems> homeTopItemsList = [];
List<HomeTopItems> persistHomeTopItemsList = [];
List<ItemData> renderItems = [];
int _indexOfKey(Key key) {
return renderItems.indexWhere((ItemData d) => d.key == key);
}
bool _reorderCallback(Key item, Key newPosition) {
int draggingIndex = _indexOfKey(item);
int newPositionIndex = _indexOfKey(newPosition);
final draggedItem = renderItems[draggingIndex];
final List<HomeTopItems> reorderedItems = reorderEnumItems(draggingIndex, newPositionIndex);
setState(() {
renderItems.removeAt(draggingIndex);
renderItems.insert(newPositionIndex, draggedItem);
homeTopItemsList = reorderedItems;
});
return true;
}
void _reorderDone(Key item) {
renderItems[_indexOfKey(item)];
setState(() => persistHomeTopItemsList = homeTopItemsList);
}
List<HomeTopItems> reorderEnumItems(int oldIndex, int newIndex) {
final List<HomeTopItems> list = [...homeTopItemsList];
final HomeTopItems item = list.removeAt(oldIndex);
list.insert(newIndex, item);
return list;
}
@override
void initState() {
final appConfigProvider = Provider.of<AppConfigProvider>(context, listen: false);
homeTopItemsList = appConfigProvider.homeTopItemsOrder;
persistHomeTopItemsList = appConfigProvider.homeTopItemsOrder;
renderItems = appConfigProvider.homeTopItemsOrder.asMap().entries.map(
(e) => ItemData(
key: ValueKey(e.key),
title: e.value,
)
).toList();
super.initState();
}
@override
Widget build(BuildContext context) {
final appConfigProvider = Provider.of<AppConfigProvider>(context);
Widget tile(HomeTopItems title) {
switch (title) {
case HomeTopItems.queriedDomains:
return CustomListTile(
title: AppLocalizations.of(context)!.topQueriedDomains,
icon: Icons.install_desktop_outlined,
padding: const EdgeInsets.all(16)
);
case HomeTopItems.blockedDomains:
return CustomListTile(
title: AppLocalizations.of(context)!.topBlockedDomains,
icon: Icons.block_rounded,
padding: const EdgeInsets.all(16)
);
case HomeTopItems.recurrentClients:
return CustomListTile(
title: AppLocalizations.of(context)!.topClients,
icon: Icons.smartphone_rounded,
padding: const EdgeInsets.all(16)
);
default:
return const SizedBox();
}
}
Future<bool> onWillPopScope() async {
if (!listEquals(appConfigProvider.homeTopItemsOrder, persistHomeTopItemsList)) {
showDialog(
context: context,
builder: (dialogContext) => AlertDialog(
title: Text(AppLocalizations.of(context)!.discardChanges),
content: Text(AppLocalizations.of(context)!.discardChangesDescription),
actions: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () {
Navigator.pop(dialogContext);
Navigator.pop(context);
},
child: Text(AppLocalizations.of(context)!.confirm)
),
const SizedBox(width: 8),
TextButton(
onPressed: () => Navigator.pop(dialogContext),
child: Text(AppLocalizations.of(context)!.cancel)
),
],
)
],
)
);
return false;
}
else {
return true;
}
}
return WillPopScope(
onWillPop: onWillPopScope,
child: Scaffold(
appBar: AppBar(
title: Text(AppLocalizations.of(context)!.topItemsOrder),
actions: [
IconButton(
onPressed: !listEquals(appConfigProvider.homeTopItemsOrder, persistHomeTopItemsList)
? () => appConfigProvider.setHomeTopItemsOrder(homeTopItemsList)
: null,
icon: const Icon(Icons.save_rounded),
tooltip: AppLocalizations.of(context)!.save,
),
const SizedBox(width: 8)
],
),
body: Column(
children: [
Card(
margin: const EdgeInsets.all(16),
child: Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
Icon(
Icons.info_rounded,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
const SizedBox(width: 16),
Flexible(
child: Text(AppLocalizations.of(context)!.topItemsReorderInfo)
)
],
),
),
),
Expanded(
child: reorderable_list_library.ReorderableList(
onReorder: _reorderCallback,
onReorderDone: _reorderDone,
child: ListView.builder(
itemBuilder: (context, index) => reorderable_list_library.ReorderableItem(
key: renderItems[index].key,
childBuilder: (context, state) => Item(
tileWidget: tile(renderItems[index].title),
isFirst: index == 0,
isLast: index == renderItems.length - 1,
state: state
),
),
itemCount: renderItems.length,
)
),
),
],
),
),
);
}
}
class Item extends StatelessWidget {
final Widget tileWidget;
final bool isFirst;
final bool isLast;
final reorderable_list_library.ReorderableItemState state;
const Item({
Key? key,
required this.tileWidget,
required this.isFirst,
required this.isLast,
required this.state,
}) : super(key: key);
@override
Widget build(BuildContext context) {
BoxDecoration decoration;
if (
state == reorderable_list_library.ReorderableItemState.dragProxy ||
state == reorderable_list_library.ReorderableItemState.dragProxyFinished
) {
decoration = BoxDecoration(
color: Theme.of(context).colorScheme.surface.withOpacity(0.7)
);
}
else {
bool placeholder = state == reorderable_list_library.ReorderableItemState.placeholder;
decoration = BoxDecoration(
border: Border(
top: isFirst && !placeholder ? BorderSide(
width: 1,
color: Theme.of(context).colorScheme.primary.withOpacity(0.1)
) : BorderSide.none,
bottom: isLast && placeholder ? BorderSide.none : BorderSide(
width: 1,
color: Theme.of(context).colorScheme.primary.withOpacity(0.1)
),
),
);
}
return reorderable_list_library.DelayedReorderableListener(
child: Container(
decoration: decoration,
child: SafeArea(
top: false,
bottom: false,
child: Opacity(
opacity: state == reorderable_list_library.ReorderableItemState.placeholder ? 0.0 : 1.0,
child: IntrinsicHeight(
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Expanded(
child: tileWidget
),
],
),
),
)
),
)
);
}
}

View file

@ -17,7 +17,7 @@ import 'package:adguard_home_manager/screens/settings/dns/dns.dart';
import 'package:adguard_home_manager/screens/settings/dns_rewrites/dns_rewrites.dart';
import 'package:adguard_home_manager/screens/servers/servers.dart';
import 'package:adguard_home_manager/screens/settings/advanced_setings.dart';
import 'package:adguard_home_manager/screens/settings/general_settings.dart';
import 'package:adguard_home_manager/screens/settings/general_settings/general_settings.dart';
import 'package:adguard_home_manager/widgets/custom_settings_tile.dart';
import 'package:adguard_home_manager/widgets/section_label.dart';

View file

@ -267,6 +267,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.3.2"
flutter_reorderable_list:
dependency: "direct main"
description:
name: flutter_reorderable_list
sha256: "0400ef34fa00b7cac69f71efc92d7e49727f425bc1080180ebe70bf47618afe0"
url: "https://pub.dev"
source: hosted
version: "1.3.1"
flutter_split_view:
dependency: "direct main"
description:

View file

@ -72,6 +72,7 @@ dependencies:
async: ^2.10.0
sentry_flutter: ^7.9.0
flutter_dotenv: ^5.0.2
flutter_reorderable_list: ^1.3.1
dev_dependencies:
flutter_test: