Merge branch 'beta'

This commit is contained in:
Juan Gilsanz Polo 2024-09-11 19:17:02 +02:00
commit 6e0f437c6b
107 changed files with 1108 additions and 771 deletions

View file

@ -42,6 +42,15 @@ This is an unofficial application. The AdGuard Home team and the development of
## Recommended configuration and lists
On [this repository](https://github.com/juanico10/Pihole_list) you can find a recommended configuration for AdGuard Home and some lists. Thanks to [juanico10](https://github.com/juanico10).
## Donations
If you like the project and you want to contribute with the development, you can [become a sponsor on GitHub](https://github.com/sponsors/JGeek00), or you can donate using PayPal.
<div align="center">
<a href="https://www.paypal.com/donate/?hosted_button_id=T63UK6AVL3MG8">
<img src="https://raw.githubusercontent.com/stefan-niedermann/paypal-donate-button/master/paypal-donate-button.png" alt="Donate with PayPal" height="100" />
</a>
</div>
## Generate production build
<ul>
<li>
@ -114,7 +123,6 @@ On [this repository](https://github.com/juanico10/Pihole_list) you can find a re
- [fl chart](https://pub.dev/packages/fl_chart)
- [flutter svg](https://pub.dev/packages/flutter_svg)
- [percent indicator](https://pub.dev/packages/percent_indicator)
- [store checker](https://pub.dev/packages/store_checker)
- [flutter markdown](https://pub.dev/packages/flutter_markdown)
- [markdown](https://pub.dev/packages/markdown)
- [html](https://pub.dev/packages/html)
@ -132,6 +140,9 @@ On [this repository](https://github.com/juanico10/Pihole_list) you can find a re
- [timezone](https://pub.dev/packages/timezone)
- [url launcher](https://pub.dev/packages/url_launcher)
- [flutter custom tabs](https://pub.dev/packages/flutter_custom_tabs)
- [shared preferences](https://pub.dev/packages/shared_preferences)
- [window manager](https://pub.dev/packages/window_manager)
- [install referrer](https://pub.dev/packages/install_referrer)
<br>

View file

@ -24,6 +24,9 @@ ThemeData lightTheme(ColorScheme? dynamicColorScheme) => ThemeData(
navigationBarTheme: NavigationBarThemeData(
surfaceTintColor: dynamicColorScheme?.surfaceTint
),
dialogTheme: DialogTheme(
surfaceTintColor: dynamicColorScheme?.surfaceTint
),
pageTransitionsTheme: const PageTransitionsTheme(
builders: {
TargetPlatform.android: PredictiveBackPageTransitionsBuilder()
@ -56,6 +59,9 @@ ThemeData darkTheme(ColorScheme? dynamicColorScheme) => ThemeData(
navigationBarTheme: NavigationBarThemeData(
surfaceTintColor: dynamicColorScheme?.surfaceTint
),
dialogTheme: DialogTheme(
surfaceTintColor: dynamicColorScheme?.surfaceTint
),
pageTransitionsTheme: const PageTransitionsTheme(
builders: {
TargetPlatform.android: PredictiveBackPageTransitionsBuilder()

View file

@ -1,6 +1,6 @@
import 'dart:io';
import 'package:store_checker/store_checker.dart';
import 'package:install_referrer/install_referrer.dart';
import 'package:adguard_home_manager/functions/compare_versions.dart';
import 'package:adguard_home_manager/services/external_requests.dart';
@ -9,7 +9,7 @@ import 'package:adguard_home_manager/models/github_release.dart';
Future<GitHubRelease?> checkAppUpdates({
required String currentBuildNumber,
required void Function(GitHubRelease?) setUpdateAvailable,
required Source installationSource,
required InstallationAppReferrer? installationSource,
required bool isBeta
}) async {
var result = isBeta
@ -35,11 +35,7 @@ Future<GitHubRelease?> checkAppUpdates({
setUpdateAvailable(gitHubRelease);
if (Platform.isAndroid) {
if (
installationSource == Source.IS_INSTALLED_FROM_LOCAL_SOURCE ||
installationSource == Source.IS_INSTALLED_FROM_PLAY_PACKAGE_INSTALLER ||
installationSource == Source.UNKNOWN
) {
if (installationSource == InstallationAppReferrer.androidManually) {
return gitHubRelease;
}
else {

View file

@ -5,7 +5,7 @@ import 'package:flutter/material.dart';
import 'package:adguard_home_manager/providers/app_config_provider.dart';
import 'package:adguard_home_manager/config/globals.dart';
void showSnacbkar({
void showSnackbar({
required AppConfigProvider appConfigProvider,
required String label,
required Color color,

View file

@ -795,5 +795,7 @@
"clientIpCopied": "Client IP copied to the clipboard",
"clientNameCopied": "Client name copied to the clipboard",
"dnsServerAddressCopied": "DNS server address copied to the clipboard",
"select": "Select"
"select": "Select",
"liveLogs": "Live logs",
"hereWillAppearRealtimeLogs": "Here there will appear the logs on realtime."
}

View file

@ -795,5 +795,7 @@
"clientIpCopied": "Dirección IP del cliente copiada al portapapeles",
"clientNameCopied": "Nombre del cliente copiado al portapapeles",
"dnsServerAddressCopied": "Dirección del servidor DNS copiada al portapapeles",
"select": "Seleccionar"
"select": "Seleccionar",
"liveLogs": "Registros en directo",
"hereWillAppearRealtimeLogs": "Aquí aparecerán los registros en tiempo real."
}

View file

@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:install_referrer/install_referrer.dart';
import 'package:provider/provider.dart';
import 'package:flutter_displaymode/flutter_displaymode.dart';
import 'package:dynamic_color/dynamic_color.dart';
@ -12,7 +13,6 @@ import 'package:package_info_plus/package_info_plus.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
import 'package:store_checker/store_checker.dart';
import 'package:window_manager/window_manager.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
@ -80,7 +80,7 @@ void main() async {
}
if (Platform.isAndroid || Platform.isIOS) {
Source installationSource = await StoreChecker.getSource;
InstallationAppReferrer installationSource = await InstallReferrer.referrer;
appConfigProvider.setInstallationSource(installationSource);
}

View file

@ -63,7 +63,7 @@ class Filter {
Map<String, dynamic> toJson() => {
"url": url,
"name": name,
"last_updated": lastUpdated != null ? lastUpdated!.toIso8601String() : null,
"last_updated": lastUpdated?.toIso8601String(),
"id": id,
"rules_count": rulesCount,
"enabled": enabled,

View file

@ -65,7 +65,7 @@ class Filter {
Map<String, dynamic> toJson() => {
"url": url,
"name": name,
"last_updated": lastUpdated != null ? lastUpdated!.toIso8601String() : null,
"last_updated": lastUpdated?.toIso8601String(),
"id": id,
"rules_count": rulesCount,
"enabled": enabled,

View file

@ -30,7 +30,7 @@ class LogsData {
Map<String, dynamic> toJson() => {
"data": List<dynamic>.from(data.map((x) => x.toJson())),
"oldest": oldest != null ? oldest!.toIso8601String() : null,
"oldest": oldest?.toIso8601String(),
};
}

View file

@ -2,9 +2,9 @@
import 'package:flutter/material.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/scheduler.dart';
import 'package:install_referrer/install_referrer.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:store_checker/store_checker.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:adguard_home_manager/constants/enums.dart';
@ -33,7 +33,7 @@ class AppConfigProvider with ChangeNotifier {
int _selectedTheme = 0;
bool _useDynamicColor = true;
int _staticColor = 0;
bool _useThemeColorForStatus = false;
final bool _useThemeColorForStatus = false;
int _selectedClientsTab = 0;
int _selectedFiltersTab = 0;
@ -58,7 +58,7 @@ class AppConfigProvider with ChangeNotifier {
GitHubRelease? _appUpdatesAvailable;
Source _installationSource = Source.UNKNOWN;
InstallationAppReferrer? _installationSource;
PackageInfo? get getAppInfo {
return _appInfo;
@ -162,7 +162,7 @@ class AppConfigProvider with ChangeNotifier {
return _appUpdatesAvailable;
}
Source get installationSource {
InstallationAppReferrer? get installationSource {
return _installationSource;
}
@ -227,7 +227,7 @@ class AppConfigProvider with ChangeNotifier {
notifyListeners();
}
void setInstallationSource(Source value) {
void setInstallationSource(InstallationAppReferrer value) {
_installationSource = value;
notifyListeners();
}

View file

@ -0,0 +1,51 @@
import 'dart:async';
import 'package:flutter/widgets.dart';
import 'package:adguard_home_manager/models/logs.dart';
import 'package:adguard_home_manager/providers/servers_provider.dart';
class LiveLogsProvider with ChangeNotifier {
ServersProvider? _serversProvider;
update(ServersProvider? provider) {
_serversProvider = provider;
}
bool _isDisposed = false;
@override
void dispose() {
_isDisposed = true;
super.dispose();
}
List<Log> _logs = [];
List<Log> get logs {
return _logs;
}
DateTime? _lastTime;
void startFetchLogs() {
_lastTime = DateTime.now();
_fetchLogs();
}
void _fetchLogs() async {
if (_lastTime == null) return;
final result = await _serversProvider!.apiClient2!.getLogs(
count: 100
);
if (result.successful == false || result.content == null) return;
final valid = (result.content as LogsData).data.where((e) => e.time.isAfter(_lastTime!));
_logs = [...valid, ..._logs];
_lastTime = DateTime.now();
notifyListeners();
await Future.delayed(const Duration(seconds: 2));
if (_isDisposed == true) return;
_fetchLogs();
}
}

View file

@ -82,14 +82,14 @@ class _AddedListState extends State<AddedList> {
processModal.close();
if (result == true) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.clientUpdatedSuccessfully,
color: Colors.green
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.clientNotUpdated,
color: Colors.red
@ -109,14 +109,14 @@ class _AddedListState extends State<AddedList> {
if (widget.splitView == true) {
Navigator.of(clientsNavigatorKey.currentContext!).popUntil((route) => false);
}
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.clientDeletedSuccessfully,
color: Colors.green
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.clientNotDeleted,
color: Colors.red

View file

@ -1,11 +1,11 @@
import 'package:flutter/material.dart';
class ClientPlaceholder extends StatelessWidget {
const ClientPlaceholder({Key? key}) : super(key: key);
const ClientPlaceholder({super.key});
@override
Widget build(BuildContext context) {
return Center(
return const Center(
child: Text("Select a client"),
);
}

View file

@ -237,7 +237,8 @@ class _LogsListClientState extends State<LogsListClient> {
context: context,
builder: (context) => LogDetailsScreen(
log: log,
dialog: true
dialog: true,
twoColumns: widget.splitView,
)
)
}
@ -246,7 +247,8 @@ class _LogsListClientState extends State<LogsListClient> {
MaterialPageRoute(
builder: (context) => LogDetailsScreen(
log: log,
dialog: false
dialog: false,
twoColumns: widget.splitView,
)
)
)

View file

@ -5,9 +5,9 @@ class RemoveClientModal extends StatelessWidget {
final void Function() onConfirm;
const RemoveClientModal({
Key? key,
super.key,
required this.onConfirm
}) : super(key: key);
});
@override
Widget build(BuildContext context) {

View file

@ -11,11 +11,11 @@ class SafeSearchModal extends StatefulWidget {
final void Function(SafeSearch) onConfirm;
const SafeSearchModal({
Key? key,
super.key,
required this.safeSearch,
required this.disabled,
required this.onConfirm
}) : super(key: key);
});
@override
State<SafeSearchModal> createState() => _SafeSearchModalState();

View file

@ -10,10 +10,10 @@ class ServicesModal extends StatefulWidget {
final void Function(List<String>) onConfirm;
const ServicesModal({
Key? key,
super.key,
required this.blockedServices,
required this.onConfirm,
}) : super(key: key);
});
@override
State<ServicesModal> createState() => _ServicesModalStateWidget();

View file

@ -33,14 +33,14 @@ class ClientsFab extends StatelessWidget {
processModal.close();
if (result == true) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.clientAddedSuccessfully,
color: Colors.green
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.clientNotAdded,
color: Colors.red

View file

@ -95,14 +95,14 @@ class _SearchClientsState extends State<SearchClients> {
processModal.close();
if (result == true) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.clientDeletedSuccessfully,
color: Colors.green
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.clientNotDeleted,
color: Colors.red
@ -119,14 +119,14 @@ class _SearchClientsState extends State<SearchClients> {
processModal.close();
if (result == true) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.clientUpdatedSuccessfully,
color: Colors.green
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.clientNotUpdated,
color: Colors.red
@ -205,7 +205,7 @@ class _SearchClientsState extends State<SearchClients> {
height: 1,
decoration: BoxDecoration(
color: showDivider == true
? Theme.of(context).colorScheme.surfaceVariant
? Theme.of(context).colorScheme.surfaceContainerHighest
: Colors.transparent
),
),

View file

@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
import 'package:adguard_home_manager/widgets/add_server/add_server_functions.dart';
class FabConnect extends StatelessWidget {
const FabConnect({Key? key}) : super(key: key);
const FabConnect({super.key});
@override
Widget build(BuildContext context) {

View file

@ -42,14 +42,14 @@ class AddFiltersButton extends StatelessWidget {
if (!context.mounted) return;
if (result == true) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.ruleAddedSuccessfully,
color: Colors.green
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.ruleNotAdded,
color: Colors.red
@ -67,14 +67,14 @@ class AddFiltersButton extends StatelessWidget {
if (!context.mounted) return;
if (result == true) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.customRulesUpdatedSuccessfully,
color: Colors.green
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.customRulesNotUpdated,
color: Colors.red
@ -146,28 +146,28 @@ class AddFiltersButton extends StatelessWidget {
if (!context.mounted) return;
if (result['success'] == true) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: "${AppLocalizations.of(context)!.listAdded} ${result['data']}.",
color: Colors.green
);
}
else if (result['success'] == false && result['error'] == 'invalid_url') {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.listUrlInvalid,
color: Colors.red
);
}
else if (result['success'] == false && result['error'] == 'url_exists') {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.listAlreadyAdded,
color: Colors.red
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.listNotAdded,
color: Colors.red

View file

@ -163,7 +163,7 @@ class _CustomRulesListState extends State<CustomRulesList> {
onPressed: () async {
final result = await filteringProvider.fetchFilters();
if (result == false) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.errorLoadFilters,
color: Colors.red
@ -203,7 +203,7 @@ class _CustomRulesListState extends State<CustomRulesList> {
onRefresh: () async {
final result = await filteringProvider.fetchFilters();
if (result == false) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.errorLoadFilters,
color: Colors.red

View file

@ -94,14 +94,14 @@ class _ListDetailsScreenState extends State<ListDetailsScreen> {
);
processModal.close();
if (result == true) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.listDataUpdated,
color: Colors.green
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.listDataNotUpdated,
color: Colors.red
@ -175,6 +175,7 @@ class _ListDetailsScreenState extends State<ListDetailsScreen> {
_Content(
isDialog: widget.dialog,
list: list,
type: widget.type,
)
],
),
@ -216,6 +217,7 @@ class _ListDetailsScreenState extends State<ListDetailsScreen> {
_Content(
isDialog: widget.dialog,
list: list,
type: widget.type,
)
],
),
@ -261,10 +263,12 @@ class _ListDetailsScreenState extends State<ListDetailsScreen> {
class _Content extends StatelessWidget {
final Filter list;
final bool isDialog;
final String type;
const _Content({
required this.list,
required this.isDialog
required this.isDialog,
required this.type,
});
@override
@ -339,7 +343,7 @@ class _Content extends StatelessWidget {
CustomListTile(
icon: Icons.shield_rounded,
title: AppLocalizations.of(context)!.listType,
subtitle: isDialog == 'whitelist'
subtitle: type == 'whitelist'
? AppLocalizations.of(context)!.whitelist
: AppLocalizations.of(context)!.blacklist,
padding: isDialog == true
@ -431,7 +435,7 @@ class _Actions extends StatelessWidget {
);
processModal.close();
if (result == true) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.listDeleted,
color: Colors.green
@ -439,7 +443,7 @@ class _Actions extends StatelessWidget {
Navigator.pop(context);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.listNotDeleted,
color: Colors.red

View file

@ -24,7 +24,7 @@ import 'package:adguard_home_manager/constants/enums.dart';
import 'package:adguard_home_manager/models/clients.dart';
class Filters extends StatefulWidget {
const Filters({Key? key}) : super(key: key);
const Filters({super.key});
@override
State<Filters> createState() => _FiltersState();
@ -57,14 +57,14 @@ class _FiltersState extends State<Filters> {
if (!mounted) return;
processModal.close();
if (result['success'] == true) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: "${result['data']['updated']} ${AppLocalizations.of(context)!.listsUpdated}",
color: Colors.green
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.listsNotUpdated,
color: Colors.red
@ -109,14 +109,14 @@ class _FiltersState extends State<Filters> {
processModal.close();
if (result == true) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.filteringStatusUpdated,
color: Colors.green
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.filteringStatusNotUpdated,
color: Colors.red
@ -133,14 +133,14 @@ class _FiltersState extends State<Filters> {
processModal.close();
if (result == true) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.updateFrequencyChanged,
color: Colors.green
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.updateFrequencyNotChanged,
color: Colors.red
@ -163,14 +163,14 @@ class _FiltersState extends State<Filters> {
processModal.close();
if (result == true) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.ruleRemovedSuccessfully,
color: Colors.green
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.ruleNotRemoved,
color: Colors.red

View file

@ -27,13 +27,13 @@ class FiltersList extends StatefulWidget {
final void Function(Filter, String) onOpenDetailsScreen;
const FiltersList({
Key? key,
super.key,
required this.loadStatus,
required this.scrollController,
required this.data,
required this.type,
required this.onOpenDetailsScreen
}) : super(key: key);
});
@override
State<FiltersList> createState() => _FiltersListState();
@ -133,7 +133,7 @@ class _FiltersListState extends State<FiltersList> {
onPressed: () async {
final result = await filteringProvider.fetchFilters();
if (result == false && mounted) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.errorLoadFilters,
color: Colors.red
@ -173,7 +173,7 @@ class _FiltersListState extends State<FiltersList> {
onRefresh: () async {
final result = await filteringProvider.fetchFilters();
if (result == false && mounted) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.errorLoadFilters,
color: Colors.red

View file

@ -84,7 +84,7 @@ class FiltersTripleColumn extends StatelessWidget {
onPressed: () async {
final result = await filteringProvider.fetchFilters();
if (result == false && context.mounted) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.errorLoadFilters,
color: Colors.red

View file

@ -59,14 +59,14 @@ class ListOptionsMenu extends StatelessWidget {
if (!context.mounted) return;
if (result == true) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.listDataUpdated,
color: Colors.green
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.listDataNotUpdated,
color: Colors.red

View file

@ -68,14 +68,14 @@ class _BlockedServicesScreenStateWidget extends State<BlockedServicesScreen> {
processModal.close();
if (result == true) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.blockedServicesUpdated,
color: Colors.green
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.blockedServicesNotUpdated,
color: Colors.red
@ -119,24 +119,26 @@ class _BlockedServicesScreenStateWidget extends State<BlockedServicesScreen> {
SliverOverlapInjector(
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
),
if (filteringProvider.blockedServicesLoadStatus == LoadStatus.loading) Container(
padding: const EdgeInsets.symmetric(horizontal: 16),
width: double.maxFinite,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const CircularProgressIndicator(),
const SizedBox(height: 30),
Text(
AppLocalizations.of(context)!.loadingBlockedServicesList,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 22,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
)
],
if (filteringProvider.blockedServicesLoadStatus == LoadStatus.loading) SliverFillRemaining(
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16),
width: double.maxFinite,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const CircularProgressIndicator(),
const SizedBox(height: 30),
Text(
AppLocalizations.of(context)!.loadingBlockedServicesList,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 22,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
)
],
),
),
),
if (filteringProvider.blockedServicesLoadStatus == LoadStatus.loaded) SliverList.builder(
@ -181,28 +183,30 @@ class _BlockedServicesScreenStateWidget extends State<BlockedServicesScreen> {
),
)
),
if (filteringProvider.blockedServicesLoadStatus == LoadStatus.error) Container(
padding: const EdgeInsets.symmetric(horizontal: 16),
width: double.maxFinite,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Icon(
Icons.error,
color: Colors.red,
size: 50,
),
const SizedBox(height: 30),
Text(
AppLocalizations.of(context)!.blockedServicesListNotLoaded,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 22,
color: Theme.of(context).colorScheme.onSurfaceVariant,
if (filteringProvider.blockedServicesLoadStatus == LoadStatus.error) SliverFillRemaining(
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16),
width: double.maxFinite,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Icon(
Icons.error,
color: Colors.red,
size: 50,
),
)
],
const SizedBox(height: 30),
Text(
AppLocalizations.of(context)!.blockedServicesListNotLoaded,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 22,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
)
],
),
),
)
],

View file

@ -276,16 +276,22 @@ class _CustomRuleEditor extends StatelessWidget {
colors: SegmentedButtonSlideColors(
barColor: Theme.of(context).colorScheme.primary.withOpacity(0.2),
backgroundSelectedColor: Theme.of(context).colorScheme.primary,
foregroundSelectedColor: Theme.of(context).colorScheme.onPrimary,
foregroundUnselectedColor: Theme.of(context).colorScheme.onSurface,
hoverColor: Theme.of(context).colorScheme.onSurfaceVariant,
),
textOverflow: TextOverflow.ellipsis,
fontSize: 14,
height: 40,
margin: const EdgeInsets.symmetric(
horizontal: 24,
),
selectedTextStyle: TextStyle(
color: Theme.of(context).colorScheme.onPrimary,
fontWeight: FontWeight.w700
),
unselectedTextStyle: TextStyle(
color: Theme.of(context).colorScheme.onSurface,
),
hoverTextStyle: TextStyle(
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
Container(height: 20),
Material(

View file

@ -5,9 +5,9 @@ class RemoveCustomRule extends StatelessWidget {
final void Function() onConfirm;
const RemoveCustomRule({
Key? key,
super.key,
required this.onConfirm
}) : super(key: key);
});
@override
Widget build(BuildContext context) {

View file

@ -235,10 +235,10 @@ class _Content extends StatelessWidget {
}
: null,
style: ButtonStyle(
overlayColor: MaterialStateProperty.all(
overlayColor: WidgetStateProperty.all(
Theme.of(context).colorScheme.primary.withOpacity(0.1)
),
foregroundColor: MaterialStateProperty.all(
foregroundColor: WidgetStateProperty.all(
selectedOption != null
? Theme.of(context).colorScheme.primary
: Colors.grey,

View file

@ -9,11 +9,11 @@ class DeleteSelectionModal extends StatefulWidget {
final void Function() onDelete;
const DeleteSelectionModal({
Key? key,
super.key,
required this.selectedBlacklists,
required this.selectedWhitelists,
required this.onDelete,
}) : super(key: key);
});
@override
State<DeleteSelectionModal> createState() => _DeleteSelectionModalState();

View file

@ -9,11 +9,11 @@ class EnableDisableSelectionModal extends StatefulWidget {
final void Function() onDelete;
const EnableDisableSelectionModal({
Key? key,
super.key,
required this.selectedBlacklists,
required this.selectedWhitelists,
required this.onDelete,
}) : super(key: key);
});
@override
State<EnableDisableSelectionModal> createState() => _EnableDisableSelectionModalState();

View file

@ -13,13 +13,13 @@ class SelectionList extends StatelessWidget {
final void Function() unselectAll;
const SelectionList({
Key? key,
super.key,
required this.lists,
required this.selectedLists,
required this.onSelect,
required this.selectAll,
required this.unselectAll,
}) : super(key: key);
});
@override
Widget build(BuildContext context) {
@ -231,11 +231,10 @@ class _CheckboxTile extends StatelessWidget {
final bool isSelected;
const _CheckboxTile({
Key? key,
required this.list,
required this.onSelect,
required this.isSelected,
}) : super(key: key);
});
@override
Widget build(BuildContext context) {

View file

@ -82,8 +82,8 @@ class _SelectionScreenState extends State<SelectionScreen> with TickerProviderSt
blacklists: _selectedBlacklists,
whitelists: _selectedWhitelists
);
if (!mounted) return;
processModal.close();
if (!context.mounted) return;
showDialog(
context: context,
builder: (ctx) => SelectionResultModal(
@ -113,8 +113,8 @@ class _SelectionScreenState extends State<SelectionScreen> with TickerProviderSt
blacklists: _selectedBlacklists,
whitelists: _selectedWhitelists
);
if (!mounted) return;
processModal.close();
if (!context.mounted) return;
showDialog(
context: context,
builder: (ctx) => SelectionResultModal(

View file

@ -37,7 +37,7 @@ class CombinedChartItem {
}
class CombinedHomeChart extends StatelessWidget {
const CombinedHomeChart({Key? key}) : super(key: key);
const CombinedHomeChart({super.key});
List<int>? removeZero(List<int> list) {
final removed = list.where((i) => i > 0);
@ -266,12 +266,11 @@ class _Legend extends StatelessWidget {
final String? secondaryValue;
const _Legend({
Key? key,
required this.label,
required this.color,
required this.primaryValue,
this.secondaryValue
}) : super(key: key);
});
@override
Widget build(BuildContext context) {

View file

@ -7,7 +7,7 @@ import 'package:adguard_home_manager/providers/status_provider.dart';
import 'package:adguard_home_manager/constants/enums.dart';
class HomeFab extends StatelessWidget {
const HomeFab({Key? key}) : super(key: key);
const HomeFab({super.key});
@override
Widget build(BuildContext context) {

View file

@ -93,7 +93,7 @@ class _HomeState extends State<Home> {
onRefresh: () async {
final result = await statusProvider.getServerStatus();
if (mounted && result == false) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.serverStatusNotRefreshed,
color: Colors.red

View file

@ -82,7 +82,7 @@ class _ManagementModalState extends State<ManagementModal> with SingleTickerProv
time: time
);
if (mounted && result == false) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.invalidUsernamePassword,
color: Colors.red

View file

@ -8,13 +8,13 @@ class SmallSwitch extends StatelessWidget {
final bool disabled;
const SmallSwitch({
Key? key,
super.key,
required this.label,
required this.icon,
required this.value,
required this.onChange,
required this.disabled,
}) : super(key: key);
});
@override
Widget build(BuildContext context) {

View file

@ -83,14 +83,14 @@ class TopItemsLists extends StatelessWidget {
if (!context.mounted) return;
if (rules == true) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.userFilteringRulesUpdated,
color: Colors.green
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.userFilteringRulesNotUpdated,
color: Colors.red
@ -121,21 +121,21 @@ class TopItemsLists extends StatelessWidget {
processModal.close();
if (result.successful == true) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.clientAddedSuccessfully,
color: Colors.green
);
}
else if (result.successful == false && result.content == 'client_another_list') {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.clientAnotherList,
color: Colors.red
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: newList == AccessSettingsList.allowed || newList == AccessSettingsList.disallowed
? AppLocalizations.of(context)!.clientNotRemoved

View file

@ -9,6 +9,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:adguard_home_manager/widgets/options_menu.dart';
import 'package:adguard_home_manager/widgets/custom_list_tile.dart';
import 'package:adguard_home_manager/widgets/floating_search_bar.dart';
import 'package:adguard_home_manager/models/menu_option.dart';
import 'package:adguard_home_manager/constants/enums.dart';
@ -16,6 +17,7 @@ import 'package:adguard_home_manager/functions/number_format.dart';
import 'package:adguard_home_manager/providers/status_provider.dart';
enum _SortingOptions { highestToLowest, lowestToHighest }
final GlobalKey _searchButtonKey = GlobalKey();
class TopItemsScreen extends StatefulWidget {
final HomeTopItems type;
@ -47,15 +49,18 @@ class TopItemsScreen extends StatefulWidget {
class _TopItemsScreenState extends State<TopItemsScreen> {
_SortingOptions _sortingOptions = _SortingOptions.highestToLowest;
bool searchActive = false;
final TextEditingController searchController = TextEditingController();
String? _currentSearchValue = "";
List<Map<String, dynamic>> data = [];
List<Map<String, dynamic>> screenData = [];
void search(String value) {
List<Map<String, dynamic>> newValues = widget.data.where((item) => item.keys.toList()[0].contains(value)).toList();
setState(() => screenData = newValues);
setState(() {
screenData = newValues;
_currentSearchValue = searchController.text;
});
}
@override
@ -75,118 +80,132 @@ class _TopItemsScreenState extends State<TopItemsScreen> {
final sortedValues = _sortingOptions == _SortingOptions.lowestToHighest
? screenData.reversed.toList()
: screenData.toList();
void showSearchDialog() {
showDialog(
context: context,
builder: (context) => FloatingSearchBar(
existingSearchValue: _currentSearchValue,
searchButtonRenderBox: _searchButtonKey.currentContext?.findRenderObject() as RenderBox?,
onSearchCompleted: (v) {
List<Map<String, dynamic>> newValues = widget.data.where((item) => item.keys.toList()[0].contains(v)).toList();
setState(() {
screenData = newValues;
_currentSearchValue = v;
});
},
),
);
}
if (widget.isFullscreen == true) {
return Dialog.fullscreen(
child: Scaffold(
appBar: AppBar(
title: searchActive == true
? Padding(
padding: const EdgeInsets.only(bottom: 3),
child: TextFormField(
controller: searchController,
onChanged: search,
decoration: InputDecoration(
hintText: AppLocalizations.of(context)!.search,
hintStyle: const TextStyle(
fontWeight: FontWeight.normal,
fontSize: 18
return Scaffold(
body: NestedScrollView(
headerSliverBuilder: (context, innerBoxIsScrolled) => [
SliverOverlapAbsorber(
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
sliver: SliverAppBar.large(
title: Text(widget.title),
actions: [
IconButton(
key: _searchButtonKey,
onPressed: showSearchDialog,
icon: const Icon(Icons.search_rounded),
tooltip: AppLocalizations.of(context)!.search,
),
PopupMenuButton(
icon: const Icon(Icons.sort_rounded),
itemBuilder: (context) => [
PopupMenuItem(
onTap: () => setState(() => _sortingOptions = _SortingOptions.highestToLowest),
child: Row(
children: [
const Icon(Icons.arrow_downward_rounded),
const SizedBox(width: 8),
Expanded(
child: Text(AppLocalizations.of(context)!.fromHighestToLowest)
),
const SizedBox(width: 16),
Icon(
_sortingOptions == _SortingOptions.highestToLowest
? Icons.radio_button_checked_rounded
: Icons.radio_button_unchecked_rounded,
color: _sortingOptions == _SortingOptions.highestToLowest
? Theme.of(context).colorScheme.primary
: Theme.of(context).colorScheme.onSurfaceVariant,
)
],
)
),
border: InputBorder.none,
),
style: const TextStyle(
fontWeight: FontWeight.normal,
fontSize: 18
),
autofocus: true,
),
)
: Text(widget.title),
leading: searchActive == true ?
IconButton(
onPressed: () => setState(() {
searchActive = false;
searchController.text = '';
screenData = data;
}),
icon: const Icon(Icons.arrow_back),
tooltip: AppLocalizations.of(context)!.exitSearch,
) : null,
actions: [
if (searchActive == false) IconButton(
onPressed: () => setState(() => searchActive = true),
icon: const Icon(Icons.search),
tooltip: AppLocalizations.of(context)!.search,
),
if (searchActive == true) IconButton(
onPressed: () => setState(() {
searchController.text = '';
screenData = data;
}),
icon: const Icon(Icons.clear_rounded),
tooltip: AppLocalizations.of(context)!.clearSearch,
),
PopupMenuButton(
icon: const Icon(Icons.sort_rounded),
itemBuilder: (context) => [
PopupMenuItem(
onTap: () => setState(() => _sortingOptions = _SortingOptions.highestToLowest),
child: Row(
children: [
const Icon(Icons.arrow_downward_rounded),
const SizedBox(width: 8),
Expanded(
child: Text(AppLocalizations.of(context)!.fromHighestToLowest)
),
const SizedBox(width: 16),
Icon(
_sortingOptions == _SortingOptions.highestToLowest
? Icons.radio_button_checked_rounded
: Icons.radio_button_unchecked_rounded,
color: _sortingOptions == _SortingOptions.highestToLowest
? Theme.of(context).colorScheme.primary
: Theme.of(context).colorScheme.onSurfaceVariant,
PopupMenuItem(
onTap: () => setState(() => _sortingOptions = _SortingOptions.lowestToHighest),
child: Row(
children: [
const Icon(Icons.arrow_upward_rounded),
const SizedBox(width: 8),
Expanded(
child: Text(AppLocalizations.of(context)!.fromLowestToHighest)
),
const SizedBox(width: 16),
Icon(
_sortingOptions == _SortingOptions.lowestToHighest
? Icons.radio_button_checked_rounded
: Icons.radio_button_unchecked_rounded,
color: _sortingOptions == _SortingOptions.lowestToHighest
? Theme.of(context).colorScheme.primary
: Theme.of(context).colorScheme.onSurfaceVariant,
)
],
)
],
)
),
],
),
PopupMenuItem(
onTap: () => setState(() => _sortingOptions = _SortingOptions.lowestToHighest),
child: Row(
children: [
const Icon(Icons.arrow_upward_rounded),
const SizedBox(width: 8),
Expanded(
child: Text(AppLocalizations.of(context)!.fromLowestToHighest)
const SizedBox(width: 8)
],
)
)
],
body: SafeArea(
top: false,
bottom: false,
child: Builder(
builder: (context) => CustomScrollView(
slivers: [
SliverOverlapInjector(
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
),
if (sortedValues.isEmpty) Center(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Text(
AppLocalizations.of(context)!.noItemsSearch,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 22,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
const SizedBox(width: 16),
Icon(
_sortingOptions == _SortingOptions.lowestToHighest
? Icons.radio_button_checked_rounded
: Icons.radio_button_unchecked_rounded,
color: _sortingOptions == _SortingOptions.lowestToHighest
? Theme.of(context).colorScheme.primary
: Theme.of(context).colorScheme.onSurfaceVariant,
)
],
)
),
),
),
if (sortedValues.isNotEmpty) SliverPadding(
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewPadding.bottom),
sliver: SliverList.builder(
itemCount: sortedValues.length,
itemBuilder: (context, index) => _Item(
data: sortedValues[index],
isClient: widget.isClient,
options: widget.options,
total: total,
withProgressBar: widget.withProgressBar,
onTapEntry: widget.onTapEntry,
buildValue: widget.buildValue,
),
),
),
],
),
const SizedBox(width: 8)
],
),
body: SafeArea(
child: _Content(
buildValue: widget.buildValue,
isClient: widget.isClient,
onTapEntry: widget.onTapEntry,
options: widget.options,
screenData: sortedValues,
total: total,
withProgressBar: widget.withProgressBar,
),
),
)
)
),
);
}
@ -242,14 +261,29 @@ class _TopItemsScreenState extends State<TopItemsScreen> {
),
),
Expanded(
child: _Content(
buildValue: widget.buildValue,
isClient: widget.isClient,
onTapEntry: widget.onTapEntry,
options: widget.options,
screenData: sortedValues,
total: total,
withProgressBar: widget.withProgressBar,
child: sortedValues.isNotEmpty ? ListView.builder(
itemCount: sortedValues.length,
itemBuilder: (context, index) => _Item(
data: sortedValues[index],
isClient: widget.isClient,
options: widget.options,
withProgressBar: widget.withProgressBar,
onTapEntry: widget.onTapEntry,
buildValue: widget.buildValue,
total: total,
),
) : Center(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Text(
AppLocalizations.of(context)!.noItemsSearch,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 22,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
),
),
),
],
@ -260,8 +294,8 @@ class _TopItemsScreenState extends State<TopItemsScreen> {
}
}
class _Content extends StatelessWidget {
final List<Map<String, dynamic>> screenData;
class _Item extends StatelessWidget {
final dynamic data;
final bool? isClient;
final List<MenuOption> Function(dynamic) options;
final bool withProgressBar;
@ -269,8 +303,8 @@ class _Content extends StatelessWidget {
final String Function(dynamic) buildValue;
final double total;
const _Content({
required this.screenData,
const _Item({
required this.data,
required this.isClient,
required this.options,
required this.withProgressBar,
@ -283,98 +317,75 @@ class _Content extends StatelessWidget {
Widget build(BuildContext context) {
final statusProvider = Provider.of<StatusProvider>(context);
if (screenData.isNotEmpty) {
return ListView.builder(
padding: const EdgeInsets.only(top: 0),
itemCount: screenData.length,
itemBuilder: (context, index) {
String? name;
if (isClient != null && isClient == true) {
try {
name = statusProvider.serverStatus!.clients.firstWhere((c) => c.ids.contains(screenData[index].keys.toList()[0])).name;
} catch (e) {
// ---- //
}
}
return OptionsMenu(
options: options,
value: screenData[index].keys.toList()[0],
onTap: onTapEntry != null
? (v) {
onTapEntry!(v);
Navigator.pop(context);
}
: null,
child: CustomListTile(
title: screenData[index].keys.toList()[0],
trailing: Text(
buildValue(screenData[index].values.toList()[0]),
style: TextStyle(
color: Theme.of(context).colorScheme.onSurfaceVariant
),
),
subtitleWidget: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (name != null) ...[
Text(
name,
style: TextStyle(
fontSize: 14,
color: Theme.of(context).colorScheme.onSurface
),
),
const SizedBox(height: 5),
],
if (withProgressBar == true) Row(
children: [
SizedBox(
width: 50,
child: Text(
"${doubleFormat((screenData[index].values.toList()[0]/total*100), Platform.localeName)}%",
style: TextStyle(
color: Theme.of(context).listTileTheme.textColor
),
),
),
const SizedBox(width: 10),
Flexible(
child: LinearPercentIndicator(
animation: true,
lineHeight: 4,
animationDuration: 500,
curve: Curves.easeOut,
percent: screenData[index].values.toList()[0]/total,
barRadius: const Radius.circular(5),
progressColor: Theme.of(context).colorScheme.primary,
backgroundColor: Theme.of(context).colorScheme.surfaceVariant,
),
),
const SizedBox(width: 10),
],
),
],
)
),
);
}
);
String? name;
if (isClient != null && isClient == true) {
try {
name = statusProvider.serverStatus!.clients.firstWhere((c) => c.ids.contains(data.keys.toList()[0])).name;
} catch (e) {
// ---- //
}
}
else {
return Center(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Text(
AppLocalizations.of(context)!.noItemsSearch,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 22,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
return OptionsMenu(
options: options,
value: data.keys.toList()[0],
onTap: onTapEntry != null
? (v) {
onTapEntry!(v);
Navigator.pop(context);
}
: null,
child: CustomListTile(
title: data.keys.toList()[0],
trailing: Text(
buildValue(data.values.toList()[0]),
style: TextStyle(
color: Theme.of(context).colorScheme.onSurfaceVariant
),
),
);
}
subtitleWidget: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (name != null) ...[
Text(
name,
style: TextStyle(
fontSize: 14,
color: Theme.of(context).colorScheme.onSurface
),
),
const SizedBox(height: 5),
],
if (withProgressBar == true) Row(
children: [
SizedBox(
width: 50,
child: Text(
"${doubleFormat((data.values.toList()[0]/total*100), Platform.localeName)}%",
style: TextStyle(
color: Theme.of(context).listTileTheme.textColor
),
),
),
const SizedBox(width: 10),
Flexible(
child: LinearPercentIndicator(
animation: true,
lineHeight: 4,
animationDuration: 500,
curve: Curves.easeOut,
percent: data.values.toList()[0]/total,
barRadius: const Radius.circular(5),
progressColor: Theme.of(context).colorScheme.primary,
backgroundColor: Theme.of(context).colorScheme.surfaceTint.withOpacity(0.2),
),
),
const SizedBox(width: 10),
],
),
],
)
),
);
}
}

View file

@ -1,6 +1,5 @@
// ignore_for_file: use_build_context_synchronously
import 'package:adguard_home_manager/functions/copy_clipboard.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
@ -11,6 +10,7 @@ import 'package:adguard_home_manager/screens/logs/details/log_list_tile.dart';
import 'package:adguard_home_manager/functions/desktop_mode.dart';
import 'package:adguard_home_manager/functions/open_url.dart';
import 'package:adguard_home_manager/functions/copy_clipboard.dart';
import 'package:adguard_home_manager/constants/urls.dart';
import 'package:adguard_home_manager/classes/process_modal.dart';
import 'package:adguard_home_manager/functions/get_filtered_status.dart';
@ -24,11 +24,13 @@ import 'package:adguard_home_manager/providers/app_config_provider.dart';
class LogDetailsScreen extends StatelessWidget {
final Log log;
final bool dialog;
final bool twoColumns;
const LogDetailsScreen({
super.key,
required this.log,
required this.dialog
required this.dialog,
required this.twoColumns,
});
@override
@ -50,14 +52,14 @@ class LogDetailsScreen extends StatelessWidget {
processModal.close();
if (rules == true) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.userFilteringRulesUpdated,
color: Colors.green
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.userFilteringRulesNotUpdated,
color: Colors.red
@ -148,6 +150,7 @@ class LogDetailsScreen extends StatelessWidget {
centerTitle: false,
forceElevated: innerBoxIsScrolled,
surfaceTintColor: isDesktop(width) ? Colors.transparent : null,
automaticallyImplyLeading: twoColumns != true,
title: Text(AppLocalizations.of(context)!.logDetails),
actions: [
IconButton(

View file

@ -187,9 +187,15 @@ class _ClientsModalState extends State<ClientsModal> {
colors: SegmentedButtonSlideColors(
barColor: Theme.of(context).colorScheme.primary.withOpacity(0.2),
backgroundSelectedColor: Theme.of(context).colorScheme.primary,
foregroundSelectedColor: Theme.of(context).colorScheme.onPrimary,
foregroundUnselectedColor: Theme.of(context).colorScheme.onSurface,
hoverColor: Theme.of(context).colorScheme.onSurfaceVariant,
),
selectedTextStyle: TextStyle(
color: Theme.of(context).colorScheme.onPrimary,
),
unselectedTextStyle: TextStyle(
color: Theme.of(context).colorScheme.onSurface,
),
hoverTextStyle: TextStyle(
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
),
@ -274,9 +280,16 @@ class _ClientsModalState extends State<ClientsModal> {
colors: SegmentedButtonSlideColors(
barColor: Theme.of(context).colorScheme.primary.withOpacity(0.2),
backgroundSelectedColor: Theme.of(context).colorScheme.primary,
foregroundSelectedColor: Theme.of(context).colorScheme.onPrimary,
foregroundUnselectedColor: Theme.of(context).colorScheme.onSurface,
hoverColor: Theme.of(context).colorScheme.onSurfaceVariant,
),
selectedTextStyle: TextStyle(
color: Theme.of(context).colorScheme.onPrimary,
fontWeight: FontWeight.w700
),
unselectedTextStyle: TextStyle(
color: Theme.of(context).colorScheme.onSurface,
),
hoverTextStyle: TextStyle(
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
),

View file

@ -0,0 +1,93 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:adguard_home_manager/screens/logs/log_tile.dart';
import 'package:adguard_home_manager/screens/logs/details/log_details_screen.dart';
import 'package:adguard_home_manager/models/logs.dart';
import 'package:adguard_home_manager/providers/live_logs_provider.dart';
class LiveLogsList extends StatelessWidget {
final Log? selectedLog;
final void Function(Log) onLogSelected;
final bool twoColumns;
const LiveLogsList({
super.key,
required this.selectedLog,
required this.onLogSelected,
required this.twoColumns,
});
@override
Widget build(BuildContext context) {
final liveLogsProvider = Provider.of<LiveLogsProvider>(context);
return NestedScrollView(
headerSliverBuilder: (context, innerBoxIsScrolled) => [
SliverOverlapAbsorber(
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
sliver: SliverAppBar.large(
title: Text(AppLocalizations.of(context)!.liveLogs),
)
)
],
body: SafeArea(
top: false,
bottom: false,
child: Builder(
builder: (context) => CustomScrollView(
slivers: [
SliverOverlapInjector(
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
),
if (liveLogsProvider.logs.isEmpty) SliverFillRemaining(
child: Center(
child: Padding(
padding: const EdgeInsets.all(16),
child: Text(
AppLocalizations.of(context)!.hereWillAppearRealtimeLogs,
textAlign: TextAlign.center,
style: TextStyle(
color: Theme.of(context).colorScheme.onSurfaceVariant,
fontSize: 22
),
),
),
),
),
if (liveLogsProvider.logs.isNotEmpty) SliverPadding(
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewPadding.bottom),
sliver: SliverList.builder(
itemCount: liveLogsProvider.logs.length,
itemBuilder: (context, index) => LogTile(
log: liveLogsProvider.logs[index],
length: liveLogsProvider.logs.length,
index: index,
onLogTap: (log) {
if (!twoColumns) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => LogDetailsScreen(
log: log,
dialog: false,
twoColumns: twoColumns,
)
)
);
}
onLogSelected(log);
},
isLogSelected: selectedLog == liveLogsProvider.logs[index],
twoColumns: twoColumns
),
),
)
],
),
)
)
);
}
}

View file

@ -0,0 +1,70 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:adguard_home_manager/screens/logs/details/log_details_screen.dart';
import 'package:adguard_home_manager/screens/logs/live/live_logs_list.dart';
import 'package:adguard_home_manager/models/logs.dart';
import 'package:adguard_home_manager/providers/live_logs_provider.dart';
class LiveLogsScreen extends StatefulWidget {
const LiveLogsScreen({super.key});
@override
State<LiveLogsScreen> createState() => _LiveLogsScreenState();
}
class _LiveLogsScreenState extends State<LiveLogsScreen> {
Log? _selectedLog;
@override
void initState() {
Provider.of<LiveLogsProvider>(context, listen: false).startFetchLogs();
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth > 800) {
return Material(
color: Colors.transparent,
child: Row(
children: [
Expanded(
flex: 2,
child: LiveLogsList(
twoColumns: true,
selectedLog: _selectedLog,
onLogSelected: (log) => setState(() => _selectedLog = log),
)
),
Expanded(
flex: 3,
child: _selectedLog != null
? LogDetailsScreen(
log: _selectedLog!,
dialog: false,
twoColumns: true,
)
: const SizedBox()
)
],
),
);
}
else {
return LiveLogsList(
twoColumns: false,
selectedLog: _selectedLog,
onLogSelected: (log) => setState(() => _selectedLog = log),
);
}
},
),
);
}
}

View file

@ -108,14 +108,14 @@ class LogTile extends StatelessWidget {
if (!context.mounted) return;
if (rules == true) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.userFilteringRulesUpdated,
color: Colors.green
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.userFilteringRulesNotUpdated,
color: Colors.red
@ -134,14 +134,14 @@ class LogTile extends StatelessWidget {
if (!context.mounted) return;
if (result == true) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.clientAddedSuccessfully,
color: Colors.green
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.clientNotAdded,
color: Colors.red
@ -163,7 +163,7 @@ class LogTile extends StatelessWidget {
if (!context.mounted) return;
if (result == true) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: isDomainBlocked(log.reason) == true
? AppLocalizations.of(context)!.domainUnblockedThisClient(log.question.name!)
@ -172,7 +172,7 @@ class LogTile extends StatelessWidget {
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.ruleNotAdded,
color: Colors.red
@ -199,21 +199,21 @@ class LogTile extends StatelessWidget {
if (!context.mounted) return;
if (result.successful == true) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.clientAddedSuccessfully,
color: Colors.green
);
}
else if (result.successful == false && result.content == 'client_another_list') {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.clientAnotherList,
color: Colors.red
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.changesNotSaved,
color: Colors.red

View file

@ -48,6 +48,7 @@ class _LogsState extends State<Logs> {
? LogDetailsScreen(
log: _selectedLog!,
dialog: false,
twoColumns: true,
)
: const SizedBox()
)

View file

@ -42,7 +42,7 @@ class _LogsListWidgetState extends State<LogsListWidget> {
final result = await statusProvider.getFilteringRules();
if (mounted && result == false) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.couldntGetFilteringStatus,
color: Colors.red
@ -56,7 +56,7 @@ class _LogsListWidgetState extends State<LogsListWidget> {
final result = await clientsProvider.fetchClients();
if (mounted && result == false) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.couldntGetFilteringStatus,
color: Colors.red
@ -188,6 +188,7 @@ class _LogsListWidgetState extends State<LogsListWidget> {
builder: (context) => LogDetailsScreen(
log: log,
dialog: false,
twoColumns: widget.twoColumns,
)
)
);

View file

@ -6,11 +6,14 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:adguard_home_manager/screens/logs/live/live_logs_screen.dart';
import 'package:adguard_home_manager/screens/logs/filters/logs_filters_modal.dart';
import 'package:adguard_home_manager/widgets/floating_search_bar.dart';
import 'package:adguard_home_manager/config/globals.dart';
import 'package:adguard_home_manager/constants/enums.dart';
import 'package:adguard_home_manager/functions/desktop_mode.dart';
import 'package:adguard_home_manager/providers/live_logs_provider.dart';
import 'package:adguard_home_manager/providers/servers_provider.dart';
import 'package:adguard_home_manager/models/applied_filters.dart';
import 'package:adguard_home_manager/providers/logs_provider.dart';
@ -58,9 +61,10 @@ class LogsListAppBar extends StatelessWidget {
void showSearchDialog() {
showDialog(
context: context,
builder: (context) => _Search(
builder: (context) => FloatingSearchBar(
existingSearchValue: logsProvider.appliedFilters.searchText ?? "",
searchButtonRenderBox: _searchButtonKey.currentContext?.findRenderObject() as RenderBox?,
onSearch: (v) {
onSearchCompleted: (v) {
logsProvider.setAppliedFilters(
AppliedFiters(
selectedResultStatus: logsProvider.appliedFilters.selectedResultStatus,
@ -70,10 +74,36 @@ class LogsListAppBar extends StatelessWidget {
);
logsProvider.filterLogs();
},
onSearchFieldUpdated: (v) {
if (v == "") {
logsProvider.setSearchText(null);
return;
}
logsProvider.setSearchText(v);
},
onSearchFieldCleared: () {
logsProvider.setSearchText(null);
},
),
);
}
void openLiveLogsScreen() {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => MultiProvider(
providers: [
ChangeNotifierProxyProvider<ServersProvider, LiveLogsProvider>(
create: (context) => LiveLogsProvider(),
update: (context, servers, logs) => logs!..update(servers),
),
],
child: const LiveLogsScreen()
)
)
);
}
final Map<String, String> translatedString = {
"all": AppLocalizations.of(context)!.all,
"filtered": AppLocalizations.of(context)!.filtered,
@ -104,10 +134,29 @@ class LogsListAppBar extends StatelessWidget {
icon: const Icon(Icons.search_rounded),
tooltip: AppLocalizations.of(context)!.search,
),
if (logsProvider.loadStatus == LoadStatus.loaded) IconButton(
onPressed: openFilersModal,
icon: const Icon(Icons.filter_list_rounded),
tooltip: AppLocalizations.of(context)!.filters,
if (logsProvider.loadStatus == LoadStatus.loaded) PopupMenuButton(
itemBuilder: (context) => [
PopupMenuItem(
onTap: openFilersModal,
child: Row(
children: [
const Icon(Icons.filter_list_rounded),
const SizedBox(width: 10),
Text(AppLocalizations.of(context)!.filters)
],
)
),
PopupMenuItem(
onTap: openLiveLogsScreen,
child: Row(
children: [
const Icon(Icons.stream_rounded),
const SizedBox(width: 10),
Text(AppLocalizations.of(context)!.liveLogs)
],
)
),
],
),
const SizedBox(width: 8),
],
@ -241,105 +290,3 @@ class LogsListAppBar extends StatelessWidget {
}
}
class _Search extends StatefulWidget {
final void Function(String) onSearch;
final RenderBox? searchButtonRenderBox;
const _Search({
required this.onSearch,
required this.searchButtonRenderBox,
});
@override
State<_Search> createState() => _SearchState();
}
class _SearchState extends State<_Search> {
final _searchController = TextEditingController();
@override
void initState() {
final logsProvider = Provider.of<LogsProvider>(context, listen: false);
_searchController.text = logsProvider.appliedFilters.searchText ?? "";
super.initState();
}
@override
Widget build(BuildContext context) {
final logsProvider = Provider.of<LogsProvider>(context);
final position = widget.searchButtonRenderBox?.localToGlobal(Offset.zero);
final topPadding = MediaQuery.of(globalNavigatorKey.currentContext!).viewPadding.top;
return GestureDetector(
onTap: () => Navigator.pop(context),
child: Material(
color: Colors.transparent,
child: LayoutBuilder(
builder: (context, constraints) {
final double width = constraints.maxWidth - 32 > 500 ? 500 : constraints.maxWidth - 32;
return Stack(
alignment: Alignment.topCenter,
children: [
Positioned(
top: position != null ? position.dy - topPadding : topPadding,
child: SizedBox(
width: width,
child: GestureDetector(
onTap: () => {},
child: Container(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(16)
),
child: ClipRRect(
borderRadius: BorderRadius.circular(16),
child: TextFormField(
controller: _searchController,
onChanged: (v) {
if (v == "") {
logsProvider.setSearchText(null);
return;
}
logsProvider.setSearchText(v);
},
onFieldSubmitted: (v) {
widget.onSearch(v);
Navigator.pop(context);
},
autofocus: true,
decoration: InputDecoration(
hintText: AppLocalizations.of(context)!.search,
prefixIcon: const Icon(Icons.search_rounded),
border: InputBorder.none,
filled: true,
fillColor: Colors.grey.withOpacity(0.2),
suffixIcon: _searchController.text != ""
? IconButton(
onPressed: () {
_searchController.text = "";
logsProvider.setSearchText(null);
},
icon: const Icon(
Icons.close_rounded,
size: 20,
),
tooltip: AppLocalizations.of(context)!.clearSearch,
)
: null
),
),
),
),
),
),
)
],
);
}
),
),
);
}
}

View file

@ -71,7 +71,7 @@ class _ClientsListState extends State<ClientsList> {
Future refetchClients() async {
final result = await clientsProvider.fetchClients();
if (result == false && mounted) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.clientsNotLoaded,
color: Colors.red
@ -104,21 +104,21 @@ class _ClientsListState extends State<ClientsList> {
processModal.close();
if (result.successful == true) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.clientRemovedSuccessfully,
color: Colors.green
);
}
else if (result.successful == false && result.content == 'client_another_list') {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.clientAnotherList,
color: Colors.red
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: type == AccessSettingsList.allowed || type == AccessSettingsList.disallowed
? AppLocalizations.of(context)!.clientNotRemoved
@ -137,21 +137,21 @@ class _ClientsListState extends State<ClientsList> {
processModal.close();
if (result.successful == true) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.clientAddedSuccessfully,
color: Colors.green
);
}
else if (result.successful == false && result.content == 'client_another_list') {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.clientAnotherList,
color: Colors.red
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: type == AccessSettingsList.allowed || type == AccessSettingsList.disallowed
? AppLocalizations.of(context)!.clientNotRemoved

View file

@ -26,14 +26,14 @@ class AdvancedSettings extends StatelessWidget {
final result = await function(newStatus);
if (!context.mounted) return;
if (result == true) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.settingsUpdatedSuccessfully,
color: Colors.green
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.cannotUpdateSettings,
color: Colors.red

View file

@ -31,7 +31,7 @@ class ColorItem extends StatelessWidget {
child: InkWell(
onTap: () => onChanged(numericValue),
borderRadius: BorderRadius.circular(50),
overlayColor: const MaterialStatePropertyAll(Colors.grey),
overlayColor: const WidgetStatePropertyAll(Colors.grey),
child: Container(
width: 50,
height: 50,

View file

@ -9,14 +9,14 @@ class ThemeModeButton extends StatelessWidget {
final bool? disabled;
const ThemeModeButton({
Key? key,
super.key,
required this.icon,
required this.value,
required this.selected,
required this.label,
required this.onChanged,
this.disabled
}) : super(key: key);
});
@override
Widget build(BuildContext context) {
@ -32,19 +32,19 @@ class ThemeModeButton extends StatelessWidget {
? () => onChanged(value)
: null,
style: ButtonStyle(
elevation: MaterialStateProperty.all(0),
shape: MaterialStateProperty.all(
elevation: WidgetStateProperty.all(0),
shape: WidgetStateProperty.all(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30),
)
),
backgroundColor: MaterialStateProperty.all(
backgroundColor: WidgetStateProperty.all(
value == selected
? disabled == null || disabled == false
? Theme.of(context).colorScheme.primary
: greyBackgroundColor
: disabled == null || disabled == false
? Theme.of(context).colorScheme.surfaceVariant
? Theme.of(context).colorScheme.surfaceTint.withOpacity(0.1)
: greyBackgroundColor,
)
),

View file

@ -5,9 +5,9 @@ class DeleteStaticLeaseModal extends StatelessWidget {
final void Function() onConfirm;
const DeleteStaticLeaseModal({
Key? key,
super.key,
required this.onConfirm
}) : super(key: key);
});
@override
Widget build(BuildContext context) {

View file

@ -221,14 +221,14 @@ class _DhcpScreenState extends State<DhcpScreen> {
if (!mounted) return;
processModal.close();
if (result.successful == true) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.settingsSaved,
color: Colors.green
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.settingsNotSaved,
color: Colors.red
@ -245,14 +245,14 @@ class _DhcpScreenState extends State<DhcpScreen> {
processModal.close();
if (result.successful == true) {
clearAll();
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.configRestored,
color: Colors.green
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.configNotRestored,
color: Colors.red
@ -276,14 +276,14 @@ class _DhcpScreenState extends State<DhcpScreen> {
data.dhcpStatus!.leases = [];
dhcpProvider.setDhcpData(data);
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.leasesRestored,
color: Colors.green
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.leasesNotRestored,
color: Colors.red

View file

@ -43,14 +43,14 @@ class DhcpLeases extends StatelessWidget {
processModal.close();
if (result == true) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.staticLeaseDeleted,
color: Colors.green
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.staticLeaseNotDeleted,
color: Colors.red
@ -67,28 +67,28 @@ class DhcpLeases extends StatelessWidget {
processModal.close();
if (result.successful == true) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.staticLeaseCreated,
color: Colors.green
);
}
else if (result.successful == false && result.content == "already_exists") {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.staticLeaseExists,
color: Colors.red
);
}
else if (result.successful == false && result.content == "server_not_configured") {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.serverNotConfigured,
color: Colors.red
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.staticLeaseNotCreated,
color: Colors.red

View file

@ -80,21 +80,21 @@ class _BootstrapDnsScreenState extends State<BootstrapDnsScreen> {
processModal.close();
if (result.successful == true) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.dnsConfigSaved,
color: Colors.green
);
}
else if (result.successful == false && result.statusCode == 400) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.someValueNotValid,
color: Colors.red
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.dnsConfigNotSaved,
color: Colors.red
@ -197,7 +197,7 @@ class _BootstrapDnsScreenState extends State<BootstrapDnsScreen> {
)
],
),
)).toList(),
)),
Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,

View file

@ -86,21 +86,21 @@ class _CacheConfigDnsScreenState extends State<CacheConfigDnsScreen> {
processModal.close();
if (result.successful == true) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.dnsConfigSaved,
color: Colors.green
);
}
else if (result.successful== false && result.statusCode == 400) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.someValueNotValid,
color: Colors.red
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.dnsConfigNotSaved,
color: Colors.red
@ -139,14 +139,14 @@ class _CacheConfigDnsScreenState extends State<CacheConfigDnsScreen> {
void clearCache() async {
final result = await clearDnsCache(context, serversProvider.selectedServer!);
if (result.successful == true) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.dnsCacheCleared,
color: Colors.green
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.dnsCacheNotCleared,
color: Colors.red

View file

@ -66,14 +66,14 @@ class _DnsSettingsState extends State<DnsSettings> {
void clearCache() async {
final result = await clearDnsCache(context, serversProvider.selectedServer!);
if (result.successful == true) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.dnsCacheCleared,
color: Colors.green
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.dnsCacheNotCleared,
color: Colors.red

View file

@ -183,21 +183,21 @@ class _DnsServerSettingsScreenState extends State<DnsServerSettingsScreen> {
processModal.close();
if (result.successful == true) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.dnsConfigSaved,
color: Colors.green
);
}
else if (result.successful == false && result.statusCode == 400) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.someValueNotValid,
color: Colors.red
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.dnsConfigNotSaved,
color: Colors.red

View file

@ -91,21 +91,21 @@ class _FallbackDnsScreenState extends State<FallbackDnsScreen> {
if (!context.mounted) return;
if (result.successful == true) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.dnsConfigSaved,
color: Colors.green
);
}
else if (result.successful == false && result.statusCode == 400) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.someValueNotValid,
color: Colors.red
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.dnsConfigNotSaved,
color: Colors.red

View file

@ -112,21 +112,21 @@ class _PrivateReverseDnsServersScreenState extends State<PrivateReverseDnsServer
processModal.close();
if (result.successful == true) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.dnsConfigSaved,
color: Colors.green
);
}
else if (result.successful == false && result.statusCode == 400) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.someValueNotValid,
color: Colors.red
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.dnsConfigNotSaved,
color: Colors.red

View file

@ -152,21 +152,21 @@ class _UpstreamDnsScreenState extends State<UpstreamDnsScreen> {
processModal.close();
if (result.successful == true) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.dnsConfigSaved,
color: Colors.green
);
}
else if (result.successful == false && result.statusCode == 400) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.someValueNotValid,
color: Colors.red
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.dnsConfigNotSaved,
color: Colors.red

View file

@ -5,9 +5,9 @@ class DeleteDnsRewrite extends StatelessWidget {
final void Function() onConfirm;
const DeleteDnsRewrite({
Key? key,
super.key,
required this.onConfirm
}) : super(key: key);
});
@override
Widget build(BuildContext context) {

View file

@ -67,14 +67,14 @@ class _DnsRewritesScreenState extends State<DnsRewritesScreen> {
processModal.close();
if (result == true) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.dnsRewriteRuleDeleted,
color: Colors.green
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.dnsRewriteRuleNotDeleted,
color: Colors.red
@ -91,14 +91,14 @@ class _DnsRewritesScreenState extends State<DnsRewritesScreen> {
processModal.close();
if (result == true) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.dnsRewriteRuleAdded,
color: Colors.green
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.dnsRewriteRuleNotAdded,
color: Colors.red
@ -115,14 +115,14 @@ class _DnsRewritesScreenState extends State<DnsRewritesScreen> {
processModal.close();
if (result == true) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.dnsRewriteRuleUpdated,
color: Colors.green
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.dnsRewriteRuleNotUpdated,
color: Colors.red
@ -169,7 +169,7 @@ class _DnsRewritesScreenState extends State<DnsRewritesScreen> {
onRefresh: () async {
final result = await rewriteRulesProvider.fetchRules();
if (result == false) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.rewriteRulesNotLoaded,
color: Colors.red

View file

@ -5,9 +5,9 @@ class EncryptionErrorModal extends StatelessWidget {
final String error;
const EncryptionErrorModal({
Key? key,
super.key,
required this.error,
}) : super(key: key);
});
@override
Widget build(BuildContext context) {

View file

@ -12,7 +12,7 @@ class EncryptionTextField extends StatelessWidget {
final String? helperText;
const EncryptionTextField({
Key? key,
super.key,
required this.enabled,
required this.controller,
required this.icon,
@ -22,7 +22,7 @@ class EncryptionTextField extends StatelessWidget {
this.keyboardType,
this.multiline,
this.helperText,
}) : super(key: key);
});
@override
Widget build(BuildContext context) {

View file

@ -233,14 +233,14 @@ class _EncryptionSettingsState extends State<EncryptionSettings> {
processModal.close();
if (result.successful == true) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.encryptionConfigSaved,
color: Colors.green
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.encryptionConfigNotSaved,
color: Colors.red
@ -282,14 +282,14 @@ class _EncryptionSettingsState extends State<EncryptionSettings> {
processModal.close();
if (result.successful == true) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.configurationResetSuccessfully,
color: Colors.green
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.configurationResetError,
color: Colors.red

View file

@ -5,9 +5,9 @@ class ErrorMessageEncryption extends StatelessWidget {
final String errorMessage;
const ErrorMessageEncryption({
Key? key,
super.key,
required this.errorMessage,
}) : super(key: key);
});
@override
Widget build(BuildContext context) {

View file

@ -6,10 +6,10 @@ class EncryptionMasterSwitch extends StatelessWidget {
final void Function(bool) onChange;
const EncryptionMasterSwitch({
Key? key,
super.key,
required this.value,
required this.onChange
}) : super(key: key);
});
@override
Widget build(BuildContext context) {

View file

@ -8,10 +8,10 @@ class Status extends StatelessWidget {
final String label;
const Status({
Key? key,
super.key,
required this.valid,
required this.label
}) : super(key: key);
});
@override
Widget build(BuildContext context) {

View file

@ -3,8 +3,8 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:install_referrer/install_referrer.dart';
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/settings.dart';
@ -49,14 +49,14 @@ class _GeneralSettingsState extends State<GeneralSettings> {
}) async {
final result = await function(newStatus);
if (result == true) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.settingsUpdatedSuccessfully,
color: Colors.green
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.cannotUpdateSettings,
color: Colors.red
@ -253,14 +253,7 @@ class _GeneralSettingsState extends State<GeneralSettings> {
right: 10
)
),
if (
!(Platform.isAndroid || Platform.isIOS) ||
(Platform.isAndroid && (
appConfigProvider.installationSource == Source.IS_INSTALLED_FROM_LOCAL_SOURCE ||
appConfigProvider.installationSource == Source.IS_INSTALLED_FROM_PLAY_PACKAGE_INSTALLER ||
appConfigProvider.installationSource == Source.UNKNOWN
))
) ...[
if (!(Platform.isAndroid || Platform.isIOS) || (Platform.isAndroid && (appConfigProvider.installationSource == InstallationAppReferrer.androidManually ))) ...[
SectionLabel(label: AppLocalizations.of(context)!.application),
CustomListTile(
icon: Icons.system_update_rounded,

View file

@ -43,14 +43,14 @@ class _TopItemsListSettingsState extends State<TopItemsListSettings> with Ticker
final result = await appConfigProvider.setHomeTopItemsOrder(persistHomeTopItemsList);
if (!context.mounted) return;
if (result == true) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.settingsSaved,
color: Colors.green
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.settingsNotSaved,
color: Colors.red

View file

@ -102,17 +102,17 @@ class _LogsSettingsState extends State<LogsSettings> {
processModal.close();
if (!mounted) return;
if (!context.mounted) return;
if (result.successful == true) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.logsCleared,
color: Colors.green
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.logsNotCleared,
color: Colors.red
@ -135,17 +135,17 @@ class _LogsSettingsState extends State<LogsSettings> {
processModal.close();
if (!mounted) return;
if (!context.mounted) return;
if (result.successful == true) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.logsConfigUpdated,
color: Colors.green
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.logsConfigNotUpdated,
color: Colors.red

View file

@ -90,7 +90,7 @@ class _SafeSearchSettingsScreenState extends State<SafeSearchSettingsScreen> {
processModal.close();
if (result == true) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.settingsUpdatedSuccessfully,
color: Colors.green,
@ -98,7 +98,7 @@ class _SafeSearchSettingsScreenState extends State<SafeSearchSettingsScreen> {
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.settingsNotSaved,
color: Colors.red,

View file

@ -148,14 +148,14 @@ class _StatisticsSettingsState extends State<StatisticsSettings> {
if (!context.mounted) return;
if (result.successful == true) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.statisticsConfigUpdated,
color: Colors.green
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.statisticsConfigNotUpdated,
color: Colors.red

View file

@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class AutoUpdateUnavailableModal extends StatelessWidget {
const AutoUpdateUnavailableModal({Key? key}) : super(key: key);
const AutoUpdateUnavailableModal({super.key});
@override
Widget build(BuildContext context) {

View file

@ -41,7 +41,7 @@ class UpdateScreen extends StatelessWidget {
if (result.successful == true) {
serversProvider.recheckPeriodServerUpdated();
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.requestStartUpdateSuccessful,
color: Colors.green,
@ -49,7 +49,7 @@ class UpdateScreen extends StatelessWidget {
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.requestStartUpdateFailed,
color: Colors.red,
@ -203,7 +203,7 @@ class UpdateScreen extends StatelessWidget {
body: Column(
children: [
Container(
color: Theme.of(context).colorScheme.surfaceVariant,
color: Theme.of(context).colorScheme.surfaceContainerHighest,
child: SafeArea(
child: headerPortrait()
)

View file

@ -88,7 +88,7 @@ class _UpdateScreenState extends State<UpdateScreen> {
if (!context.mounted) return;
if (result.successful == true) {
serversProvider.recheckPeriodServerUpdated();
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.requestStartUpdateSuccessful,
color: Colors.green,
@ -96,7 +96,7 @@ class _UpdateScreenState extends State<UpdateScreen> {
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.requestStartUpdateFailed,
color: Colors.red,
@ -220,7 +220,7 @@ class _Header extends SliverPersistentHeaderDelegate {
return LayoutBuilder(
builder: (context, constraints) => Container(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surfaceVariant,
color: Theme.of(context).colorScheme.surfaceContainerHighest,
),
child: Align(
alignment: Alignment.topLeft,

View file

@ -206,14 +206,14 @@ class ApiClientV2 {
}
Future<ApiResponse> getLogs({
required int count,
int? count,
int? offset,
DateTime? olderThan,
String? responseStatus,
String? search
}) async {
final result = await HttpRequestClient.get(
urlPath: '/querylog?limit=$count${offset != null ? '&offset=$offset' : ''}${olderThan != null ? '&older_than=${olderThan.toIso8601String()}' : ''}${responseStatus != null ? '&response_status=$responseStatus' : ''}${search != null ? '&search=$search' : ''}',
urlPath: '/querylog?${count != null ? 'limit=$count' : ''}${offset != null ? '&offset=$offset' : ''}${olderThan != null ? '&older_than=${olderThan.toIso8601String()}' : ''}${responseStatus != null ? '&response_status=$responseStatus' : ''}${search != null ? '&search=$search' : ''}',
server: server
);
if (result.successful == true) {

View file

@ -157,7 +157,7 @@ class _AddServerModalState extends State<AddServerModal> {
if (result != AuthStatus.success) {
cancelConnecting();
if (mounted) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: getErrorMessage(result),
color: Colors.red
@ -206,7 +206,7 @@ class _AddServerModalState extends State<AddServerModal> {
if (serverCreated != null) {
if (mounted) setState(() => isConnecting = false);
if (mounted) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.connectionNotCreated,
color: Colors.red
@ -256,7 +256,7 @@ class _AddServerModalState extends State<AddServerModal> {
if (result != AuthStatus.success) {
cancelConnecting();
if (mounted) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: getErrorMessage(result),
color: Colors.red
@ -306,7 +306,7 @@ class _AddServerModalState extends State<AddServerModal> {
)
);
if (mounted) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.connectionNotCreated,
color: Colors.red
@ -419,16 +419,22 @@ class _AddServerModalState extends State<AddServerModal> {
colors: SegmentedButtonSlideColors(
barColor: Theme.of(context).colorScheme.primary.withOpacity(0.2),
backgroundSelectedColor: Theme.of(context).colorScheme.primary,
foregroundSelectedColor: Theme.of(context).colorScheme.onPrimary,
foregroundUnselectedColor: Theme.of(context).colorScheme.onSurface,
hoverColor: Theme.of(context).colorScheme.onSurfaceVariant,
),
textOverflow: TextOverflow.ellipsis,
fontSize: 14,
height: 40,
margin: const EdgeInsets.symmetric(
horizontal: 24,
),
selectedTextStyle: TextStyle(
color: Theme.of(context).colorScheme.onPrimary,
fontWeight: FontWeight.w700
),
unselectedTextStyle: TextStyle(
color: Theme.of(context).colorScheme.onSurface,
),
hoverTextStyle: TextStyle(
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
Card(
margin: const EdgeInsets.only(

View file

@ -13,7 +13,7 @@ class FormTextField extends StatelessWidget {
final bool isConnecting;
const FormTextField({
Key? key,
super.key,
required this.label,
required this.controller,
this.error,
@ -24,7 +24,7 @@ class FormTextField extends StatelessWidget {
this.hintText,
this.helperText,
required this.isConnecting
}) : super(key: key);
});
@override
Widget build(BuildContext context) {

View file

@ -15,12 +15,12 @@ class CustomCombinedLineChart extends StatelessWidget {
final bool daysInterval;
const CustomCombinedLineChart({
Key? key,
super.key,
required this.inputData,
required this.context,
required this.dates,
required this.daysInterval
}) : super(key: key);
});
LineChartData mainData(Map<String, dynamic> data, ThemeMode selectedTheme) {
String chartDate(DateTime date) {
@ -55,11 +55,11 @@ class CustomCombinedLineChart extends StatelessWidget {
}
return LineChartData(
gridData: FlGridData(
gridData: const FlGridData(
show: false,
drawVerticalLine: false,
),
titlesData: FlTitlesData(
titlesData: const FlTitlesData(
show: false,
),
borderData: FlBorderData(
@ -73,7 +73,7 @@ class CustomCombinedLineChart extends StatelessWidget {
barWidth: 2,
isStrokeCapRound: true,
preventCurveOverShooting: true,
dotData: FlDotData(
dotData: const FlDotData(
show: false,
),
belowBarData: BarAreaData(

View file

@ -8,12 +8,12 @@ class ConfirmActionModal extends StatelessWidget {
final void Function() onConfirm;
const ConfirmActionModal({
Key? key,
super.key,
required this.icon,
required this.title,
required this.message,
required this.onConfirm
}) : super(key: key);
});
@override
Widget build(BuildContext context) {

View file

@ -9,14 +9,14 @@ class CustomCheckboxListTile extends StatelessWidget {
final EdgeInsets? padding;
const CustomCheckboxListTile({
Key? key,
super.key,
required this.value,
required this.onChanged,
required this.title,
this.disabled,
this.subtitle,
this.padding
}) : super(key: key);
});
@override
Widget build(BuildContext context) {

View file

@ -14,7 +14,7 @@ class CustomListTile extends StatelessWidget {
final Color? color;
const CustomListTile({
Key? key,
super.key,
required this.title,
this.subtitle,
this.subtitleWidget,
@ -26,7 +26,7 @@ class CustomListTile extends StatelessWidget {
this.disabled,
this.onHover,
this.color,
}) : super(key: key);
});
@override
Widget build(BuildContext context) {

View file

@ -7,12 +7,12 @@ class CustomRadio extends StatelessWidget {
final Color backgroundColor;
const CustomRadio({
Key? key,
super.key,
required this.value,
required this.groupValue,
this.onChange,
required this.backgroundColor,
}) : super(key: key);
});
@override
Widget build(BuildContext context) {

View file

@ -11,14 +11,14 @@ class CustomRadioListTile extends StatelessWidget {
final void Function(String) onChanged;
const CustomRadioListTile({
Key? key,
super.key,
required this.groupValue,
required this.value,
required this.radioBackgroundColor,
required this.title,
this.subtitle,
required this.onChanged,
}) : super(key: key);
});
@override
Widget build(BuildContext context) {

View file

@ -7,12 +7,12 @@ class CustomRadioToggle extends StatelessWidget {
final void Function(String) onTap;
const CustomRadioToggle({
Key? key,
super.key,
required this.groupSelected,
required this.value,
required this.label,
required this.onTap,
}) : super(key: key);
});
@override
Widget build(BuildContext context) {

View file

@ -0,0 +1,110 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:adguard_home_manager/config/globals.dart';
class FloatingSearchBar extends StatefulWidget {
final void Function(String) onSearchCompleted;
final RenderBox? searchButtonRenderBox;
final String? existingSearchValue;
final void Function(String)? onSearchFieldUpdated;
final void Function()? onSearchFieldCleared;
const FloatingSearchBar({
super.key,
required this.onSearchCompleted,
required this.searchButtonRenderBox,
this.existingSearchValue,
this.onSearchFieldUpdated,
this.onSearchFieldCleared,
});
@override
State<FloatingSearchBar> createState() => _SearchState();
}
class _SearchState extends State<FloatingSearchBar> {
final _searchController = TextEditingController();
@override
void initState() {
if (widget.existingSearchValue != null) {
_searchController.text = widget.existingSearchValue!;
}
super.initState();
}
@override
Widget build(BuildContext context) {
final position = widget.searchButtonRenderBox?.localToGlobal(Offset.zero);
final topPadding = MediaQuery.of(globalNavigatorKey.currentContext!).viewPadding.top;
return GestureDetector(
onTap: () => Navigator.pop(context),
child: Material(
color: Colors.transparent,
child: LayoutBuilder(
builder: (context, constraints) {
final double width = constraints.maxWidth - 32 > 500 ? 500 : constraints.maxWidth - 32;
return Stack(
alignment: Alignment.topCenter,
children: [
Positioned(
top: position != null ? position.dy - topPadding : topPadding,
child: SizedBox(
width: width,
child: GestureDetector(
onTap: () => {},
child: Container(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(16)
),
child: ClipRRect(
borderRadius: BorderRadius.circular(16),
child: TextFormField(
controller: _searchController,
onChanged: widget.onSearchFieldUpdated,
onFieldSubmitted: (v) {
widget.onSearchCompleted(v);
Navigator.pop(context);
},
autofocus: true,
decoration: InputDecoration(
hintText: AppLocalizations.of(context)!.search,
prefixIcon: const Icon(Icons.search_rounded),
border: InputBorder.none,
filled: true,
fillColor: Colors.grey.withOpacity(0.2),
suffixIcon: _searchController.text != ""
? IconButton(
onPressed: () {
setState(() => _searchController.text = "");
if (widget.onSearchFieldCleared != null) {
widget.onSearchFieldCleared!();
}
},
icon: const Icon(
Icons.close_rounded,
size: 20,
),
tooltip: AppLocalizations.of(context)!.clearSearch,
)
: null,
contentPadding: const EdgeInsets.symmetric(vertical: 12),
),
),
),
),
),
),
)
],
);
}
),
),
);
}
}

View file

@ -13,13 +13,13 @@ class CustomLineChart extends StatelessWidget {
final BuildContext context;
const CustomLineChart({
Key? key,
super.key,
required this.data,
required this.color,
required this.dates,
required this.daysInterval,
required this.context
}) : super(key: key);
});
String chartDate(DateTime date) {
String twoDigits(int number) => number.toString().padLeft(2, '0');
@ -55,11 +55,11 @@ class CustomLineChart extends StatelessWidget {
LineChartData mainData(Map<String, dynamic> data, ThemeMode selectedTheme) {
return LineChartData(
gridData: FlGridData(
gridData: const FlGridData(
show: false,
drawVerticalLine: false,
),
titlesData: FlTitlesData(
titlesData: const FlTitlesData(
show: false,
),
borderData: FlBorderData(
@ -73,7 +73,7 @@ class CustomLineChart extends StatelessWidget {
barWidth: 2,
isStrokeCapRound: true,
preventCurveOverShooting: true,
dotData: FlDotData(
dotData: const FlDotData(
show: false,
),
belowBarData: BarAreaData(

View file

@ -11,9 +11,9 @@ class CustomMenuBar extends StatelessWidget {
final Widget child;
const CustomMenuBar({
Key? key,
super.key,
required this.child
}) : super(key: key);
});
@override
Widget build(BuildContext context) {

View file

@ -7,12 +7,12 @@ class OptionBox extends StatelessWidget {
final String label;
const OptionBox({
Key? key,
super.key,
required this.optionsValue,
required this.itemValue,
required this.onTap,
required this.label,
}) : super(key: key);
});
@override
Widget build(BuildContext context) {

View file

@ -4,9 +4,9 @@ class ProcessDialog extends StatelessWidget {
final String message;
const ProcessDialog({
Key? key,
super.key,
required this.message,
}) : super(key: key);
});
@override
Widget build(BuildContext context) {

View file

@ -5,10 +5,10 @@ class SectionLabel extends StatelessWidget {
final EdgeInsets? padding;
const SectionLabel({
Key? key,
super.key,
required this.label,
this.padding
}) : super(key: key);
});
@override
Widget build(BuildContext context) {

View file

@ -13,9 +13,9 @@ class DeleteModal extends StatelessWidget {
final Server serverToDelete;
const DeleteModal({
Key? key,
super.key,
required this.serverToDelete,
}) : super(key: key);
});
@override
Widget build(BuildContext context) {
@ -34,14 +34,14 @@ class DeleteModal extends StatelessWidget {
appConfigProvider.setSelectedScreen(0);
}
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.connectionRemoved,
color: Colors.green
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.connectionCannotBeRemoved,
color: Colors.red

View file

@ -50,14 +50,15 @@ void showDeleteModal({
required BuildContext context,
required Server server
}) async {
await Future.delayed(const Duration(seconds: 0), () => {
await Future.delayed(const Duration(seconds: 0), () {
if (!context.mounted) return;
showDialog(
context: context,
builder: (context) => DeleteModal(
serverToDelete: server,
),
barrierDismissible: false
)
);
});
}
@ -133,7 +134,7 @@ void connectToServer({
process.close();
if (!context.mounted) return;
final appConfigProvider = Provider.of<AppConfigProvider>(context, listen: false);
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.cannotConnect,
color: Colors.red
@ -150,14 +151,14 @@ void setDefaultServer({
if (!context.mounted) return;
final appConfigProvider = Provider.of<AppConfigProvider>(context, listen: false);
if (result == null) {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.connectionDefaultSuccessfully,
color: Colors.green
);
}
else {
showSnacbkar(
showSnackbar(
appConfigProvider: appConfigProvider,
label: AppLocalizations.of(context)!.connectionDefaultFailed,
color: Colors.red

View file

@ -16,13 +16,13 @@ class ServersList extends StatelessWidget {
final double breakingWidth;
const ServersList({
Key? key,
super.key,
required this.context,
required this.controllers,
required this.onChange,
required this.scrollController,
required this.breakingWidth
}) : super(key: key);
});
@override
Widget build(BuildContext context) {

Some files were not shown because too many files have changed in this diff Show more