diff --git a/.metadata b/.metadata
index e27f5cc..656c923 100644
--- a/.metadata
+++ b/.metadata
@@ -4,7 +4,7 @@
# This file should be version controlled.
version:
- revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
+ revision: f72efea43c3013323d1b95cff571f3c1caa37583
channel: stable
project_type: app
@@ -13,26 +13,11 @@ project_type: app
migration:
platforms:
- platform: root
- create_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
- base_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
- - platform: android
- create_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
- base_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
- - platform: ios
- create_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
- base_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
+ create_revision: f72efea43c3013323d1b95cff571f3c1caa37583
+ base_revision: f72efea43c3013323d1b95cff571f3c1caa37583
- platform: linux
- create_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
- base_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
- - platform: macos
- create_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
- base_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
- - platform: web
- create_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
- base_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
- - platform: windows
- create_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
- base_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
+ create_revision: f72efea43c3013323d1b95cff571f3c1caa37583
+ base_revision: f72efea43c3013323d1b95cff571f3c1caa37583
# User provided section
diff --git a/assets/icon/icon-circle.ico b/assets/icon/icon-circle.ico
new file mode 100644
index 0000000..e4da1bd
Binary files /dev/null and b/assets/icon/icon-circle.ico differ
diff --git a/assets/icon/icon-circle.png b/assets/icon/icon-circle.png
new file mode 100644
index 0000000..5b39396
Binary files /dev/null and b/assets/icon/icon-circle.png differ
diff --git a/assets/icon/icon-macos.png b/assets/icon/icon-macos.png
new file mode 100755
index 0000000..c201d70
Binary files /dev/null and b/assets/icon/icon-macos.png differ
diff --git a/debian/compile_deb.txt b/debian/compile_deb.txt
new file mode 100644
index 0000000..4a18706
--- /dev/null
+++ b/debian/compile_deb.txt
@@ -0,0 +1 @@
+https://pub.dev/packages/flutter_to_debian
\ No newline at end of file
diff --git a/debian/debian.yaml b/debian/debian.yaml
new file mode 100644
index 0000000..ddc864c
--- /dev/null
+++ b/debian/debian.yaml
@@ -0,0 +1,14 @@
+flutter_app:
+ command: adguard_home_manager
+ arch: x64
+ parent: /usr/local/lib
+
+control:
+ Package: AdGuardHomeManager
+ Version: 2.0.0
+ Architecture: amd64
+ Essential: no
+ Priority: optional
+ Depends:
+ Maintainer: JGeek00
+ Description: AdGuard Home control app
\ No newline at end of file
diff --git a/debian/gui/adguard-home-manager.desktop b/debian/gui/adguard-home-manager.desktop
new file mode 100644
index 0000000..a011591
--- /dev/null
+++ b/debian/gui/adguard-home-manager.desktop
@@ -0,0 +1,8 @@
+[Desktop Entry]
+Name=AdGuard Home Manager
+Comment=Manage your AdGuard Home server
+Exec=adguard-home-manager
+Icon=${SNAP}/meta/gui/adguard-home-manager.png
+Terminal=false
+Type=Application
+Categories=Utilities;
diff --git a/debian/gui/adguard-home-manager.png b/debian/gui/adguard-home-manager.png
new file mode 100644
index 0000000..5b39396
Binary files /dev/null and b/debian/gui/adguard-home-manager.png differ
diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj
index 1ffa568..7d212ec 100644
--- a/ios/Runner.xcodeproj/project.pbxproj
+++ b/ios/Runner.xcodeproj/project.pbxproj
@@ -365,7 +365,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- PRODUCT_BUNDLE_IDENTIFIER = com.example.adguardHomeManager;
+ PRODUCT_BUNDLE_IDENTIFIER = com.jgeek00.adguard_home_manager;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
@@ -494,7 +494,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- PRODUCT_BUNDLE_IDENTIFIER = com.example.adguardHomeManager;
+ PRODUCT_BUNDLE_IDENTIFIER = com.jgeek00.adguard_home_manager;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -517,7 +517,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- PRODUCT_BUNDLE_IDENTIFIER = com.example.adguardHomeManager;
+ PRODUCT_BUNDLE_IDENTIFIER = com.jgeek00.adguard_home_manager;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
diff --git a/lib/base.dart b/lib/base.dart
index 327b6ca..c33fce3 100644
--- a/lib/base.dart
+++ b/lib/base.dart
@@ -1,6 +1,7 @@
// ignore_for_file: use_build_context_synchronously, depend_on_referenced_packages
import 'dart:async';
+import 'dart:io';
import 'package:flutter/material.dart';
import 'package:animations/animations.dart';
@@ -10,7 +11,9 @@ import 'package:store_checker/store_checker.dart';
import 'package:flutter/services.dart';
import 'package:adguard_home_manager/widgets/bottom_nav_bar.dart';
+import 'package:adguard_home_manager/widgets/menu_bar.dart';
import 'package:adguard_home_manager/widgets/update_modal.dart';
+import 'package:adguard_home_manager/widgets/navigation_rail.dart';
import 'package:adguard_home_manager/providers/app_config_provider.dart';
import 'package:adguard_home_manager/models/github_release.dart';
@@ -53,11 +56,23 @@ class _BaseState extends State with WidgetsBindingObserver {
}
Future checkInstallationSource() async {
- Source installationSource = await StoreChecker.getSource;
- if (installationSource != Source.IS_INSTALLED_FROM_PLAY_STORE) {
- final result = await checkAppUpdatesGitHub();
- if (result['result'] == 'success') {
- if (updateExists(widget.appConfigProvider.getAppInfo!.version, result['body'].tagName)) {
+ final result = await checkAppUpdatesGitHub();
+ if (result['result'] == 'success') {
+ final update = updateExists(widget.appConfigProvider.getAppInfo!.version, result['body'].tagName);
+ if (update == true) {
+ if (Platform.isAndroid) {
+ Source installationSource = await StoreChecker.getSource;
+ if (installationSource == Source.IS_INSTALLED_FROM_PLAY_STORE) {
+ return null;
+ }
+ else {
+ return result['body'];
+ }
+ }
+ else if (Platform.isIOS) {
+ return null;
+ }
+ else {
return result['body'];
}
}
@@ -107,38 +122,51 @@ class _BaseState extends State with WidgetsBindingObserver {
final serversProvider = Provider.of(context);
final appConfigProvider = Provider.of(context);
+ final width = MediaQuery.of(context).size.width;
+
List screens = serversProvider.selectedServer != null
? screensServerConnected
: screensSelectServer;
- return AnnotatedRegion(
- value: SystemUiOverlayStyle(
- statusBarColor: Colors.transparent,
- statusBarBrightness: Theme.of(context).brightness == Brightness.light
- ? Brightness.light
- : Brightness.dark,
- statusBarIconBrightness: Theme.of(context).brightness == Brightness.light
- ? Brightness.dark
- : Brightness.light,
- systemNavigationBarColor: Theme.of(context).scaffoldBackgroundColor,
- systemNavigationBarIconBrightness: Theme.of(context).brightness == Brightness.light
- ? Brightness.dark
- : Brightness.light,
- ),
- child: Scaffold(
- body: PageTransitionSwitcher(
- duration: const Duration(milliseconds: 200),
- transitionBuilder: (
- (child, primaryAnimation, secondaryAnimation) => FadeThroughTransition(
- animation: primaryAnimation,
- secondaryAnimation: secondaryAnimation,
- child: child,
- )
- ),
- child: screens[appConfigProvider.selectedScreen].body,
+ return CustomMenuBar(
+ child: AnnotatedRegion(
+ value: SystemUiOverlayStyle(
+ statusBarColor: Colors.transparent,
+ statusBarBrightness: Theme.of(context).brightness == Brightness.light
+ ? Brightness.light
+ : Brightness.dark,
+ statusBarIconBrightness: Theme.of(context).brightness == Brightness.light
+ ? Brightness.dark
+ : Brightness.light,
+ systemNavigationBarColor: Theme.of(context).scaffoldBackgroundColor,
+ systemNavigationBarIconBrightness: Theme.of(context).brightness == Brightness.light
+ ? Brightness.dark
+ : Brightness.light,
),
- bottomNavigationBar: const BottomNavBar(),
- )
+ child: Scaffold(
+ body: Row(
+ children: [
+ if (width > 900) const SideNavigationRail(),
+ Expanded(
+ child: PageTransitionSwitcher(
+ duration: const Duration(milliseconds: 200),
+ transitionBuilder: (
+ (child, primaryAnimation, secondaryAnimation) => FadeThroughTransition(
+ animation: primaryAnimation,
+ secondaryAnimation: secondaryAnimation,
+ child: child,
+ )
+ ),
+ child: screens[appConfigProvider.selectedScreen].body,
+ ),
+ ),
+ ],
+ ),
+ bottomNavigationBar: width <= 900
+ ? const BottomNavBar()
+ : null,
+ )
+ ),
);
}
}
\ No newline at end of file
diff --git a/lib/functions/open_url.dart b/lib/functions/open_url.dart
index b4d52d5..b435e47 100644
--- a/lib/functions/open_url.dart
+++ b/lib/functions/open_url.dart
@@ -1,17 +1,30 @@
-import 'package:flutter_web_browser/flutter_web_browser.dart';
+import 'dart:io';
-void openUrl(String url) {
- FlutterWebBrowser.openWebPage(
- url: url,
- customTabsOptions: const CustomTabsOptions(
- instantAppsEnabled: true,
- showTitle: true,
- urlBarHidingEnabled: false,
- ),
- safariVCOptions: const SafariViewControllerOptions(
- barCollapsingEnabled: true,
- dismissButtonStyle: SafariViewControllerDismissButtonStyle.close,
- modalPresentationCapturesStatusBarAppearance: true,
- )
- );
-}
\ No newline at end of file
+import 'package:flutter_web_browser/flutter_web_browser.dart';
+import 'package:url_launcher/url_launcher.dart';
+
+void openUrl(String url) async {
+ if (Platform.isAndroid || Platform.isIOS) {
+ FlutterWebBrowser.openWebPage(
+ url: url,
+ customTabsOptions: const CustomTabsOptions(
+ instantAppsEnabled: true,
+ showTitle: true,
+ urlBarHidingEnabled: false,
+ ),
+ safariVCOptions: const SafariViewControllerOptions(
+ barCollapsingEnabled: true,
+ dismissButtonStyle: SafariViewControllerDismissButtonStyle.close,
+ modalPresentationCapturesStatusBarAppearance: true,
+ )
+ );
+ }
+ else {
+ final uri = Uri.parse(url);
+ if (await canLaunchUrl(uri)) {
+ await launchUrl(uri);
+ } else {
+ throw 'Could not launch $url';
+ }
+ }
+}
\ No newline at end of file
diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb
index 2b66b5e..3b087c6 100644
--- a/lib/l10n/app_en.arb
+++ b/lib/l10n/app_en.arb
@@ -45,10 +45,10 @@
"save": "Save",
"serverStatus": "Server status",
"connectionNotUpdated": "Connection not updated",
- "ruleFilteringWidget": "Rule\nfiltering",
- "safeBrowsingWidget": "Safe\nbrowsing",
- "parentalFilteringWidget": "Parental\nfiltering",
- "safeSearchWidget": "Safe\nsearch",
+ "ruleFilteringWidget": "Rule filtering",
+ "safeBrowsingWidget": "Safe browsing",
+ "parentalFilteringWidget": "Parental filtering",
+ "safeSearchWidget": "Safe search",
"ruleFiltering": "Rule filtering",
"safeBrowsing": "Safe browsing",
"parentalFiltering": "Parental filtering",
@@ -606,5 +606,11 @@
"remainingTime": "Remaining time",
"safeSearchSettings": "Safe search settings",
"loadingSafeSearchSettings": "Loading safe search settings...",
- "safeSearchSettingsNotLoaded": "Error when loading safe search settings."
+ "safeSearchSettingsNotLoaded": "Error when loading safe search settings.",
+ "loadingLogsSettings": "Loading logs settings...",
+ "selectOptionLeftColumn": "Select an option of the left column",
+ "selectClientLeftColumn": "Select a client of the left column",
+ "disableList": "Disable list",
+ "enableList": "Enable list",
+ "screens": "Screens"
}
\ No newline at end of file
diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb
index 76cff1b..90c396a 100644
--- a/lib/l10n/app_es.arb
+++ b/lib/l10n/app_es.arb
@@ -45,10 +45,10 @@
"save": "Guardar",
"connectionNotUpdated": "Conexión no actualizada",
"serverStatus": "Estado del servidor",
- "ruleFilteringWidget": "Bloqueo por\nfiltros",
- "safeBrowsingWidget": "Navegación\nsegura",
- "parentalFilteringWidget": "Control\nparental",
- "safeSearchWidget": "Búsqueda\nsegura",
+ "ruleFilteringWidget": "Bloqueo por filtros",
+ "safeBrowsingWidget": "Navegación segura",
+ "parentalFilteringWidget": "Control parental",
+ "safeSearchWidget": "Búsqueda segura",
"ruleFiltering": "Bloqueo por filtros",
"safeBrowsing": "Navegación segura",
"parentalFiltering": "Control parental",
@@ -606,5 +606,11 @@
"remainingTime": "Tiempo restante",
"safeSearchSettings": "Configuración de búsqueda segura",
"loadingSafeSearchSettings": "Cargando configuración de búsqueda segura...",
- "safeSearchSettingsNotLoaded": "Error al cargar la configuración de búsqueda segura."
+ "safeSearchSettingsNotLoaded": "Error al cargar la configuración de búsqueda segura.",
+ "loadingLogsSettings": "Cargando configuración de registros...",
+ "selectOptionLeftColumn": "Selecciona una opción de la columna de la izquierda",
+ "selectClientLeftColumn": "Selecciona un cliente de la columna de la izquierda",
+ "disableList": "Deshabilitar lista",
+ "enableList": "Habilitar lista",
+ "screens": "Pantallas"
}
\ No newline at end of file
diff --git a/lib/main.dart b/lib/main.dart
index 5ef8610..6e8f4bf 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -7,25 +7,33 @@ import 'package:provider/provider.dart';
import 'package:dynamic_color/dynamic_color.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:package_info_plus/package_info_plus.dart';
+import 'package:sqflite_common_ffi/sqflite_ffi.dart';
+import 'package:window_size/window_size.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:adguard_home_manager/base.dart';
import 'package:adguard_home_manager/classes/http_override.dart';
-import 'package:adguard_home_manager/services/database.dart';
+import 'package:adguard_home_manager/services/db/database.dart';
import 'package:adguard_home_manager/constants/colors.dart';
import 'package:adguard_home_manager/providers/logs_provider.dart';
import 'package:adguard_home_manager/providers/app_config_provider.dart';
-import 'package:adguard_home_manager/providers/servers_provider.dart';
import 'package:adguard_home_manager/config/theme.dart';
+import 'package:adguard_home_manager/providers/servers_provider.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
- SystemChrome.setPreferredOrientations(
- [DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]
- );
+
+ if (Platform.isWindows || Platform.isLinux || Platform.isMacOS) {
+ setWindowMinSize(const Size(500, 500));
+ }
+
+ if (Platform.isWindows || Platform.isLinux) {
+ sqfliteFfiInit();
+ databaseFactory = databaseFactoryFfi;
+ }
AppConfigProvider appConfigProvider = AppConfigProvider();
ServersProvider serversProvider = ServersProvider();
diff --git a/lib/providers/app_config_provider.dart b/lib/providers/app_config_provider.dart
index 262a448..caa7dff 100644
--- a/lib/providers/app_config_provider.dart
+++ b/lib/providers/app_config_provider.dart
@@ -4,6 +4,7 @@ import 'package:flutter/scheduler.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:sqflite/sqlite_api.dart';
+import 'package:adguard_home_manager/services/db/queries.dart';
import 'package:adguard_home_manager/functions/conversions.dart';
import 'package:adguard_home_manager/models/app_log.dart';
@@ -16,6 +17,8 @@ class AppConfigProvider with ChangeNotifier {
int _selectedScreen = 0;
+ int? _selectedSettingsScreen;
+
bool _showingSnackbar = false;
int _selectedTheme = 0;
@@ -118,6 +121,10 @@ class AppConfigProvider with ChangeNotifier {
return _doNotRememberVersion;
}
+ int? get selectedSettingsScreen {
+ return _selectedSettingsScreen;
+ }
+
void setDbInstance(Database db) {
_dbInstance = db;
}
@@ -159,8 +166,19 @@ class AppConfigProvider with ChangeNotifier {
notifyListeners();
}
+ void setSelectedSettingsScreen({required int? screen, bool? notify}) {
+ _selectedSettingsScreen = screen;
+ if (notify == true) {
+ notifyListeners();
+ }
+ }
+
Future setOverrideSslCheck(bool status) async {
- final updated = await _updateOverrideSslCheck(status == true ? 1 : 0);
+ final updated = await updateConfigQuery(
+ db: _dbInstance!,
+ column: 'overrideSslCheck',
+ value: status == true ? 1 : 0
+ );
if (updated == true) {
_overrideSslCheck = status == true ? 1 : 0;
notifyListeners();
@@ -172,7 +190,11 @@ class AppConfigProvider with ChangeNotifier {
}
Future setHideZeroValues(bool status) async {
- final updated = await _updateSetHideZeroValues(status == true ? 1 : 0);
+ final updated = await updateConfigQuery(
+ db: _dbInstance!,
+ column: 'overrideSslCheck',
+ value: status == true ? 1 : 0
+ );
if (updated == true) {
_hideZeroValues = status == true ? 1 : 0;
notifyListeners();
@@ -184,7 +206,11 @@ class AppConfigProvider with ChangeNotifier {
}
Future setShowNameTimeLogs(bool status) async {
- final updated = await _updateShowNameTimeLogsDb(status == true ? 1 : 0);
+ final updated = await updateConfigQuery(
+ db: _dbInstance!,
+ column: 'showNameTimeLogs',
+ value: status == true ? 1 : 0
+ );
if (updated == true) {
_showNameTimeLogs = status == true ? 1 : 0;
notifyListeners();
@@ -196,7 +222,11 @@ class AppConfigProvider with ChangeNotifier {
}
Future setSelectedTheme(int value) async {
- final updated = await _updateThemeDb(value);
+ final updated = await updateConfigQuery(
+ db: _dbInstance!,
+ column: 'theme',
+ value: value
+ );
if (updated == true) {
_selectedTheme = value;
notifyListeners();
@@ -208,7 +238,11 @@ class AppConfigProvider with ChangeNotifier {
}
Future setUseDynamicColor(bool value) async {
- final updated = await _updateDynamicColorDb(value == true ? 1 : 0);
+ final updated = await updateConfigQuery(
+ db: _dbInstance!,
+ column: 'useDynamicColor',
+ value: value == true ? 1 : 0
+ );
if (updated == true) {
_useDynamicColor = value;
notifyListeners();
@@ -220,7 +254,11 @@ class AppConfigProvider with ChangeNotifier {
}
Future setUseThemeColorForStatus(bool value) async {
- final updated = await _updateUseThemeColorForStatusDb(value == true ? 1 : 0);
+ final updated = await updateConfigQuery(
+ db: _dbInstance!,
+ column: 'useThemeColorForStatus',
+ value: value == true ? 1 : 0
+ );
if (updated == true) {
_useThemeColorForStatus = value;
notifyListeners();
@@ -232,7 +270,11 @@ class AppConfigProvider with ChangeNotifier {
}
Future setStaticColor(int value) async {
- final updated = await _updateStaticColorDb(value);
+ final updated = await updateConfigQuery(
+ db: _dbInstance!,
+ column: 'staticColor',
+ value: value
+ );
if (updated == true) {
_staticColor = value;
notifyListeners();
@@ -244,109 +286,12 @@ class AppConfigProvider with ChangeNotifier {
}
Future setDoNotRememberVersion(String value) async {
- try {
- return await _dbInstance!.transaction((txn) async {
- await txn.rawUpdate(
- 'UPDATE appConfig SET doNotRememberVersion = "$value"',
- );
- _doNotRememberVersion = value;
- notifyListeners();
- return true;
- });
- } catch (e) {
- return false;
- }
- }
-
- Future _updateThemeDb(int value) async {
- try {
- return await _dbInstance!.transaction((txn) async {
- await txn.rawUpdate(
- 'UPDATE appConfig SET theme = $value',
- );
- return true;
- });
- } catch (e) {
- return false;
- }
- }
-
- Future _updateDynamicColorDb(int value) async {
- try {
- return await _dbInstance!.transaction((txn) async {
- await txn.rawUpdate(
- 'UPDATE appConfig SET useDynamicColor = $value',
- );
- return true;
- });
- } catch (e) {
- return false;
- }
- }
-
- Future _updateUseThemeColorForStatusDb(int value) async {
- try {
- return await _dbInstance!.transaction((txn) async {
- await txn.rawUpdate(
- 'UPDATE appConfig SET useThemeColorForStatus = $value',
- );
- return true;
- });
- } catch (e) {
- return false;
- }
- }
-
- Future _updateStaticColorDb(int value) async {
- try {
- return await _dbInstance!.transaction((txn) async {
- await txn.rawUpdate(
- 'UPDATE appConfig SET staticColor = $value',
- );
- return true;
- });
- } catch (e) {
- return false;
- }
- }
-
- Future _updateOverrideSslCheck(int value) async {
- try {
- return await _dbInstance!.transaction((txn) async {
- await txn.rawUpdate(
- 'UPDATE appConfig SET overrideSslCheck = $value',
- );
- return true;
- });
- } catch (e) {
- return false;
- }
- }
-
- Future _updateSetHideZeroValues(int value) async {
- try {
- return await _dbInstance!.transaction((txn) async {
- await txn.rawUpdate(
- 'UPDATE appConfig SET hideZeroValues = $value',
- );
- return true;
- });
- } catch (e) {
- return false;
- }
- }
-
- Future _updateShowNameTimeLogsDb(int value) async {
- try {
- return await _dbInstance!.transaction((txn) async {
- await txn.rawUpdate(
- 'UPDATE appConfig SET showNameTimeLogs = $value',
- );
- return true;
- });
- } catch (e) {
- return false;
- }
+ final updated = await updateConfigQuery(
+ db: _dbInstance!,
+ column: 'doNotRememberVersion',
+ value: value
+ );
+ return updated;
}
void saveFromDb(Database dbInstance, Map dbData) {
diff --git a/lib/providers/servers_provider.dart b/lib/providers/servers_provider.dart
index c5cde45..e59d8ae 100644
--- a/lib/providers/servers_provider.dart
+++ b/lib/providers/servers_provider.dart
@@ -7,14 +7,15 @@ import 'package:adguard_home_manager/models/dns_info.dart';
import 'package:adguard_home_manager/models/rewrite_rules.dart';
import 'package:adguard_home_manager/models/filtering_status.dart';
import 'package:adguard_home_manager/models/clients_allowed_blocked.dart';
-import 'package:adguard_home_manager/models/update_available.dart';
import 'package:adguard_home_manager/models/blocked_services.dart';
import 'package:adguard_home_manager/models/clients.dart';
import 'package:adguard_home_manager/models/server_status.dart';
import 'package:adguard_home_manager/models/server.dart';
+import 'package:adguard_home_manager/models/update_available.dart';
import 'package:adguard_home_manager/services/http_requests.dart';
import 'package:adguard_home_manager/functions/time_server_disabled.dart';
import 'package:adguard_home_manager/functions/conversions.dart';
+import 'package:adguard_home_manager/services/db/queries.dart';
import 'package:adguard_home_manager/functions/compare_versions.dart';
import 'package:adguard_home_manager/constants/enums.dart';
@@ -33,6 +34,9 @@ class ServersProvider with ChangeNotifier {
loadStatus: LoadStatus.loading,
data: null
);
+ String? _searchTermClients;
+ List _filteredActiveClients = [];
+ List _filteredAddedClients = [];
final Filtering _filtering = Filtering(
loadStatus: LoadStatus.loading,
@@ -86,6 +90,18 @@ class ServersProvider with ChangeNotifier {
return _clients;
}
+ String? get searchTermClients {
+ return _searchTermClients;
+ }
+
+ List get filteredActiveClients {
+ return _filteredActiveClients;
+ }
+
+ List get filteredAddedClients {
+ return _filteredAddedClients;
+ }
+
FilteringStatus? get filteringStatus {
return _filteringStatus;
}
@@ -147,6 +163,43 @@ class ServersProvider with ChangeNotifier {
void setClientsData(ClientsData data) {
_clients.data = data;
+ if (_searchTermClients != null && _searchTermClients != '') {
+ _filteredActiveClients = _clients.data!.autoClientsData.where(
+ (client) => client.ip.contains(_searchTermClients!.toLowerCase()) || (client.name != null ? client.name!.contains(_searchTermClients!.toLowerCase()) : false)
+ ).toList();
+ _filteredAddedClients = _clients.data!.clients.where(
+ (client) {
+ isContained(String value) => value.contains(value.toLowerCase());
+ return client.ids.any(isContained);
+ }
+ ).toList();
+ }
+ else {
+ _filteredActiveClients = data.autoClientsData;
+ _filteredAddedClients = data.clients;
+ }
+ notifyListeners();
+ }
+
+ void setSearchTermClients(String? value) {
+ _searchTermClients = value;
+ if (value != null && value != '') {
+ if (_clients.data != null) {
+ _filteredActiveClients = _clients.data!.autoClientsData.where(
+ (client) => client.ip.contains(value.toLowerCase()) || (client.name != null ? client.name!.contains(value.toLowerCase()) : false)
+ ).toList();
+ _filteredAddedClients = _clients.data!.clients.where(
+ (client) {
+ isContained(String value) => value.contains(value.toLowerCase());
+ return client.ids.any(isContained);
+ }
+ ).toList();
+ }
+ }
+ else {
+ if (_clients.data != null) _filteredActiveClients = _clients.data!.autoClientsData;
+ if (_clients.data != null) _filteredAddedClients = _clients.data!.clients;
+ }
notifyListeners();
}
@@ -249,7 +302,7 @@ class ServersProvider with ChangeNotifier {
}
Future createServer(Server server) async {
- final saved = await saveServerIntoDb(server);
+ final saved = await saveServerQuery(_dbInstance!, server);
if (saved == null) {
if (server.defaultServer == true) {
final defaultServer = await setDefaultServer(server);
@@ -274,7 +327,7 @@ class ServersProvider with ChangeNotifier {
}
Future setDefaultServer(Server server) async {
- final updated = await setDefaultServerDb(server.id);
+ final updated = await setDefaultServerQuery(_dbInstance!, server.id);
if (updated == null) {
List newServers = _serversList.map((s) {
if (s.id == server.id) {
@@ -296,7 +349,7 @@ class ServersProvider with ChangeNotifier {
}
Future editServer(Server server) async {
- final result = await editServerDb(server);
+ final result = await editServerQuery(_dbInstance!, server);
if (result == null) {
List newServers = _serversList.map((s) {
if (s.id == server.id) {
@@ -316,7 +369,7 @@ class ServersProvider with ChangeNotifier {
}
Future removeServer(Server server) async {
- final result = await removeFromDb(server.id);
+ final result = await removeServerQuery(_dbInstance!, server.id);
if (result == true) {
_selectedServer = null;
List newServers = _serversList.where((s) => s.id != server.id).toList();
@@ -472,63 +525,6 @@ class ServersProvider with ChangeNotifier {
}
}
- Future saveServerIntoDb(Server server) async {
- try {
- return await _dbInstance!.transaction((txn) async {
- await txn.rawInsert(
- 'INSERT INTO servers (id, name, connectionMethod, domain, path, port, user, password, defaultServer, authToken, runningOnHa) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
- [server.id, server.name, server.connectionMethod, server.domain, server.path, server.port, server.user, server.password, server.defaultServer, server.authToken, convertFromBoolToInt(server.runningOnHa)]
- );
- return null;
- });
- } catch (e) {
- return e;
- }
- }
-
- Future editServerDb(Server server) async {
- try {
- return await _dbInstance!.transaction((txn) async {
- await txn.rawUpdate(
- 'UPDATE servers SET name = ?, connectionMethod = ?, domain = ?, path = ?, port = ?, user = ?, password = ?, authToken = ?, runningOnHa = ? WHERE id = "${server.id}"',
- [server.name, server.connectionMethod, server.domain, server.path, server.port, server.user, server.password, server.authToken, server.runningOnHa]
- );
- return null;
- });
- } catch (e) {
- return e;
- }
- }
-
- Future removeFromDb(String id) async {
- try {
- return await _dbInstance!.transaction((txn) async {
- await txn.rawDelete(
- 'DELETE FROM servers WHERE id = "$id"',
- );
- return true;
- });
- } catch (e) {
- return false;
- }
- }
-
- Future setDefaultServerDb(String id) async {
- try {
- return await _dbInstance!.transaction((txn) async {
- await txn.rawUpdate(
- 'UPDATE servers SET defaultServer = 0 WHERE defaultServer = 1',
- );
- await txn.rawUpdate(
- 'UPDATE servers SET defaultServer = 1 WHERE id = "$id"',
- );
- return null;
- });
- } catch (e) {
- return e;
- }
- }
-
void checkServerUpdatesAvailable(Server server) async {
setUpdateAvailableLoadStatus(LoadStatus.loading, true);
final result = await checkServerUpdates(server: server);
diff --git a/lib/screens/clients/active_client_tile.dart b/lib/screens/clients/active_client_tile.dart
new file mode 100644
index 0000000..cabbb6c
--- /dev/null
+++ b/lib/screens/clients/active_client_tile.dart
@@ -0,0 +1,100 @@
+import 'package:flutter/material.dart';
+
+import 'package:adguard_home_manager/widgets/custom_list_tile.dart';
+
+import 'package:adguard_home_manager/models/clients.dart';
+
+class ActiveClientTile extends StatelessWidget {
+ final AutoClient client;
+ final void Function(AutoClient) onTap;
+ final bool splitView;
+ final AutoClient? selectedClient;
+
+ const ActiveClientTile({
+ Key? key,
+ required this.client,
+ required this.onTap,
+ required this.splitView,
+ this.selectedClient
+ }) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ if (splitView == true) {
+ return Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 12),
+ child: Material(
+ color: Colors.transparent,
+ borderRadius: BorderRadius.circular(28),
+ child: InkWell(
+ borderRadius: BorderRadius.circular(28),
+ onTap: () => onTap(client),
+ child: Container(
+ width: double.maxFinite,
+ padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(28),
+ color: client == selectedClient
+ ? Theme.of(context).colorScheme.primaryContainer
+ : null
+ ),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Flexible(
+ child: Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Flexible(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ client.name != ''
+ ? client.name!
+ : client.ip,
+ style: TextStyle(
+ fontSize: 16,
+ fontWeight: FontWeight.w400,
+ color: Theme.of(context).colorScheme.onSurface,
+ ),
+ ),
+ if (client.name != '') Text(client.ip)
+ ],
+ ),
+ )
+ ],
+ ),
+ ),
+ Text(
+ client.source,
+ style: TextStyle(
+ color: Theme.of(context).colorScheme.onSurface
+ ),
+ ),
+ ],
+ )
+ ),
+ ),
+ ),
+ );
+ }
+ else {
+ return CustomListTile(
+ title: client.name != ''
+ ? client.name!
+ : client.ip,
+ subtitle: client.name != ''
+ ? client.ip
+ : null,
+ trailing: Text(
+ client.source,
+ style: TextStyle(
+ color: Theme.of(context).colorScheme.onSurface
+ ),
+ ),
+ onTap: () => onTap(client),
+ );
+ }
+ }
+}
\ No newline at end of file
diff --git a/lib/screens/clients/added_client_tile.dart b/lib/screens/clients/added_client_tile.dart
new file mode 100644
index 0000000..d8a4b8f
--- /dev/null
+++ b/lib/screens/clients/added_client_tile.dart
@@ -0,0 +1,250 @@
+import 'package:flutter/material.dart';
+import 'package:provider/provider.dart';
+
+import 'package:adguard_home_manager/widgets/custom_list_tile.dart';
+
+import 'package:adguard_home_manager/functions/compare_versions.dart';
+import 'package:adguard_home_manager/models/clients.dart';
+import 'package:adguard_home_manager/providers/servers_provider.dart';
+import 'package:adguard_home_manager/providers/app_config_provider.dart';
+
+class AddedClientTile extends StatefulWidget {
+ final Client client;
+ final void Function(Client) onTap;
+ final void Function(Client) onLongPress;
+ final void Function(Client) onEdit;
+ final Client? selectedClient;
+ final bool? splitView;
+
+ const AddedClientTile({
+ Key? key,
+ required this.client,
+ required this.onTap,
+ required this.onLongPress,
+ required this.onEdit,
+ this.selectedClient,
+ required this.splitView
+ }) : super(key: key);
+
+ @override
+ State createState() => _AddedClientTileState();
+}
+
+class _AddedClientTileState extends State {
+ bool hover = false;
+
+ @override
+ Widget build(BuildContext context) {
+ final serversProvider = Provider.of(context);
+ final appConfigProvider = Provider.of(context);
+
+ if (widget.splitView == true) {
+ return Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 12),
+ child: Material(
+ color: Colors.transparent,
+ borderRadius: BorderRadius.circular(28),
+ child: InkWell(
+ borderRadius: BorderRadius.circular(28),
+ onTap: () => widget.onTap(widget.client),
+ onHover: (v) => setState(() => hover = v),
+ child: Container(
+ width: double.maxFinite,
+ padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(28),
+ color: widget.client == widget.selectedClient
+ ? Theme.of(context).colorScheme.primaryContainer
+ : null
+ ),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Flexible(
+ child: Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Flexible(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ widget.client.ids.toString().replaceAll(RegExp(r'^\[|\]$'), ''),
+ style: TextStyle(
+ fontSize: 16,
+ fontWeight: FontWeight.w400,
+ color: Theme.of(context).colorScheme.onSurface,
+ ),
+ ),
+ const SizedBox(height: 8),
+ Row(
+ children: [
+ Icon(
+ Icons.filter_list_rounded,
+ size: 19,
+ color: widget.client.filteringEnabled == true
+ ? appConfigProvider.useThemeColorForStatus == true
+ ? Theme.of(context).colorScheme.primary
+ : Colors.green
+ : appConfigProvider.useThemeColorForStatus == true
+ ? Colors.grey
+ : Colors.red,
+ ),
+ const SizedBox(width: 10),
+ Icon(
+ Icons.vpn_lock_rounded,
+ size: 18,
+ color: widget.client.safebrowsingEnabled == true
+ ? appConfigProvider.useThemeColorForStatus == true
+ ? Theme.of(context).colorScheme.primary
+ : Colors.green
+ : appConfigProvider.useThemeColorForStatus == true
+ ? Colors.grey
+ : Colors.red,
+ ),
+ const SizedBox(width: 10),
+ Icon(
+ Icons.block,
+ size: 18,
+ color: widget.client.parentalEnabled == true
+ ? appConfigProvider.useThemeColorForStatus == true
+ ? Theme.of(context).colorScheme.primary
+ : Colors.green
+ : appConfigProvider.useThemeColorForStatus == true
+ ? Colors.grey
+ : Colors.red,
+ ),
+ const SizedBox(width: 10),
+ Icon(
+ Icons.search_rounded,
+ size: 19,
+ color: serverVersionIsAhead(
+ currentVersion: serversProvider.serverStatus.data!.serverVersion,
+ referenceVersion: 'v0.107.28',
+ referenceVersionBeta: 'v0.108.0-b.33'
+ ) == true
+ ? widget.client.safeSearch != null && widget.client.safeSearch!.enabled == true
+ ? appConfigProvider.useThemeColorForStatus == true
+ ? Theme.of(context).colorScheme.primary
+ : Colors.green
+ : appConfigProvider.useThemeColorForStatus == true
+ ? Colors.grey
+ : Colors.red
+ : widget.client.safesearchEnabled == true
+ ? appConfigProvider.useThemeColorForStatus == true
+ ? Theme.of(context).colorScheme.primary
+ : Colors.green
+ : appConfigProvider.useThemeColorForStatus == true
+ ? Colors.grey
+ : Colors.red,
+ )
+ ],
+ )
+ ],
+ ),
+ )
+ ],
+ ),
+ ),
+ if (hover == true) IconButton(
+ onPressed: () => widget.onEdit(widget.client),
+ icon: const Icon(Icons.edit_rounded)
+ )
+ ],
+ )
+ ),
+ ),
+ ),
+ );
+ }
+ else {
+ return CustomListTile(
+ onLongPress: () => widget.onLongPress(widget.client),
+ onTap: () => widget.onTap(widget.client),
+ onHover: (v) => setState(() => hover = v),
+ title: widget.client.name,
+ subtitleWidget: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ widget.client.ids.toString().replaceAll(RegExp(r'^\[|\]$'), ''),
+ style: TextStyle(
+ color: Theme.of(context).listTileTheme.textColor
+ ),
+ ),
+ const SizedBox(height: 8),
+ Row(
+ children: [
+ Icon(
+ Icons.filter_list_rounded,
+ size: 19,
+ color: widget.client.filteringEnabled == true
+ ? appConfigProvider.useThemeColorForStatus == true
+ ? Theme.of(context).colorScheme.primary
+ : Colors.green
+ : appConfigProvider.useThemeColorForStatus == true
+ ? Colors.grey
+ : Colors.red,
+ ),
+ const SizedBox(width: 10),
+ Icon(
+ Icons.vpn_lock_rounded,
+ size: 18,
+ color: widget.client.safebrowsingEnabled == true
+ ? appConfigProvider.useThemeColorForStatus == true
+ ? Theme.of(context).colorScheme.primary
+ : Colors.green
+ : appConfigProvider.useThemeColorForStatus == true
+ ? Colors.grey
+ : Colors.red,
+ ),
+ const SizedBox(width: 10),
+ Icon(
+ Icons.block,
+ size: 18,
+ color: widget.client.parentalEnabled == true
+ ? appConfigProvider.useThemeColorForStatus == true
+ ? Theme.of(context).colorScheme.primary
+ : Colors.green
+ : appConfigProvider.useThemeColorForStatus == true
+ ? Colors.grey
+ : Colors.red,
+ ),
+ const SizedBox(width: 10),
+ Icon(
+ Icons.search_rounded,
+ size: 19,
+ color: serverVersionIsAhead(
+ currentVersion: serversProvider.serverStatus.data!.serverVersion,
+ referenceVersion: 'v0.107.28',
+ referenceVersionBeta: 'v0.108.0-b.33'
+ ) == true
+ ? widget.client.safeSearch != null && widget.client.safeSearch!.enabled == true
+ ? appConfigProvider.useThemeColorForStatus == true
+ ? Theme.of(context).colorScheme.primary
+ : Colors.green
+ : appConfigProvider.useThemeColorForStatus == true
+ ? Colors.grey
+ : Colors.red
+ : widget.client.safesearchEnabled == true
+ ? appConfigProvider.useThemeColorForStatus == true
+ ? Theme.of(context).colorScheme.primary
+ : Colors.green
+ : appConfigProvider.useThemeColorForStatus == true
+ ? Colors.grey
+ : Colors.red,
+ )
+ ],
+ )
+ ],
+ ),
+ trailing: hover == true
+ ? IconButton(
+ onPressed: () => widget.onEdit(widget.client),
+ icon: const Icon(Icons.edit_rounded)
+ )
+ : null,
+ );
+ }
+ }
+}
\ No newline at end of file
diff --git a/lib/screens/clients/added_list.dart b/lib/screens/clients/added_list.dart
index 922d2de..87cae35 100644
--- a/lib/screens/clients/added_list.dart
+++ b/lib/screens/clients/added_list.dart
@@ -1,12 +1,16 @@
// ignore_for_file: use_build_context_synchronously
+import 'dart:io';
+
import 'package:flutter/material.dart';
import 'package:animations/animations.dart';
import 'package:flutter/rendering.dart';
+import 'package:flutter_split_view/flutter_split_view.dart';
import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:adguard_home_manager/screens/clients/client_screen.dart';
+import 'package:adguard_home_manager/screens/clients/added_client_tile.dart';
import 'package:adguard_home_manager/screens/clients/remove_client_modal.dart';
import 'package:adguard_home_manager/screens/clients/fab.dart';
import 'package:adguard_home_manager/screens/clients/options_modal.dart';
@@ -27,13 +31,19 @@ class AddedList extends StatefulWidget {
final LoadStatus loadStatus;
final List data;
final Future Function() fetchClients;
+ final void Function(Client) onClientSelected;
+ final Client? selectedClient;
+ final bool splitView;
const AddedList({
Key? key,
required this.scrollController,
required this.loadStatus,
required this.data,
- required this.fetchClients
+ required this.fetchClients,
+ required this.onClientSelected,
+ this.selectedClient,
+ required this.splitView
}) : super(key: key);
@override
@@ -69,6 +79,8 @@ class _AddedListState extends State {
final serversProvider = Provider.of(context);
final appConfigProvider = Provider.of(context);
+ final width = MediaQuery.of(context).size.width;
+
void confirmEditClient(Client client) async {
ProcessModal processModal = ProcessModal(context: context);
processModal.open(AppLocalizations.of(context)!.addingClient);
@@ -130,6 +142,10 @@ class _AddedListState extends State {
clientsData.clients = clientsData.clients.where((c) => c.name != client.name).toList();
serversProvider.setClientsData(clientsData);
+ if (widget.splitView == true) {
+ SplitView.of(context).popUntil(0);
+ }
+
showSnacbkar(
context: context,
appConfigProvider: appConfigProvider,
@@ -150,15 +166,31 @@ class _AddedListState extends State {
}
void openClientModal(Client client) {
- Navigator.push(context, MaterialPageRoute(
- fullscreenDialog: true,
- builder: (BuildContext context) => ClientScreen(
- onConfirm: confirmEditClient,
- serverVersion: serversProvider.serverStatus.data!.serverVersion,
- onDelete: deleteClient,
- client: client,
- )
- ));
+ if (width > 900 || !(Platform.isAndroid | Platform.isIOS)) {
+ showDialog(
+ barrierDismissible: false,
+ context: context,
+ builder: (BuildContext context) => ClientScreen(
+ onConfirm: confirmEditClient,
+ serverVersion: serversProvider.serverStatus.data!.serverVersion,
+ onDelete: deleteClient,
+ client: client,
+ dialog: true,
+ )
+ );
+ }
+ else {
+ Navigator.push(context, MaterialPageRoute(
+ fullscreenDialog: true,
+ builder: (BuildContext context) => ClientScreen(
+ onConfirm: confirmEditClient,
+ serverVersion: serversProvider.serverStatus.data!.serverVersion,
+ onDelete: deleteClient,
+ client: client,
+ dialog: false,
+ )
+ ));
+ }
}
void openDeleteModal(Client client) {
@@ -181,9 +213,12 @@ class _AddedListState extends State {
}
return CustomTabContentList(
+ noSliver: !(Platform.isAndroid || Platform.isIOS),
+ listPadding: widget.splitView == true
+ ? const EdgeInsets.only(top: 8)
+ : null,
loadingGenerator: () => SizedBox(
width: double.maxFinite,
- height: MediaQuery.of(context).size.height-171,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
@@ -202,109 +237,28 @@ class _AddedListState extends State {
),
),
itemsCount: widget.data.length,
- contentWidget: (index) => ListTile(
- contentPadding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
- isThreeLine: true,
- onLongPress: () => openOptionsModal(widget.data[index]),
- onTap: () => openClientModal(widget.data[index]),
- title: Padding(
- padding: const EdgeInsets.only(bottom: 5),
- child: Text(
- widget.data[index].name,
- style: TextStyle(
- fontSize: 18,
- fontWeight: FontWeight.normal,
- color: Theme.of(context).colorScheme.onSurface
- ),
- ),
- ),
- subtitle: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- widget.data[index].ids.toString().replaceAll(RegExp(r'^\[|\]$'), ''),
- style: TextStyle(
- color: Theme.of(context).listTileTheme.textColor
- ),
- ),
- const SizedBox(height: 7),
- Row(
- children: [
- Icon(
- Icons.filter_list_rounded,
- size: 19,
- color: widget.data[index].filteringEnabled == true
- ? appConfigProvider.useThemeColorForStatus == true
- ? Theme.of(context).colorScheme.primary
- : Colors.green
- : appConfigProvider.useThemeColorForStatus == true
- ? Colors.grey
- : Colors.red,
- ),
- const SizedBox(width: 10),
- Icon(
- Icons.vpn_lock_rounded,
- size: 18,
- color: widget.data[index].safebrowsingEnabled == true
- ? appConfigProvider.useThemeColorForStatus == true
- ? Theme.of(context).colorScheme.primary
- : Colors.green
- : appConfigProvider.useThemeColorForStatus == true
- ? Colors.grey
- : Colors.red,
- ),
- const SizedBox(width: 10),
- Icon(
- Icons.block,
- size: 18,
- color: widget.data[index].parentalEnabled == true
- ? appConfigProvider.useThemeColorForStatus == true
- ? Theme.of(context).colorScheme.primary
- : Colors.green
- : appConfigProvider.useThemeColorForStatus == true
- ? Colors.grey
- : Colors.red,
- ),
- const SizedBox(width: 10),
- Icon(
- Icons.search_rounded,
- size: 19,
- color: serverVersionIsAhead(
- currentVersion: serversProvider.serverStatus.data!.serverVersion,
- referenceVersion: 'v0.107.28',
- referenceVersionBeta: 'v0.108.0-b.33'
- ) == true
- ? widget.data[index].safeSearch != null && widget.data[index].safeSearch!.enabled == true
- ? appConfigProvider.useThemeColorForStatus == true
- ? Theme.of(context).colorScheme.primary
- : Colors.green
- : appConfigProvider.useThemeColorForStatus == true
- ? Colors.grey
- : Colors.red
- : widget.data[index].safesearchEnabled == true
- ? appConfigProvider.useThemeColorForStatus == true
- ? Theme.of(context).colorScheme.primary
- : Colors.green
- : appConfigProvider.useThemeColorForStatus == true
- ? Colors.grey
- : Colors.red,
- )
- ],
- )
- ],
- ),
- ),
+ contentWidget: (index) => AddedClientTile(
+ selectedClient: widget.selectedClient,
+ client: widget.data[index],
+ onTap: widget.onClientSelected,
+ onLongPress: openOptionsModal,
+ onEdit: openClientModal,
+ splitView: widget.splitView,
+ ),
noData: SizedBox(
width: double.maxFinite,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
- Text(
- AppLocalizations.of(context)!.noClientsList,
- textAlign: TextAlign.center,
- style: TextStyle(
- fontSize: 24,
- color: Theme.of(context).colorScheme.onSurfaceVariant,
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 16),
+ child: Text(
+ AppLocalizations.of(context)!.noClientsList,
+ textAlign: TextAlign.center,
+ style: TextStyle(
+ fontSize: 24,
+ color: Theme.of(context).colorScheme.onSurfaceVariant,
+ ),
),
),
const SizedBox(height: 30),
@@ -318,7 +272,6 @@ class _AddedListState extends State {
),
errorGenerator: () => SizedBox(
width: double.maxFinite,
- height: MediaQuery.of(context).size.height-171,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
diff --git a/lib/screens/clients/client_screen.dart b/lib/screens/clients/client_screen.dart
index 461ba42..2e282f5 100644
--- a/lib/screens/clients/client_screen.dart
+++ b/lib/screens/clients/client_screen.dart
@@ -19,6 +19,7 @@ class ClientScreen extends StatefulWidget {
final String serverVersion;
final void Function(Client) onConfirm;
final void Function(Client)? onDelete;
+ final bool dialog;
const ClientScreen({
Key? key,
@@ -26,6 +27,7 @@ class ClientScreen extends StatefulWidget {
required this.serverVersion,
required this.onConfirm,
this.onDelete,
+ required this.dialog
}) : super(key: key);
@override
@@ -300,51 +302,13 @@ class _ClientScreenState extends State {
),
);
}
-
- return Scaffold(
- appBar: AppBar(
- leading: IconButton(
- onPressed: () => Navigator.pop(context),
- icon: const Icon(Icons.close)
- ),
- title: Text(
- widget.client != null
- ? AppLocalizations.of(context)!.client
- : AppLocalizations.of(context)!.addClient
- ),
- actions: [
- if (widget.client == null || (widget.client != null && editMode == true)) IconButton(
- onPressed: validValues == true
- ? () {
- createClient();
- Navigator.pop(context);
- }
- : null,
- icon: Icon(
- widget.client != null && editMode == true
- ? Icons.save_rounded
- : Icons.check_rounded
- ),
- tooltip: widget.client != null && editMode == true
- ? AppLocalizations.of(context)!.save
- : AppLocalizations.of(context)!.confirm,
- ),
- if (widget.client != null && editMode == false) IconButton(
- onPressed: () => setState(() => editMode = true),
- icon: const Icon(Icons.edit_rounded),
- tooltip: AppLocalizations.of(context)!.edit,
- ),
- if (widget.client != null) IconButton(
- onPressed: openDeleteClientScreen,
- icon: const Icon(Icons.delete_rounded),
- tooltip: AppLocalizations.of(context)!.delete,
- ),
- const SizedBox(width: 10),
- ],
- ),
- body: ListView(
+
+ Widget content(bool withPaddingTop) {
+ return ListView(
+ padding: const EdgeInsets.only(top: 0),
children: [
- const SizedBox(height: 24),
+ if (withPaddingTop == true) const SizedBox(height: 24),
+ if (withPaddingTop == false) const SizedBox(height: 6),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: TextFormField(
@@ -693,10 +657,7 @@ class _ClientScreenState extends State {
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
- SizedBox(
- width: editMode == true
- ? MediaQuery.of(context).size.width - 108
- : MediaQuery.of(context).size.width - 40,
+ Expanded(
child: TextFormField(
enabled: editMode,
controller: controller['controller'],
@@ -751,7 +712,125 @@ class _ClientScreenState extends State {
),
const SizedBox(height: 20)
],
- ),
- );
+ );
+ }
+
+ if (widget.dialog == true) {
+ return Dialog(
+ child: ConstrainedBox(
+ constraints: const BoxConstraints(
+ maxWidth: 500
+ ),
+ child: Column(
+ children: [
+ Padding(
+ padding: const EdgeInsets.all(16),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Row(
+ children: [
+ IconButton(
+ onPressed: () => Navigator.pop(context),
+ icon: const Icon(Icons.clear_rounded)
+ ),
+ const SizedBox(width: 8),
+ Text(
+ widget.client != null
+ ? AppLocalizations.of(context)!.client
+ : AppLocalizations.of(context)!.addClient,
+ style: const TextStyle(
+ fontSize: 22
+ ),
+ ),
+ ],
+ ),
+ Row(
+ children: [
+ if (widget.client == null || (widget.client != null && editMode == true)) IconButton(
+ onPressed: validValues == true
+ ? () {
+ createClient();
+ Navigator.pop(context);
+ }
+ : null,
+ icon: Icon(
+ widget.client != null && editMode == true
+ ? Icons.save_rounded
+ : Icons.check_rounded
+ ),
+ tooltip: widget.client != null && editMode == true
+ ? AppLocalizations.of(context)!.save
+ : AppLocalizations.of(context)!.confirm,
+ ),
+ if (widget.client != null && editMode == false) IconButton(
+ onPressed: () => setState(() => editMode = true),
+ icon: const Icon(Icons.edit_rounded),
+ tooltip: AppLocalizations.of(context)!.edit,
+ ),
+ if (widget.client != null) IconButton(
+ onPressed: openDeleteClientScreen,
+ icon: const Icon(Icons.delete_rounded),
+ tooltip: AppLocalizations.of(context)!.delete,
+ ),
+ const SizedBox(width: 10),
+ ],
+ )
+ ],
+ ),
+ ),
+ Flexible(
+ child: content(false)
+ )
+ ],
+ ),
+ ),
+ );
+ }
+ else {
+ return Scaffold(
+ appBar: AppBar(
+ leading: IconButton(
+ onPressed: () => Navigator.pop(context),
+ icon: const Icon(Icons.close)
+ ),
+ title: Text(
+ widget.client != null
+ ? AppLocalizations.of(context)!.client
+ : AppLocalizations.of(context)!.addClient
+ ),
+ actions: [
+ if (widget.client == null || (widget.client != null && editMode == true)) IconButton(
+ onPressed: validValues == true
+ ? () {
+ createClient();
+ Navigator.pop(context);
+ }
+ : null,
+ icon: Icon(
+ widget.client != null && editMode == true
+ ? Icons.save_rounded
+ : Icons.check_rounded
+ ),
+ tooltip: widget.client != null && editMode == true
+ ? AppLocalizations.of(context)!.save
+ : AppLocalizations.of(context)!.confirm,
+ ),
+ if (widget.client != null && editMode == false) IconButton(
+ onPressed: () => setState(() => editMode = true),
+ icon: const Icon(Icons.edit_rounded),
+ tooltip: AppLocalizations.of(context)!.edit,
+ ),
+ if (widget.client != null) IconButton(
+ onPressed: openDeleteClientScreen,
+ icon: const Icon(Icons.delete_rounded),
+ tooltip: AppLocalizations.of(context)!.delete,
+ ),
+ const SizedBox(width: 10),
+ ],
+ ),
+ body: content(true)
+ );
+ }
}
}
\ No newline at end of file
diff --git a/lib/screens/clients/clients.dart b/lib/screens/clients/clients.dart
index 549b242..9e13c77 100644
--- a/lib/screens/clients/clients.dart
+++ b/lib/screens/clients/clients.dart
@@ -1,9 +1,14 @@
+import 'dart:io';
+
import 'package:flutter/material.dart';
+import 'package:flutter_split_view/flutter_split_view.dart';
import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:adguard_home_manager/screens/clients/clients_list.dart';
import 'package:adguard_home_manager/screens/clients/search_clients.dart';
+import 'package:adguard_home_manager/screens/clients/logs_list_client.dart';
+import 'package:adguard_home_manager/screens/clients/clients_desktop_view.dart';
import 'package:adguard_home_manager/screens/clients/added_list.dart';
import 'package:adguard_home_manager/models/app_log.dart';
@@ -56,6 +61,9 @@ class _ClientsWidgetState extends State with TickerProviderStateM
late TabController tabController;
final ScrollController scrollController = ScrollController();
+ bool searchMode = false;
+ final TextEditingController searchController = TextEditingController();
+
Future fetchClients() async {
widget.setLoadingStatus(LoadStatus.loading, false);
final result = await getClients(widget.server);
@@ -90,83 +98,207 @@ class _ClientsWidgetState extends State with TickerProviderStateM
@override
Widget build(BuildContext context) {
final serversProvider = Provider.of(context);
+ final appConfigProvider = Provider.of(context);
+
+ final width = MediaQuery.of(context).size.width;
- return DefaultTabController(
- length: 2,
- child: NestedScrollView(
- controller: scrollController,
- headerSliverBuilder: ((context, innerBoxIsScrolled) {
- return [
- SliverOverlapAbsorber(
- handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
- sliver: SliverAppBar(
- title: Text(AppLocalizations.of(context)!.clients),
- pinned: true,
- floating: true,
- centerTitle: false,
- forceElevated: innerBoxIsScrolled,
- actions: [
- if (serversProvider.clients.loadStatus == LoadStatus.loaded) ...[
- IconButton(
- onPressed: () => {
- Navigator.push(context, MaterialPageRoute(
- builder: (context) => const SearchClients()
- ))
- },
- icon: const Icon(Icons.search),
- tooltip: AppLocalizations.of(context)!.searchClients,
- ),
- const SizedBox(width: 10),
- ]
- ],
- bottom: TabBar(
- controller: tabController,
- unselectedLabelColor: Theme.of(context).colorScheme.onSurfaceVariant,
- tabs: [
- Tab(
- icon: const Icon(Icons.devices),
- text: AppLocalizations.of(context)!.activeClients,
- ),
- Tab(
- icon: const Icon(Icons.add_rounded),
- text: AppLocalizations.of(context)!.added,
- ),
- ]
- )
- ),
- )
- ];
- }),
- body: Container(
- decoration: BoxDecoration(
- color: Theme.of(context).scaffoldBackgroundColor,
- border: Border(
- top: BorderSide(
- color: Theme.of(context).colorScheme.onSurface.withOpacity(0.1)
- )
- )
+ PreferredSizeWidget tabBar() {
+ return TabBar(
+ controller: tabController,
+ unselectedLabelColor: Theme.of(context).colorScheme.onSurfaceVariant,
+ tabs: [
+ Tab(
+ child: Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ const Icon(Icons.devices),
+ const SizedBox(width: 8),
+ Text(AppLocalizations.of(context)!.activeClients)
+ ],
+ ),
),
- child: TabBarView(
- controller: tabController,
- children: [
- ClientsList(
- scrollController: scrollController,
- loadStatus: serversProvider.clients.loadStatus,
- data: serversProvider.clients.loadStatus == LoadStatus.loaded
- ? serversProvider.clients.data!.autoClientsData : [],
- fetchClients: fetchClients,
+ Tab(
+ child: Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ const Icon(Icons.add_rounded),
+ const SizedBox(width: 8),
+ Text(AppLocalizations.of(context)!.added)
+ ],
+ ),
+ ),
+ ]
+ );
+ }
+
+ Widget tabBarView(bool sliver) {
+ return TabBarView(
+ controller: tabController,
+ children: [
+ ClientsList(
+ scrollController: scrollController,
+ loadStatus: serversProvider.clients.loadStatus,
+ data: serversProvider.clients.loadStatus == LoadStatus.loaded
+ ? serversProvider.filteredActiveClients : [],
+ fetchClients: fetchClients,
+ onClientSelected: (client) => Navigator.push(context, MaterialPageRoute(
+ builder: (context) => LogsListClient(
+ ip: client.ip,
+ serversProvider: serversProvider,
+ appConfigProvider: appConfigProvider
+ )
+ )),
+ splitView: false,
+ sliver: sliver,
+ ),
+ AddedList(
+ scrollController: scrollController,
+ loadStatus: serversProvider.clients.loadStatus,
+ data: serversProvider.clients.loadStatus == LoadStatus.loaded
+ ? serversProvider.filteredAddedClients : [],
+ fetchClients: fetchClients,
+ onClientSelected: (client) => Navigator.push(context, MaterialPageRoute(
+ builder: (context) => LogsListClient(
+ ip: client.ids[0],
+ serversProvider: serversProvider,
+ appConfigProvider: appConfigProvider
+ )
+ )),
+ splitView: false,
+ ),
+ ]
+ );
+ }
+
+ if (width > 900) {
+ return SplitView.material(
+ hideDivider: true,
+ flexWidth: const FlexWidth(mainViewFlexWidth: 1, secondaryViewFlexWidth: 2),
+ placeholder: Center(
+ child: Padding(
+ padding: const EdgeInsets.all(24),
+ child: Text(
+ AppLocalizations.of(context)!.selectClientLeftColumn,
+ textAlign: TextAlign.center,
+ style: TextStyle(
+ fontSize: 24,
+ color: Theme.of(context).colorScheme.onSurfaceVariant
),
- AddedList(
- scrollController: scrollController,
- loadStatus: serversProvider.clients.loadStatus,
- data: serversProvider.clients.loadStatus == LoadStatus.loaded
- ? serversProvider.clients.data!.clients : [],
- fetchClients: fetchClients,
- ),
- ]
- )
+ ),
+ ),
),
- )
- );
+ child: ClientsDesktopView(
+ serversProvider: serversProvider,
+ appConfigProvider: appConfigProvider,
+ fetchClients: fetchClients,
+ )
+ );
+ }
+ else {
+ if (!(Platform.isAndroid || Platform.isIOS)) {
+ return DefaultTabController(
+ length: 2,
+ child: Scaffold(
+ appBar: AppBar(
+ title: Text(AppLocalizations.of(context)!.clients),
+ centerTitle: false,
+ actions: [
+ if (serversProvider.clients.loadStatus == LoadStatus.loaded) ...[
+ IconButton(
+ onPressed: () => {
+ Navigator.push(context, MaterialPageRoute(
+ builder: (context) => const SearchClients()
+ ))
+ },
+ icon: const Icon(Icons.search),
+ tooltip: AppLocalizations.of(context)!.searchClients,
+ ),
+ const SizedBox(width: 10),
+ ]
+ ],
+ bottom: tabBar()
+ ),
+ body: tabBarView(false),
+ ),
+ );
+ }
+ else {
+ return DefaultTabController(
+ length: 2,
+ child: NestedScrollView(
+ controller: scrollController,
+ headerSliverBuilder: ((context, innerBoxIsScrolled) {
+ return [
+ SliverOverlapAbsorber(
+ handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
+ sliver: SliverAppBar(
+ title: searchMode == true
+ ? Row(
+ children: [
+ IconButton(
+ onPressed: () {
+ setState(() {
+ searchMode = false;
+ searchController.text = "";
+ serversProvider.setSearchTermClients(null);
+ });
+ },
+ icon: const Icon(Icons.arrow_back_rounded)
+ ),
+ const SizedBox(width: 16),
+ Expanded(
+ child: TextField(
+ controller: searchController,
+ onChanged: (value) => serversProvider.setSearchTermClients(value),
+ decoration: InputDecoration(
+ suffixIcon: IconButton(
+ onPressed: () {
+ setState(() {
+ searchController.text = "";
+ serversProvider.setSearchTermClients(null);
+ });
+ },
+ icon: const Icon(Icons.clear_rounded)
+ ),
+ hintText: AppLocalizations.of(context)!.search,
+ hintStyle: const TextStyle(
+ fontWeight: FontWeight.normal,
+ fontSize: 18
+ ),
+ border: InputBorder.none,
+ ),
+ style: const TextStyle(
+ fontWeight: FontWeight.normal,
+ fontSize: 18
+ ),
+ ),
+ )
+ ],
+ )
+ : Text(AppLocalizations.of(context)!.clients),
+ pinned: true,
+ floating: true,
+ centerTitle: false,
+ forceElevated: innerBoxIsScrolled,
+ actions: [
+ if (serversProvider.clients.loadStatus == LoadStatus.loaded && searchMode == false) ...[
+ IconButton(
+ onPressed: () => setState(() => searchMode = true),
+ icon: const Icon(Icons.search),
+ tooltip: AppLocalizations.of(context)!.searchClients,
+ ),
+ const SizedBox(width: 10),
+ ]
+ ],
+ bottom: tabBar()
+ ),
+ )
+ ];
+ }),
+ body: tabBarView(true)
+ )
+ );
+ }
+ }
}
}
\ No newline at end of file
diff --git a/lib/screens/clients/clients_desktop_view.dart b/lib/screens/clients/clients_desktop_view.dart
new file mode 100644
index 0000000..0a4b783
--- /dev/null
+++ b/lib/screens/clients/clients_desktop_view.dart
@@ -0,0 +1,248 @@
+import 'dart:io';
+
+import 'package:flutter/material.dart';
+import 'package:flutter_split_view/flutter_split_view.dart';
+import 'package:provider/provider.dart';
+import 'package:flutter_gen/gen_l10n/app_localizations.dart';
+
+import 'package:adguard_home_manager/screens/clients/logs_list_client.dart';
+import 'package:adguard_home_manager/screens/clients/added_list.dart';
+import 'package:adguard_home_manager/screens/clients/clients_list.dart';
+
+import 'package:adguard_home_manager/providers/app_config_provider.dart';
+import 'package:adguard_home_manager/constants/enums.dart';
+import 'package:adguard_home_manager/models/clients.dart';
+import 'package:adguard_home_manager/providers/servers_provider.dart';
+
+
+class ClientsDesktopView extends StatefulWidget {
+ final ServersProvider serversProvider;
+ final AppConfigProvider appConfigProvider;
+ final Future Function() fetchClients;
+
+ const ClientsDesktopView({
+ Key? key,
+ required this.serversProvider,
+ required this.appConfigProvider,
+ required this.fetchClients
+ }) : super(key: key);
+
+ @override
+ State createState() => _ClientsDesktopViewState();
+}
+
+class _ClientsDesktopViewState extends State with TickerProviderStateMixin {
+ late TabController tabController;
+ final ScrollController scrollController = ScrollController();
+
+ AutoClient? selectedActiveClient;
+ Client? selectedAddedClient;
+
+ bool searchMode = false;
+ final TextEditingController searchController = TextEditingController();
+
+ @override
+ void initState() {
+ super.initState();
+ tabController = TabController(
+ initialIndex: 0,
+ length: 2,
+ vsync: this,
+ );
+ tabController.addListener(() => widget.appConfigProvider.setSelectedClientsTab(tabController.index));
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final serversProvider = Provider.of(context);
+ final appConfigProvider = Provider.of(context);
+
+ PreferredSizeWidget tabBar() {
+ return TabBar(
+ controller: tabController,
+ unselectedLabelColor: Theme.of(context).colorScheme.onSurfaceVariant,
+ tabs: [
+ Tab(
+ child: Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ const Icon(Icons.devices),
+ const SizedBox(width: 8),
+ Text(AppLocalizations.of(context)!.activeClients)
+ ],
+ ),
+ ),
+ Tab(
+ child: Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ const Icon(Icons.add_rounded),
+ const SizedBox(width: 8),
+ Text(AppLocalizations.of(context)!.added)
+ ],
+ ),
+ ),
+ ]
+ );
+ }
+
+ Widget tabBarView(bool sliver) {
+ return TabBarView(
+ controller: tabController,
+ children: [
+ ClientsList(
+ scrollController: scrollController,
+ loadStatus: serversProvider.clients.loadStatus,
+ data: serversProvider.clients.loadStatus == LoadStatus.loaded
+ ? serversProvider.filteredActiveClients : [],
+ fetchClients: widget.fetchClients,
+ onClientSelected: (client) => setState(() {
+ selectedAddedClient = null;
+ selectedActiveClient = client;
+ SplitView.of(context).setSecondary(
+ LogsListClient(
+ ip: client.ip,
+ name: client.name,
+ serversProvider: serversProvider,
+ appConfigProvider: appConfigProvider,
+ )
+ );
+ }),
+ selectedClient: selectedActiveClient,
+ splitView: true,
+ sliver: sliver,
+ ),
+ AddedList(
+ scrollController: scrollController,
+ loadStatus: serversProvider.clients.loadStatus,
+ data: serversProvider.clients.loadStatus == LoadStatus.loaded
+ ? serversProvider.filteredAddedClients : [],
+ fetchClients: widget.fetchClients,
+ onClientSelected: (client) => setState(() {
+ selectedActiveClient = null;
+ selectedAddedClient = client;
+ SplitView.of(context).setSecondary(
+ LogsListClient(
+ ip: client.ids[0],
+ name: client.name,
+ serversProvider: serversProvider,
+ appConfigProvider: appConfigProvider,
+ )
+ );
+ }),
+ selectedClient: selectedAddedClient,
+ splitView: true,
+ ),
+ ]
+ );
+ }
+
+ Widget title() {
+ if (searchMode == true) {
+ return Row(
+ children: [
+ IconButton(
+ onPressed: () {
+ setState(() {
+ searchMode = false;
+ searchController.text = "";
+ serversProvider.setSearchTermClients(null);
+ });
+ },
+ icon: const Icon(Icons.arrow_back_rounded)
+ ),
+ const SizedBox(width: 16),
+ Expanded(
+ child: TextField(
+ controller: searchController,
+ onChanged: (value) => serversProvider.setSearchTermClients(value),
+ decoration: InputDecoration(
+ suffixIcon: IconButton(
+ onPressed: () {
+ setState(() {
+ searchController.text = "";
+ serversProvider.setSearchTermClients(null);
+ });
+ },
+ icon: const Icon(Icons.clear_rounded)
+ ),
+ hintText: AppLocalizations.of(context)!.search,
+ hintStyle: const TextStyle(
+ fontWeight: FontWeight.normal,
+ fontSize: 18
+ ),
+ border: InputBorder.none,
+ ),
+ style: const TextStyle(
+ fontWeight: FontWeight.normal,
+ fontSize: 18
+ ),
+ ),
+ )
+ ],
+ );
+ }
+ else {
+ return Text(AppLocalizations.of(context)!.clients);
+ }
+ }
+
+ if (!(Platform.isAndroid || Platform.isIOS)) {
+ return DefaultTabController(
+ length: 2,
+ child: Scaffold(
+ appBar: AppBar(
+ title: title(),
+ centerTitle: false,
+ actions: [
+ if (serversProvider.clients.loadStatus == LoadStatus.loaded && searchMode == false) ...[
+ IconButton(
+ onPressed: () => setState(() => searchMode = true),
+ icon: const Icon(Icons.search),
+ tooltip: AppLocalizations.of(context)!.searchClients,
+ ),
+ const SizedBox(width: 10),
+ ]
+ ],
+ bottom: tabBar()
+ ),
+ body: tabBarView(false),
+ ),
+ );
+ }
+ else {
+ return DefaultTabController(
+ length: 2,
+ child: NestedScrollView(
+ controller: scrollController,
+ headerSliverBuilder: ((context, innerBoxIsScrolled) {
+ return [
+ SliverOverlapAbsorber(
+ handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
+ sliver: SliverAppBar(
+ title: title(),
+ pinned: true,
+ floating: true,
+ centerTitle: false,
+ forceElevated: innerBoxIsScrolled,
+ actions: [
+ if (serversProvider.clients.loadStatus == LoadStatus.loaded && searchMode == false) ...[
+ IconButton(
+ onPressed: () => setState(() => searchMode = true),
+ icon: const Icon(Icons.search),
+ tooltip: AppLocalizations.of(context)!.searchClients,
+ ),
+ const SizedBox(width: 10),
+ ]
+ ],
+ bottom: tabBar()
+ ),
+ )
+ ];
+ }),
+ body: tabBarView(true)
+ )
+ );
+ }
+ }
+}
\ No newline at end of file
diff --git a/lib/screens/clients/clients_list.dart b/lib/screens/clients/clients_list.dart
index af1ee86..9d2f11b 100644
--- a/lib/screens/clients/clients_list.dart
+++ b/lib/screens/clients/clients_list.dart
@@ -1,36 +1,44 @@
+import 'dart:io';
+
import 'package:flutter/material.dart';
-import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
-import 'package:adguard_home_manager/widgets/custom_list_tile.dart';
+import 'package:adguard_home_manager/screens/clients/active_client_tile.dart';
+
import 'package:adguard_home_manager/widgets/tab_content_list.dart';
import 'package:adguard_home_manager/models/clients.dart';
-import 'package:adguard_home_manager/models/applied_filters.dart';
-import 'package:adguard_home_manager/providers/app_config_provider.dart';
import 'package:adguard_home_manager/constants/enums.dart';
-import 'package:adguard_home_manager/providers/logs_provider.dart';
class ClientsList extends StatelessWidget {
final ScrollController scrollController;
final LoadStatus loadStatus;
final List data;
final Future Function() fetchClients;
+ final void Function(AutoClient) onClientSelected;
+ final AutoClient? selectedClient;
+ final bool splitView;
+ final bool sliver;
const ClientsList({
Key? key,
required this.scrollController,
required this.loadStatus,
required this.data,
- required this.fetchClients
+ required this.fetchClients,
+ required this.onClientSelected,
+ this.selectedClient,
+ required this.splitView,
+ required this.sliver
}) : super(key: key);
@override
Widget build(BuildContext context) {
- final appConfigProvider = Provider.of(context);
- final logsProvider = Provider.of(context);
-
return CustomTabContentList(
+ listPadding: splitView == true
+ ? const EdgeInsets.only(top: 8)
+ : null,
+ noSliver: !sliver,
loadingGenerator: () => SizedBox(
width: double.maxFinite,
height: MediaQuery.of(context).size.height-171,
@@ -52,32 +60,12 @@ class ClientsList extends StatelessWidget {
),
),
itemsCount: data.length,
- contentWidget: (index) => CustomListTile(
- title: data[index].name != ''
- ? data[index].name!
- : data[index].ip,
- subtitle: data[index].name != ''
- ? data[index].ip
- : null,
- trailing: Text(
- data[index].source,
- style: TextStyle(
- color: Theme.of(context).colorScheme.onSurface
- ),
- ),
- onTap: () {
- logsProvider.setSearchText(null);
- logsProvider.setSelectedClients([data[index].ip]);
- logsProvider.setAppliedFilters(
- AppliedFiters(
- selectedResultStatus: 'all',
- searchText: null,
- clients: [data[index].ip]
- )
- );
- appConfigProvider.setSelectedScreen(2);
- },
- ),
+ contentWidget: (index) => ActiveClientTile(
+ client: data[index],
+ onTap: onClientSelected,
+ splitView: splitView,
+ selectedClient: selectedClient,
+ ),
noData: SizedBox(
width: double.maxFinite,
child: Column(
diff --git a/lib/screens/clients/fab.dart b/lib/screens/clients/fab.dart
index 4eff04e..83cf864 100644
--- a/lib/screens/clients/fab.dart
+++ b/lib/screens/clients/fab.dart
@@ -1,5 +1,7 @@
// ignore_for_file: use_build_context_synchronously
+import 'dart:io';
+
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
@@ -23,6 +25,8 @@ class ClientsFab extends StatelessWidget {
final serversProvider = Provider.of(context);
final appConfigProvider = Provider.of(context);
+ final width = MediaQuery.of(context).size.width;
+
void confirmAddClient(Client client) async {
ProcessModal processModal = ProcessModal(context: context);
processModal.open(AppLocalizations.of(context)!.addingClient);
@@ -65,13 +69,27 @@ class ClientsFab extends StatelessWidget {
}
void openAddClient() {
- Navigator.push(context, MaterialPageRoute(
- fullscreenDialog: true,
- builder: (BuildContext context) => ClientScreen(
- onConfirm: confirmAddClient,
- serverVersion: serversProvider.serverStatus.data!.serverVersion,
- )
- ));
+ if (width > 900 || !(Platform.isAndroid | Platform.isIOS)) {
+ showDialog(
+ barrierDismissible: false,
+ context: context,
+ builder: (BuildContext context) => ClientScreen(
+ onConfirm: confirmAddClient,
+ serverVersion: serversProvider.serverStatus.data!.serverVersion,
+ dialog: true,
+ )
+ );
+ }
+ else {
+ Navigator.push(context, MaterialPageRoute(
+ fullscreenDialog: true,
+ builder: (BuildContext context) => ClientScreen(
+ onConfirm: confirmAddClient,
+ serverVersion: serversProvider.serverStatus.data!.serverVersion,
+ dialog: false,
+ )
+ ));
+ }
}
return FloatingActionButton(
diff --git a/lib/screens/clients/logs_list_client.dart b/lib/screens/clients/logs_list_client.dart
new file mode 100644
index 0000000..91b8424
--- /dev/null
+++ b/lib/screens/clients/logs_list_client.dart
@@ -0,0 +1,229 @@
+import 'dart:io';
+
+import 'package:flutter/material.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/log_details_screen.dart';
+
+import 'package:adguard_home_manager/models/logs.dart';
+import 'package:adguard_home_manager/providers/app_config_provider.dart';
+import 'package:adguard_home_manager/providers/servers_provider.dart';
+import 'package:adguard_home_manager/services/http_requests.dart';
+
+class LogsListClient extends StatefulWidget {
+ final String ip;
+ final String? name;
+ final ServersProvider serversProvider;
+ final AppConfigProvider appConfigProvider;
+
+ const LogsListClient({
+ Key? key,
+ required this.ip,
+ this.name,
+ required this.serversProvider,
+ required this.appConfigProvider
+ }) : super(key: key);
+
+ @override
+ State createState() => _LogsListClientState();
+}
+
+class _LogsListClientState extends State {
+ late ScrollController scrollController;
+
+ bool isLoadingMore = false;
+
+ int logsQuantity = 100;
+ int offset = 0;
+
+ int loadStatus = 0;
+ LogsData? logsData;
+
+ String previousIp = "";
+
+ bool showDivider = true;
+
+ Future fetchLogs({
+ int? inOffset,
+ bool? loadingMore,
+ String? responseStatus,
+ String? searchText,
+ }) async {
+ int offst = inOffset ?? offset;
+
+ if (loadingMore != null && loadingMore == true) {
+ setState(() => isLoadingMore = true);
+ }
+
+ final result = await getLogs(
+ server: widget.serversProvider.selectedServer!,
+ count: logsQuantity,
+ offset: offst,
+ search: '"${widget.ip}"'
+ );
+
+ if (loadingMore != null && loadingMore == true) {
+ setState(() => isLoadingMore = false);
+ }
+
+ if (mounted) {
+ if (result['result'] == 'success') {
+ setState(() => offset = inOffset != null ? inOffset+logsQuantity : offset+logsQuantity);
+ if (loadingMore != null && loadingMore == true && logsData != null) {
+ LogsData newLogsData = result['data'];
+ newLogsData.data = [...logsData!.data, ...result['data'].data];
+ setState(() => logsData = newLogsData);
+ }
+ else {
+ LogsData newLogsData = result['data'];
+ setState(() => logsData = newLogsData);
+ }
+ setState(() => loadStatus = 1);
+ }
+ else {
+ setState(() => loadStatus = 2);
+ widget.appConfigProvider.addLog(result['log']);
+ }
+ }
+ }
+
+
+ void scrollListener() {
+ if (scrollController.position.extentAfter < 500 && isLoadingMore == false) {
+ fetchLogs(loadingMore: true);
+ }
+ if (scrollController.position.pixels > 0) {
+ setState(() => showDivider = false);
+ }
+ else {
+ setState(() => showDivider = true);
+ }
+ }
+
+ @override
+ void initState() {
+ scrollController = ScrollController()..addListener(scrollListener);
+ fetchLogs(inOffset: 0);
+ setState(() => previousIp = widget.ip);
+ super.initState();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ if (widget.ip != previousIp) {
+ setState(() => loadStatus = 0);
+ if (scrollController.hasClients) scrollController.animateTo(0, duration: const Duration(milliseconds: 1), curve: Curves.ease);
+ fetchLogs(inOffset: 0);
+ setState(() => previousIp = widget.ip);
+ }
+
+ Widget status() {
+ switch (loadStatus) {
+ case 0:
+ return SizedBox(
+ width: double.maxFinite,
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ const CircularProgressIndicator(),
+ const SizedBox(height: 30),
+ Text(
+ AppLocalizations.of(context)!.loadingLogs,
+ textAlign: TextAlign.center,
+ style: TextStyle(
+ fontSize: 22,
+ color: Theme.of(context).colorScheme.onSurfaceVariant,
+ ),
+ )
+ ],
+ ),
+ );
+
+ case 1:
+ return RefreshIndicator(
+ onRefresh: fetchLogs,
+ child: ListView.builder(
+ controller: scrollController,
+ padding: const EdgeInsets.only(top: 0),
+ itemCount: isLoadingMore == true
+ ? logsData!.data.length+1
+ : logsData!.data.length,
+ itemBuilder: (context, index) {
+ if (isLoadingMore == true && index == logsData!.data.length) {
+ return const Padding(
+ padding: EdgeInsets.symmetric(vertical: 20),
+ child: Center(
+ child: CircularProgressIndicator(),
+ ),
+ );
+ }
+ else {
+ return LogTile(
+ log: logsData!.data[index],
+ index: index,
+ length: logsData!.data.length,
+ useAlwaysNormalTile: true,
+ onLogTap: (log) => showDialog(
+ context: context,
+ builder: (context) => LogDetailsScreen(
+ log: log,
+ dialog: true
+ )
+ )
+ );
+ }
+ }
+ ),
+ );
+
+ case 2:
+ return SizedBox(
+ 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)!.logsNotLoaded,
+ textAlign: TextAlign.center,
+ style: TextStyle(
+ fontSize: 22,
+ color: Theme.of(context).colorScheme.onSurfaceVariant,
+ ),
+ )
+ ],
+ ),
+ );
+
+ default:
+ return const SizedBox();
+ }
+ }
+
+ return Scaffold(
+ appBar: AppBar(
+ title: Text(widget.name != null && widget.name != '' ? widget.name! : widget.ip),
+ centerTitle: true,
+ actions: [
+ if (!(Platform.isAndroid || Platform.isIOS)) ...[
+ IconButton(
+ onPressed: fetchLogs,
+ icon: const Icon(Icons.refresh_rounded),
+ tooltip: AppLocalizations.of(context)!.refresh,
+ ),
+ const SizedBox(width: 8)
+ ]
+ ],
+ ),
+ body: status(),
+ );
+ }
+}
\ No newline at end of file
diff --git a/lib/screens/clients/safe_search_modal.dart b/lib/screens/clients/safe_search_modal.dart
index 9d51f01..1be694c 100644
--- a/lib/screens/clients/safe_search_modal.dart
+++ b/lib/screens/clients/safe_search_modal.dart
@@ -64,109 +64,114 @@ class _SafeSearchModalState extends State {
)
],
),
- content: Wrap(
- children: [
- Padding(
- padding: const EdgeInsets.symmetric(horizontal: 16),
- child: Material(
- color: Theme.of(context).colorScheme.primary.withOpacity(0.1),
- borderRadius: BorderRadius.circular(28),
- child: InkWell(
- onTap: widget.disabled == true
- ? null
- : () => setState(() => generalEnabled = !generalEnabled),
+ content: ConstrainedBox(
+ constraints: const BoxConstraints(
+ maxWidth: 400
+ ),
+ child: Wrap(
+ children: [
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 16),
+ child: Material(
+ color: Theme.of(context).colorScheme.primary.withOpacity(0.1),
borderRadius: BorderRadius.circular(28),
- child: Padding(
- padding: const EdgeInsets.symmetric(
- horizontal: 20,
- vertical: 5
- ),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Text(
- AppLocalizations.of(context)!.enable,
- style: TextStyle(
- fontSize: 16,
- color: widget.disabled == true
- ? Colors.grey
- : Theme.of(context).colorScheme.onSurface
+ child: InkWell(
+ onTap: widget.disabled == true
+ ? null
+ : () => setState(() => generalEnabled = !generalEnabled),
+ borderRadius: BorderRadius.circular(28),
+ child: Padding(
+ padding: const EdgeInsets.symmetric(
+ horizontal: 20,
+ vertical: 5
+ ),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Text(
+ AppLocalizations.of(context)!.enable,
+ style: TextStyle(
+ fontSize: 16,
+ color: widget.disabled == true
+ ? Colors.grey
+ : Theme.of(context).colorScheme.onSurface
+ ),
),
- ),
- Switch(
- value: generalEnabled,
- onChanged: widget.disabled == true
- ? null
- : (value) => setState(() => generalEnabled = value),
- )
- ],
+ Switch(
+ value: generalEnabled,
+ onChanged: widget.disabled == true
+ ? null
+ : (value) => setState(() => generalEnabled = value),
+ )
+ ],
+ ),
),
),
),
),
- ),
- const SizedBox(height: 4, width: double.maxFinite),
- CustomCheckboxListTile(
- value: bingEnabled,
- onChanged: (value) => setState(() => bingEnabled = value),
- title: "Bing",
- disabled: widget.disabled || !generalEnabled,
- padding: const EdgeInsets.symmetric(
- horizontal: 36,
- vertical: 4
+ const SizedBox(height: 4, width: double.maxFinite),
+ CustomCheckboxListTile(
+ value: bingEnabled,
+ onChanged: (value) => setState(() => bingEnabled = value),
+ title: "Bing",
+ disabled: widget.disabled || !generalEnabled,
+ padding: const EdgeInsets.symmetric(
+ horizontal: 36,
+ vertical: 4
+ ),
),
- ),
- CustomCheckboxListTile(
- value: duckduckgoEnabled,
- onChanged: (value) => setState(() => duckduckgoEnabled = value),
- title: "DuckDuckGo",
- disabled: widget.disabled || !generalEnabled,
- padding: const EdgeInsets.symmetric(
- horizontal: 36,
- vertical: 4
+ CustomCheckboxListTile(
+ value: duckduckgoEnabled,
+ onChanged: (value) => setState(() => duckduckgoEnabled = value),
+ title: "DuckDuckGo",
+ disabled: widget.disabled || !generalEnabled,
+ padding: const EdgeInsets.symmetric(
+ horizontal: 36,
+ vertical: 4
+ ),
),
- ),
- CustomCheckboxListTile(
- value: googleEnabled,
- onChanged: (value) => setState(() => googleEnabled = value),
- title: "Google",
- disabled: widget.disabled || !generalEnabled,
- padding: const EdgeInsets.symmetric(
- horizontal: 36,
- vertical: 4
+ CustomCheckboxListTile(
+ value: googleEnabled,
+ onChanged: (value) => setState(() => googleEnabled = value),
+ title: "Google",
+ disabled: widget.disabled || !generalEnabled,
+ padding: const EdgeInsets.symmetric(
+ horizontal: 36,
+ vertical: 4
+ ),
),
- ),
- CustomCheckboxListTile(
- value: pixabayEnabled,
- onChanged: (value) => setState(() => pixabayEnabled = value),
- title: "Pixabay",
- disabled: widget.disabled || !generalEnabled,
- padding: const EdgeInsets.symmetric(
- horizontal: 36,
- vertical: 4
+ CustomCheckboxListTile(
+ value: pixabayEnabled,
+ onChanged: (value) => setState(() => pixabayEnabled = value),
+ title: "Pixabay",
+ disabled: widget.disabled || !generalEnabled,
+ padding: const EdgeInsets.symmetric(
+ horizontal: 36,
+ vertical: 4
+ ),
),
- ),
- CustomCheckboxListTile(
- value: yandexEnabled,
- onChanged: (value) => setState(() => yandexEnabled = value),
- title: "Yandex",
- disabled: widget.disabled || !generalEnabled,
- padding: const EdgeInsets.symmetric(
- horizontal: 36,
- vertical: 4
+ CustomCheckboxListTile(
+ value: yandexEnabled,
+ onChanged: (value) => setState(() => yandexEnabled = value),
+ title: "Yandex",
+ disabled: widget.disabled || !generalEnabled,
+ padding: const EdgeInsets.symmetric(
+ horizontal: 36,
+ vertical: 4
+ ),
),
- ),
- CustomCheckboxListTile(
- value: youtubeEnabled,
- onChanged: (value) => setState(() => youtubeEnabled = value),
- title: "YouTube",
- disabled: widget.disabled || !generalEnabled,
- padding: const EdgeInsets.symmetric(
- horizontal: 36,
- vertical: 4
+ CustomCheckboxListTile(
+ value: youtubeEnabled,
+ onChanged: (value) => setState(() => youtubeEnabled = value),
+ title: "YouTube",
+ disabled: widget.disabled || !generalEnabled,
+ padding: const EdgeInsets.symmetric(
+ horizontal: 36,
+ vertical: 4
+ ),
),
- ),
- ],
+ ],
+ ),
),
actions: [
TextButton(
diff --git a/lib/screens/clients/search_clients.dart b/lib/screens/clients/search_clients.dart
index ce5d2bf..e22e238 100644
--- a/lib/screens/clients/search_clients.dart
+++ b/lib/screens/clients/search_clients.dart
@@ -1,5 +1,7 @@
// ignore_for_file: use_build_context_synchronously
+import 'dart:io';
+
import 'package:flutter/material.dart';
import 'package:animations/animations.dart';
import 'package:provider/provider.dart';
@@ -99,6 +101,8 @@ class _SearchClientsWidgetState extends State {
final serversProvider = Provider.of(context);
final appConfigProvider = Provider.of(context);
+ final width = MediaQuery.of(context).size.width;
+
void deleteClient(Client client) async {
ProcessModal processModal = ProcessModal(context: context);
processModal.open(AppLocalizations.of(context)!.removingClient);
@@ -183,15 +187,31 @@ class _SearchClientsWidgetState extends State {
}
void openClientModal(Client client) {
- Navigator.push(context, MaterialPageRoute(
- fullscreenDialog: true,
- builder: (BuildContext context) => ClientScreen(
- onConfirm: confirmEditClient,
- onDelete: deleteClient,
- client: client,
- serverVersion: serversProvider.serverStatus.data!.serverVersion,
- )
- ));
+ if (width > 900 || !(Platform.isAndroid | Platform.isIOS)) {
+ showDialog(
+ barrierDismissible: false,
+ context: context,
+ builder: (BuildContext context) => ClientScreen(
+ onConfirm: confirmEditClient,
+ serverVersion: serversProvider.serverStatus.data!.serverVersion,
+ onDelete: deleteClient,
+ client: client,
+ dialog: true,
+ )
+ );
+ }
+ else {
+ Navigator.push(context, MaterialPageRoute(
+ fullscreenDialog: true,
+ builder: (BuildContext context) => ClientScreen(
+ onConfirm: confirmEditClient,
+ serverVersion: serversProvider.serverStatus.data!.serverVersion,
+ onDelete: deleteClient,
+ client: client,
+ dialog: false,
+ )
+ ));
+ }
}
void openDeleteModal(Client client) {
diff --git a/lib/screens/connect/connect.dart b/lib/screens/connect/connect.dart
index ff55daf..0e19480 100644
--- a/lib/screens/connect/connect.dart
+++ b/lib/screens/connect/connect.dart
@@ -66,6 +66,7 @@ class _ConnectState extends State {
controllers: expandableControllerList,
onChange: expandOrContract,
scrollController: scrollController,
+ breakingWidth: 700,
),
AnimatedPositioned(
duration: const Duration(milliseconds: 100),
diff --git a/lib/screens/connect/fab.dart b/lib/screens/connect/fab.dart
index 0c3204d..c20cafc 100644
--- a/lib/screens/connect/fab.dart
+++ b/lib/screens/connect/fab.dart
@@ -6,12 +6,27 @@ class FabConnect extends StatelessWidget {
@override
Widget build(BuildContext context) {
+ final width = MediaQuery.of(context).size.width;
+
void openAddServerModal() async {
await Future.delayed(const Duration(seconds: 0), (() => {
- Navigator.push(context, MaterialPageRoute(
- fullscreenDialog: true,
- builder: (BuildContext context) => const AddServerModal()
- ))
+ if (width > 700) {
+ showDialog(
+ context: context,
+ barrierDismissible: false,
+ builder: (context) => const AddServerModal(
+ window: true,
+ ),
+ )
+ }
+ else {
+ Navigator.push(context, MaterialPageRoute(
+ fullscreenDialog: true,
+ builder: (BuildContext context) => const AddServerModal(
+ window: false,
+ )
+ ))
+ }
}));
}
diff --git a/lib/screens/filters/fab.dart b/lib/screens/filters/add_button.dart
similarity index 78%
rename from lib/screens/filters/fab.dart
rename to lib/screens/filters/add_button.dart
index 2a5d036..f2ba43e 100644
--- a/lib/screens/filters/fab.dart
+++ b/lib/screens/filters/add_button.dart
@@ -1,5 +1,7 @@
// ignore_for_file: use_build_context_synchronously
+import 'dart:io';
+
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
@@ -15,12 +17,14 @@ import 'package:adguard_home_manager/constants/enums.dart';
import 'package:adguard_home_manager/models/filtering.dart';
import 'package:adguard_home_manager/providers/servers_provider.dart';
-class FiltersFab extends StatelessWidget {
+class AddFiltersButton extends StatelessWidget {
final String type;
+ final Widget Function(void Function()) widget;
- const FiltersFab({
+ const AddFiltersButton({
Key? key,
required this.type,
+ required this.widget
}) : super(key: key);
@override
@@ -28,6 +32,8 @@ class FiltersFab extends StatelessWidget {
final serversProvider = Provider.of(context);
final appConfigProvider = Provider.of(context);
+ final width = MediaQuery.of(context).size.width;
+
void confirmAddRule(String rule) async {
ProcessModal processModal = ProcessModal(context: context);
processModal.open(AppLocalizations.of(context)!.addingRule);
@@ -64,14 +70,27 @@ class FiltersFab extends StatelessWidget {
}
void openAddCustomRule() {
- Navigator.of(context).push(
- MaterialPageRoute(
- fullscreenDialog: true,
+ if (width > 700 || !(Platform.isAndroid || Platform.isIOS)) {
+ showDialog(
+ context: context,
builder: (context) => AddCustomRule(
- onConfirm: confirmAddRule
+ onConfirm: confirmAddRule,
+ dialog: true,
),
- )
- );
+ barrierDismissible: false
+ );
+ }
+ else {
+ Navigator.of(context).push(
+ MaterialPageRoute(
+ fullscreenDialog: true,
+ builder: (context) => AddCustomRule(
+ onConfirm: confirmAddRule,
+ dialog: false,
+ ),
+ )
+ );
+ }
}
void confirmAddList({required String name, required String url, required String type}) async {
@@ -154,22 +173,34 @@ class FiltersFab extends StatelessWidget {
}
void openAddWhitelistBlacklist() {
- showModalBottomSheet(
- context: context,
- builder: (ctx) => AddListModal(
- type: type,
- onConfirm: confirmAddList,
- ),
- isScrollControlled: true,
- backgroundColor: Colors.transparent
- );
+ if (width > 700 || !(Platform.isAndroid || Platform.isIOS)) {
+ showDialog(
+ context: context,
+ builder: (ctx) => AddListModal(
+ type: type,
+ onConfirm: confirmAddList,
+ dialog: true,
+ ),
+ );
+ }
+ else {
+ showModalBottomSheet(
+ context: context,
+ builder: (ctx) => AddListModal(
+ type: type,
+ onConfirm: confirmAddList,
+ dialog: false,
+ ),
+ isScrollControlled: true,
+ backgroundColor: Colors.transparent
+ );
+ }
}
- return FloatingActionButton(
- onPressed: type == 'blacklist' || type == 'whitelist'
+ return widget(
+ type == 'blacklist' || type == 'whitelist'
? () => openAddWhitelistBlacklist()
: () => openAddCustomRule(),
- child: const Icon(Icons.add),
);
}
}
\ No newline at end of file
diff --git a/lib/screens/filters/add_custom_rule.dart b/lib/screens/filters/add_custom_rule.dart
index 3f1e1f2..dfb5753 100644
--- a/lib/screens/filters/add_custom_rule.dart
+++ b/lib/screens/filters/add_custom_rule.dart
@@ -1,17 +1,17 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
-import 'package:provider/provider.dart';
-import 'package:flutter_web_browser/flutter_web_browser.dart';
+import 'package:adguard_home_manager/functions/open_url.dart';
import 'package:adguard_home_manager/constants/urls.dart';
-import 'package:adguard_home_manager/providers/app_config_provider.dart';
class AddCustomRule extends StatefulWidget {
final void Function(String) onConfirm;
+ final bool dialog;
const AddCustomRule({
Key? key,
- required this.onConfirm
+ required this.onConfirm,
+ required this.dialog
}) : super(key: key);
@override
@@ -72,293 +72,338 @@ class _AddCustomRuleState extends State {
return rule;
}
-
- void openDocsPage() {
- FlutterWebBrowser.openWebPage(
- url: Urls.customRuleDocs,
- customTabsOptions: const CustomTabsOptions(
- instantAppsEnabled: true,
- showTitle: true,
- urlBarHidingEnabled: false,
- ),
- safariVCOptions: const SafariViewControllerOptions(
- barCollapsingEnabled: true,
- dismissButtonStyle: SafariViewControllerDismissButtonStyle.close,
- modalPresentationCapturesStatusBarAppearance: true,
- )
- );
- }
@override
Widget build(BuildContext context) {
- return Scaffold(
- appBar: AppBar(
- title: Text(AppLocalizations.of(context)!.addCustomRule),
- actions: [
- IconButton(
- onPressed: checkValidValues() == true
- ? () {
- Navigator.pop(context);
- widget.onConfirm(buildRule());
- }
- : null,
- icon: const Icon(Icons.check)
- ),
- const SizedBox(width: 10)
- ],
- ),
- body: ListView(
- children: [
- const SizedBox(height: 24),
- Row(
- mainAxisAlignment: MainAxisAlignment.center,
- mainAxisSize: MainAxisSize.min,
- children: [
- Container(
- padding: const EdgeInsets.symmetric(
- horizontal: 10,
- vertical: 5
- ),
- decoration: BoxDecoration(
- color: Theme.of(context).colorScheme.primary.withOpacity(0.1),
- borderRadius: BorderRadius.circular(30),
- border: Border.all(
- color: Theme.of(context).colorScheme.primary
- )
- ),
- child: Text(
- buildRule(),
- textAlign: TextAlign.center,
- style: TextStyle(
- color: Theme.of(context).colorScheme.primary,
- fontWeight: FontWeight.w500
- ),
+
+ List content() {
+ return [
+ const SizedBox(height: 24),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Container(
+ padding: const EdgeInsets.symmetric(
+ horizontal: 10,
+ vertical: 5
+ ),
+ decoration: BoxDecoration(
+ color: Theme.of(context).colorScheme.primary.withOpacity(0.1),
+ borderRadius: BorderRadius.circular(30),
+ border: Border.all(
+ color: Theme.of(context).colorScheme.primary
)
),
- ],
- ),
- const SizedBox(height: 30),
- Padding(
- padding: const EdgeInsets.symmetric(horizontal: 24),
- child: TextFormField(
- controller: domainController,
- onChanged: (value) => setState(() => {}),
- decoration: InputDecoration(
- prefixIcon: const Icon(Icons.link_rounded),
- border: const OutlineInputBorder(
- borderRadius: BorderRadius.all(
- Radius.circular(10)
- )
+ child: Text(
+ buildRule(),
+ textAlign: TextAlign.center,
+ style: TextStyle(
+ color: Theme.of(context).colorScheme.primary,
+ fontWeight: FontWeight.w500
),
- errorText: domainError,
- labelText: AppLocalizations.of(context)!.domain,
+ )
+ ),
+ ],
+ ),
+ Container(height: 30),
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 24),
+ child: TextFormField(
+ controller: domainController,
+ onChanged: (value) => setState(() => {}),
+ decoration: InputDecoration(
+ prefixIcon: const Icon(Icons.link_rounded),
+ border: const OutlineInputBorder(
+ borderRadius: BorderRadius.all(
+ Radius.circular(10)
+ )
),
+ errorText: domainError,
+ labelText: AppLocalizations.of(context)!.domain,
),
),
- const SizedBox(height: 30),
- Padding(
- padding: const EdgeInsets.symmetric(horizontal: 24),
- child: SegmentedButton(
- segments: [
- ButtonSegment(
- value: BlockingPresets.block,
- label: Text(AppLocalizations.of(context)!.block)
- ),
- ButtonSegment(
- value: BlockingPresets.unblock,
- label: Text(AppLocalizations.of(context)!.unblock)
- ),
- ButtonSegment(
- value: BlockingPresets.custom,
- label: Text(AppLocalizations.of(context)!.custom)
- ),
- ],
- selected: {preset},
- onSelectionChanged: (value) => setState(() => preset = value.first),
- ),
+ ),
+ Container(height: 30),
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 24),
+ child: SegmentedButton(
+ segments: [
+ ButtonSegment(
+ value: BlockingPresets.block,
+ label: Text(AppLocalizations.of(context)!.block)
+ ),
+ ButtonSegment(
+ value: BlockingPresets.unblock,
+ label: Text(AppLocalizations.of(context)!.unblock)
+ ),
+ ButtonSegment(
+ value: BlockingPresets.custom,
+ label: Text(AppLocalizations.of(context)!.custom)
+ ),
+ ],
+ selected: {preset},
+ onSelectionChanged: (value) => setState(() => preset = value.first),
),
- const SizedBox(height: 20),
- Material(
- color: Colors.transparent,
- child: InkWell(
- onTap: () => setState(() => addImportant = !addImportant),
- child: Padding(
- padding: const EdgeInsets.symmetric(horizontal: 28),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Padding(
- padding: const EdgeInsets.only(left: 10),
- child: Text(
- AppLocalizations.of(context)!.addImportant,
- style: TextStyle(
- fontSize: 16,
- color: Theme.of(context).colorScheme.onSurface
- ),
+ ),
+ Container(height: 20),
+ Material(
+ color: Colors.transparent,
+ child: InkWell(
+ onTap: () => setState(() => addImportant = !addImportant),
+ child: Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 28),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(left: 10),
+ child: Text(
+ AppLocalizations.of(context)!.addImportant,
+ style: TextStyle(
+ fontSize: 16,
+ color: Theme.of(context).colorScheme.onSurface
),
),
- Switch(
- value: addImportant,
- onChanged: (value) => setState(() => addImportant = value),
- )
- ],
- ),
+ ),
+ Switch(
+ value: addImportant,
+ onChanged: (value) => setState(() => addImportant = value),
+ )
+ ],
),
),
),
- const SizedBox(height: 20),
- Padding(
- padding: const EdgeInsets.symmetric(horizontal: 24),
- child: Card(
- child: Padding(
- padding: const EdgeInsets.all(20),
- child: Column(
+ ),
+ Container(height: 20),
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 24),
+ child: Card(
+ child: Padding(
+ padding: const EdgeInsets.all(20),
+ child: Column(
+ children: [
+ Row(
+ children: [
+ Icon(
+ Icons.info,
+ color: Theme.of(context).colorScheme.onSurface
+ ),
+ const SizedBox(width: 20),
+ Text(
+ AppLocalizations.of(context)!.examples,
+ style: TextStyle(
+ fontSize: 18,
+ color: Theme.of(context).colorScheme.onSurface
+ ),
+ )
+ ],
+ ),
+ const SizedBox(height: 20),
+ SizedBox(
+ width: double.maxFinite,
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ "||example.org^",
+ textAlign: TextAlign.left,
+ style: TextStyle(
+ fontSize: 16,
+ fontWeight: FontWeight.w500,
+ color: Theme.of(context).colorScheme.primary
+ ),
+ ),
+ const SizedBox(height: 5),
+ Text(
+ AppLocalizations.of(context)!.example1,
+ style: TextStyle(
+ fontSize: 14,
+ color: Theme.of(context).colorScheme.primary
+ ),
+ ),
+ const SizedBox(height: 20),
+ Text(
+ "@@||example.org^",
+ textAlign: TextAlign.left,
+ style: TextStyle(
+ fontSize: 16,
+ fontWeight: FontWeight.w500,
+ color: Theme.of(context).colorScheme.primary
+ ),
+ ),
+ const SizedBox(height: 5),
+ Text(
+ AppLocalizations.of(context)!.example2,
+ style: TextStyle(
+ fontSize: 14,
+ color: Theme.of(context).colorScheme.primary
+ ),
+ ),
+ const SizedBox(height: 20),
+ Text(
+ "! Here goes a comment",
+ textAlign: TextAlign.left,
+ style: TextStyle(
+ fontSize: 16,
+ fontWeight: FontWeight.w500,
+ color: Theme.of(context).colorScheme.primary
+ ),
+ ),
+ Text(
+ "# Also a comment",
+ textAlign: TextAlign.left,
+ style: TextStyle(
+ fontSize: 16,
+ fontWeight: FontWeight.w500,
+ color: Theme.of(context).colorScheme.primary
+ ),
+ ),
+ const SizedBox(height: 5),
+ Text(
+ AppLocalizations.of(context)!.example3,
+ style: TextStyle(
+ fontSize: 14,
+ color: Theme.of(context).colorScheme.primary
+ ),
+ ),
+ const SizedBox(height: 20),
+ Text(
+ "/REGEX/",
+ textAlign: TextAlign.left,
+ style: TextStyle(
+ fontSize: 16,
+ fontWeight: FontWeight.w500,
+ color: Theme.of(context).colorScheme.primary
+ ),
+ ),
+ const SizedBox(height: 5),
+ Text(
+ AppLocalizations.of(context)!.example4,
+ style: TextStyle(
+ fontSize: 14,
+ color: Theme.of(context).colorScheme.primary
+ ),
+ ),
+ ],
+ ),
+ )
+ ],
+ ),
+ ),
+ ),
+ ),
+ Container(height: 20),
+ Material(
+ color: Colors.transparent,
+ child: InkWell(
+ onTap: () => openUrl(Urls.customRuleDocs),
+ child: Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 28, vertical: 10),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(left: 10),
+ child: Text(
+ AppLocalizations.of(context)!.moreInformation,
+ style: TextStyle(
+ fontSize: 16,
+ color: Theme.of(context).colorScheme.onSurface
+ ),
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.only(right: 15),
+ child: Icon(
+ Icons.open_in_new,
+ color: Theme.of(context).colorScheme.onSurface
+ ),
+ )
+ ],
+ ),
+ ),
+ ),
+ ),
+ Container(height: 20)
+ ];
+ }
+
+ if (widget.dialog == true) {
+ return Dialog(
+ child: ConstrainedBox(
+ constraints: const BoxConstraints(
+ maxWidth: 500
+ ),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Padding(
+ padding: const EdgeInsets.all(16),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
- Icon(
- Icons.info,
- color: Theme.of(context).colorScheme.onSurface
+ IconButton(
+ onPressed: () => Navigator.pop(context),
+ icon: const Icon(Icons.clear_rounded),
+ tooltip: AppLocalizations.of(context)!.close,
),
- const SizedBox(width: 20),
+ const SizedBox(width: 8),
Text(
- AppLocalizations.of(context)!.examples,
- style: TextStyle(
- fontSize: 18,
- color: Theme.of(context).colorScheme.onSurface
+ AppLocalizations.of(context)!.addCustomRule,
+ style: const TextStyle(
+ fontSize: 22
),
- )
+ ),
],
),
- const SizedBox(height: 20),
- SizedBox(
- width: double.maxFinite,
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- "||example.org^",
- textAlign: TextAlign.left,
- style: TextStyle(
- fontSize: 16,
- fontWeight: FontWeight.w500,
- color: Theme.of(context).colorScheme.primary
- ),
- ),
- const SizedBox(height: 5),
- Text(
- AppLocalizations.of(context)!.example1,
- style: TextStyle(
- fontSize: 14,
- color: Theme.of(context).colorScheme.primary
- ),
- ),
- const SizedBox(height: 20),
- Text(
- "@@||example.org^",
- textAlign: TextAlign.left,
- style: TextStyle(
- fontSize: 16,
- fontWeight: FontWeight.w500,
- color: Theme.of(context).colorScheme.primary
- ),
- ),
- const SizedBox(height: 5),
- Text(
- AppLocalizations.of(context)!.example2,
- style: TextStyle(
- fontSize: 14,
- color: Theme.of(context).colorScheme.primary
- ),
- ),
- const SizedBox(height: 20),
- Text(
- "! Here goes a comment",
- textAlign: TextAlign.left,
- style: TextStyle(
- fontSize: 16,
- fontWeight: FontWeight.w500,
- color: Theme.of(context).colorScheme.primary
- ),
- ),
- Text(
- "# Also a comment",
- textAlign: TextAlign.left,
- style: TextStyle(
- fontSize: 16,
- fontWeight: FontWeight.w500,
- color: Theme.of(context).colorScheme.primary
- ),
- ),
- const SizedBox(height: 5),
- Text(
- AppLocalizations.of(context)!.example3,
- style: TextStyle(
- fontSize: 14,
- color: Theme.of(context).colorScheme.primary
- ),
- ),
- const SizedBox(height: 20),
- Text(
- "/REGEX/",
- textAlign: TextAlign.left,
- style: TextStyle(
- fontSize: 16,
- fontWeight: FontWeight.w500,
- color: Theme.of(context).colorScheme.primary
- ),
- ),
- const SizedBox(height: 5),
- Text(
- AppLocalizations.of(context)!.example4,
- style: TextStyle(
- fontSize: 14,
- color: Theme.of(context).colorScheme.primary
- ),
- ),
- ],
- ),
+ IconButton(
+ onPressed: checkValidValues() == true
+ ? () {
+ Navigator.pop(context);
+ widget.onConfirm(buildRule());
+ }
+ : null,
+ icon: const Icon(Icons.check)
)
],
),
),
- ),
- ),
- const SizedBox(height: 20),
- Material(
- color: Colors.transparent,
- child: InkWell(
- onTap: openDocsPage,
- child: Padding(
- padding: const EdgeInsets.symmetric(horizontal: 28, vertical: 10),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Padding(
- padding: const EdgeInsets.only(left: 10),
- child: Text(
- AppLocalizations.of(context)!.moreInformation,
- style: TextStyle(
- fontSize: 16,
- color: Theme.of(context).colorScheme.onSurface
- ),
- ),
- ),
- Padding(
- padding: const EdgeInsets.only(right: 15),
- child: Icon(
- Icons.open_in_new,
- color: Theme.of(context).colorScheme.onSurface
- ),
- )
- ],
+ Flexible(
+ child: SingleChildScrollView(
+ child: Wrap(
+ alignment: WrapAlignment.center,
+ children: content(),
+ ),
),
- ),
- ),
+ )
+ ],
),
- const SizedBox(height: 20)
- ],
- ),
- );
+ ),
+ );
+ }
+ else {
+ return Scaffold(
+ appBar: AppBar(
+ title: Text(AppLocalizations.of(context)!.addCustomRule),
+ actions: [
+ IconButton(
+ onPressed: checkValidValues() == true
+ ? () {
+ Navigator.pop(context);
+ widget.onConfirm(buildRule());
+ }
+ : null,
+ icon: const Icon(Icons.check)
+ ),
+ const SizedBox(width: 10)
+ ],
+ ),
+ body: ListView(
+ children: content(),
+ )
+ );
+ }
}
}
\ No newline at end of file
diff --git a/lib/screens/filters/add_list_modal.dart b/lib/screens/filters/add_list_modal.dart
index 196cda1..fceebf9 100644
--- a/lib/screens/filters/add_list_modal.dart
+++ b/lib/screens/filters/add_list_modal.dart
@@ -10,6 +10,7 @@ class AddListModal extends StatefulWidget {
final Filter? list;
final void Function({required String name, required String url, required String type})? onConfirm;
final void Function({required Filter list, required String type})? onEdit;
+ final bool dialog;
const AddListModal({
Key? key,
@@ -17,6 +18,7 @@ class AddListModal extends StatefulWidget {
this.list,
this.onConfirm,
this.onEdit,
+ required this.dialog
}) : super(key: key);
@override
@@ -68,51 +70,49 @@ class _AddListModalState extends State {
@override
Widget build(BuildContext context) {
- return Padding(
- padding: MediaQuery.of(context).viewInsets,
- child: Container(
- height: Platform.isIOS ? 386 : 370,
- decoration: BoxDecoration(
- borderRadius: const BorderRadius.only(
- topLeft: Radius.circular(28),
- topRight: Radius.circular(28)
- ),
- color: Theme.of(context).dialogBackgroundColor
- ),
- child: Column(
- children: [
- Expanded(
- child: ListView(
- physics: (Platform.isIOS ? 426 : 410) < MediaQuery.of(context).size.height
- ? const NeverScrollableScrollPhysics()
- : null,
+ Widget content() {
+ return Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Flexible(
+ child: SingleChildScrollView(
+ child: Wrap(
children: [
- Padding(
- padding: const EdgeInsets.only(top: 24),
- child: Icon(
- widget.type == 'whitelist'
- ? Icons.verified_user_rounded
- : Icons.gpp_bad_rounded,
- size: 24,
- color: Theme.of(context).listTileTheme.iconColor
- ),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Column(
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(top: 24),
+ child: Icon(
+ widget.type == 'whitelist'
+ ? Icons.verified_user_rounded
+ : Icons.gpp_bad_rounded,
+ size: 24,
+ color: Theme.of(context).listTileTheme.iconColor
+ ),
+ ),
+ const SizedBox(height: 16),
+ Text(
+ widget.list != null
+ ? widget.type == 'whitelist'
+ ? AppLocalizations.of(context)!.editWhitelist
+ : AppLocalizations.of(context)!.editBlacklist
+ : widget.type == 'whitelist'
+ ? AppLocalizations.of(context)!.addWhitelist
+ : AppLocalizations.of(context)!.addBlacklist,
+ textAlign: TextAlign.center,
+ style: TextStyle(
+ fontSize: 24,
+ color: Theme.of(context).colorScheme.onSurface
+ ),
+ ),
+ const SizedBox(height: 16),
+ ],
+ ),
+ ],
),
- const SizedBox(height: 16),
- Text(
- widget.list != null
- ? widget.type == 'whitelist'
- ? AppLocalizations.of(context)!.editWhitelist
- : AppLocalizations.of(context)!.editBlacklist
- : widget.type == 'whitelist'
- ? AppLocalizations.of(context)!.addWhitelist
- : AppLocalizations.of(context)!.addBlacklist,
- textAlign: TextAlign.center,
- style: TextStyle(
- fontSize: 24,
- color: Theme.of(context).colorScheme.onSurface
- ),
- ),
- const SizedBox(height: 16),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: TextFormField(
@@ -129,7 +129,7 @@ class _AddListModalState extends State {
),
),
),
- const SizedBox(height: 30),
+ Container(height: 30),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: TextFormField(
@@ -151,54 +151,80 @@ class _AddListModalState extends State {
],
),
),
- Padding(
- padding: const EdgeInsets.all(24),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.end,
- children: [
- TextButton(
- onPressed: () => Navigator.pop(context),
- child: Text(AppLocalizations.of(context)!.cancel)
- ),
- const SizedBox(width: 20),
- TextButton(
- onPressed: () {
- Navigator.pop(context);
- if (widget.list != null) {
- final Filter newList = Filter(
- url: urlController.text,
- name: nameController.text,
- lastUpdated: widget.list!.lastUpdated,
- id: widget.list!.id,
- rulesCount: widget.list!.rulesCount,
- enabled: widget.list!.enabled
- );
- widget.onEdit!(
- list: newList,
- type: widget.type
- );
- }
- else {
- widget.onConfirm!(
- name: nameController.text,
- url: urlController.text,
- type: widget.type
- );
- }
- },
- child: Text(
- widget.list != null
- ? AppLocalizations.of(context)!.save
- : AppLocalizations.of(context)!.confirm
- )
- ),
- ],
- ),
+ ),
+ Padding(
+ padding: const EdgeInsets.all(24),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.end,
+ children: [
+ TextButton(
+ onPressed: () => Navigator.pop(context),
+ child: Text(AppLocalizations.of(context)!.cancel)
+ ),
+ const SizedBox(width: 20),
+ TextButton(
+ onPressed: () {
+ Navigator.pop(context);
+ if (widget.list != null) {
+ final Filter newList = Filter(
+ url: urlController.text,
+ name: nameController.text,
+ lastUpdated: widget.list!.lastUpdated,
+ id: widget.list!.id,
+ rulesCount: widget.list!.rulesCount,
+ enabled: widget.list!.enabled
+ );
+ widget.onEdit!(
+ list: newList,
+ type: widget.type
+ );
+ }
+ else {
+ widget.onConfirm!(
+ name: nameController.text,
+ url: urlController.text,
+ type: widget.type
+ );
+ }
+ },
+ child: Text(
+ widget.list != null
+ ? AppLocalizations.of(context)!.save
+ : AppLocalizations.of(context)!.confirm
+ )
+ ),
+ ],
),
- if (Platform.isIOS) const SizedBox(height: 16)
- ],
+ ),
+ if (Platform.isIOS) const SizedBox(height: 16)
+ ],
+ );
+ }
+
+ if (widget.dialog == true) {
+ return Dialog(
+ child: ConstrainedBox(
+ constraints: const BoxConstraints(
+ maxWidth: 400
+ ),
+ child: content()
),
- ),
- );
+ );
+ }
+ else {
+ return Padding(
+ padding: MediaQuery.of(context).viewInsets,
+ child: Container(
+ decoration: BoxDecoration(
+ borderRadius: const BorderRadius.only(
+ topLeft: Radius.circular(28),
+ topRight: Radius.circular(28)
+ ),
+ color: Theme.of(context).dialogBackgroundColor
+ ),
+ child: content()
+ ),
+ );
+ }
}
}
\ No newline at end of file
diff --git a/lib/screens/filters/blocked_services_screen.dart b/lib/screens/filters/blocked_services_screen.dart
index fba09d2..2c0912e 100644
--- a/lib/screens/filters/blocked_services_screen.dart
+++ b/lib/screens/filters/blocked_services_screen.dart
@@ -12,7 +12,12 @@ import 'package:adguard_home_manager/providers/app_config_provider.dart';
import 'package:adguard_home_manager/providers/servers_provider.dart';
class BlockedServicesScreen extends StatelessWidget {
- const BlockedServicesScreen({Key? key}) : super(key: key);
+ final bool dialog;
+
+ const BlockedServicesScreen({
+ Key? key,
+ required this.dialog
+ }) : super(key: key);
@override
Widget build(BuildContext context) {
@@ -21,7 +26,8 @@ class BlockedServicesScreen extends StatelessWidget {
return BlockedServicesScreenWidget(
serversProvider: serversProvider,
- appConfigProvider: appConfigProvider
+ appConfigProvider: appConfigProvider,
+ dialog: dialog,
);
}
}
@@ -29,11 +35,13 @@ class BlockedServicesScreen extends StatelessWidget {
class BlockedServicesScreenWidget extends StatefulWidget {
final ServersProvider serversProvider;
final AppConfigProvider appConfigProvider;
+ final bool dialog;
const BlockedServicesScreenWidget({
Key? key,
required this.serversProvider,
required this.appConfigProvider,
+ required this.dialog
}) : super(key: key);
@override
@@ -209,24 +217,74 @@ class _BlockedServicesScreenStateWidget extends State Navigator.pop(context),
+ icon: const Icon(Icons.clear_rounded),
+ tooltip: AppLocalizations.of(context)!.close,
+ ),
+ const SizedBox(width: 8),
+ Text(
+ AppLocalizations.of(context)!.blockedServices,
+ style: const TextStyle(
+ fontSize: 22
+ ),
+ )
+ ],
+ ),
+ IconButton(
+ onPressed: updateBlockedServices,
+ icon: const Icon(
+ Icons.save_rounded
+ ),
+ tooltip: AppLocalizations.of(context)!.save,
+ ),
+ ],
+ ),
+ ),
+ Expanded(
+ child: body()
+ ),
+ ],
+ )
+ ),
+ );
+ }
+ else {
+ return Scaffold(
+ appBar: AppBar(
+ title: Text(AppLocalizations.of(context)!.blockedServices),
+ actions: [
+ IconButton(
+ onPressed: updateBlockedServices,
+ icon: const Icon(
+ Icons.save_rounded
+ ),
+ tooltip: AppLocalizations.of(context)!.save,
+ ),
+ const SizedBox(width: 10)
+ ],
+ ),
+ body: RefreshIndicator(
+ onRefresh: loadBlockedServices,
+ child: body()
+ ),
+ );
+ }
}
}
\ No newline at end of file
diff --git a/lib/screens/filters/check_host_modal.dart b/lib/screens/filters/check_host_modal.dart
index 18f1f0f..6678ddb 100644
--- a/lib/screens/filters/check_host_modal.dart
+++ b/lib/screens/filters/check_host_modal.dart
@@ -10,7 +10,12 @@ import 'package:adguard_home_manager/services/http_requests.dart';
import 'package:adguard_home_manager/providers/servers_provider.dart';
class CheckHostModal extends StatefulWidget {
- const CheckHostModal({Key? key}) : super(key: key);
+ final bool dialog;
+
+ const CheckHostModal({
+ Key? key,
+ required this.dialog
+ }) : super(key: key);
@override
State createState() => _CheckHostModalState();
@@ -117,126 +122,141 @@ class _CheckHostModalState extends State {
}
}
- return Padding(
- padding: MediaQuery.of(context).viewInsets,
- child: Container(
- height: 330,
- width: double.maxFinite,
- decoration: BoxDecoration(
- borderRadius: const BorderRadius.only(
- topLeft: Radius.circular(28),
- topRight: Radius.circular(28),
- ),
- color: Theme.of(context).dialogBackgroundColor
- ),
- child: Center(
- child: Column(
- children: [
- Expanded(
- child: ListView(
- physics: 350 < MediaQuery.of(context).size.height
- ? const NeverScrollableScrollPhysics()
- : null,
- children: [
- Padding(
- padding: const EdgeInsets.only(top: 24),
- child: Icon(
- Icons.shield_rounded,
- size: 24,
- color: Theme.of(context).listTileTheme.iconColor
- ),
- ),
- const SizedBox(height: 16),
- Text(
- AppLocalizations.of(context)!.checkHostFiltered,
- textAlign: TextAlign.center,
- style: TextStyle(
- fontSize: 24,
- color: Theme.of(context).colorScheme.onSurface
- ),
- ),
- const SizedBox(height: 16),
- Padding(
- padding: const EdgeInsets.symmetric(horizontal: 24),
- child: TextFormField(
- controller: domainController,
- onChanged: validateDomain,
- decoration: InputDecoration(
- prefixIcon: const Icon(Icons.link_rounded),
- border: const OutlineInputBorder(
- borderRadius: BorderRadius.all(
- Radius.circular(10)
- )
+ Widget content() {
+ return Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Flexible(
+ child: SingleChildScrollView(
+ child: Wrap(
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Column(
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(top: 24),
+ child: Icon(
+ Icons.shield_rounded,
+ size: 24,
+ color: Theme.of(context).listTileTheme.iconColor
+ ),
),
- errorText: domainError,
- labelText: AppLocalizations.of(context)!.domain,
+ const SizedBox(height: 16),
+ Text(
+ AppLocalizations.of(context)!.checkHostFiltered,
+ textAlign: TextAlign.center,
+ style: TextStyle(
+ fontSize: 24,
+ color: Theme.of(context).colorScheme.onSurface
+ ),
+ ),
+ const SizedBox(height: 16),
+ ],
+ ),
+ ],
+ ),
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 24),
+ child: TextFormField(
+ controller: domainController,
+ onChanged: validateDomain,
+ decoration: InputDecoration(
+ prefixIcon: const Icon(Icons.link_rounded),
+ border: const OutlineInputBorder(
+ borderRadius: BorderRadius.all(
+ Radius.circular(10)
+ )
+ ),
+ errorText: domainError,
+ labelText: AppLocalizations.of(context)!.domain,
+ ),
+ ),
+ ),
+ if (resultWidget != null) Padding(
+ padding: const EdgeInsets.all(24),
+ child: resultWidget,
+ ),
+ if (resultWidget == null) Padding(
+ padding: const EdgeInsets.all(24),
+ child: Center(
+ child: Text(
+ AppLocalizations.of(context)!.insertDomain,
+ textAlign: TextAlign.center,
+ style: const TextStyle(
+ fontSize: 16,
),
),
),
- if (resultWidget != null) Padding(
- padding: const EdgeInsets.only(
- top: 20,
- left: 20,
- right: 20
- ),
- child: resultWidget,
+ ),
+ ],
+ ),
+ ),
+ ),
+ Column(
+ mainAxisAlignment: MainAxisAlignment.end,
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(
+ bottom: 24,
+ right: 24
+ ),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.end,
+ children: [
+ TextButton(
+ onPressed: () => Navigator.pop(context),
+ child: Text(AppLocalizations.of(context)!.close),
),
- if (resultWidget == null) Padding(
- padding: const EdgeInsets.only(
- top: 20,
- left: 20,
- right: 20
- ),
- child: Center(
- child: Text(
- AppLocalizations.of(context)!.insertDomain,
- style: const TextStyle(
- fontSize: 16,
- ),
+ const SizedBox(width: 20),
+ TextButton(
+ onPressed: domainController.text != '' && domainError == null
+ ? () => checkHost()
+ : null,
+ child: Text(
+ AppLocalizations.of(context)!.check,
+ style: TextStyle(
+ color: domainController.text != '' && domainError == null
+ ? Theme.of(context).colorScheme.primary
+ : Colors.grey
),
),
),
],
),
- ),
- Column(
- mainAxisAlignment: MainAxisAlignment.end,
- children: [
- Padding(
- padding: const EdgeInsets.only(
- bottom: 24,
- right: 24
- ),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.end,
- children: [
- TextButton(
- onPressed: () => Navigator.pop(context),
- child: Text(AppLocalizations.of(context)!.close),
- ),
- const SizedBox(width: 20),
- TextButton(
- onPressed: domainController.text != '' && domainError == null
- ? () => checkHost()
- : null,
- child: Text(
- AppLocalizations.of(context)!.check,
- style: TextStyle(
- color: domainController.text != '' && domainError == null
- ? Theme.of(context).colorScheme.primary
- : Colors.grey
- ),
- ),
- ),
- ],
- ),
- )
- ],
)
],
+ )
+ ],
+ );
+ }
+
+ if (widget.dialog == true) {
+ return Dialog(
+ child: ConstrainedBox(
+ constraints: const BoxConstraints(
+ maxWidth: 400
),
+ child: content()
),
- ),
- );
+ );
+ }
+ else {
+ return Padding(
+ padding: MediaQuery.of(context).viewInsets,
+ child: Container(
+ width: double.maxFinite,
+ decoration: BoxDecoration(
+ borderRadius: const BorderRadius.only(
+ topLeft: Radius.circular(28),
+ topRight: Radius.circular(28),
+ ),
+ color: Theme.of(context).dialogBackgroundColor
+ ),
+ child: content()
+ ),
+ );
+ }
}
}
\ No newline at end of file
diff --git a/lib/screens/filters/custom_rules_list.dart b/lib/screens/filters/custom_rules_list.dart
index 41b1d0f..5a96716 100644
--- a/lib/screens/filters/custom_rules_list.dart
+++ b/lib/screens/filters/custom_rules_list.dart
@@ -2,33 +2,27 @@
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
-import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
-import 'package:adguard_home_manager/screens/filters/fab.dart';
-import 'package:adguard_home_manager/screens/filters/remove_custom_rule_modal.dart';
+import 'package:adguard_home_manager/screens/filters/add_button.dart';
import 'package:adguard_home_manager/widgets/tab_content_list.dart';
-import 'package:adguard_home_manager/functions/snackbar.dart';
import 'package:adguard_home_manager/constants/enums.dart';
-import 'package:adguard_home_manager/models/filtering.dart';
-import 'package:adguard_home_manager/providers/app_config_provider.dart';
-import 'package:adguard_home_manager/services/http_requests.dart';
-import 'package:adguard_home_manager/providers/servers_provider.dart';
-import 'package:adguard_home_manager/classes/process_modal.dart';
class CustomRulesList extends StatefulWidget {
final LoadStatus loadStatus;
final ScrollController scrollController;
final List data;
final Future Function() fetchData;
+ final void Function(String) onRemoveCustomRule;
const CustomRulesList({
Key? key,
required this.loadStatus,
required this.scrollController,
required this.data,
- required this.fetchData
+ required this.fetchData,
+ required this.onRemoveCustomRule
}) : super(key: key);
@override
@@ -61,52 +55,6 @@ class _CustomRulesListState extends State {
@override
Widget build(BuildContext context) {
- final serversProvider = Provider.of(context);
- final appConfigProvider = Provider.of(context);
-
- void removeCustomRule(String rule) async {
- ProcessModal processModal = ProcessModal(context: context);
- processModal.open(AppLocalizations.of(context)!.deletingRule);
-
- final List newRules = serversProvider.filtering.data!.userRules.where((r) => r != rule).toList();
-
- final result = await setCustomRules(server: serversProvider.selectedServer!, rules: newRules);
-
- processModal.close();
-
- if (result['result'] == 'success') {
- FilteringData filteringData = serversProvider.filtering.data!;
- filteringData.userRules = newRules;
- serversProvider.setFilteringData(filteringData);
-
- showSnacbkar(
- context: context,
- appConfigProvider: appConfigProvider,
- label: AppLocalizations.of(context)!.ruleRemovedSuccessfully,
- color: Colors.green
- );
- }
- else {
- appConfigProvider.addLog(result['log']);
-
- showSnacbkar(
- context: context,
- appConfigProvider: appConfigProvider,
- label: AppLocalizations.of(context)!.ruleNotRemoved,
- color: Colors.red
- );
- }
- }
-
- void openRemoveCustomRuleModal(String rule) {
- showDialog(
- context: context,
- builder: (context) => RemoveCustomRule(
- onConfirm: () => removeCustomRule(rule),
- )
- );
- }
-
bool checkIfComment(String value) {
final regex = RegExp(r'^(!|#).*$');
if (regex.hasMatch(value)) {
@@ -184,7 +132,7 @@ class _CustomRulesListState extends State {
),
subtitle: generateSubtitle(widget.data[index]),
trailing: IconButton(
- onPressed: () => openRemoveCustomRuleModal(widget.data[index]),
+ onPressed: () => widget.onRemoveCustomRule(widget.data[index]),
icon: const Icon(Icons.delete)
),
),
@@ -239,8 +187,12 @@ class _CustomRulesListState extends State {
),
loadStatus: widget.loadStatus,
onRefresh: widget.fetchData,
- fab: const FiltersFab(
+ fab: AddFiltersButton(
type: 'custom_rule',
+ widget: (fn) => FloatingActionButton(
+ onPressed: fn,
+ child: const Icon(Icons.add),
+ ),
),
fabVisible: isVisible,
);
diff --git a/lib/screens/filters/filter_list_tile.dart b/lib/screens/filters/filter_list_tile.dart
deleted file mode 100644
index 1782d62..0000000
--- a/lib/screens/filters/filter_list_tile.dart
+++ /dev/null
@@ -1,60 +0,0 @@
-import 'package:flutter/material.dart';
-
-class FilterListTile extends StatelessWidget {
- final IconData icon;
- final String title;
- final String subtitle;
- final Color? color;
- final bool? bold;
-
- const FilterListTile({
- Key? key,
- required this.icon,
- required this.title,
- required this.subtitle,
- this.color,
- this.bold,
- }) : super(key: key);
-
- @override
- Widget build(BuildContext context) {
- return Container(
- padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.start,
- children: [
- Icon(
- icon,
- size: 24,
- color: Theme.of(context).listTileTheme.iconColor,
- ),
- const SizedBox(width: 16),
- Flexible(
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- title,
- style: TextStyle(
- fontSize: 16,
- fontWeight: FontWeight.w400,
- color: Theme.of(context).colorScheme.onSurface
- ),
- ),
- const SizedBox(height: 3),
- Text(
- subtitle,
- style: TextStyle(
- fontSize: 14,
- color: color ?? Theme.of(context).listTileTheme.textColor,
- fontWeight: bold == true ? FontWeight.bold : FontWeight.w400
- ),
- ),
- ],
- ),
- )
- ],
- ),
- );
- }
-}
\ No newline at end of file
diff --git a/lib/screens/filters/filters.dart b/lib/screens/filters/filters.dart
index f985f48..607d791 100644
--- a/lib/screens/filters/filters.dart
+++ b/lib/screens/filters/filters.dart
@@ -1,18 +1,23 @@
// ignore_for_file: use_build_context_synchronously
+import 'dart:io';
+
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/filters/filters_list.dart';
import 'package:adguard_home_manager/screens/filters/check_host_modal.dart';
-import 'package:adguard_home_manager/screens/filters/custom_rules_list.dart';
+import 'package:adguard_home_manager/screens/filters/filters_tabs_view.dart';
+import 'package:adguard_home_manager/screens/filters/filters_triple_column.dart';
+import 'package:adguard_home_manager/screens/filters/list_details_screen.dart';
+import 'package:adguard_home_manager/screens/filters/remove_custom_rule_modal.dart';
import 'package:adguard_home_manager/screens/filters/blocked_services_screen.dart';
import 'package:adguard_home_manager/screens/filters/update_interval_lists_modal.dart';
import 'package:adguard_home_manager/functions/snackbar.dart';
import 'package:adguard_home_manager/classes/process_modal.dart';
import 'package:adguard_home_manager/providers/app_config_provider.dart';
+import 'package:adguard_home_manager/models/filtering.dart';
import 'package:adguard_home_manager/constants/enums.dart';
import 'package:adguard_home_manager/services/http_requests.dart';
import 'package:adguard_home_manager/models/clients.dart';
@@ -47,10 +52,7 @@ class FiltersWidget extends StatefulWidget {
State createState() => _FiltersWidgetState();
}
-class _FiltersWidgetState extends State with TickerProviderStateMixin {
- late TabController tabController;
- final ScrollController scrollController = ScrollController();
-
+class _FiltersWidgetState extends State {
Future fetchFilters() async {
widget.serversProvider.setFilteringLoadStatus(LoadStatus.loading, false);
@@ -68,20 +70,14 @@ class _FiltersWidgetState extends State with TickerProviderStateM
}
}
+ List generateClientsList(List clients, List ips) {
+ return clients.where((client) => ips.contains(client.ip)).toList();
+ }
+
@override
void initState() {
fetchFilters();
super.initState();
- tabController = TabController(
- initialIndex: 0,
- length: 3,
- vsync: this,
- );
- tabController.addListener(() => widget.appConfigProvider.setSelectedFiltersTab(tabController.index));
- }
-
- List generateClientsList(List clients, List ips) {
- return clients.where((client) => ips.contains(client.ip)).toList();
}
@override
@@ -89,6 +85,8 @@ class _FiltersWidgetState extends State with TickerProviderStateM
final serversProvider = Provider.of(context);
final appConfigProvider = Provider.of(context);
+ final width = MediaQuery.of(context).size.width;
+
void fetchUpdateLists() async {
ProcessModal processModal = ProcessModal(context: context);
processModal.open(AppLocalizations.of(context)!.updatingLists);
@@ -139,12 +137,24 @@ class _FiltersWidgetState extends State with TickerProviderStateM
void showCheckHostModal() {
Future.delayed(const Duration(seconds: 0), () {
- showModalBottomSheet(
- context: context,
- builder: (context) => const CheckHostModal(),
- backgroundColor: Colors.transparent,
- isScrollControlled: true,
- );
+ if (width > 700 || !(Platform.isAndroid || Platform.isIOS)) {
+ showDialog(
+ context: context,
+ builder: (context) => const CheckHostModal(
+ dialog: true,
+ ),
+ );
+ }
+ else {
+ showModalBottomSheet(
+ context: context,
+ builder: (context) => const CheckHostModal(
+ dialog: false,
+ ),
+ backgroundColor: Colors.transparent,
+ isScrollControlled: true,
+ );
+ }
});
}
@@ -216,169 +226,222 @@ class _FiltersWidgetState extends State with TickerProviderStateM
void openBlockedServicesModal() {
Future.delayed(const Duration(seconds: 0), () {
- Navigator.of(context).push(
- MaterialPageRoute(
- builder: (context) => const BlockedServicesScreen(),
- )
- );
+ if (width > 700 || !(Platform.isAndroid || Platform.isIOS)) {
+ showDialog(
+ context: context,
+ builder: (context) => const BlockedServicesScreen(
+ dialog: true,
+ ),
+ barrierDismissible: false
+ );
+ }
+ else {
+ Navigator.of(context).push(
+ MaterialPageRoute(
+ builder: (context) => const BlockedServicesScreen(
+ dialog: false,
+ ),
+ )
+ );
+ }
});
}
- return DefaultTabController(
- length: 3,
- child: NestedScrollView(
- controller: scrollController,
- headerSliverBuilder: ((context, innerBoxIsScrolled) {
- return [
- SliverOverlapAbsorber(
- handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
- sliver: SliverAppBar(
- title: Text(AppLocalizations.of(context)!.filters),
- pinned: true,
- floating: true,
- forceElevated: innerBoxIsScrolled,
- centerTitle: false,
- actions: serversProvider.filtering.loadStatus == LoadStatus.loaded ? [
- IconButton(
- onPressed: enableDisableFiltering,
- tooltip: serversProvider.filtering.data!.enabled == true
- ? AppLocalizations.of(context)!.disableFiltering
- : AppLocalizations.of(context)!.enableFiltering,
- icon: Stack(
- children: [
- const Icon(Icons.power_settings_new_rounded),
- Positioned(
- bottom: 0,
- right: 0,
- child: Stack(
- children: [
- Container(
- decoration: BoxDecoration(
- borderRadius: BorderRadius.circular(30),
- color: Colors.white
- ),
- child: Icon(
- serversProvider.filtering.data!.enabled == true
- ? Icons.check_circle_rounded
- : Icons.cancel,
- size: 12,
- color: serversProvider.filtering.data!.enabled == true
- ? appConfigProvider.useThemeColorForStatus == true
- ? Theme.of(context).colorScheme.primary
- : Colors.green
- : appConfigProvider.useThemeColorForStatus == true
- ? Colors.grey
- : Colors.red
- ),
- ),
- ],
- ),
- )
- ],
- )
- ),
- IconButton(
- onPressed: () {
- showModalBottomSheet(
- context: context,
- builder: (context) => UpdateIntervalListsModal(
- interval: serversProvider.filtering.data!.interval,
- onChange: setUpdateFrequency
+ void removeCustomRule(String rule) async {
+ ProcessModal processModal = ProcessModal(context: context);
+ processModal.open(AppLocalizations.of(context)!.deletingRule);
+
+ final List newRules = serversProvider.filtering.data!.userRules.where((r) => r != rule).toList();
+
+ final result = await setCustomRules(server: serversProvider.selectedServer!, rules: newRules);
+
+ processModal.close();
+
+ if (result['result'] == 'success') {
+ FilteringData filteringData = serversProvider.filtering.data!;
+ filteringData.userRules = newRules;
+ serversProvider.setFilteringData(filteringData);
+
+ showSnacbkar(
+ context: context,
+ appConfigProvider: appConfigProvider,
+ label: AppLocalizations.of(context)!.ruleRemovedSuccessfully,
+ color: Colors.green
+ );
+ }
+ else {
+ appConfigProvider.addLog(result['log']);
+
+ showSnacbkar(
+ context: context,
+ appConfigProvider: appConfigProvider,
+ label: AppLocalizations.of(context)!.ruleNotRemoved,
+ color: Colors.red
+ );
+ }
+ }
+
+ void openRemoveCustomRuleModal(String rule) {
+ showDialog(
+ context: context,
+ builder: (context) => RemoveCustomRule(
+ onConfirm: () => removeCustomRule(rule),
+ )
+ );
+ }
+
+ void openListDetails(Filter filter, String type) {
+ if (width > 900 || !(Platform.isAndroid || Platform.isIOS)) {
+ showDialog(
+ context: context,
+ builder: (context) => ListDetailsScreen(
+ list: filter,
+ type: type,
+ dialog: true,
+ ),
+ barrierDismissible: false
+ );
+ }
+ else {
+ Navigator.of(context).push(
+ MaterialPageRoute(
+ builder: (context) => ListDetailsScreen(
+ list: filter,
+ type: type,
+ dialog: false,
+ )
+ )
+ );
+ }
+ }
+
+ List actions() {
+ if (serversProvider.filtering.loadStatus == LoadStatus.loaded) {
+ return [
+ IconButton(
+ onPressed: enableDisableFiltering,
+ tooltip: serversProvider.filtering.data!.enabled == true
+ ? AppLocalizations.of(context)!.disableFiltering
+ : AppLocalizations.of(context)!.enableFiltering,
+ icon: Stack(
+ children: [
+ const Icon(Icons.power_settings_new_rounded),
+ Positioned(
+ bottom: 0,
+ right: 0,
+ child: Stack(
+ children: [
+ Container(
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(30),
+ color: Colors.white
),
- backgroundColor: Colors.transparent,
- isScrollControlled: true
- );
- },
- icon: const Icon(Icons.update_rounded)
+ child: Icon(
+ serversProvider.filtering.data!.enabled == true
+ ? Icons.check_circle_rounded
+ : Icons.cancel,
+ size: 12,
+ color: serversProvider.filtering.data!.enabled == true
+ ? appConfigProvider.useThemeColorForStatus == true
+ ? Theme.of(context).colorScheme.primary
+ : Colors.green
+ : appConfigProvider.useThemeColorForStatus == true
+ ? Colors.grey
+ : Colors.red
+ ),
+ ),
+ ],
),
- PopupMenuButton(
- itemBuilder: (context) => [
- PopupMenuItem(
- onTap: fetchUpdateLists,
- child: Row(
- children: [
- const Icon(Icons.sync_rounded),
- const SizedBox(width: 10),
- Text(AppLocalizations.of(context)!.updateLists)
- ],
- )
- ),
- PopupMenuItem(
- onTap: openBlockedServicesModal,
- child: Row(
- children: [
- const Icon(Icons.block),
- const SizedBox(width: 10),
- Text(AppLocalizations.of(context)!.blockedServices)
- ],
- )
- ),
- PopupMenuItem(
- onTap: showCheckHostModal,
- child: Row(
- children: [
- const Icon(Icons.shield_rounded),
- const SizedBox(width: 10),
- Text(AppLocalizations.of(context)!.checkHostFiltered)
- ],
- )
- ),
- ]
+ )
+ ],
+ )
+ ),
+ IconButton(
+ onPressed: () {
+ if (width > 900 || !(Platform.isAndroid || Platform.isIOS)) {
+ showDialog(
+ context: context,
+ builder: (context) => UpdateIntervalListsModal(
+ interval: serversProvider.filtering.data!.interval,
+ onChange: setUpdateFrequency,
+ dialog: true,
),
- const SizedBox(width: 5),
- ] : [],
- bottom: TabBar(
- controller: tabController,
- isScrollable: false,
- unselectedLabelColor: Theme.of(context).colorScheme.onSurfaceVariant,
- tabs: [
- Tab(
- icon: const Icon(Icons.verified_user_rounded),
- text: AppLocalizations.of(context)!.whitelists,
- ),
- Tab(
- icon: const Icon(Icons.gpp_bad_rounded),
- text: AppLocalizations.of(context)!.blacklist,
- ),
- Tab(
- icon: const Icon(Icons.shield_rounded),
- text: AppLocalizations.of(context)!.customRules,
- ),
- ]
+ );
+ }
+ else {
+ showModalBottomSheet(
+ context: context,
+ builder: (context) => UpdateIntervalListsModal(
+ interval: serversProvider.filtering.data!.interval,
+ onChange: setUpdateFrequency,
+ dialog: false,
+ ),
+ backgroundColor: Colors.transparent,
+ isScrollControlled: true
+ );
+ }
+ },
+ icon: const Icon(Icons.update_rounded),
+ tooltip: AppLocalizations.of(context)!.updateFrequency,
+ ),
+ PopupMenuButton(
+ itemBuilder: (context) => [
+ PopupMenuItem(
+ onTap: fetchUpdateLists,
+ child: Row(
+ children: [
+ const Icon(Icons.sync_rounded),
+ const SizedBox(width: 10),
+ Text(AppLocalizations.of(context)!.updateLists)
+ ],
)
),
- )
- ];
- }),
- body: TabBarView(
- controller: tabController,
- children: [
- FiltersList(
- loadStatus: serversProvider.filtering.loadStatus,
- scrollController: scrollController,
- type: 'whitelist',
- data: serversProvider.filtering.loadStatus == LoadStatus.loaded
- ? serversProvider.filtering.data!.whitelistFilters : [],
- fetchData: fetchFilters,
- ),
- FiltersList(
- loadStatus: serversProvider.filtering.loadStatus,
- scrollController: scrollController,
- type: 'blacklist',
- data: serversProvider.filtering.loadStatus == LoadStatus.loaded
- ? serversProvider.filtering.data!.filters : [],
- fetchData: fetchFilters,
- ),
- CustomRulesList(
- loadStatus: serversProvider.filtering.loadStatus,
- scrollController: scrollController,
- data: serversProvider.filtering.loadStatus == LoadStatus.loaded
- ? serversProvider.filtering.data!.userRules : [],
- fetchData: fetchFilters,
- ),
- ]
- )
- )
- );
+ PopupMenuItem(
+ onTap: openBlockedServicesModal,
+ child: Row(
+ children: [
+ const Icon(Icons.block),
+ const SizedBox(width: 10),
+ Text(AppLocalizations.of(context)!.blockedServices)
+ ],
+ )
+ ),
+ PopupMenuItem(
+ onTap: showCheckHostModal,
+ child: Row(
+ children: [
+ const Icon(Icons.shield_rounded),
+ const SizedBox(width: 10),
+ Text(AppLocalizations.of(context)!.checkHostFiltered)
+ ],
+ )
+ ),
+ ]
+ ),
+ const SizedBox(width: 5),
+ ];
+ }
+ else {
+ return [];
+ }
+ }
+
+ if (width > 1200) {
+ return FiltersTripleColumn(
+ onRemoveCustomRule: openRemoveCustomRuleModal,
+ onOpenDetailsModal: openListDetails,
+ actions: actions(),
+ refreshData: fetchFilters,
+ );
+ }
+ else {
+ return FiltersTabsView(
+ appConfigProvider: appConfigProvider,
+ fetchFilters: fetchFilters,
+ actions: actions(),
+ onRemoveCustomRule: openRemoveCustomRuleModal,
+ onOpenDetailsModal: openListDetails,
+ );
+ }
}
}
\ No newline at end of file
diff --git a/lib/screens/filters/filters_list.dart b/lib/screens/filters/filters_list.dart
index effe42d..be4ff75 100644
--- a/lib/screens/filters/filters_list.dart
+++ b/lib/screens/filters/filters_list.dart
@@ -7,8 +7,7 @@ import 'package:provider/provider.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
-import 'package:adguard_home_manager/screens/filters/fab.dart';
-import 'package:adguard_home_manager/screens/filters/list_details_screen.dart';
+import 'package:adguard_home_manager/screens/filters/add_button.dart';
import 'package:adguard_home_manager/widgets/custom_list_tile.dart';
import 'package:adguard_home_manager/widgets/tab_content_list.dart';
@@ -23,6 +22,7 @@ class FiltersList extends StatefulWidget {
final List data;
final Future Function() fetchData;
final String type;
+ final void Function(Filter, String) onOpenDetailsScreen;
const FiltersList({
Key? key,
@@ -31,6 +31,7 @@ class FiltersList extends StatefulWidget {
required this.data,
required this.fetchData,
required this.type,
+ required this.onOpenDetailsScreen
}) : super(key: key);
@override
@@ -64,17 +65,6 @@ class _FiltersListState extends State {
@override
Widget build(BuildContext context) {
final appConfigProvider = Provider.of(context);
-
- void openDetailsModal(Filter filter) {
- Navigator.of(context).push(
- MaterialPageRoute(
- builder: (context) => ListDetailsScreen(
- list: filter,
- type: widget.type,
- )
- )
- );
- }
return CustomTabContentList(
loadingGenerator: () => SizedBox(
@@ -112,7 +102,7 @@ class _FiltersListState extends State {
? Colors.grey
: Colors.red
),
- onTap: () => openDetailsModal(widget.data[index]),
+ onTap: () => widget.onOpenDetailsScreen(widget.data[index], widget.type),
),
noData: Container(
width: double.maxFinite,
@@ -166,8 +156,12 @@ class _FiltersListState extends State {
),
loadStatus: widget.loadStatus,
onRefresh: widget.fetchData,
- fab: FiltersFab(
+ fab: AddFiltersButton(
type: widget.type,
+ widget: (fn) => FloatingActionButton(
+ onPressed: fn,
+ child: const Icon(Icons.add),
+ ),
),
fabVisible: isVisible,
);
diff --git a/lib/screens/filters/filters_tabs_view.dart b/lib/screens/filters/filters_tabs_view.dart
new file mode 100644
index 0000000..17f74a9
--- /dev/null
+++ b/lib/screens/filters/filters_tabs_view.dart
@@ -0,0 +1,143 @@
+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/filters/custom_rules_list.dart';
+import 'package:adguard_home_manager/screens/filters/filters_list.dart';
+
+import 'package:adguard_home_manager/constants/enums.dart';
+import 'package:adguard_home_manager/models/filtering.dart';
+import 'package:adguard_home_manager/providers/app_config_provider.dart';
+import 'package:adguard_home_manager/providers/servers_provider.dart';
+
+class FiltersTabsView extends StatefulWidget {
+ final AppConfigProvider appConfigProvider;
+ final Future Function() fetchFilters;
+ final List actions;
+ final void Function(String) onRemoveCustomRule;
+ final void Function(Filter, String) onOpenDetailsModal;
+
+ const FiltersTabsView({
+ Key? key,
+ required this.appConfigProvider,
+ required this.fetchFilters,
+ required this.actions,
+ required this.onOpenDetailsModal,
+ required this.onRemoveCustomRule
+ }) : super(key: key);
+
+ @override
+ State createState() => _FiltersTabsViewState();
+}
+
+class _FiltersTabsViewState extends State with TickerProviderStateMixin {
+ late TabController tabController;
+ final ScrollController scrollController = ScrollController();
+
+ @override
+ void initState() {
+ widget.fetchFilters();
+ super.initState();
+ tabController = TabController(
+ initialIndex: 0,
+ length: 3,
+ vsync: this,
+ );
+ tabController.addListener(() => widget.appConfigProvider.setSelectedFiltersTab(tabController.index));
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final serversProvider = Provider.of(context);
+
+ return DefaultTabController(
+ length: 3,
+ child: NestedScrollView(
+ controller: scrollController,
+ headerSliverBuilder: ((context, innerBoxIsScrolled) {
+ return [
+ SliverOverlapAbsorber(
+ handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
+ sliver: SliverAppBar(
+ title: Text(AppLocalizations.of(context)!.filters),
+ pinned: true,
+ floating: true,
+ forceElevated: innerBoxIsScrolled,
+ centerTitle: false,
+ actions: widget.actions,
+ bottom: TabBar(
+ controller: tabController,
+ isScrollable: true,
+ unselectedLabelColor: Theme.of(context).colorScheme.onSurfaceVariant,
+ tabs: [
+ Tab(
+ child: Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ const Icon(Icons.verified_user_rounded),
+ const SizedBox(width: 8),
+ Text(AppLocalizations.of(context)!.whitelists,)
+ ],
+ ),
+ ),
+ Tab(
+ child: Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ const Icon(Icons.gpp_bad_rounded),
+ const SizedBox(width: 8),
+ Text(AppLocalizations.of(context)!.blacklists)
+ ],
+ ),
+ ),
+ Tab(
+ child: Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ const Icon(Icons.shield_rounded),
+ const SizedBox(width: 8),
+ Text(AppLocalizations.of(context)!.customRules)
+ ],
+ ),
+ ),
+ ]
+ )
+ ),
+ )
+ ];
+ }),
+ body: TabBarView(
+ controller: tabController,
+ children: [
+ FiltersList(
+ loadStatus: serversProvider.filtering.loadStatus,
+ scrollController: scrollController,
+ type: 'whitelist',
+ data: serversProvider.filtering.loadStatus == LoadStatus.loaded
+ ? serversProvider.filtering.data!.whitelistFilters : [],
+ fetchData: widget.fetchFilters,
+ onOpenDetailsScreen: widget.onOpenDetailsModal,
+ ),
+ FiltersList(
+ loadStatus: serversProvider.filtering.loadStatus,
+ scrollController: scrollController,
+ type: 'blacklist',
+ data: serversProvider.filtering.loadStatus == LoadStatus.loaded
+ ? serversProvider.filtering.data!.filters : [],
+ fetchData: widget.fetchFilters,
+ onOpenDetailsScreen: widget.onOpenDetailsModal,
+ ),
+ CustomRulesList(
+ loadStatus: serversProvider.filtering.loadStatus,
+ scrollController: scrollController,
+ data: serversProvider.filtering.loadStatus == LoadStatus.loaded
+ ? serversProvider.filtering.data!.userRules : [],
+ fetchData: widget.fetchFilters,
+ onRemoveCustomRule: widget.onRemoveCustomRule,
+ ),
+ ]
+ )
+ )
+ );
+ }
+}
\ No newline at end of file
diff --git a/lib/screens/filters/filters_triple_column.dart b/lib/screens/filters/filters_triple_column.dart
new file mode 100644
index 0000000..3e82fc5
--- /dev/null
+++ b/lib/screens/filters/filters_triple_column.dart
@@ -0,0 +1,311 @@
+import 'dart:io';
+
+import 'package:adguard_home_manager/screens/filters/add_button.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_gen/gen_l10n/app_localizations.dart';
+import 'package:provider/provider.dart';
+
+import 'package:adguard_home_manager/widgets/custom_list_tile.dart';
+
+import 'package:adguard_home_manager/constants/enums.dart';
+import 'package:adguard_home_manager/models/filtering.dart';
+import 'package:adguard_home_manager/functions/number_format.dart';
+import 'package:adguard_home_manager/providers/app_config_provider.dart';
+import 'package:adguard_home_manager/providers/servers_provider.dart';
+
+class FiltersTripleColumn extends StatelessWidget {
+ final void Function(String) onRemoveCustomRule;
+ final void Function(Filter, String) onOpenDetailsModal;
+ final List actions;
+ final Future Function() refreshData;
+
+ const FiltersTripleColumn({
+ Key? key,
+ required this.onRemoveCustomRule,
+ required this.onOpenDetailsModal,
+ required this.actions,
+ required this.refreshData
+ }) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ final serversProvider = Provider.of(context);
+ final appConfigProvider = Provider.of(context);
+
+ bool checkIfComment(String value) {
+ final regex = RegExp(r'^(!|#).*$');
+ if (regex.hasMatch(value)) {
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ Widget? generateSubtitle(String rule) {
+ final allowRegex = RegExp(r'^@@.*$');
+ final blockRegex = RegExp(r'^\|\|.*$');
+ final commentRegex = RegExp(r'^(#|!).*$');
+
+ if (allowRegex.hasMatch(rule)) {
+ return Text(
+ AppLocalizations.of(context)!.allowed,
+ style: const TextStyle(
+ color: Colors.green
+ ),
+ );
+ }
+ else if (blockRegex.hasMatch(rule)) {
+ return Text(
+ AppLocalizations.of(context)!.blocked,
+ style: const TextStyle(
+ color: Colors.red
+ ),
+ );
+ }
+ else if (commentRegex.hasMatch(rule)) {
+ return Text(
+ AppLocalizations.of(context)!.comment,
+ style: const TextStyle(
+ color: Colors.grey
+ ),
+ );
+ }
+ else {
+ return null;
+ }
+ }
+
+ Widget content() {
+ switch (serversProvider.filtering.loadStatus) {
+ case LoadStatus.loading:
+ return Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ mainAxisSize: MainAxisSize.max,
+ children: [
+ Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ const CircularProgressIndicator(),
+ const SizedBox(height: 30),
+ Text(
+ AppLocalizations.of(context)!.loadingFilters,
+ style: TextStyle(
+ fontSize: 22,
+ color: Theme.of(context).colorScheme.onSurfaceVariant,
+ ),
+ )
+ ],
+ ),
+ ],
+ );
+
+ case LoadStatus.loaded:
+ return Row(
+ children: [
+ Expanded(
+ flex: 1,
+ child: Column(
+ children: [
+ Padding(
+ padding: const EdgeInsets.all(16),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Text(
+ AppLocalizations.of(context)!.whitelists,
+ style: const TextStyle(
+ fontSize: 18,
+ fontWeight: FontWeight.w500
+ ),
+ ),
+ AddFiltersButton(
+ type: 'whitelist',
+ widget: (fn) => IconButton(
+ onPressed: fn,
+ icon: const Icon(Icons.add_rounded)
+ )
+ )
+ ],
+ ),
+ ),
+ Expanded(
+ child: ListView.builder(
+ itemCount: serversProvider.filtering.data!.whitelistFilters.length,
+ itemBuilder: (context, index) => CustomListTile(
+ title: serversProvider.filtering.data!.whitelistFilters[index].name,
+ subtitle: "${intFormat(serversProvider.filtering.data!.whitelistFilters[index].rulesCount, Platform.localeName)} ${AppLocalizations.of(context)!.enabledRules}",
+ trailing: Icon(
+ serversProvider.filtering.data!.whitelistFilters[index].enabled == true
+ ? Icons.check_circle_rounded
+ : Icons.cancel,
+ color: serversProvider.filtering.data!.whitelistFilters[index].enabled == true
+ ? appConfigProvider.useThemeColorForStatus == true
+ ? Theme.of(context).colorScheme.primary
+ : Colors.green
+ : appConfigProvider.useThemeColorForStatus == true
+ ? Colors.grey
+ : Colors.red
+ ),
+ onTap: () => onOpenDetailsModal(serversProvider.filtering.data!.whitelistFilters[index], 'whitelist'),
+ ),
+ ),
+ )
+ ],
+ ),
+ ),
+ Expanded(
+ flex: 1,
+ child: Column(
+ children: [
+ Padding(
+ padding: const EdgeInsets.all(16),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Text(
+ AppLocalizations.of(context)!.blacklists,
+ style: const TextStyle(
+ fontSize: 18,
+ fontWeight: FontWeight.w500
+ ),
+ ),
+ AddFiltersButton(
+ type: 'blacklist',
+ widget: (fn) => IconButton(
+ onPressed: fn,
+ icon: const Icon(Icons.add_rounded)
+ )
+ )
+ ],
+ ),
+ ),
+ Expanded(
+ child: ListView.builder(
+ itemCount: serversProvider.filtering.data!.filters.length,
+ itemBuilder: (context, index) => CustomListTile(
+ title: serversProvider.filtering.data!.filters[index].name,
+ subtitle: "${intFormat(serversProvider.filtering.data!.filters[index].rulesCount, Platform.localeName)} ${AppLocalizations.of(context)!.enabledRules}",
+ trailing: Icon(
+ serversProvider.filtering.data!.filters[index].enabled == true
+ ? Icons.check_circle_rounded
+ : Icons.cancel,
+ color: serversProvider.filtering.data!.filters[index].enabled == true
+ ? appConfigProvider.useThemeColorForStatus == true
+ ? Theme.of(context).colorScheme.primary
+ : Colors.green
+ : appConfigProvider.useThemeColorForStatus == true
+ ? Colors.grey
+ : Colors.red
+ ),
+ onTap: () => onOpenDetailsModal(serversProvider.filtering.data!.filters[index], 'blacklist'),
+ ),
+ ),
+ )
+ ],
+ ),
+ ),
+ Expanded(
+ flex: 1,
+ child: Column(
+ children: [
+ Padding(
+ padding: const EdgeInsets.all(16),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Text(
+ AppLocalizations.of(context)!.customRules,
+ style: const TextStyle(
+ fontSize: 18,
+ fontWeight: FontWeight.w500
+ ),
+ ),
+ AddFiltersButton(
+ type: '',
+ widget: (fn) => IconButton(
+ onPressed: fn,
+ icon: const Icon(Icons.add_rounded)
+ )
+ )
+ ],
+ ),
+ ),
+ Expanded(
+ child: ListView.builder(
+ itemCount: serversProvider.filtering.data!.userRules.length,
+ itemBuilder: (context, index) => ListTile(
+ title: Text(
+ serversProvider.filtering.data!.userRules[index],
+ style: TextStyle(
+ color: checkIfComment(serversProvider.filtering.data!.userRules[index]) == true
+ ? Theme.of(context).colorScheme.onSurface.withOpacity(0.6)
+ : Theme.of(context).colorScheme.onSurface,
+ fontWeight: FontWeight.normal,
+ ),
+ ),
+ subtitle: generateSubtitle(serversProvider.filtering.data!.userRules[index]),
+ trailing: IconButton(
+ onPressed: () => onRemoveCustomRule(serversProvider.filtering.data!.userRules[index]),
+ icon: const Icon(Icons.delete)
+ ),
+ ),
+ ),
+ )
+ ],
+ ),
+ ),
+ ],
+ );
+
+ case LoadStatus.error:
+ return SizedBox.expand(
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ mainAxisSize: MainAxisSize.max,
+ children: [
+ 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)!.filtersNotLoaded,
+ style: TextStyle(
+ fontSize: 22,
+ color: Theme.of(context).colorScheme.onSurfaceVariant,
+ ),
+ )
+ ],
+ ),
+ ],
+ ),
+ );
+
+ default:
+ return const SizedBox();
+ }
+ }
+
+ return Scaffold(
+ appBar: AppBar(
+ title: Text(AppLocalizations.of(context)!.filters),
+ actions: [
+ IconButton(
+ onPressed: refreshData,
+ icon: const Icon(Icons.refresh_rounded),
+ tooltip: AppLocalizations.of(context)!.refresh,
+ ),
+ ...actions
+ ],
+ ),
+ body: content(),
+ );
+ }
+}
\ No newline at end of file
diff --git a/lib/screens/filters/list_details_screen.dart b/lib/screens/filters/list_details_screen.dart
index 7d8cf53..92ae4e0 100644
--- a/lib/screens/filters/list_details_screen.dart
+++ b/lib/screens/filters/list_details_screen.dart
@@ -7,9 +7,9 @@ import 'package:flutter/rendering.dart';
import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
-import 'package:adguard_home_manager/screens/filters/filter_list_tile.dart';
import 'package:adguard_home_manager/screens/filters/add_list_modal.dart';
import 'package:adguard_home_manager/screens/filters/delete_list_modal.dart';
+import 'package:adguard_home_manager/widgets/custom_list_tile.dart';
import 'package:adguard_home_manager/functions/format_time.dart';
import 'package:adguard_home_manager/providers/app_config_provider.dart';
@@ -23,11 +23,13 @@ import 'package:adguard_home_manager/models/filtering.dart';
class ListDetailsScreen extends StatefulWidget {
final Filter list;
final String type;
+ final bool dialog;
const ListDetailsScreen({
Key? key,
required this.list,
required this.type,
+ required this.dialog
}) : super(key: key);
@override
@@ -68,6 +70,8 @@ class _ListDetailsScreenState extends State {
final serversProvider = Provider.of(context);
final appConfigProvider = Provider.of(context);
+ final width = MediaQuery.of(context).size.width;
+
void enableDisableList(Filter list, bool newStatus) async {
ProcessModal processModal = ProcessModal(context: context);
processModal.open(
@@ -216,108 +220,234 @@ class _ListDetailsScreenState extends State {
}
}
- return Scaffold(
- appBar: AppBar(
- title: Text(AppLocalizations.of(context)!.listDetails),
- actions: [
- IconButton(
- onPressed: () => {
+ List content() {
+ return [
+ CustomListTile(
+ icon: Icons.shield_rounded,
+ title: AppLocalizations.of(context)!.currentStatus,
+ subtitleWidget: Text(
+ enabled == true
+ ? AppLocalizations.of(context)!.enabled
+ : AppLocalizations.of(context)!.disabled,
+ style: TextStyle(
+ color: enabled == true
+ ? appConfigProvider.useThemeColorForStatus == true
+ ? Theme.of(context).colorScheme.primary
+ : Colors.green
+ : appConfigProvider.useThemeColorForStatus == true
+ ? Colors.grey
+ : Colors.red,
+ fontWeight: FontWeight.w500
+ ),
+ ),
+ padding: widget.dialog == true
+ ? const EdgeInsets.symmetric(
+ horizontal: 24,
+ vertical: 8
+ )
+ : null,
+ ),
+ CustomListTile(
+ icon: Icons.badge_rounded,
+ title: AppLocalizations.of(context)!.name,
+ subtitle: name,
+ padding: widget.dialog == true
+ ? const EdgeInsets.symmetric(
+ horizontal: 24,
+ vertical: 8
+ )
+ : null,
+ ),
+ CustomListTile(
+ icon: Icons.link_rounded,
+ title: "URL",
+ subtitle: widget.list.url,
+ padding: widget.dialog == true
+ ? const EdgeInsets.symmetric(
+ horizontal: 24,
+ vertical: 8
+ )
+ : null,
+ ),
+ CustomListTile(
+ icon: Icons.list_rounded,
+ title: AppLocalizations.of(context)!.rules,
+ subtitle: widget.list.rulesCount.toString(),
+ padding: widget.dialog == true
+ ? const EdgeInsets.symmetric(
+ horizontal: 24,
+ vertical: 8
+ )
+ : null,
+ ),
+ CustomListTile(
+ icon: Icons.shield_rounded,
+ title: AppLocalizations.of(context)!.listType,
+ subtitle: widget.type == 'whitelist'
+ ? AppLocalizations.of(context)!.whitelist
+ : AppLocalizations.of(context)!.blacklist,
+ padding: widget.dialog == true
+ ? const EdgeInsets.symmetric(
+ horizontal: 24,
+ vertical: 8
+ )
+ : null,
+ ),
+ if (widget.list.lastUpdated != null) CustomListTile(
+ icon: Icons.schedule_rounded,
+ title: AppLocalizations.of(context)!.latestUpdate,
+ subtitle: convertTimestampLocalTimezone(widget.list.lastUpdated!, 'dd-MM-yyyy HH:mm'),
+ padding: widget.dialog == true
+ ? const EdgeInsets.symmetric(
+ horizontal: 24,
+ vertical: 8
+ )
+ : null,
+ ),
+ if (widget.dialog == true) Container(height: 16)
+ ];
+ }
+
+ List actions() {
+ return [
+ IconButton(
+ onPressed: () => {
+ if (width > 700 || !(Platform.isAndroid || Platform.isIOS)) {
+ showDialog(
+ context: context,
+ builder: (ctx) => AddListModal(
+ list: widget.list,
+ type: widget.type,
+ onEdit: confirmEditList,
+ dialog: true,
+ ),
+ )
+ }
+ else {
showModalBottomSheet(
context: context,
builder: (ctx) => AddListModal(
list: widget.list,
type: widget.type,
- onEdit: confirmEditList
+ onEdit: confirmEditList,
+ dialog: false,
),
isScrollControlled: true,
backgroundColor: Colors.transparent
)
- },
- icon: const Icon(Icons.edit),
- tooltip: AppLocalizations.of(context)!.edit,
+ }
+ },
+ icon: const Icon(Icons.edit),
+ tooltip: AppLocalizations.of(context)!.edit,
+ ),
+ IconButton(
+ onPressed: () {
+ showDialog(
+ context: context,
+ builder: (context) => DeleteListModal(
+ onConfirm: () => deleteList(widget.list, widget.type),
+ )
+ );
+ },
+ icon: const Icon(Icons.delete),
+ tooltip: AppLocalizations.of(context)!.delete,
+ ),
+ const SizedBox(width: 10),
+ ];
+ }
+
+ if (widget.dialog == true) {
+ return Dialog(
+ child: ConstrainedBox(
+ constraints: const BoxConstraints(
+ maxWidth: 500
),
- IconButton(
- onPressed: () {
- showDialog(
- context: context,
- builder: (context) => DeleteListModal(
- onConfirm: () => deleteList(widget.list, widget.type),
- )
- );
- },
- icon: const Icon(Icons.delete),
- tooltip: AppLocalizations.of(context)!.delete,
- ),
- const SizedBox(width: 10),
- ],
- ),
- body: Stack(
- children: [
- ListView(
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
children: [
- FilterListTile(
- icon: Icons.shield_rounded,
- title: AppLocalizations.of(context)!.currentStatus,
- subtitle: enabled == true
- ? AppLocalizations.of(context)!.enabled
- : AppLocalizations.of(context)!.disabled,
- color: enabled == true
- ? appConfigProvider.useThemeColorForStatus == true
- ? Theme.of(context).colorScheme.primary
- : Colors.green
- : appConfigProvider.useThemeColorForStatus == true
- ? Colors.grey
- : Colors.red,
- bold: true,
- ),
- FilterListTile(
- icon: Icons.badge_rounded,
- title: AppLocalizations.of(context)!.name,
- subtitle: name
- ),
- FilterListTile(
- icon: Icons.link_rounded,
- title: "URL",
- subtitle: widget.list.url
- ),
- FilterListTile(
- icon: Icons.list_rounded,
- title: AppLocalizations.of(context)!.rules,
- subtitle: widget.list.rulesCount.toString()
- ),
- FilterListTile(
- icon: Icons.shield_rounded,
- title: AppLocalizations.of(context)!.listType,
- subtitle: widget.type == 'whitelist'
- ? AppLocalizations.of(context)!.whitelist
- : AppLocalizations.of(context)!.blacklist,
- ),
- if (widget.list.lastUpdated != null) FilterListTile(
- icon: Icons.schedule_rounded,
- title: AppLocalizations.of(context)!.latestUpdate,
- subtitle: convertTimestampLocalTimezone(widget.list.lastUpdated!, 'dd-MM-yyyy HH:mm'),
+ Padding(
+ padding: const EdgeInsets.all(16),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Row(
+ children: [
+ IconButton(
+ onPressed: () => Navigator.pop(context),
+ icon: const Icon(Icons.clear_rounded),
+ tooltip: AppLocalizations.of(context)!.close,
+ ),
+ const SizedBox(width: 8),
+ Text(
+ AppLocalizations.of(context)!.listDetails,
+ style: const TextStyle(
+ fontSize: 22
+ ),
+ )
+ ],
+ ),
+ Row(
+ children: [
+ IconButton(
+ onPressed: () => enableDisableList(widget.list, !enabled),
+ icon: Icon(
+ enabled == true
+ ? Icons.gpp_bad_rounded
+ : Icons.verified_user_rounded,
+ ),
+ tooltip: enabled == true
+ ? AppLocalizations.of(context)!.disableList
+ : AppLocalizations.of(context)!.enableList,
+ ),
+ ...actions()
+ ],
+ )
+ ],
+ ),
),
+ Flexible(
+ child: SingleChildScrollView(
+ child: Wrap(
+ children: content(),
+ ),
+ )
+ )
],
),
- AnimatedPositioned(
- duration: const Duration(milliseconds: 100),
- curve: Curves.easeInOut,
- bottom: fabVisible ?
- appConfigProvider.showingSnackbar
- ? 70 : (Platform.isIOS ? 40 : 20)
- : -70,
- right: 20,
- child: FloatingActionButton(
- onPressed: () => enableDisableList(widget.list, !enabled),
- child: Icon(
- enabled == true
- ? Icons.gpp_bad_rounded
- : Icons.verified_user_rounded,
- ),
+ )
+ );
+ }
+ else {
+ return Scaffold(
+ appBar: AppBar(
+ title: Text(AppLocalizations.of(context)!.listDetails),
+ actions: actions(),
+ ),
+ body: Stack(
+ children: [
+ ListView(
+ children: content(),
),
- )
- ],
- ),
- );
+ AnimatedPositioned(
+ duration: const Duration(milliseconds: 100),
+ curve: Curves.easeInOut,
+ bottom: fabVisible ?
+ appConfigProvider.showingSnackbar
+ ? 70 : (Platform.isIOS ? 40 : 20)
+ : -70,
+ right: 20,
+ child: FloatingActionButton(
+ onPressed: () => enableDisableList(widget.list, !enabled),
+ child: Icon(
+ enabled == true
+ ? Icons.gpp_bad_rounded
+ : Icons.verified_user_rounded,
+ ),
+ ),
+ )
+ ],
+ ),
+ );
+ }
}
}
\ No newline at end of file
diff --git a/lib/screens/filters/update_interval_lists_modal.dart b/lib/screens/filters/update_interval_lists_modal.dart
index 1779bf9..c168793 100644
--- a/lib/screens/filters/update_interval_lists_modal.dart
+++ b/lib/screens/filters/update_interval_lists_modal.dart
@@ -9,11 +9,13 @@ import 'package:adguard_home_manager/widgets/option_box.dart';
class UpdateIntervalListsModal extends StatefulWidget {
final int interval;
final void Function(int) onChange;
+ final bool dialog;
const UpdateIntervalListsModal({
Key? key,
required this.interval,
required this.onChange,
+ required this.dialog
}) : super(key: key);
@override
@@ -37,272 +39,266 @@ class _UpdateIntervalListsModalState extends State {
@override
Widget build(BuildContext context) {
- final MediaQueryData mediaQueryData = MediaQuery.of(context);
+ final MediaQueryData mediaQueryData = MediaQuery.of(context);
- return Padding(
- padding: mediaQueryData.viewInsets,
- child: Container(
- height: Platform.isIOS ? 406 : 390,
- decoration: BoxDecoration(
- color: Theme.of(context).dialogBackgroundColor,
- borderRadius: const BorderRadius.only(
- topLeft: Radius.circular(28),
- topRight: Radius.circular(28)
- ),
- ),
- child: Column(
- mainAxisSize: MainAxisSize.min,
- children: [
- Expanded(
- child: ListView(
- physics: (Platform.isIOS ? 426 : 410) < MediaQuery.of(context).size.height
- ? const NeverScrollableScrollPhysics()
- : null,
+ Widget content() {
+ return Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Flexible(
+ child: SingleChildScrollView(
+ child: Wrap(
children: [
- Padding(
- padding: const EdgeInsets.only(top: 24),
- child: Icon(
- Icons.update_rounded,
- size: 24,
- color: Theme.of(context).listTileTheme.iconColor
- ),
- ),
- Container(
- padding: const EdgeInsets.symmetric(
- horizontal: 24,
- vertical: 16
- ),
- width: double.maxFinite,
- child: Text(
- AppLocalizations.of(context)!.updateFrequency,
- textAlign: TextAlign.center,
- overflow: TextOverflow.ellipsis,
- style: TextStyle(
- fontSize: 24,
- color: Theme.of(context).colorScheme.onSurface
- ),
- ),
- ),
- SizedBox(
- width: double.maxFinite,
- child: Padding(
- padding: const EdgeInsets.symmetric(horizontal: 20),
- child: Column(
+ Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Column(
children: [
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceEvenly,
- children: [
- Container(
- width: (mediaQueryData.size.width-70)/2,
- margin: const EdgeInsets.only(
- top: 10,
- right: 5,
- bottom: 5
- ),
- child: OptionBox(
- optionsValue: selectedOption,
- itemValue: 0,
- onTap: _updateRadioValue,
- child: Center(
- child: AnimatedDefaultTextStyle(
- duration: const Duration(milliseconds: 250),
- style: TextStyle(
- fontWeight: FontWeight.bold,
- fontSize: 14,
- color: selectedOption == 0
- ? Theme.of(context).colorScheme.onInverseSurface
- : Theme.of(context).colorScheme.onSurface
- ),
- child: Text(AppLocalizations.of(context)!.never),
- ),
- ),
- ),
- ),
- Container(
- width: (mediaQueryData.size.width-70)/2,
- margin: const EdgeInsets.only(
- top: 10,
- left: 5,
- bottom: 5
- ),
- child: OptionBox(
- optionsValue: selectedOption,
- itemValue: 1,
- onTap: _updateRadioValue,
- child: Center(
- child: AnimatedDefaultTextStyle(
- duration: const Duration(milliseconds: 250),
- style: TextStyle(
- fontWeight: FontWeight.bold,
- fontSize: 14,
- color: selectedOption == 1
- ? Theme.of(context).colorScheme.onInverseSurface
- : Theme.of(context).colorScheme.onSurface
- ),
- child: Text(AppLocalizations.of(context)!.hour1),
- ),
- ),
- ),
- ),
- ],
+ Padding(
+ padding: const EdgeInsets.only(top: 24),
+ child: Icon(
+ Icons.update_rounded,
+ size: 24,
+ color: Theme.of(context).listTileTheme.iconColor
+ ),
),
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceEvenly,
- children: [
- Container(
- width: (mediaQueryData.size.width-70)/2,
- margin: const EdgeInsets.only(
- top: 5,
- right: 5,
- bottom: 5
- ),
- child: OptionBox(
- optionsValue: selectedOption,
- itemValue: 12,
- onTap: _updateRadioValue,
- child: Center(
- child: AnimatedDefaultTextStyle(
- duration: const Duration(milliseconds: 250),
- style: TextStyle(
- fontWeight: FontWeight.bold,
- fontSize: 14,
- color: selectedOption == 12
- ? Theme.of(context).colorScheme.onInverseSurface
- : Theme.of(context).colorScheme.onSurface
- ),
- child: Text(AppLocalizations.of(context)!.hours12),
- ),
- ),
- ),
+ Container(
+ padding: const EdgeInsets.symmetric(
+ horizontal: 24,
+ vertical: 16
+ ),
+ child: Text(
+ AppLocalizations.of(context)!.updateFrequency,
+ textAlign: TextAlign.center,
+ overflow: TextOverflow.ellipsis,
+ style: TextStyle(
+ fontSize: 24,
+ color: Theme.of(context).colorScheme.onSurface
),
- Container(
- width: (mediaQueryData.size.width-70)/2,
- margin: const EdgeInsets.only(
- top: 5,
- left: 5,
- bottom: 5
- ),
- child: OptionBox(
- optionsValue: selectedOption,
- itemValue: 24,
- onTap: _updateRadioValue,
- child: Center(
- child: AnimatedDefaultTextStyle(
- duration: const Duration(milliseconds: 250),
- style: TextStyle(
- fontWeight: FontWeight.bold,
- fontSize: 14,
- color: selectedOption == 24
- ? Theme.of(context).colorScheme.onInverseSurface
- : Theme.of(context).colorScheme.onSurface
- ),
- child: Text(AppLocalizations.of(context)!.hours24),
- ),
- ),
- ),
- ),
- ],
- ),
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceEvenly,
- children: [
- Container(
- width: (mediaQueryData.size.width-70)/2,
- margin: const EdgeInsets.only(
- top: 5,
- right: 5,
- bottom: 10
- ),
- child: OptionBox(
- optionsValue: selectedOption,
- itemValue: 72,
- onTap: _updateRadioValue,
- child: Center(
- child: AnimatedDefaultTextStyle(
- duration: const Duration(milliseconds: 250),
- style: TextStyle(
- fontWeight: FontWeight.bold,
- fontSize: 14,
- color: selectedOption == 72
- ? Theme.of(context).colorScheme.onInverseSurface
- : Theme.of(context).colorScheme.onSurface
- ),
- child: Text(AppLocalizations.of(context)!.days3),
- ),
- ),
- ),
- ),
- Container(
- width: (mediaQueryData.size.width-70)/2,
- margin: const EdgeInsets.only(
- top: 5,
- left: 5,
- bottom: 10
- ),
- child: OptionBox(
- optionsValue: selectedOption,
- itemValue: 168,
- onTap: _updateRadioValue,
- child: Center(
- child: AnimatedDefaultTextStyle(
- duration: const Duration(milliseconds: 250),
- style: TextStyle(
- fontWeight: FontWeight.bold,
- fontSize: 14,
- color: selectedOption == 168
- ? Theme.of(context).colorScheme.onInverseSurface
- : Theme.of(context).colorScheme.onSurface
- ),
- child: Text(AppLocalizations.of(context)!.days7),
- ),
- ),
- ),
- ),
- ],
+ ),
),
],
- ),
+ )
+ ],
+ ),
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 24),
+ child: Wrap(
+ runSpacing: 16,
+ children: [
+ FractionallySizedBox(
+ widthFactor: 0.5,
+ child: Padding(
+ padding: const EdgeInsets.only(right: 6),
+ child: OptionBox(
+ optionsValue: selectedOption,
+ itemValue: 0,
+ onTap: _updateRadioValue,
+ child: Center(
+ child: AnimatedDefaultTextStyle(
+ duration: const Duration(milliseconds: 250),
+ style: TextStyle(
+ fontWeight: FontWeight.bold,
+ fontSize: 14,
+ color: selectedOption == 0
+ ? Theme.of(context).colorScheme.onInverseSurface
+ : Theme.of(context).colorScheme.onSurface
+ ),
+ child: Text(AppLocalizations.of(context)!.never),
+ ),
+ ),
+ ),
+ ),
+ ),
+ FractionallySizedBox(
+ widthFactor: 0.5,
+ child: Padding(
+ padding: const EdgeInsets.only(left: 6),
+ child: OptionBox(
+ optionsValue: selectedOption,
+ itemValue: 1,
+ onTap: _updateRadioValue,
+ child: Center(
+ child: AnimatedDefaultTextStyle(
+ duration: const Duration(milliseconds: 250),
+ style: TextStyle(
+ fontWeight: FontWeight.bold,
+ fontSize: 14,
+ color: selectedOption == 1
+ ? Theme.of(context).colorScheme.onInverseSurface
+ : Theme.of(context).colorScheme.onSurface
+ ),
+ child: Text(AppLocalizations.of(context)!.hour1),
+ ),
+ ),
+ ),
+ ),
+ ),
+ FractionallySizedBox(
+ widthFactor: 0.5,
+ child: Padding(
+ padding: const EdgeInsets.only(right: 6),
+ child: OptionBox(
+ optionsValue: selectedOption,
+ itemValue: 12,
+ onTap: _updateRadioValue,
+ child: Center(
+ child: AnimatedDefaultTextStyle(
+ duration: const Duration(milliseconds: 250),
+ style: TextStyle(
+ fontWeight: FontWeight.bold,
+ fontSize: 14,
+ color: selectedOption == 12
+ ? Theme.of(context).colorScheme.onInverseSurface
+ : Theme.of(context).colorScheme.onSurface
+ ),
+ child: Text(AppLocalizations.of(context)!.hours12),
+ ),
+ ),
+ ),
+ ),
+ ),
+ FractionallySizedBox(
+ widthFactor: 0.5,
+ child: Padding(
+ padding: const EdgeInsets.only(left: 6),
+ child: OptionBox(
+ optionsValue: selectedOption,
+ itemValue: 24,
+ onTap: _updateRadioValue,
+ child: Center(
+ child: AnimatedDefaultTextStyle(
+ duration: const Duration(milliseconds: 250),
+ style: TextStyle(
+ fontWeight: FontWeight.bold,
+ fontSize: 14,
+ color: selectedOption == 24
+ ? Theme.of(context).colorScheme.onInverseSurface
+ : Theme.of(context).colorScheme.onSurface
+ ),
+ child: Text(AppLocalizations.of(context)!.hours24),
+ ),
+ ),
+ ),
+ ),
+ ),
+ FractionallySizedBox(
+ widthFactor: 0.5,
+ child: Padding(
+ padding: const EdgeInsets.only(right: 6),
+ child: OptionBox(
+ optionsValue: selectedOption,
+ itemValue: 72,
+ onTap: _updateRadioValue,
+ child: Center(
+ child: AnimatedDefaultTextStyle(
+ duration: const Duration(milliseconds: 250),
+ style: TextStyle(
+ fontWeight: FontWeight.bold,
+ fontSize: 14,
+ color: selectedOption == 72
+ ? Theme.of(context).colorScheme.onInverseSurface
+ : Theme.of(context).colorScheme.onSurface
+ ),
+ child: Text(AppLocalizations.of(context)!.days3),
+ ),
+ ),
+ ),
+ ),
+ ),
+ FractionallySizedBox(
+ widthFactor: 0.5,
+ child: Padding(
+ padding: const EdgeInsets.only(left: 6),
+ child: OptionBox(
+ optionsValue: selectedOption,
+ itemValue: 168,
+ onTap: _updateRadioValue,
+ child: Center(
+ child: AnimatedDefaultTextStyle(
+ duration: const Duration(milliseconds: 250),
+ style: TextStyle(
+ fontWeight: FontWeight.bold,
+ fontSize: 14,
+ color: selectedOption == 168
+ ? Theme.of(context).colorScheme.onInverseSurface
+ : Theme.of(context).colorScheme.onSurface
+ ),
+ child: Text(AppLocalizations.of(context)!.days7),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ],
),
- ),
+ )
],
),
),
- Padding(
- padding: const EdgeInsets.all(24),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.end,
- children: [
- TextButton(
- onPressed: () => Navigator.pop(context),
- child: Text(AppLocalizations.of(context)!.cancel),
- ),
- const SizedBox(width: 20),
- TextButton(
- onPressed: selectedOption != null
- ? () {
- Navigator.pop(context);
- widget.onChange(selectedOption!);
- }
- : null,
- style: ButtonStyle(
- overlayColor: MaterialStateProperty.all(
- Theme.of(context).colorScheme.primary.withOpacity(0.1)
- ),
- foregroundColor: MaterialStateProperty.all(
- selectedOption != null
- ? Theme.of(context).colorScheme.primary
- : Colors.grey,
- ),
- ),
- child: Text(AppLocalizations.of(context)!.confirm),
- ),
- ],
- ),
+ ),
+ Padding(
+ padding: const EdgeInsets.all(24),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.end,
+ children: [
+ TextButton(
+ onPressed: () => Navigator.pop(context),
+ child: Text(AppLocalizations.of(context)!.cancel),
+ ),
+ const SizedBox(width: 20),
+ TextButton(
+ onPressed: selectedOption != null
+ ? () {
+ Navigator.pop(context);
+ widget.onChange(selectedOption!);
+ }
+ : null,
+ style: ButtonStyle(
+ overlayColor: MaterialStateProperty.all(
+ Theme.of(context).colorScheme.primary.withOpacity(0.1)
+ ),
+ foregroundColor: MaterialStateProperty.all(
+ selectedOption != null
+ ? Theme.of(context).colorScheme.primary
+ : Colors.grey,
+ ),
+ ),
+ child: Text(AppLocalizations.of(context)!.confirm),
+ ),
+ ],
),
- if (Platform.isIOS) const SizedBox(height: 16)
- ],
+ ),
+ if (Platform.isIOS) const SizedBox(height: 16)
+ ],
+ );
+ }
+
+ if (widget.dialog == true) {
+ return Dialog(
+ child: ConstrainedBox(
+ constraints: const BoxConstraints(
+ maxWidth: 500
+ ),
+ child: content()
),
- ),
- );
+ );
+ }
+ else {
+ return Padding(
+ padding: mediaQueryData.viewInsets,
+ child: Container(
+ height: Platform.isIOS ? 406 : 390,
+ decoration: BoxDecoration(
+ color: Theme.of(context).dialogBackgroundColor,
+ borderRadius: const BorderRadius.only(
+ topLeft: Radius.circular(28),
+ topRight: Radius.circular(28)
+ ),
+ ),
+ child: content()
+ ),
+ );
+ }
}
}
\ No newline at end of file
diff --git a/lib/screens/home/chart.dart b/lib/screens/home/chart.dart
index 315d421..2ea4535 100644
--- a/lib/screens/home/chart.dart
+++ b/lib/screens/home/chart.dart
@@ -48,12 +48,15 @@ class HomeChart extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
- Text(
- label,
- style: TextStyle(
- fontSize: 18,
- fontWeight: FontWeight.w500,
- color: Theme.of(context).colorScheme.onSurface
+ Flexible(
+ child: Text(
+ label,
+ overflow: TextOverflow.ellipsis,
+ style: TextStyle(
+ fontSize: 18,
+ fontWeight: FontWeight.w500,
+ color: Theme.of(context).colorScheme.onSurface
+ ),
),
),
!isEmpty
diff --git a/lib/screens/home/fab.dart b/lib/screens/home/fab.dart
index ce5f8a9..1369535 100644
--- a/lib/screens/home/fab.dart
+++ b/lib/screens/home/fab.dart
@@ -12,13 +12,27 @@ class HomeFab extends StatelessWidget {
Widget build(BuildContext context) {
final serversProvider = Provider.of(context);
+ final width = MediaQuery.of(context).size.width;
+
void openManagementBottomSheet() {
- showModalBottomSheet(
- context: context,
- isScrollControlled: true,
- builder: (context) => const ManagementModal(),
- backgroundColor: Colors.transparent,
- );
+ if (width > 700) {
+ showDialog(
+ context: context,
+ builder: (context) => const ManagementModal(
+ dialog: true,
+ ),
+ );
+ }
+ else {
+ showModalBottomSheet(
+ context: context,
+ isScrollControlled: true,
+ builder: (context) => const ManagementModal(
+ dialog: false,
+ ),
+ backgroundColor: Colors.transparent,
+ );
+ }
}
return serversProvider.serverStatus.loadStatus == 1
diff --git a/lib/screens/home/home.dart b/lib/screens/home/home.dart
index 932ddfe..541f732 100644
--- a/lib/screens/home/home.dart
+++ b/lib/screens/home/home.dart
@@ -55,6 +55,8 @@ class _HomeState extends State {
final serversProvider = Provider.of(context);
final appConfigProvider = Provider.of(context);
+ final width = MediaQuery.of(context).size.width;
+
Widget status() {
switch (serversProvider.serverStatus.loadStatus) {
case 0:
@@ -92,72 +94,138 @@ class _HomeState extends State {
),
const SizedBox(height: 20),
- HomeChart(
- data: serversProvider.serverStatus.data!.stats.dnsQueries,
- label: AppLocalizations.of(context)!.dnsQueries,
- primaryValue: intFormat(serversProvider.serverStatus.data!.stats.numDnsQueries, Platform.localeName),
- secondaryValue: "${doubleFormat(serversProvider.serverStatus.data!.stats.avgProcessingTime*1000, Platform.localeName)} ms",
- color: Colors.blue,
- ),
-
- HomeChart(
- data: serversProvider.serverStatus.data!.stats.blockedFiltering,
- label: AppLocalizations.of(context)!.blockedFilters,
- primaryValue: intFormat(serversProvider.serverStatus.data!.stats.numBlockedFiltering, Platform.localeName),
- secondaryValue: "${serversProvider.serverStatus.data!.stats.numDnsQueries > 0 ? doubleFormat((serversProvider.serverStatus.data!.stats.numBlockedFiltering/serversProvider.serverStatus.data!.stats.numDnsQueries)*100, Platform.localeName) : 0}%",
- color: Colors.red,
+ Wrap(
+ children: [
+ FractionallySizedBox(
+ widthFactor: width > 700 ? 0.5 : 1,
+ child: HomeChart(
+ data: serversProvider.serverStatus.data!.stats.dnsQueries,
+ label: AppLocalizations.of(context)!.dnsQueries,
+ primaryValue: intFormat(serversProvider.serverStatus.data!.stats.numDnsQueries, Platform.localeName),
+ secondaryValue: "${doubleFormat(serversProvider.serverStatus.data!.stats.avgProcessingTime*1000, Platform.localeName)} ms",
+ color: Colors.blue,
+ ),
+ ),
+ FractionallySizedBox(
+ widthFactor: width > 700 ? 0.5 : 1,
+ child: HomeChart(
+ data: serversProvider.serverStatus.data!.stats.blockedFiltering,
+ label: AppLocalizations.of(context)!.blockedFilters,
+ primaryValue: intFormat(serversProvider.serverStatus.data!.stats.numBlockedFiltering, Platform.localeName),
+ secondaryValue: "${serversProvider.serverStatus.data!.stats.numDnsQueries > 0 ? doubleFormat((serversProvider.serverStatus.data!.stats.numBlockedFiltering/serversProvider.serverStatus.data!.stats.numDnsQueries)*100, Platform.localeName) : 0}%",
+ color: Colors.red,
+ ),
+ ),
+ FractionallySizedBox(
+ widthFactor: width > 700 ? 0.5 : 1,
+ child: HomeChart(
+ data: serversProvider.serverStatus.data!.stats.replacedSafebrowsing,
+ label: AppLocalizations.of(context)!.malwarePhisingBlocked,
+ primaryValue: intFormat(serversProvider.serverStatus.data!.stats.numReplacedSafebrowsing, Platform.localeName),
+ secondaryValue: "${serversProvider.serverStatus.data!.stats.numDnsQueries > 0 ? doubleFormat((serversProvider.serverStatus.data!.stats.numReplacedSafebrowsing/serversProvider.serverStatus.data!.stats.numDnsQueries)*100, Platform.localeName) : 0}%",
+ color: Colors.green,
+ ),
+ ),
+ FractionallySizedBox(
+ widthFactor: width > 700 ? 0.5 : 1,
+ child: HomeChart(
+ data: serversProvider.serverStatus.data!.stats.replacedParental,
+ label: AppLocalizations.of(context)!.blockedAdultWebsites,
+ primaryValue: intFormat(serversProvider.serverStatus.data!.stats.numReplacedParental, Platform.localeName),
+ secondaryValue: "${serversProvider.serverStatus.data!.stats.numDnsQueries > 0 ? doubleFormat((serversProvider.serverStatus.data!.stats.numReplacedParental/serversProvider.serverStatus.data!.stats.numDnsQueries)*100, Platform.localeName) : 0}%",
+ color: Colors.orange,
+ ),
+ ),
+
+ ],
),
- HomeChart(
- data: serversProvider.serverStatus.data!.stats.replacedSafebrowsing,
- label: AppLocalizations.of(context)!.malwarePhisingBlocked,
- primaryValue: intFormat(serversProvider.serverStatus.data!.stats.numReplacedSafebrowsing, Platform.localeName),
- secondaryValue: "${serversProvider.serverStatus.data!.stats.numDnsQueries > 0 ? doubleFormat((serversProvider.serverStatus.data!.stats.numReplacedSafebrowsing/serversProvider.serverStatus.data!.stats.numDnsQueries)*100, Platform.localeName) : 0}%",
- color: Colors.green,
- ),
-
- HomeChart(
- data: serversProvider.serverStatus.data!.stats.replacedParental,
- label: AppLocalizations.of(context)!.blockedAdultWebsites,
- primaryValue: intFormat(serversProvider.serverStatus.data!.stats.numReplacedParental, Platform.localeName),
- secondaryValue: "${serversProvider.serverStatus.data!.stats.numDnsQueries > 0 ? doubleFormat((serversProvider.serverStatus.data!.stats.numReplacedParental/serversProvider.serverStatus.data!.stats.numDnsQueries)*100, Platform.localeName) : 0}%",
- color: Colors.orange,
- ),
-
- TopItems(
- label: AppLocalizations.of(context)!.topQueriedDomains,
- data: serversProvider.serverStatus.data!.stats.topQueriedDomains,
- type: 'topQueriedDomains',
- ),
- Padding(
- padding: const EdgeInsets.symmetric(horizontal: 20),
- child: Divider(
- thickness: 1,
- color: Theme.of(context).colorScheme.onSurface.withOpacity(0.2),
+ if (width <= 700) ...[
+ TopItems(
+ label: AppLocalizations.of(context)!.topQueriedDomains,
+ data: serversProvider.serverStatus.data!.stats.topQueriedDomains,
+ type: 'topQueriedDomains',
),
- ),
- const SizedBox(height: 20),
-
- TopItems(
- label: AppLocalizations.of(context)!.topBlockedDomains,
- data: serversProvider.serverStatus.data!.stats.topBlockedDomains,
- type: 'topBlockedDomains',
- ),
- Padding(
- padding: const EdgeInsets.symmetric(horizontal: 20),
- child: Divider(
- thickness: 1,
- color: Theme.of(context).colorScheme.onSurface.withOpacity(0.2),
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 20),
+ child: Divider(
+ thickness: 1,
+ color: Theme.of(context).colorScheme.onSurface.withOpacity(0.2),
+ ),
),
- ),
- const SizedBox(height: 20),
- TopItems(
- label: AppLocalizations.of(context)!.topClients,
- data: serversProvider.serverStatus.data!.stats.topClients,
- type: 'topClients',
- clients: true,
- ),
+ const SizedBox(height: 20),
+
+ TopItems(
+ label: AppLocalizations.of(context)!.topBlockedDomains,
+ data: serversProvider.serverStatus.data!.stats.topBlockedDomains,
+ type: 'topBlockedDomains',
+ ),
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 20),
+ child: Divider(
+ thickness: 1,
+ color: Theme.of(context).colorScheme.onSurface.withOpacity(0.2),
+ ),
+ ),
+ const SizedBox(height: 20),
+
+ TopItems(
+ label: AppLocalizations.of(context)!.topClients,
+ data: serversProvider.serverStatus.data!.stats.topClients,
+ type: 'topClients',
+ clients: true,
+ ),
+ ],
+ if (width > 700) Column(
+ children: [
+ const SizedBox(height: 16),
+ Wrap(
+ alignment: WrapAlignment.center,
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(bottom: 16),
+ child: ConstrainedBox(
+ constraints: const BoxConstraints(
+ maxWidth: 500
+ ),
+ child: TopItems(
+ label: AppLocalizations.of(context)!.topQueriedDomains,
+ data: serversProvider.serverStatus.data!.stats.topQueriedDomains,
+ type: 'topQueriedDomains',
+ ),
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.only(bottom: 16),
+ child: ConstrainedBox(
+ constraints: const BoxConstraints(
+ maxWidth: 500
+ ),
+ child: TopItems(
+ label: AppLocalizations.of(context)!.topBlockedDomains,
+ data: serversProvider.serverStatus.data!.stats.topBlockedDomains,
+ type: 'topBlockedDomains',
+ ),
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.only(bottom: 16),
+ child: ConstrainedBox(
+ constraints: const BoxConstraints(
+ maxWidth: 500
+ ),
+ child: TopItems(
+ label: AppLocalizations.of(context)!.topBlockedDomains,
+ data: serversProvider.serverStatus.data!.stats.topBlockedDomains,
+ type: 'topBlockedDomains',
+ ),
+ ),
+ ),
+ ],
+ ),
+ ],
+ )
],
);
diff --git a/lib/screens/home/management_modal.dart b/lib/screens/home/management_modal.dart
index e36fbc7..15eac57 100644
--- a/lib/screens/home/management_modal.dart
+++ b/lib/screens/home/management_modal.dart
@@ -15,7 +15,12 @@ import 'package:adguard_home_manager/services/http_requests.dart';
import 'package:adguard_home_manager/providers/servers_provider.dart';
class ManagementModal extends StatefulWidget {
- const ManagementModal({Key? key}) : super(key: key);
+ final bool dialog;
+
+ const ManagementModal({
+ Key? key,
+ required this.dialog
+ }) : super(key: key);
@override
State createState() => _ManagementModalState();
@@ -364,8 +369,112 @@ class _ManagementModalState extends State with SingleTickerProv
);
}
- return SafeArea(
- child: Container(
+ Widget header() {
+ return Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(top: 24),
+ child: Icon(
+ Icons.shield_rounded,
+ size: 24,
+ color: Theme.of(context).listTileTheme.iconColor
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.symmetric(vertical: 16),
+ child: Text(
+ AppLocalizations.of(context)!.manageServer,
+ textAlign: TextAlign.center,
+ style: TextStyle(
+ fontSize: 24,
+ color: Theme.of(context).colorScheme.onSurface,
+ ),
+ ),
+ ),
+ ],
+ ),
+ ],
+ );
+ }
+
+ List toggles() {
+ return [
+ mainSwitch(),
+ Container(height: 10),
+ smallSwitch(
+ AppLocalizations.of(context)!.ruleFiltering,
+ Icons.filter_list_rounded,
+ serversProvider.serverStatus.data!.filteringEnabled,
+ (value) => updateBlocking(value: value, filter: 'filtering'),
+ serversProvider.protectionsManagementProcess.contains('filtering')
+ ),
+ smallSwitch(
+ AppLocalizations.of(context)!.safeBrowsing,
+ Icons.vpn_lock_rounded,
+ serversProvider.serverStatus.data!.safeBrowsingEnabled,
+ (value) => updateBlocking(value: value, filter: 'safeBrowsing'),
+ serversProvider.protectionsManagementProcess.contains('safeBrowsing')
+ ),
+ smallSwitch(
+ AppLocalizations.of(context)!.parentalFiltering,
+ Icons.block,
+ serversProvider.serverStatus.data!.parentalControlEnabled,
+ (value) => updateBlocking(value: value, filter: 'parentalControl'),
+ serversProvider.protectionsManagementProcess.contains('parentalControl')
+ ),
+ smallSwitch(
+ AppLocalizations.of(context)!.safeSearch,
+ Icons.search_rounded,
+ serversProvider.serverStatus.data!.safeSearchEnabled,
+ (value) => updateBlocking(value: value, filter: 'safeSearch'),
+ serversProvider.protectionsManagementProcess.contains('safeSearch')
+ ),
+ ];
+ }
+
+ if (widget.dialog == true) {
+ return Dialog(
+ child: ConstrainedBox(
+ constraints: const BoxConstraints(
+ maxWidth: 400
+ ),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Flexible(
+ child: SingleChildScrollView(
+ child: Wrap(
+ children: [
+ header(),
+ ...toggles()
+ ],
+ ),
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.all(24),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.end,
+ children: [
+ TextButton(
+ onPressed: () => Navigator.pop(context),
+ child: Text(AppLocalizations.of(context)!.close),
+ ),
+ ],
+ ),
+ ),
+ if (Platform.isIOS) const SizedBox(height: 16)
+ ],
+ ),
+ ),
+ );
+ }
+ else {
+ return Container(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
borderRadius: const BorderRadius.only(
@@ -373,66 +482,18 @@ class _ManagementModalState extends State with SingleTickerProv
topRight: Radius.circular(28)
)
),
- child: Wrap(
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
children: [
- Row(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- Column(
- crossAxisAlignment: CrossAxisAlignment.center,
+ Flexible(
+ child: SingleChildScrollView(
+ child: Wrap(
children: [
- Padding(
- padding: const EdgeInsets.only(top: 24),
- child: Icon(
- Icons.shield_rounded,
- size: 24,
- color: Theme.of(context).listTileTheme.iconColor
- ),
- ),
- Padding(
- padding: const EdgeInsets.symmetric(vertical: 16),
- child: Text(
- AppLocalizations.of(context)!.manageServer,
- textAlign: TextAlign.center,
- style: TextStyle(
- fontSize: 24,
- color: Theme.of(context).colorScheme.onSurface,
- ),
- ),
- ),
+ header(),
+ ...toggles()
],
),
- ],
- ),
- mainSwitch(),
- const SizedBox(height: 10),
- smallSwitch(
- AppLocalizations.of(context)!.ruleFiltering,
- Icons.filter_list_rounded,
- serversProvider.serverStatus.data!.filteringEnabled,
- (value) => updateBlocking(value: value, filter: 'filtering'),
- serversProvider.protectionsManagementProcess.contains('filtering')
- ),
- smallSwitch(
- AppLocalizations.of(context)!.safeBrowsing,
- Icons.vpn_lock_rounded,
- serversProvider.serverStatus.data!.safeBrowsingEnabled,
- (value) => updateBlocking(value: value, filter: 'safeBrowsing'),
- serversProvider.protectionsManagementProcess.contains('safeBrowsing')
- ),
- smallSwitch(
- AppLocalizations.of(context)!.parentalFiltering,
- Icons.block,
- serversProvider.serverStatus.data!.parentalControlEnabled,
- (value) => updateBlocking(value: value, filter: 'parentalControl'),
- serversProvider.protectionsManagementProcess.contains('parentalControl')
- ),
- smallSwitch(
- AppLocalizations.of(context)!.safeSearch,
- Icons.search_rounded,
- serversProvider.serverStatus.data!.safeSearchEnabled,
- (value) => updateBlocking(value: value, filter: 'safeSearch'),
- serversProvider.protectionsManagementProcess.contains('safeSearch')
+ ),
),
Padding(
padding: const EdgeInsets.all(24),
@@ -449,7 +510,7 @@ class _ManagementModalState extends State with SingleTickerProv
if (Platform.isIOS) const SizedBox(height: 16)
],
),
- ),
- );
+ );
+ }
}
}
\ No newline at end of file
diff --git a/lib/screens/home/server_status.dart b/lib/screens/home/server_status.dart
index c4d8d97..1df9767 100644
--- a/lib/screens/home/server_status.dart
+++ b/lib/screens/home/server_status.dart
@@ -15,8 +15,12 @@ class ServerStatus extends StatelessWidget {
@override
Widget build(BuildContext context) {
+ final width = MediaQuery.of(context).size.width;
+
return Container(
- padding: const EdgeInsets.all(20),
+ padding: width > 700
+ ? const EdgeInsets.only(left: 20, right: 20, bottom: 20)
+ : const EdgeInsets.all(20),
child: Column(
children: [
Text(
@@ -29,11 +33,11 @@ class ServerStatus extends StatelessWidget {
),
const SizedBox(height: 20),
SizedBox(
- height: 140,
+ height: width > 700 ? 70 : 140,
child: GridView(
physics: const NeverScrollableScrollPhysics(),
- gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
- crossAxisCount: 2,
+ gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
+ crossAxisCount: width > 700 ? 4 : 2,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
mainAxisExtent: 65
diff --git a/lib/screens/home/status_box.dart b/lib/screens/home/status_box.dart
index 62d4279..2f57106 100644
--- a/lib/screens/home/status_box.dart
+++ b/lib/screens/home/status_box.dart
@@ -44,13 +44,15 @@ class StatusBox extends StatelessWidget {
: Colors.grey.computeLuminance() > 0.5 ? Colors.black : Colors.white,
),
const SizedBox(width: 12),
- Text(
- label,
- style: TextStyle(
- color: appConfigProvider.useThemeColorForStatus == true
- ? Theme.of(context).colorScheme.primary.computeLuminance() > 0.5 ? Colors.black : Colors.white
- : Colors.grey.computeLuminance() > 0.5 ? Colors.black : Colors.white,
- fontWeight: FontWeight.w500
+ Flexible(
+ child: Text(
+ label,
+ style: TextStyle(
+ color: appConfigProvider.useThemeColorForStatus == true
+ ? Theme.of(context).colorScheme.primary.computeLuminance() > 0.5 ? Colors.black : Colors.white
+ : Colors.grey.computeLuminance() > 0.5 ? Colors.black : Colors.white,
+ fontWeight: FontWeight.w500
+ ),
),
)
],
diff --git a/lib/screens/home/top_items.dart b/lib/screens/home/top_items.dart
index 5883523..0bf85d2 100644
--- a/lib/screens/home/top_items.dart
+++ b/lib/screens/home/top_items.dart
@@ -1,5 +1,9 @@
// ignore_for_file: use_build_context_synchronously
+import 'dart:io';
+
+import 'package:adguard_home_manager/screens/top_items/top_items_modal.dart';
+import 'package:animations/animations.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
@@ -35,6 +39,8 @@ class TopItems extends StatelessWidget {
final appConfigProvider = Provider.of(context);
final logsProvider = Provider.of(context);
+ final width = MediaQuery.of(context).size.width;
+
bool? getIsBlocked() {
if (type == 'topBlockedDomains') {
return true;
@@ -266,16 +272,32 @@ class TopItems extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
- onPressed: () => Navigator.of(context).push(
- MaterialPageRoute(
- builder: (context) => TopItemsScreen(
- type: type,
- title: label,
- isClient: clients,
- data: generateData(),
+ onPressed: () => {
+ if (width > 700 || !(Platform.isAndroid || Platform.isIOS)) {
+ showDialog(
+ context: context,
+ barrierDismissible: false,
+ builder: (context) => TopItemsModal(
+ type: type,
+ title: label,
+ isClient: clients,
+ data: generateData(),
+ )
)
- )
- ),
+ }
+ else {
+ Navigator.of(context).push(
+ MaterialPageRoute(
+ builder: (context) => TopItemsScreen(
+ type: type,
+ title: label,
+ isClient: clients,
+ data: generateData(),
+ )
+ )
+ )
+ }
+ },
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
diff --git a/lib/screens/logs/clients_modal.dart b/lib/screens/logs/clients_modal.dart
index f9b9099..bd39541 100644
--- a/lib/screens/logs/clients_modal.dart
+++ b/lib/screens/logs/clients_modal.dart
@@ -8,10 +8,12 @@ import 'package:adguard_home_manager/providers/logs_provider.dart';
class ClientsModal extends StatefulWidget {
final List? value;
+ final bool dialog;
const ClientsModal({
Key? key,
- required this.value
+ required this.value,
+ required this.dialog
}) : super(key: key);
@override
@@ -94,44 +96,36 @@ class _ClientsModalState extends State {
});
}
- return Container(
- height: height >= (logsProvider.clients!.length*64) == true
- ? logsProvider.clients!.length*64
- : height-50,
- decoration: BoxDecoration(
- borderRadius: const BorderRadius.only(
- topLeft: Radius.circular(28),
- topRight: Radius.circular(28)
- ),
- color: Theme.of(context).dialogBackgroundColor
- ),
- child: Column(
+ Widget content() {
+ return Column(
+ mainAxisSize: MainAxisSize.min,
children: [
- Padding(
- padding: const EdgeInsets.only(
- top: 24,
- bottom: 16,
- ),
- child: Icon(
- Icons.smartphone_rounded,
- size: 24,
- color: Theme.of(context).listTileTheme.iconColor
- ),
+ Column(
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(
+ top: 24,
+ bottom: 16,
+ ),
+ child: Icon(
+ Icons.smartphone_rounded,
+ size: 24,
+ color: Theme.of(context).listTileTheme.iconColor
+ ),
+ ),
+ Text(
+ AppLocalizations.of(context)!.clients,
+ style: TextStyle(
+ fontSize: 24,
+ fontWeight: FontWeight.w400,
+ color: Theme.of(context).colorScheme.onSurface
+ ),
+ ),
+ const SizedBox(height: 16),
+ ],
),
- Text(
- AppLocalizations.of(context)!.clients,
- style: TextStyle(
- fontSize: 24,
- fontWeight: FontWeight.w400,
- color: Theme.of(context).colorScheme.onSurface
- ),
- ),
- const SizedBox(height: 16),
- Expanded(
+ Flexible(
child: ListView.builder(
- physics: height >= (logsProvider.clients!.length*64) == true
- ? const NeverScrollableScrollPhysics()
- : null,
itemCount: logsProvider.clients!.length,
itemBuilder: (context, index) => listItem(
label: logsProvider.clients![index].ip,
@@ -150,7 +144,7 @@ class _ClientsModalState extends State {
}
}
)
- ),
+ )
),
Padding(
padding: const EdgeInsets.all(24),
@@ -176,7 +170,35 @@ class _ClientsModalState extends State {
),
if (Platform.isIOS) const SizedBox(height: 16)
],
- ),
- );
+ );
+ }
+
+ if (widget.dialog == true) {
+ return Dialog(
+ child: ConstrainedBox(
+ constraints: const BoxConstraints(
+ maxWidth: 500
+ ),
+ child: content()
+ ),
+ );
+ }
+ else {
+ return ConstrainedBox(
+ constraints: BoxConstraints(
+ maxHeight: height-50
+ ),
+ child: Container(
+ decoration: BoxDecoration(
+ borderRadius: const BorderRadius.only(
+ topLeft: Radius.circular(28),
+ topRight: Radius.circular(28)
+ ),
+ color: Theme.of(context).dialogBackgroundColor
+ ),
+ child: content()
+ ),
+ );
+ }
}
}
\ No newline at end of file
diff --git a/lib/screens/logs/filter_status_modal.dart b/lib/screens/logs/filter_status_modal.dart
index 16d9c5c..aec5aac 100644
--- a/lib/screens/logs/filter_status_modal.dart
+++ b/lib/screens/logs/filter_status_modal.dart
@@ -8,10 +8,12 @@ import 'package:adguard_home_manager/providers/logs_provider.dart';
class FilterStatusModal extends StatefulWidget {
final String value;
+ final bool dialog;
const FilterStatusModal({
Key? key,
- required this.value
+ required this.value,
+ required this.dialog
}) : super(key: key);
@override
@@ -31,8 +33,6 @@ class _FilterStatusModalState extends State {
Widget build(BuildContext context) {
final logsProvider = Provider.of(context);
- final height = MediaQuery.of(context).size.height;
-
void apply() async {
logsProvider.setSelectedResultStatus(selectedResultStatus);
@@ -83,95 +83,94 @@ class _FilterStatusModalState extends State {
);
}
- return Container(
- height: height >= (Platform.isIOS ? 736 : 720) == true
- ? (Platform.isIOS ? 736 : 720)
- : height-25,
- decoration: BoxDecoration(
- borderRadius: const BorderRadius.only(
- topLeft: Radius.circular(28),
- topRight: Radius.circular(28)
- ),
- color: Theme.of(context).dialogBackgroundColor
- ),
- child: Column(
+ Widget content() {
+ return Column(
+ mainAxisSize: MainAxisSize.min,
children: [
- Padding(
- padding: const EdgeInsets.only(
- top: 24,
- bottom: 16,
- ),
- child: Icon(
- Icons.shield_rounded,
- size: 24,
- color: Theme.of(context).listTileTheme.iconColor
- ),
- ),
- Text(
- AppLocalizations.of(context)!.responseStatus,
- style: TextStyle(
- fontSize: 24,
- fontWeight: FontWeight.w400,
- color: Theme.of(context).colorScheme.onSurface
- ),
- ),
- const SizedBox(height: 16),
- Expanded(
- child: ListView(
- physics: height >= 720 == true
- ? const NeverScrollableScrollPhysics()
- : null,
- children: [
- filterStatusListItem(
- id: "all",
- icon: Icons.shield_rounded,
- label: AppLocalizations.of(context)!.all,
- onChanged: (value) => setState(() => selectedResultStatus = value!)
- ),
- filterStatusListItem(
- id: "filtered",
- icon: Icons.shield_rounded,
- label: AppLocalizations.of(context)!.filtered,
- onChanged: (value) => setState(() => selectedResultStatus = value!)
- ),
- filterStatusListItem(
- id: "processed",
- icon: Icons.verified_user_rounded,
- label: AppLocalizations.of(context)!.processedRow,
- onChanged: (value) => setState(() => selectedResultStatus = value!)
- ),
- filterStatusListItem(
- id: "whitelisted",
- icon: Icons.verified_user_rounded,
- label: AppLocalizations.of(context)!.processedWhitelistRow,
- onChanged: (value) => setState(() => selectedResultStatus = value!)
- ),
- filterStatusListItem(
- id: "blocked",
- icon: Icons.gpp_bad_rounded,
- label: AppLocalizations.of(context)!.blocked,
- onChanged: (value) => setState(() => selectedResultStatus = value!)
- ),
- filterStatusListItem(
- id: "blocked_safebrowsing",
- icon: Icons.gpp_bad_rounded,
- label: AppLocalizations.of(context)!.blockedSafeBrowsingRow,
- onChanged: (value) => setState(() => selectedResultStatus = value!)
- ),
- filterStatusListItem(
- id: "blocked_parental",
- icon: Icons.gpp_bad_rounded,
- label: AppLocalizations.of(context)!.blockedParentalRow,
- onChanged: (value) => setState(() => selectedResultStatus = value!)
- ),
- filterStatusListItem(
- id: "safe_search",
- icon: Icons.gpp_bad_rounded,
- label: AppLocalizations.of(context)!.blockedSafeSearchRow,
- onChanged: (value) => setState(() => selectedResultStatus = value!)
- ),
-
- ],
+ Flexible(
+ child: SingleChildScrollView(
+ child: Wrap(
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Column(
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(
+ top: 24,
+ bottom: 16,
+ ),
+ child: Icon(
+ Icons.shield_rounded,
+ size: 24,
+ color: Theme.of(context).listTileTheme.iconColor
+ ),
+ ),
+ Text(
+ AppLocalizations.of(context)!.responseStatus,
+ style: TextStyle(
+ fontSize: 24,
+ fontWeight: FontWeight.w400,
+ color: Theme.of(context).colorScheme.onSurface
+ ),
+ ),
+ ],
+ )
+ ],
+ ),
+ Container(height: 16),
+ filterStatusListItem(
+ id: "all",
+ icon: Icons.shield_rounded,
+ label: AppLocalizations.of(context)!.all,
+ onChanged: (value) => setState(() => selectedResultStatus = value!)
+ ),
+ filterStatusListItem(
+ id: "filtered",
+ icon: Icons.shield_rounded,
+ label: AppLocalizations.of(context)!.filtered,
+ onChanged: (value) => setState(() => selectedResultStatus = value!)
+ ),
+ filterStatusListItem(
+ id: "processed",
+ icon: Icons.verified_user_rounded,
+ label: AppLocalizations.of(context)!.processedRow,
+ onChanged: (value) => setState(() => selectedResultStatus = value!)
+ ),
+ filterStatusListItem(
+ id: "whitelisted",
+ icon: Icons.verified_user_rounded,
+ label: AppLocalizations.of(context)!.processedWhitelistRow,
+ onChanged: (value) => setState(() => selectedResultStatus = value!)
+ ),
+ filterStatusListItem(
+ id: "blocked",
+ icon: Icons.gpp_bad_rounded,
+ label: AppLocalizations.of(context)!.blocked,
+ onChanged: (value) => setState(() => selectedResultStatus = value!)
+ ),
+ filterStatusListItem(
+ id: "blocked_safebrowsing",
+ icon: Icons.gpp_bad_rounded,
+ label: AppLocalizations.of(context)!.blockedSafeBrowsingRow,
+ onChanged: (value) => setState(() => selectedResultStatus = value!)
+ ),
+ filterStatusListItem(
+ id: "blocked_parental",
+ icon: Icons.gpp_bad_rounded,
+ label: AppLocalizations.of(context)!.blockedParentalRow,
+ onChanged: (value) => setState(() => selectedResultStatus = value!)
+ ),
+ filterStatusListItem(
+ id: "safe_search",
+ icon: Icons.gpp_bad_rounded,
+ label: AppLocalizations.of(context)!.blockedSafeSearchRow,
+ onChanged: (value) => setState(() => selectedResultStatus = value!)
+ ),
+
+ ],
+ ),
),
),
Padding(
@@ -188,7 +187,30 @@ class _FilterStatusModalState extends State {
),
if (Platform.isIOS) const SizedBox(height: 16)
],
- ),
- );
+ );
+ }
+
+ if (widget.dialog == true) {
+ return Dialog(
+ child: ConstrainedBox(
+ constraints: const BoxConstraints(
+ maxWidth: 400
+ ),
+ child: content()
+ ),
+ );
+ }
+ else {
+ return Container(
+ decoration: BoxDecoration(
+ borderRadius: const BorderRadius.only(
+ topLeft: Radius.circular(28),
+ topRight: Radius.circular(28)
+ ),
+ color: Theme.of(context).dialogBackgroundColor
+ ),
+ child: content()
+ );
+ }
}
}
\ No newline at end of file
diff --git a/lib/screens/logs/log_details_screen.dart b/lib/screens/logs/log_details_screen.dart
index a6960a1..5ee50b7 100644
--- a/lib/screens/logs/log_details_screen.dart
+++ b/lib/screens/logs/log_details_screen.dart
@@ -19,10 +19,12 @@ import 'package:adguard_home_manager/providers/app_config_provider.dart';
class LogDetailsScreen extends StatelessWidget {
final Log log;
+ final bool dialog;
const LogDetailsScreen({
Key? key,
- required this.log
+ required this.log,
+ required this.dialog
}) : super(key: key);
@override
@@ -105,25 +107,8 @@ class LogDetailsScreen extends StatelessWidget {
}
}
- return Scaffold(
- appBar: AppBar(
- title: Text(AppLocalizations.of(context)!.logDetails),
- actions: [
- IconButton(
- onPressed: () => blockUnblock(log, getFilteredStatus(context, appConfigProvider, log.reason, true)['filtered'] == true ? 'unblock' : 'block'),
- icon: Icon(
- getFilteredStatus(context, appConfigProvider, log.reason, true)['filtered'] == true
- ? Icons.check_circle_rounded
- : Icons.block
- ),
- tooltip: getFilteredStatus(context, appConfigProvider, log.reason, true)['filtered'] == true
- ? AppLocalizations.of(context)!.unblockDomain
- : AppLocalizations.of(context)!.blockDomain,
- ),
- const SizedBox(width: 10)
- ],
- ),
- body: ListView(
+ Widget content() {
+ return ListView(
children: [
SectionLabel(label: AppLocalizations.of(context)!.status),
LogListTile(
@@ -247,7 +232,87 @@ class LogDetailsScreen extends StatelessWidget {
)).toList()
]
],
- ),
- );
+ );
+ }
+
+ if (dialog) {
+ return Dialog(
+ child: ConstrainedBox(
+ constraints: const BoxConstraints(
+ maxWidth: 500
+ ),
+ child: Column(
+ children: [
+ Padding(
+ padding: const EdgeInsets.all(16),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Row(
+ children: [
+ IconButton(
+ onPressed: () => Navigator.pop(context),
+ icon: const Icon(Icons.clear_rounded)
+ ),
+ const SizedBox(width: 16),
+ Text(
+ AppLocalizations.of(context)!.logDetails,
+ style: const TextStyle(
+ fontSize: 22
+ ),
+ ),
+ ],
+ ),
+ Row(
+ children: [
+ IconButton(
+ onPressed: () => blockUnblock(log, getFilteredStatus(context, appConfigProvider, log.reason, true)['filtered'] == true ? 'unblock' : 'block'),
+ icon: Icon(
+ getFilteredStatus(context, appConfigProvider, log.reason, true)['filtered'] == true
+ ? Icons.check_circle_rounded
+ : Icons.block
+ ),
+ tooltip: getFilteredStatus(context, appConfigProvider, log.reason, true)['filtered'] == true
+ ? AppLocalizations.of(context)!.unblockDomain
+ : AppLocalizations.of(context)!.blockDomain,
+ ),
+ ],
+ )
+ ],
+ ),
+ ),
+ Expanded(
+ child: Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 8),
+ child: content(),
+ ),
+ )
+ ],
+ ),
+ ),
+ );
+ }
+ else {
+ return Scaffold(
+ appBar: AppBar(
+ title: Text(AppLocalizations.of(context)!.logDetails),
+ actions: [
+ IconButton(
+ onPressed: () => blockUnblock(log, getFilteredStatus(context, appConfigProvider, log.reason, true)['filtered'] == true ? 'unblock' : 'block'),
+ icon: Icon(
+ getFilteredStatus(context, appConfigProvider, log.reason, true)['filtered'] == true
+ ? Icons.check_circle_rounded
+ : Icons.block
+ ),
+ tooltip: getFilteredStatus(context, appConfigProvider, log.reason, true)['filtered'] == true
+ ? AppLocalizations.of(context)!.unblockDomain
+ : AppLocalizations.of(context)!.blockDomain,
+ ),
+ const SizedBox(width: 10)
+ ],
+ ),
+ body: content()
+ );
+ }
}
}
\ No newline at end of file
diff --git a/lib/screens/logs/log_tile.dart b/lib/screens/logs/log_tile.dart
index ff7b058..a4c082f 100644
--- a/lib/screens/logs/log_tile.dart
+++ b/lib/screens/logs/log_tile.dart
@@ -1,10 +1,10 @@
// ignore_for_file: use_build_context_synchronously
+
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/home/top_items_options_modal.dart';
-import 'package:adguard_home_manager/screens/logs/log_details_screen.dart';
import 'package:adguard_home_manager/functions/copy_clipboard.dart';
import 'package:adguard_home_manager/functions/block_unblock_domain.dart';
@@ -18,12 +18,18 @@ class LogTile extends StatelessWidget {
final Log log;
final int length;
final int index;
+ final bool? isLogSelected;
+ final void Function(Log) onLogTap;
+ final bool? useAlwaysNormalTile;
const LogTile({
Key? key,
required this.log,
required this.length,
- required this.index
+ required this.index,
+ this.isLogSelected,
+ required this.onLogTap,
+ this.useAlwaysNormalTile
}) : super(key: key);
@override
@@ -38,7 +44,7 @@ class LogTile extends StatelessWidget {
required String text
}) {
return SizedBox(
- width: 70,
+ width: 80,
child: Column(
children: [
Icon(
@@ -95,133 +101,250 @@ class LogTile extends StatelessWidget {
)
);
}
-
- return Material(
- color: Colors.transparent,
- child: InkWell(
- onTap: () => Navigator.push(context, MaterialPageRoute(
- builder: (context) => LogDetailsScreen(log: log)
- )),
- onLongPress: () => openOptionsModal(log),
- child: Container(
- width: double.maxFinite,
- padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- SizedBox(
- width: width-130,
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- log.question.name,
- style: TextStyle(
- fontSize: 16,
- height: 1.5,
- fontWeight: FontWeight.w400,
- color: Theme.of(context).colorScheme.onSurface
- ),
- ),
- const SizedBox(height: 5),
- if (log.client.length <= 15 && appConfigProvider.showNameTimeLogs == false) Row(
+
+ if (width > 1100 && !(useAlwaysNormalTile == true)) {
+ return Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 12),
+ child: Material(
+ color: Colors.transparent,
+ borderRadius: BorderRadius.circular(28),
+ child: InkWell(
+ borderRadius: BorderRadius.circular(28),
+ onTap: () => onLogTap(log),
+ child: Container(
+ width: double.maxFinite,
+ padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(28),
+ color: isLogSelected == true
+ ? Theme.of(context).colorScheme.primaryContainer
+ : null
+ ),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Flexible(
+ child: Row(
+ mainAxisSize: MainAxisSize.min,
children: [
- ...[
- Icon(
- Icons.smartphone_rounded,
- size: 16,
- color: Theme.of(context).listTileTheme.textColor,
- ),
- const SizedBox(width: 5),
- Flexible(
- child: Text(
- log.client,
- overflow: TextOverflow.ellipsis,
- style: TextStyle(
- color: Theme.of(context).listTileTheme.textColor,
- fontSize: 14,
- height: 1.4,
- fontWeight: FontWeight.w400,
+ Flexible(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ log.question.name,
+ style: TextStyle(
+ fontSize: 16,
+ fontWeight: FontWeight.w400,
+ color: Theme.of(context).colorScheme.onSurface,
+ ),
),
- ),
- )
- ],
- const SizedBox(width: 15),
- ...[
- Icon(
- Icons.schedule_rounded,
- size: 16,
- color: Theme.of(context).listTileTheme.textColor,
- ),
- const SizedBox(width: 5),
- Flexible(
- child: Text(
- convertTimestampLocalTimezone(log.time, 'HH:mm:ss'),
- overflow: TextOverflow.ellipsis,
- style: TextStyle(
- color: Theme.of(context).listTileTheme.textColor,
- fontSize: 13
+ const SizedBox(height: 5),
+ if (log.client.length <= 15 && appConfigProvider.showNameTimeLogs == false) Row(
+ children: [
+ ...[
+ Icon(
+ Icons.smartphone_rounded,
+ size: 16,
+ color: Theme.of(context).listTileTheme.textColor,
+ ),
+ const SizedBox(width: 5),
+ Flexible(
+ child: Text(
+ log.client,
+ overflow: TextOverflow.ellipsis,
+ style: TextStyle(
+ color: Theme.of(context).listTileTheme.textColor,
+ fontSize: 14,
+ height: 1.4,
+ fontWeight: FontWeight.w400,
+ ),
+ ),
+ )
+ ],
+ const SizedBox(width: 15),
+ ...[
+ Icon(
+ Icons.schedule_rounded,
+ size: 16,
+ color: Theme.of(context).listTileTheme.textColor,
+ ),
+ const SizedBox(width: 5),
+ Flexible(
+ child: Text(
+ convertTimestampLocalTimezone(log.time, 'HH:mm:ss'),
+ overflow: TextOverflow.ellipsis,
+ style: TextStyle(
+ color: Theme.of(context).listTileTheme.textColor,
+ fontSize: 13
+ ),
+ ),
+ ),
+ ],
+ ],
),
- ),
+ if (log.client.length > 15 || appConfigProvider.showNameTimeLogs == true) Column(
+ children: [
+ Row(
+ children: [
+ Icon(
+ Icons.smartphone_rounded,
+ size: 16,
+ color: Theme.of(context).listTileTheme.textColor,
+ ),
+ const SizedBox(width: 15),
+ Flexible(
+ child: Text(
+ log.client,
+ overflow: TextOverflow.ellipsis,
+ style: TextStyle(
+ color: Theme.of(context).listTileTheme.textColor,
+ fontSize: 13
+ ),
+ ),
+ )
+ ],
+ ),
+ if (appConfigProvider.showNameTimeLogs == true && log.clientInfo!.name != '') ...[
+ const SizedBox(height: 10),
+ Row(
+ children: [
+ Icon(
+ Icons.badge_rounded,
+ size: 16,
+ color: Theme.of(context).colorScheme.onSurfaceVariant,
+ ),
+ const SizedBox(width: 15),
+ Flexible(
+ child: Text(
+ log.clientInfo!.name,
+ overflow: TextOverflow.ellipsis,
+ style: TextStyle(
+ color: Theme.of(context).listTileTheme.textColor,
+ fontSize: 13
+ ),
+ ),
+ )
+ ],
+ ),
+ ],
+ const SizedBox(height: 10),
+ Row(
+ children: [
+ Icon(
+ Icons.schedule_rounded,
+ size: 16,
+ color: Theme.of(context).listTileTheme.textColor,
+ ),
+ const SizedBox(width: 15),
+ SizedBox(
+ child: Text(
+ convertTimestampLocalTimezone(log.time, 'HH:mm:ss'),
+ overflow: TextOverflow.ellipsis,
+ style: TextStyle(
+ color: Theme.of(context).listTileTheme.textColor,
+ fontSize: 13
+ ),
+ ),
+ )
+ ],
+ ),
+ if (appConfigProvider.showNameTimeLogs == true && log.elapsedMs != '') ...[
+ const SizedBox(height: 10),
+ Row(
+ children: [
+ Icon(
+ Icons.timer,
+ size: 16,
+ color: Theme.of(context).listTileTheme.textColor,
+ ),
+ const SizedBox(width: 15),
+ SizedBox(
+ child: Text(
+ "${double.parse(log.elapsedMs).toStringAsFixed(2)} ms",
+ overflow: TextOverflow.ellipsis,
+ style: TextStyle(
+ color: Theme.of(context).listTileTheme.textColor,
+ fontSize: 13
+ ),
+ ),
+ )
+ ],
+ ),
+ ],
+ ],
+ ),
+ ],
),
- ],
+ )
],
),
- if (log.client.length > 15 || appConfigProvider.showNameTimeLogs == true) Column(
- children: [
- Row(
- children: [
+ ),
+ generateLogStatus()
+ ],
+ )
+ ),
+ ),
+ ),
+ );
+ }
+ else {
+ return Material(
+ color: Colors.transparent,
+ child: InkWell(
+ onTap: () => onLogTap(log),
+ onLongPress: () => openOptionsModal(log),
+ child: Container(
+ width: double.maxFinite,
+ padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ log.question.name,
+ style: TextStyle(
+ fontSize: 16,
+ height: 1.5,
+ fontWeight: FontWeight.w400,
+ color: Theme.of(context).colorScheme.onSurface
+ ),
+ ),
+ const SizedBox(height: 5),
+ if (log.client.length <= 15 && appConfigProvider.showNameTimeLogs == false) Row(
+ children: [
+ ...[
Icon(
Icons.smartphone_rounded,
size: 16,
color: Theme.of(context).listTileTheme.textColor,
),
- const SizedBox(width: 15),
+ const SizedBox(width: 5),
Flexible(
child: Text(
log.client,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: Theme.of(context).listTileTheme.textColor,
- fontSize: 13
+ fontSize: 14,
+ height: 1.4,
+ fontWeight: FontWeight.w400,
),
),
)
],
- ),
- if (appConfigProvider.showNameTimeLogs == true && log.clientInfo!.name != '') ...[
- const SizedBox(height: 10),
- Row(
- children: [
- Icon(
- Icons.badge_rounded,
- size: 16,
- color: Theme.of(context).colorScheme.onSurfaceVariant,
- ),
- const SizedBox(width: 15),
- Flexible(
- child: Text(
- log.clientInfo!.name,
- overflow: TextOverflow.ellipsis,
- style: TextStyle(
- color: Theme.of(context).listTileTheme.textColor,
- fontSize: 13
- ),
- ),
- )
- ],
- ),
- ],
- const SizedBox(height: 10),
- Row(
- children: [
+ const SizedBox(width: 15),
+ ...[
Icon(
Icons.schedule_rounded,
size: 16,
color: Theme.of(context).listTileTheme.textColor,
),
- const SizedBox(width: 15),
- SizedBox(
+ const SizedBox(width: 5),
+ Flexible(
child: Text(
convertTimestampLocalTimezone(log.time, 'HH:mm:ss'),
overflow: TextOverflow.ellipsis,
@@ -230,22 +353,23 @@ class LogTile extends StatelessWidget {
fontSize: 13
),
),
- )
+ ),
],
- ),
- if (appConfigProvider.showNameTimeLogs == true && log.elapsedMs != '') ...[
- const SizedBox(height: 10),
+ ],
+ ),
+ if (log.client.length > 15 || appConfigProvider.showNameTimeLogs == true) Column(
+ children: [
Row(
children: [
Icon(
- Icons.timer,
+ Icons.smartphone_rounded,
size: 16,
color: Theme.of(context).listTileTheme.textColor,
),
const SizedBox(width: 15),
- SizedBox(
+ Flexible(
child: Text(
- "${double.parse(log.elapsedMs).toStringAsFixed(2)} ms",
+ log.client,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: Theme.of(context).listTileTheme.textColor,
@@ -255,18 +379,85 @@ class LogTile extends StatelessWidget {
)
],
),
+ if (appConfigProvider.showNameTimeLogs == true && log.clientInfo!.name != '') ...[
+ const SizedBox(height: 10),
+ Row(
+ children: [
+ Icon(
+ Icons.badge_rounded,
+ size: 16,
+ color: Theme.of(context).colorScheme.onSurfaceVariant,
+ ),
+ const SizedBox(width: 15),
+ Flexible(
+ child: Text(
+ log.clientInfo!.name,
+ overflow: TextOverflow.ellipsis,
+ style: TextStyle(
+ color: Theme.of(context).listTileTheme.textColor,
+ fontSize: 13
+ ),
+ ),
+ )
+ ],
+ ),
+ ],
+ const SizedBox(height: 10),
+ Row(
+ children: [
+ Icon(
+ Icons.schedule_rounded,
+ size: 16,
+ color: Theme.of(context).listTileTheme.textColor,
+ ),
+ const SizedBox(width: 15),
+ SizedBox(
+ child: Text(
+ convertTimestampLocalTimezone(log.time, 'HH:mm:ss'),
+ overflow: TextOverflow.ellipsis,
+ style: TextStyle(
+ color: Theme.of(context).listTileTheme.textColor,
+ fontSize: 13
+ ),
+ ),
+ )
+ ],
+ ),
+ if (appConfigProvider.showNameTimeLogs == true && log.elapsedMs != '') ...[
+ const SizedBox(height: 10),
+ Row(
+ children: [
+ Icon(
+ Icons.timer,
+ size: 16,
+ color: Theme.of(context).listTileTheme.textColor,
+ ),
+ const SizedBox(width: 15),
+ SizedBox(
+ child: Text(
+ "${double.parse(log.elapsedMs).toStringAsFixed(2)} ms",
+ overflow: TextOverflow.ellipsis,
+ style: TextStyle(
+ color: Theme.of(context).listTileTheme.textColor,
+ fontSize: 13
+ ),
+ ),
+ )
+ ],
+ ),
+ ],
],
- ],
- ),
- ],
+ ),
+ ],
+ ),
),
- ),
- const SizedBox(width: 10),
- generateLogStatus()
- ],
+ const SizedBox(width: 10),
+ generateLogStatus()
+ ],
+ ),
),
),
- ),
- );
+ );
+ }
}
}
\ No newline at end of file
diff --git a/lib/screens/logs/logs.dart b/lib/screens/logs/logs.dart
index 5393c04..45494e6 100644
--- a/lib/screens/logs/logs.dart
+++ b/lib/screens/logs/logs.dart
@@ -1,5 +1,7 @@
// ignore_for_file: use_build_context_synchronously
+import 'dart:io';
+
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
@@ -7,6 +9,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:adguard_home_manager/screens/logs/logs_filters_modal.dart';
import 'package:adguard_home_manager/screens/logs/logs_config_modal.dart';
import 'package:adguard_home_manager/screens/logs/log_tile.dart';
+import 'package:adguard_home_manager/screens/logs/log_details_screen.dart';
import 'package:adguard_home_manager/functions/snackbar.dart';
import 'package:adguard_home_manager/classes/process_modal.dart';
@@ -64,6 +67,8 @@ class _LogsWidgetState extends State {
bool showDivider = true;
+ Log? selectedLog;
+
Future fetchLogs({
int? inOffset,
bool? loadingMore,
@@ -188,6 +193,8 @@ class _LogsWidgetState extends State {
final appConfigProvider = Provider.of(context);
final logsProvider = Provider.of(context);
+ final width = MediaQuery.of(context).size.width;
+
void updateConfig(Map data) async {
ProcessModal processModal = ProcessModal(context: context);
processModal.open(AppLocalizations.of(context)!.updatingSettings);
@@ -252,12 +259,25 @@ class _LogsWidgetState extends State {
void openFilersModal() {
- showModalBottomSheet(
- context: context,
- builder: (context) => const LogsFiltersModal(),
- backgroundColor: Colors.transparent,
- isScrollControlled: true
- );
+ if (width > 700 || !(Platform.isAndroid || Platform.isIOS)) {
+ showDialog(
+ context: context,
+ builder: (context) => const LogsFiltersModal(
+ dialog: true,
+ ),
+ barrierDismissible: false
+ );
+ }
+ else {
+ showModalBottomSheet(
+ context: context,
+ builder: (context) => const LogsFiltersModal(
+ dialog: false,
+ ),
+ backgroundColor: Colors.transparent,
+ isScrollControlled: true
+ );
+ }
}
final Map translatedString = {
@@ -319,6 +339,18 @@ class _LogsWidgetState extends State {
log: logsProvider.logsData!.data[index],
index: index,
length: logsProvider.logsData!.data.length,
+ isLogSelected: selectedLog != null && selectedLog == logsProvider.logsData!.data[index],
+ onLogTap: (log) {
+ if (width <= 1100) {
+ Navigator.push(context, MaterialPageRoute(
+ builder: (context) => LogDetailsScreen(
+ log: log,
+ dialog: false,
+ )
+ ));
+ }
+ setState(() => selectedLog = log);
+ }
);
}
}
@@ -383,161 +415,210 @@ class _LogsWidgetState extends State {
}
}
- return Scaffold(
- appBar: AppBar(
- title: Text(AppLocalizations.of(context)!.logs),
- centerTitle: false,
- actions: [
- logsProvider.loadStatus == 1
- ? IconButton(
- onPressed: openFilersModal,
- icon: const Icon(Icons.filter_list_rounded)
- )
- : const SizedBox(),
- IconButton(
- onPressed: () => {
- showModalBottomSheet(
- context: context,
- builder: (context) => LogsConfigModal(
- onConfirm: updateConfig,
- onClear: clearQueries,
- ),
- backgroundColor: Colors.transparent,
- isScrollControlled: true
- )
- },
- icon: const Icon(Icons.settings)
- ),
- const SizedBox(width: 5),
- ],
- bottom: logsProvider.appliedFilters.searchText != null || logsProvider.appliedFilters.selectedResultStatus != 'all' || logsProvider.appliedFilters.clients != null
- ? PreferredSize(
- preferredSize: const Size(double.maxFinite, 50),
- child: Container(
- height: 50,
- width: double.maxFinite,
- padding: const EdgeInsets.only(bottom: 10),
- decoration: BoxDecoration(
- border: Border(
- bottom: BorderSide(
- color: showDivider == true
- ? Theme.of(context).colorScheme.onSurface.withOpacity(0.1)
- : Colors.transparent,
- )
+ Widget logsScreen() {
+ return Scaffold(
+ appBar: AppBar(
+ title: Text(AppLocalizations.of(context)!.logs),
+ centerTitle: false,
+ actions: [
+ if (!(Platform.isAndroid || Platform.isIOS)) IconButton(
+ onPressed: () => fetchLogs(inOffset: 0),
+ icon: const Icon(Icons.refresh_rounded),
+ tooltip: AppLocalizations.of(context)!.refresh,
+ ),
+ logsProvider.loadStatus == 1
+ ? IconButton(
+ onPressed: openFilersModal,
+ icon: const Icon(Icons.filter_list_rounded),
+ tooltip: AppLocalizations.of(context)!.filters,
+ )
+ : const SizedBox(),
+ IconButton(
+ tooltip: AppLocalizations.of(context)!.settings,
+ onPressed: () => {
+ if (width > 700 || !(Platform.isAndroid || Platform.isIOS)) {
+ showDialog(
+ context: context,
+ builder: (context) => LogsConfigModal(
+ onConfirm: updateConfig,
+ onClear: clearQueries,
+ dialog: true,
+ ),
+ barrierDismissible: false
)
- ),
- child: ListView(
- scrollDirection: Axis.horizontal,
- children: [
- if (logsProvider.appliedFilters.searchText != null) ...[
+ }
+ else {
+ showModalBottomSheet(
+ context: context,
+ builder: (context) => LogsConfigModal(
+ onConfirm: updateConfig,
+ onClear: clearQueries,
+ dialog: false,
+ ),
+ backgroundColor: Colors.transparent,
+ isScrollControlled: true
+ )
+ }
+ },
+ icon: const Icon(Icons.settings)
+ ),
+ const SizedBox(width: 5),
+ ],
+ bottom: logsProvider.appliedFilters.searchText != null || logsProvider.appliedFilters.selectedResultStatus != 'all' || logsProvider.appliedFilters.clients != null
+ ? PreferredSize(
+ preferredSize: const Size(double.maxFinite, 50),
+ child: Container(
+ height: 50,
+ width: double.maxFinite,
+ padding: const EdgeInsets.only(bottom: 10),
+ decoration: BoxDecoration(
+ border: Border(
+ bottom: BorderSide(
+ color: showDivider == true
+ ? Theme.of(context).colorScheme.onSurface.withOpacity(0.1)
+ : Colors.transparent,
+ )
+ )
+ ),
+ child: ListView(
+ scrollDirection: Axis.horizontal,
+ children: [
+ if (logsProvider.appliedFilters.searchText != null) ...[
+ const SizedBox(width: 15),
+ Chip(
+ avatar: const Icon(
+ Icons.link_rounded,
+ ),
+ label: Row(
+ children: [
+ Text(
+ logsProvider.appliedFilters.searchText!,
+ ),
+ ],
+ ),
+ deleteIcon: const Icon(
+ Icons.clear,
+ size: 18,
+ ),
+ onDeleted: () {
+ logsProvider.setAppliedFilters(
+ AppliedFiters(
+ selectedResultStatus: logsProvider.appliedFilters.selectedResultStatus,
+ searchText: null,
+ clients: logsProvider.appliedFilters.clients
+ )
+ );
+ logsProvider.setSearchText(null);
+ fetchLogs(
+ inOffset: 0,
+ searchText: ''
+ );
+ },
+ ),
+ ],
+ if (logsProvider.appliedFilters.selectedResultStatus != 'all') ...[
+ const SizedBox(width: 15),
+ Chip(
+ avatar: const Icon(
+ Icons.shield_rounded,
+ ),
+ label: Row(
+ children: [
+ Text(
+ translatedString[logsProvider.appliedFilters.selectedResultStatus]!,
+ ),
+ ],
+ ),
+ deleteIcon: const Icon(
+ Icons.clear,
+ size: 18,
+ ),
+ onDeleted: () {
+ logsProvider.setAppliedFilters(
+ AppliedFiters(
+ selectedResultStatus: 'all',
+ searchText: logsProvider.appliedFilters.searchText,
+ clients: logsProvider.appliedFilters.clients
+ )
+ );
+ logsProvider.setSelectedResultStatus('all');
+ fetchLogs(
+ inOffset: 0,
+ responseStatus: 'all'
+ );
+ },
+ ),
+ ],
+ if (logsProvider.appliedFilters.clients != null) ...[
+ const SizedBox(width: 15),
+ Chip(
+ avatar: const Icon(
+ Icons.smartphone_rounded,
+ ),
+ label: Row(
+ children: [
+ Text(
+ logsProvider.appliedFilters.clients!.length == 1
+ ? logsProvider.appliedFilters.clients![0]
+ : "${logsProvider.appliedFilters.clients!.length} ${AppLocalizations.of(context)!.clients}",
+ ),
+ ],
+ ),
+ deleteIcon: const Icon(
+ Icons.clear,
+ size: 18,
+ ),
+ onDeleted: () {
+ logsProvider.setAppliedFilters(
+ AppliedFiters(
+ selectedResultStatus: logsProvider.appliedFilters.selectedResultStatus,
+ searchText: logsProvider.appliedFilters.searchText,
+ clients: null
+ )
+ );
+ logsProvider.setSelectedClients(null);
+ fetchLogs(
+ inOffset: 0,
+ responseStatus: logsProvider.appliedFilters.selectedResultStatus
+ );
+ },
+ ),
+ ],
const SizedBox(width: 15),
- Chip(
- avatar: const Icon(
- Icons.link_rounded,
- ),
- label: Row(
- children: [
- Text(
- logsProvider.appliedFilters.searchText!,
- ),
- ],
- ),
- deleteIcon: const Icon(
- Icons.clear,
- size: 18,
- ),
- onDeleted: () {
- logsProvider.setAppliedFilters(
- AppliedFiters(
- selectedResultStatus: logsProvider.appliedFilters.selectedResultStatus,
- searchText: null,
- clients: logsProvider.appliedFilters.clients
- )
- );
- logsProvider.setSearchText(null);
- fetchLogs(
- inOffset: 0,
- searchText: ''
- );
- },
- ),
],
- if (logsProvider.appliedFilters.selectedResultStatus != 'all') ...[
- const SizedBox(width: 15),
- Chip(
- avatar: const Icon(
- Icons.shield_rounded,
- ),
- label: Row(
- children: [
- Text(
- translatedString[logsProvider.appliedFilters.selectedResultStatus]!,
- ),
- ],
- ),
- deleteIcon: const Icon(
- Icons.clear,
- size: 18,
- ),
- onDeleted: () {
- logsProvider.setAppliedFilters(
- AppliedFiters(
- selectedResultStatus: 'all',
- searchText: logsProvider.appliedFilters.searchText,
- clients: logsProvider.appliedFilters.clients
- )
- );
- logsProvider.setSelectedResultStatus('all');
- fetchLogs(
- inOffset: 0,
- responseStatus: 'all'
- );
- },
- ),
- ],
- if (logsProvider.appliedFilters.clients != null) ...[
- const SizedBox(width: 15),
- Chip(
- avatar: const Icon(
- Icons.smartphone_rounded,
- ),
- label: Row(
- children: [
- Text(
- logsProvider.appliedFilters.clients!.length == 1
- ? logsProvider.appliedFilters.clients![0]
- : "${logsProvider.appliedFilters.clients!.length} ${AppLocalizations.of(context)!.clients}",
- ),
- ],
- ),
- deleteIcon: const Icon(
- Icons.clear,
- size: 18,
- ),
- onDeleted: () {
- logsProvider.setAppliedFilters(
- AppliedFiters(
- selectedResultStatus: logsProvider.appliedFilters.selectedResultStatus,
- searchText: logsProvider.appliedFilters.searchText,
- clients: null
- )
- );
- logsProvider.setSelectedClients(null);
- fetchLogs(
- inOffset: 0,
- responseStatus: logsProvider.appliedFilters.selectedResultStatus
- );
- },
- ),
- ],
- const SizedBox(width: 15),
- ],
- ),
+ ),
+ )
)
+ : null,
+ ),
+ body: generateBody()
+ );
+ }
+
+ if (width > 1100) {
+ return Material(
+ color: Colors.transparent,
+ child: Row(
+ children: [
+ Expanded(
+ flex: 1,
+ child: logsScreen()
+ ),
+ Expanded(
+ flex: 2,
+ child: selectedLog != null
+ ? LogDetailsScreen(
+ log: selectedLog!,
+ dialog: false,
+ )
+ : const SizedBox()
)
- : null,
- ),
- body: generateBody()
- );
+ ],
+ ),
+ );
+ }
+ else {
+ return logsScreen();
+ }
}
}
\ No newline at end of file
diff --git a/lib/screens/logs/logs_config_modal.dart b/lib/screens/logs/logs_config_modal.dart
index 9af7335..a540559 100644
--- a/lib/screens/logs/logs_config_modal.dart
+++ b/lib/screens/logs/logs_config_modal.dart
@@ -12,11 +12,13 @@ import 'package:adguard_home_manager/providers/servers_provider.dart';
class LogsConfigModal extends StatelessWidget {
final void Function(Map) onConfirm;
final void Function() onClear;
+ final bool dialog;
const LogsConfigModal({
Key? key,
required this.onConfirm,
required this.onClear,
+ required this.dialog
}) : super(key: key);
@override
@@ -30,6 +32,7 @@ class LogsConfigModal extends StatelessWidget {
context: context,
onConfirm: onConfirm,
onClear: onClear,
+ dialog: dialog,
);
}
}
@@ -40,6 +43,7 @@ class LogsConfigModalWidget extends StatefulWidget {
final BuildContext context;
final void Function(Map) onConfirm;
final void Function() onClear;
+ final bool dialog;
const LogsConfigModalWidget({
Key? key,
@@ -48,6 +52,7 @@ class LogsConfigModalWidget extends StatefulWidget {
required this.context,
required this.onConfirm,
required this.onClear,
+ required this.dialog
}) : super(key: key);
@override
@@ -146,125 +151,154 @@ class _LogsConfigModalWidgetState extends State {
Widget generateBody() {
switch (loadStatus) {
case 0:
- return const Center(
- child: CircularProgressIndicator(),
+ return Padding(
+ padding: const EdgeInsets.all(24),
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ const CircularProgressIndicator(),
+ const SizedBox(height: 30),
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 20),
+ child: Text(
+ AppLocalizations.of(context)!.loadingLogsSettings,
+ textAlign: TextAlign.center,
+ style: TextStyle(
+ fontSize: 22,
+ color: Theme.of(context).colorScheme.onSurfaceVariant
+ ),
+ ),
+ )
+ ],
+ ),
);
case 1:
return Column(
+ mainAxisSize: MainAxisSize.min,
children: [
- Expanded(
- child: ListView(
- physics: (Platform.isIOS ? 436 : 420) < MediaQuery.of(context).size.height
- ? const NeverScrollableScrollPhysics()
- : null,
- children: [
- Padding(
- padding: const EdgeInsets.only(top: 24),
- child: Icon(
- Icons.settings,
- size: 24,
- color: Theme.of(context).listTileTheme.iconColor
- ),
- ),
- const SizedBox(height: 16),
- Text(
- AppLocalizations.of(context)!.logsSettings,
- textAlign: TextAlign.center,
- style: TextStyle(
- fontSize: 24,
- color: Theme.of(context).colorScheme.onSurface
- ),
- ),
- const SizedBox(height: 16),
- Padding(
- padding: const EdgeInsets.symmetric(horizontal: 24),
- child: Material(
- color: Theme.of(context).colorScheme.primary.withOpacity(0.1),
- borderRadius: BorderRadius.circular(28),
- child: InkWell(
- onTap: () => setState(() => generalSwitch = !generalSwitch),
- borderRadius: BorderRadius.circular(28),
- child: Padding(
- padding: const EdgeInsets.symmetric(
- horizontal: 20,
- vertical: 8
- ),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Text(
- AppLocalizations.of(context)!.enableLog,
- style: const TextStyle(
- fontSize: 18,
- ),
+ Flexible(
+ child: SingleChildScrollView(
+ child: Wrap(
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Column(
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(top: 24),
+ child: Icon(
+ Icons.settings,
+ size: 24,
+ color: Theme.of(context).listTileTheme.iconColor
),
- Switch(
- value: generalSwitch,
- onChanged: (value) => setState(() => generalSwitch = value),
- )
- ],
+ ),
+ const SizedBox(height: 16),
+ Text(
+ AppLocalizations.of(context)!.logsSettings,
+ textAlign: TextAlign.center,
+ style: TextStyle(
+ fontSize: 24,
+ color: Theme.of(context).colorScheme.onSurface
+ ),
+ ),
+ const SizedBox(height: 16),
+ ],
+ ),
+ ],
+ ),
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 24),
+ child: Material(
+ color: Theme.of(context).colorScheme.primary.withOpacity(0.1),
+ borderRadius: BorderRadius.circular(28),
+ child: InkWell(
+ onTap: () => setState(() => generalSwitch = !generalSwitch),
+ borderRadius: BorderRadius.circular(28),
+ child: Padding(
+ padding: const EdgeInsets.symmetric(
+ horizontal: 20,
+ vertical: 8
+ ),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Text(
+ AppLocalizations.of(context)!.enableLog,
+ style: const TextStyle(
+ fontSize: 18,
+ ),
+ ),
+ Switch(
+ value: generalSwitch,
+ onChanged: (value) => setState(() => generalSwitch = value),
+ )
+ ],
+ ),
),
),
),
),
- ),
- const SizedBox(height: 16),
- Padding(
- padding: const EdgeInsets.symmetric(horizontal: 14),
- child: Column(
- children: [
- Material(
- color: Colors.transparent,
- child: InkWell(
- onTap: () => setState(() => anonymizeClientIp = !anonymizeClientIp),
- child: Padding(
- padding: const EdgeInsets.symmetric(horizontal: 30),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Text(
- AppLocalizations.of(context)!.anonymizeClientIp,
- style: const TextStyle(
- fontSize: 16
+ Container(height: 16),
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 14),
+ child: Column(
+ children: [
+ Material(
+ color: Colors.transparent,
+ child: InkWell(
+ onTap: () => setState(() => anonymizeClientIp = !anonymizeClientIp),
+ child: Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 30),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Text(
+ AppLocalizations.of(context)!.anonymizeClientIp,
+ style: const TextStyle(
+ fontSize: 16
+ ),
),
- ),
- Switch(
- value: anonymizeClientIp,
- onChanged: (value) => setState(() => anonymizeClientIp = value),
+ Switch(
+ value: anonymizeClientIp,
+ onChanged: (value) => setState(() => anonymizeClientIp = value),
+ )
+ ],
+ ),
+ ),
+ ),
+ ),
+ Container(height: 16),
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 24),
+ child: DropdownButtonFormField(
+ items: retentionItems.map>((Map item) {
+ return DropdownMenuItem(
+ value: item['value'].toString(),
+ child: Text(item['label']),
+ );
+ }).toList(),
+ value: retentionTime,
+ onChanged: (value) => setState(() => retentionTime = value),
+ decoration: InputDecoration(
+ border: const OutlineInputBorder(
+ borderRadius: BorderRadius.all(
+ Radius.circular(10)
)
- ],
+ ),
+ label: Text(AppLocalizations.of(context)!.retentionTime)
),
+ borderRadius: BorderRadius.circular(20),
),
),
- ),
- const SizedBox(height: 16),
- Padding(
- padding: const EdgeInsets.symmetric(horizontal: 24),
- child: DropdownButtonFormField(
- items: retentionItems.map>((Map item) {
- return DropdownMenuItem(
- value: item['value'].toString(),
- child: Text(item['label']),
- );
- }).toList(),
- value: retentionTime,
- onChanged: (value) => setState(() => retentionTime = value),
- decoration: InputDecoration(
- border: const OutlineInputBorder(
- borderRadius: BorderRadius.all(
- Radius.circular(10)
- )
- ),
- label: Text(AppLocalizations.of(context)!.retentionTime)
- ),
- borderRadius: BorderRadius.circular(20),
- ),
- ),
- ],
- ),
- )
- ],
+ ],
+ ),
+ )
+ ],
+ ),
),
),
Padding(
@@ -316,31 +350,29 @@ class _LogsConfigModalWidgetState extends State {
);
case 2:
- return SizedBox(
- 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),
- Padding(
- padding: const EdgeInsets.symmetric(horizontal: 20),
- child: Text(
- AppLocalizations.of(context)!.logSettingsNotLoaded,
- textAlign: TextAlign.center,
- style: const TextStyle(
- fontSize: 22,
- color: Colors.grey,
- ),
+ return Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ const Icon(
+ Icons.error,
+ color: Colors.red,
+ size: 50,
+ ),
+ const SizedBox(height: 30),
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 20),
+ child: Text(
+ AppLocalizations.of(context)!.logSettingsNotLoaded,
+ textAlign: TextAlign.center,
+ style: TextStyle(
+ fontSize: 22,
+ color: Theme.of(context).colorScheme.onSurfaceVariant
),
- )
- ],
- ),
+ ),
+ )
+ ],
);
default:
@@ -348,16 +380,28 @@ class _LogsConfigModalWidgetState extends State {
}
}
- return Container(
- height: Platform.isIOS ? 436 : 420,
- decoration: BoxDecoration(
- borderRadius: const BorderRadius.only(
- topLeft: Radius.circular(28),
- topRight: Radius.circular(28)
+ if (widget.dialog == true) {
+ return Dialog(
+ child: ConstrainedBox(
+ constraints: const BoxConstraints(
+ maxWidth: 500
+ ),
+ child: generateBody()
),
- color: Theme.of(context).dialogBackgroundColor
- ),
- child: generateBody()
- );
+ );
+ }
+ else {
+ return Container(
+ height: Platform.isIOS ? 436 : 420,
+ decoration: BoxDecoration(
+ borderRadius: const BorderRadius.only(
+ topLeft: Radius.circular(28),
+ topRight: Radius.circular(28)
+ ),
+ color: Theme.of(context).dialogBackgroundColor
+ ),
+ child: generateBody()
+ );
+ }
}
}
\ No newline at end of file
diff --git a/lib/screens/logs/logs_filters_modal.dart b/lib/screens/logs/logs_filters_modal.dart
index e9a6052..19ba0c0 100644
--- a/lib/screens/logs/logs_filters_modal.dart
+++ b/lib/screens/logs/logs_filters_modal.dart
@@ -18,24 +18,32 @@ import 'package:adguard_home_manager/models/applied_filters.dart';
import 'package:adguard_home_manager/providers/logs_provider.dart';
class LogsFiltersModal extends StatelessWidget {
- const LogsFiltersModal({Key? key}) : super(key: key);
+ final bool dialog;
+
+ const LogsFiltersModal({
+ Key? key,
+ required this.dialog
+ }) : super(key: key);
@override
Widget build(BuildContext context) {
final logsProvider = Provider.of(context);
return LogsFiltersModalWidget(
- logsProvider: logsProvider
+ logsProvider: logsProvider,
+ dialog: dialog,
);
}
}
class LogsFiltersModalWidget extends StatefulWidget {
final LogsProvider logsProvider;
+ final bool dialog;
const LogsFiltersModalWidget({
Key? key,
- required this.logsProvider
+ required this.logsProvider,
+ required this.dialog
}) : super(key: key);
@override
@@ -57,6 +65,8 @@ class _LogsFiltersModalWidgetState extends State {
final serversProvider = Provider.of(context);
final appConfigProvider = Provider.of(context);
+ final width = MediaQuery.of(context).size.width;
+
final Map translatedString = {
"all": AppLocalizations.of(context)!.all,
"filtered": AppLocalizations.of(context)!.filtered,
@@ -101,25 +111,51 @@ class _LogsFiltersModalWidgetState extends State {
}
void openSelectFilterStatus() {
- showModalBottomSheet(
- context: context,
- builder: (context) => FilterStatusModal(
- value: logsProvider.selectedResultStatus,
- ),
- isScrollControlled: true,
- backgroundColor: Colors.transparent
- );
+ if (width > 700 || !(Platform.isAndroid || Platform.isIOS)) {
+ showDialog(
+ barrierDismissible: false,
+ context: context,
+ builder: (context) => FilterStatusModal(
+ value: logsProvider.selectedResultStatus,
+ dialog: true,
+ ),
+ );
+ }
+ else {
+ showModalBottomSheet(
+ context: context,
+ builder: (context) => FilterStatusModal(
+ value: logsProvider.selectedResultStatus,
+ dialog: false,
+ ),
+ isScrollControlled: true,
+ backgroundColor: Colors.transparent
+ );
+ }
}
void openSelectClients() {
- showModalBottomSheet(
- context: context,
- builder: (context) => ClientsModal(
- value: logsProvider.selectedClients,
- ),
- isScrollControlled: true,
- backgroundColor: Colors.transparent
- );
+ if (width > 700 || !(Platform.isAndroid || Platform.isIOS)) {
+ showDialog(
+ context: context,
+ builder: (context) => ClientsModal(
+ value: logsProvider.selectedClients,
+ dialog: true,
+ ),
+ barrierDismissible: false
+ );
+ }
+ else {
+ showModalBottomSheet(
+ context: context,
+ builder: (context) => ClientsModal(
+ value: logsProvider.selectedClients,
+ dialog: false,
+ ),
+ isScrollControlled: true,
+ backgroundColor: Colors.transparent
+ );
+ }
}
void filterLogs() async {
@@ -161,47 +197,45 @@ class _LogsFiltersModalWidgetState extends State {
}
}
- return Padding(
- padding: MediaQuery.of(context).viewInsets,
- child: Container(
- height: Platform.isIOS ? 446 : 430,
- decoration: BoxDecoration(
- color: Theme.of(context).dialogBackgroundColor,
- borderRadius: const BorderRadius.only(
- topLeft: Radius.circular(28),
- topRight: Radius.circular(28)
- )
- ),
- child: Column(
- children: [
- Expanded(
- child: ListView(
- physics: (Platform.isIOS ? 416 : 400) < MediaQuery.of(context).size.height
- ? const NeverScrollableScrollPhysics()
- : null,
+ Widget content() {
+ return Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Flexible(
+ child: SingleChildScrollView(
+ child: Wrap(
children: [
- Padding(
- padding: const EdgeInsets.only(
- top: 24,
- bottom: 16,
- ),
- child: Icon(
- Icons.filter_list_rounded,
- size: 24,
- color: Theme.of(context).listTileTheme.iconColor
- ),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Column(
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(
+ top: 24,
+ bottom: 16,
+ ),
+ child: Icon(
+ Icons.filter_list_rounded,
+ size: 24,
+ color: Theme.of(context).listTileTheme.iconColor
+ ),
+ ),
+ Text(
+ AppLocalizations.of(context)!.filters,
+ textAlign: TextAlign.center,
+ style: TextStyle(
+ fontSize: 24,
+ fontWeight: FontWeight.w400,
+ height: 1.3,
+ color: Theme.of(context).colorScheme.onSurface
+ ),
+ ),
+ const SizedBox(height: 16),
+ ],
+ ),
+ ],
),
- Text(
- AppLocalizations.of(context)!.filters,
- textAlign: TextAlign.center,
- style: TextStyle(
- fontSize: 24,
- fontWeight: FontWeight.w400,
- height: 1.3,
- color: Theme.of(context).colorScheme.onSurface
- ),
- ),
- const SizedBox(height: 16),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: Row(
@@ -233,7 +267,7 @@ class _LogsFiltersModalWidgetState extends State {
],
),
),
- const SizedBox(height: 16),
+ Container(height: 16),
CustomListTile(
title: AppLocalizations.of(context)!.client,
subtitle: logsProvider.selectedClients != null
@@ -270,26 +304,55 @@ class _LogsFiltersModalWidgetState extends State {
],
),
),
- Padding(
- padding: const EdgeInsets.all(24),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- TextButton(
- onPressed: resetFilters,
- child: Text(AppLocalizations.of(context)!.resetFilters)
- ),
- TextButton(
- onPressed: () => filterLogs(),
- child: Text(AppLocalizations.of(context)!.apply)
- ),
- ],
- ),
+ ),
+ Padding(
+ padding: const EdgeInsets.all(24),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ TextButton(
+ onPressed: resetFilters,
+ child: Text(AppLocalizations.of(context)!.resetFilters)
+ ),
+ TextButton(
+ onPressed: () => filterLogs(),
+ child: Text(AppLocalizations.of(context)!.apply)
+ ),
+ ],
),
- if (Platform.isIOS) const SizedBox(height: 16)
- ],
+ ),
+ if (Platform.isIOS) const SizedBox(height: 16)
+ ],
+ );
+ }
+
+ if (widget.dialog == true) {
+ return Padding(
+ padding: MediaQuery.of(context).viewInsets,
+ child: Dialog(
+ child: ConstrainedBox(
+ constraints: const BoxConstraints(
+ maxWidth: 500
+ ),
+ child: content()
+ )
),
- ),
- );
+ );
+ }
+ else {
+ return Padding(
+ padding: MediaQuery.of(context).viewInsets,
+ child: Container(
+ decoration: BoxDecoration(
+ color: Theme.of(context).dialogBackgroundColor,
+ borderRadius: const BorderRadius.only(
+ topLeft: Radius.circular(28),
+ topRight: Radius.circular(28)
+ )
+ ),
+ child: content()
+ ),
+ );
+ }
}
}
\ No newline at end of file
diff --git a/lib/screens/servers/servers.dart b/lib/screens/servers/servers.dart
index 9e98e29..ba1e7b3 100644
--- a/lib/screens/servers/servers.dart
+++ b/lib/screens/servers/servers.dart
@@ -13,7 +13,12 @@ import 'package:adguard_home_manager/providers/servers_provider.dart';
import 'package:adguard_home_manager/providers/app_config_provider.dart';
class Servers extends StatefulWidget {
- const Servers({Key? key}) : super(key: key);
+ final double? breakingWidth;
+
+ const Servers({
+ Key? key,
+ this.breakingWidth
+ }) : super(key: key);
@override
State createState() => _ServersState();
@@ -55,16 +60,31 @@ class _ServersState extends State {
final serversProvider = Provider.of(context);
final appConfigProvider = Provider.of(context);
+ final width = MediaQuery.of(context).size.width;
+
for (var i = 0; i < serversProvider.serversList.length; i++) {
expandableControllerList.add(ExpandableController());
}
void openAddServerModal() async {
await Future.delayed(const Duration(seconds: 0), (() => {
- Navigator.push(context, MaterialPageRoute(
- fullscreenDialog: true,
- builder: (BuildContext context) => const AddServerModal()
- ))
+ if (width > 700) {
+ showDialog(
+ context: context,
+ barrierDismissible: false,
+ builder: (context) => const AddServerModal(
+ window: true,
+ ),
+ )
+ }
+ else {
+ Navigator.push(context, MaterialPageRoute(
+ fullscreenDialog: true,
+ builder: (BuildContext context) => const AddServerModal(
+ window: false,
+ )
+ ))
+ }
}));
}
@@ -79,7 +99,8 @@ class _ServersState extends State {
context: context,
controllers: expandableControllerList,
onChange: expandOrContract,
- scrollController: scrollController
+ scrollController: scrollController,
+ breakingWidth: widget.breakingWidth ?? 700,
),
AnimatedPositioned(
duration: const Duration(milliseconds: 100),
diff --git a/lib/screens/settings/access_settings/access_settings.dart b/lib/screens/settings/access_settings/access_settings.dart
index 9fc8a86..9f03328 100644
--- a/lib/screens/settings/access_settings/access_settings.dart
+++ b/lib/screens/settings/access_settings/access_settings.dart
@@ -1,3 +1,5 @@
+import 'dart:io';
+
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
@@ -72,78 +74,114 @@ class _AccessSettingsWidgetState extends State with Ticker
@override
Widget build(BuildContext context) {
final serversProvider = Provider.of(context);
- return Scaffold(
- body: DefaultTabController(
- length: 3,
- child: NestedScrollView(
- controller: scrollController,
- headerSliverBuilder: ((context, innerBoxIsScrolled) {
- return [
- SliverOverlapAbsorber(
- handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
- sliver: SliverSafeArea(
- top: false,
- sliver: SliverAppBar(
- title: Text(AppLocalizations.of(context)!.accessSettings),
- pinned: true,
- floating: true,
- centerTitle: false,
- forceElevated: innerBoxIsScrolled,
- bottom: TabBar(
- controller: tabController,
- isScrollable: true,
- unselectedLabelColor: Theme.of(context).colorScheme.onSurfaceVariant,
- tabs: [
- Tab(
- icon: const Icon(Icons.check),
- text: AppLocalizations.of(context)!.allowedClients,
- ),
- Tab(
- icon: const Icon(Icons.block),
- text: AppLocalizations.of(context)!.disallowedClients,
- ),
- Tab(
- icon: const Icon(Icons.link_rounded),
- text: AppLocalizations.of(context)!.disallowedDomains,
- ),
- ]
- )
+
+ Widget body() {
+ return TabBarView(
+ controller: tabController,
+ children: [
+ ClientsList(
+ type: 'allowed',
+ scrollController: scrollController,
+ loadStatus: serversProvider.clients.loadStatus,
+ data: serversProvider.clients.loadStatus == LoadStatus.loaded
+ ? serversProvider.clients.data!.clientsAllowedBlocked!.allowedClients : [],
+ fetchClients: fetchClients
+ ),
+ ClientsList(
+ type: 'disallowed',
+ scrollController: scrollController,
+ loadStatus: serversProvider.clients.loadStatus,
+ data: serversProvider.clients.loadStatus == LoadStatus.loaded
+ ? serversProvider.clients.data!.clientsAllowedBlocked!.disallowedClients : [],
+ fetchClients: fetchClients
+ ),
+ ClientsList(
+ type: 'domains',
+ scrollController: scrollController,
+ loadStatus: serversProvider.clients.loadStatus,
+ data: serversProvider.clients.loadStatus == LoadStatus.loaded
+ ? serversProvider.clients.data!.clientsAllowedBlocked!.blockedHosts : [],
+ fetchClients: fetchClients
+ ),
+ ]
+ );
+ }
+
+ PreferredSizeWidget tabBar() {
+ return TabBar(
+ controller: tabController,
+ isScrollable: true,
+ unselectedLabelColor: Theme.of(context).colorScheme.onSurfaceVariant,
+ tabs: [
+ Tab(
+ child: Row(
+ children: [
+ const Icon(Icons.check),
+ const SizedBox(width: 8),
+ Text(AppLocalizations.of(context)!.allowedClients)
+ ],
+ ),
+ ),
+ Tab(
+ child: Row(
+ children: [
+ const Icon(Icons.block),
+ const SizedBox(width: 8),
+ Text(AppLocalizations.of(context)!.disallowedClients)
+ ],
+ ),
+ ),
+ Tab(
+ child: Row(
+ children: [
+ const Icon(Icons.link_rounded),
+ const SizedBox(width: 8),
+ Text(AppLocalizations.of(context)!.disallowedDomains)
+ ],
+ ),
+ ),
+ ]
+ );
+ }
+
+ if (Platform.isAndroid || Platform.isIOS) {
+ return Scaffold(
+ body: DefaultTabController(
+ length: 3,
+ child: NestedScrollView(
+ controller: scrollController,
+ headerSliverBuilder: ((context, innerBoxIsScrolled) {
+ return [
+ SliverOverlapAbsorber(
+ handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
+ sliver: SliverSafeArea(
+ top: false,
+ sliver: SliverAppBar(
+ title: Text(AppLocalizations.of(context)!.accessSettings),
+ pinned: true,
+ floating: true,
+ centerTitle: false,
+ forceElevated: innerBoxIsScrolled,
+ bottom: tabBar()
+ ),
),
- ),
- )
- ];
- }),
- body: TabBarView(
- controller: tabController,
- children: [
- ClientsList(
- type: 'allowed',
- scrollController: scrollController,
- loadStatus: serversProvider.clients.loadStatus,
- data: serversProvider.clients.loadStatus == LoadStatus.loaded
- ? serversProvider.clients.data!.clientsAllowedBlocked!.allowedClients : [],
- fetchClients: fetchClients
- ),
- ClientsList(
- type: 'disallowed',
- scrollController: scrollController,
- loadStatus: serversProvider.clients.loadStatus,
- data: serversProvider.clients.loadStatus == LoadStatus.loaded
- ? serversProvider.clients.data!.clientsAllowedBlocked!.disallowedClients : [],
- fetchClients: fetchClients
- ),
- ClientsList(
- type: 'domains',
- scrollController: scrollController,
- loadStatus: serversProvider.clients.loadStatus,
- data: serversProvider.clients.loadStatus == LoadStatus.loaded
- ? serversProvider.clients.data!.clientsAllowedBlocked!.blockedHosts : [],
- fetchClients: fetchClients
- ),
- ]
+ )
+ ];
+ }),
+ body: body()
)
- )
- ),
- );
+ ),
+ );
+ }
+ else {
+ return Scaffold(
+ appBar: AppBar(
+ title: Text(AppLocalizations.of(context)!.accessSettings),
+ centerTitle: false,
+ bottom: tabBar()
+ ),
+ body: body(),
+ );
+ }
}
}
\ No newline at end of file
diff --git a/lib/screens/settings/access_settings/add_client_modal.dart b/lib/screens/settings/access_settings/add_client_modal.dart
index 385409b..c7e4012 100644
--- a/lib/screens/settings/access_settings/add_client_modal.dart
+++ b/lib/screens/settings/access_settings/add_client_modal.dart
@@ -6,11 +6,13 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class AddClientModal extends StatefulWidget {
final String type;
final void Function(String, String) onConfirm;
+ final bool dialog;
const AddClientModal({
Key? key,
required this.type,
- required this.onConfirm
+ required this.onConfirm,
+ required this.dialog,
}) : super(key: key);
@override
@@ -65,59 +67,61 @@ class _AddClientModalState extends State {
}
}
- return Padding(
- padding: MediaQuery.of(context).viewInsets,
- child: Container(
- height: Platform.isIOS ? 321 : 305,
+ Widget content() {
+ return Padding(
padding: const EdgeInsets.all(24),
- decoration: BoxDecoration(
- color: Theme.of(context).dialogBackgroundColor,
- borderRadius: const BorderRadius.only(
- topLeft: Radius.circular(28),
- topRight: Radius.circular(28)
- )
- ),
child: Column(
+ mainAxisSize: MainAxisSize.min,
children: [
- Expanded(
- child: ListView(
- physics: (Platform.isIOS ? 338 : 322) < MediaQuery.of(context).size.height
- ? const NeverScrollableScrollPhysics()
- : null,
- children: [
- Icon(
- icon(),
- size: 24,
- color: Theme.of(context).listTileTheme.iconColor
- ),
- const SizedBox(height: 16),
- Text(
- title(),
- textAlign: TextAlign.center,
- style: TextStyle(
- fontSize: 24,
- color: Theme.of(context).colorScheme.onSurface
+ Flexible(
+ child: SingleChildScrollView(
+ child: Wrap(
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Icon(
+ icon(),
+ size: 24,
+ color: Theme.of(context).listTileTheme.iconColor
+ ),
+ ],
),
- ),
- const SizedBox(height: 16),
- TextFormField(
- controller: fieldController,
- onChanged: (_) => checkValidValues(),
- decoration: InputDecoration(
- prefixIcon: const Icon(Icons.link_rounded),
- border: const OutlineInputBorder(
- borderRadius: BorderRadius.all(
- Radius.circular(10)
- )
+ Padding(
+ padding: const EdgeInsets.symmetric(vertical: 16),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Text(
+ title(),
+ textAlign: TextAlign.center,
+ style: TextStyle(
+ fontSize: 24,
+ color: Theme.of(context).colorScheme.onSurface
+ ),
+ ),
+ ],
),
- helperText: widget.type == 'allowed' || widget.type == 'disallowed'
- ? AppLocalizations.of(context)!.addClientFieldDescription : null,
- labelText: widget.type == 'allowed' || widget.type == 'disallowed'
- ? AppLocalizations.of(context)!.clientIdentifier
- : AppLocalizations.of(context)!.domain,
),
- ),
- ],
+ TextFormField(
+ controller: fieldController,
+ onChanged: (_) => checkValidValues(),
+ decoration: InputDecoration(
+ prefixIcon: const Icon(Icons.link_rounded),
+ border: const OutlineInputBorder(
+ borderRadius: BorderRadius.all(
+ Radius.circular(10)
+ )
+ ),
+ helperText: widget.type == 'allowed' || widget.type == 'disallowed'
+ ? AppLocalizations.of(context)!.addClientFieldDescription : null,
+ labelText: widget.type == 'allowed' || widget.type == 'disallowed'
+ ? AppLocalizations.of(context)!.clientIdentifier
+ : AppLocalizations.of(context)!.domain,
+ ),
+ ),
+ ],
+ ),
),
),
Padding(
@@ -129,7 +133,7 @@ class _AddClientModalState extends State {
onPressed: () => Navigator.pop(context),
child: Text(AppLocalizations.of(context)!.cancel)
),
- const SizedBox(width: 20),
+ const SizedBox(width: 16),
TextButton(
onPressed: validData == true
? () {
@@ -149,10 +153,38 @@ class _AddClientModalState extends State {
],
),
),
- if (Platform.isIOS) const SizedBox(height: 16)
],
),
- ),
- );
+ );
+ }
+
+ if (widget.dialog == true) {
+ return Padding(
+ padding: MediaQuery.of(context).viewInsets,
+ child: Dialog(
+ child: ConstrainedBox(
+ constraints: const BoxConstraints(
+ maxWidth: 400
+ ),
+ child: content()
+ ),
+ ),
+ );
+ }
+ else {
+ return Padding(
+ padding: MediaQuery.of(context).viewInsets,
+ child: Container(
+ decoration: BoxDecoration(
+ color: Theme.of(context).dialogBackgroundColor,
+ borderRadius: const BorderRadius.only(
+ topLeft: Radius.circular(28),
+ topRight: Radius.circular(28)
+ )
+ ),
+ child: content()
+ ),
+ );
+ }
}
}
\ No newline at end of file
diff --git a/lib/screens/settings/access_settings/clients_list.dart b/lib/screens/settings/access_settings/clients_list.dart
index 6e561db..5b495a1 100644
--- a/lib/screens/settings/access_settings/clients_list.dart
+++ b/lib/screens/settings/access_settings/clients_list.dart
@@ -1,5 +1,7 @@
// ignore_for_file: use_build_context_synchronously
+import 'dart:io';
+
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:provider/provider.dart';
@@ -68,6 +70,8 @@ class _ClientsListState extends State {
final serversProvider = Provider.of(context);
final appConfigProvider = Provider.of(context);
+ final width = MediaQuery.of(context).size.width;
+
void confirmRemoveItem(String client, String type) async {
Map> body = {
"allowed_clients": serversProvider.clients.data!.clientsAllowedBlocked?.allowedClients ?? [],
@@ -209,6 +213,7 @@ class _ClientsListState extends State {
}
return CustomTabContentList(
+ noSliver: !(Platform.isAndroid || Platform.isIOS) ? true : false,
loadingGenerator: () => SizedBox(
width: double.maxFinite,
height: MediaQuery.of(context).size.height-171,
@@ -362,15 +367,28 @@ class _ClientsListState extends State {
refreshIndicatorOffset: 0,
fab: FloatingActionButton(
onPressed: () {
- showModalBottomSheet(
- context: context,
- builder: (context) => AddClientModal(
- type: widget.type,
- onConfirm: confirmAddItem
- ),
- backgroundColor: Colors.transparent,
- isScrollControlled: true
- );
+ if (width > 900 || !(Platform.isAndroid || Platform.isIOS)) {
+ showDialog(
+ context: context,
+ builder: (context) => AddClientModal(
+ type: widget.type,
+ onConfirm: confirmAddItem,
+ dialog: true,
+ ),
+ );
+ }
+ else {
+ showModalBottomSheet(
+ context: context,
+ builder: (context) => AddClientModal(
+ type: widget.type,
+ onConfirm: confirmAddItem,
+ dialog: false,
+ ),
+ backgroundColor: Colors.transparent,
+ isScrollControlled: true
+ );
+ }
},
child: const Icon(Icons.add),
),
diff --git a/lib/screens/settings/advanced_setings.dart b/lib/screens/settings/advanced_setings.dart
index 987d406..0012259 100644
--- a/lib/screens/settings/advanced_setings.dart
+++ b/lib/screens/settings/advanced_setings.dart
@@ -1,6 +1,9 @@
// ignore_for_file: use_build_context_synchronously
+import 'dart:io';
+
import 'package:flutter/material.dart';
+import 'package:flutter_split_view/flutter_split_view.dart';
import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
@@ -15,6 +18,8 @@ class AdvancedSettings extends StatelessWidget {
@override
Widget build(BuildContext context) {
final appConfigProvider = Provider.of(context);
+
+ final width = MediaQuery.of(context).size.width;
Future updateSslCheck(bool newStatus) async {
final result = await appConfigProvider.setOverrideSslCheck(newStatus);
@@ -64,11 +69,16 @@ class AdvancedSettings extends StatelessWidget {
title: AppLocalizations.of(context)!.logs,
subtitle: AppLocalizations.of(context)!.checkAppLogs,
onTap: () => {
- Navigator.of(context).push(
- MaterialPageRoute(
- builder: (context) => const AppLogs()
+ if (width > 900 || !(Platform.isAndroid || Platform.isIOS)) {
+ SplitView.of(context).push(const AppLogs())
+ }
+ else {
+ Navigator.of(context).push(
+ MaterialPageRoute(
+ builder: (context) => const AppLogs()
+ )
)
- )
+ }
},
padding: const EdgeInsets.only(
top: 10,
diff --git a/lib/screens/settings/dhcp/add_static_lease_modal.dart b/lib/screens/settings/dhcp/add_static_lease_modal.dart
index 2c8e316..a7f2f5f 100644
--- a/lib/screens/settings/dhcp/add_static_lease_modal.dart
+++ b/lib/screens/settings/dhcp/add_static_lease_modal.dart
@@ -5,10 +5,12 @@ import 'package:adguard_home_manager/models/dhcp.dart';
class AddStaticLeaseModal extends StatefulWidget {
final void Function(Lease) onConfirm;
+ final bool dialog;
const AddStaticLeaseModal({
Key? key,
required this.onConfirm,
+ required this.dialog
}) : super(key: key);
@override
@@ -65,45 +67,47 @@ class _AddStaticLeaseModalState extends State {
@override
Widget build(BuildContext context) {
- return Padding(
- padding: MediaQuery.of(context).viewInsets,
- child: Container(
- height: 510,
- decoration: BoxDecoration(
- color: Theme.of(context).dialogBackgroundColor,
- borderRadius: const BorderRadius.only(
- topLeft: Radius.circular(28),
- topRight: Radius.circular(28)
- )
- ),
- child: Column(
- children: [
- Expanded(
- child: ListView(
- physics: 550 < MediaQuery.of(context).size.height
- ? const NeverScrollableScrollPhysics()
- : null,
+ Widget content() {
+ return Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Flexible(
+ child: SingleChildScrollView(
+ child: Wrap(
children: [
Padding(
- padding: const EdgeInsets.only(top: 24),
- child: Icon(
- Icons.add,
- size: 24,
- color: Theme.of(context).listTileTheme.iconColor
+ padding: const EdgeInsets.only(bottom: 16),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Column(
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(top: 24),
+ child: Icon(
+ Icons.add,
+ size: 24,
+ color: Theme.of(context).listTileTheme.iconColor
+ ),
+ ),
+ const SizedBox(height: 16),
+ Text(
+ AppLocalizations.of(context)!.addStaticLease,
+ textAlign: TextAlign.center,
+ style: TextStyle(
+ fontSize: 24,
+ color: Theme.of(context).colorScheme.onSurface
+ ),
+ ),
+ ],
+ ),
+ ],
),
),
- const SizedBox(height: 16),
- Text(
- AppLocalizations.of(context)!.addStaticLease,
- textAlign: TextAlign.center,
- style: TextStyle(
- fontSize: 24,
- color: Theme.of(context).colorScheme.onSurface
- ),
- ),
- const SizedBox(height: 16),
Padding(
- padding: const EdgeInsets.symmetric(horizontal: 28),
+ padding: const EdgeInsets.only(
+ left: 24, right: 24, bottom: 12
+ ),
child: TextFormField(
controller: macController,
onChanged: validateMac,
@@ -119,9 +123,8 @@ class _AddStaticLeaseModalState extends State {
),
),
),
- const SizedBox(height: 30),
Padding(
- padding: const EdgeInsets.symmetric(horizontal: 28),
+ padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
child: TextFormField(
controller: ipController,
onChanged: validateIp,
@@ -137,9 +140,10 @@ class _AddStaticLeaseModalState extends State {
),
),
),
- const SizedBox(height: 30),
Padding(
- padding: const EdgeInsets.symmetric(horizontal: 28),
+ padding: const EdgeInsets.only(
+ left: 24, right: 24, top: 12
+ ),
child: TextFormField(
controller: hostNameController,
onChanged: (value) {
@@ -166,44 +170,70 @@ class _AddStaticLeaseModalState extends State {
],
),
),
- Padding(
- padding: const EdgeInsets.all(20),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.end,
- children: [
- TextButton(
- onPressed: () => Navigator.pop(context),
- child: Text(AppLocalizations.of(context)!.cancel),
- ),
- const SizedBox(width: 20),
- TextButton(
- onPressed: validData == true
- ? () {
- Navigator.pop(context);
- widget.onConfirm(
- Lease(
- mac: macController.text,
- hostname: hostNameController.text,
- ip: ipController.text
- )
- );
- }
- : null,
- child: Text(
- AppLocalizations.of(context)!.confirm,
- style: TextStyle(
- color: validData == true
- ? Theme.of(context).colorScheme.primary
- : Colors.grey
- ),
+ ),
+ Padding(
+ padding: const EdgeInsets.all(24),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.end,
+ children: [
+ TextButton(
+ onPressed: () => Navigator.pop(context),
+ child: Text(AppLocalizations.of(context)!.cancel),
+ ),
+ const SizedBox(width: 20),
+ TextButton(
+ onPressed: validData == true
+ ? () {
+ Navigator.pop(context);
+ widget.onConfirm(
+ Lease(
+ mac: macController.text,
+ hostname: hostNameController.text,
+ ip: ipController.text
+ )
+ );
+ }
+ : null,
+ child: Text(
+ AppLocalizations.of(context)!.confirm,
+ style: TextStyle(
+ color: validData == true
+ ? Theme.of(context).colorScheme.primary
+ : Colors.grey
),
),
- ],
- ),
- )
- ],
+ ),
+ ],
+ ),
+ )
+ ],
+ );
+ }
+
+ if (widget.dialog == true) {
+ return Dialog(
+ child: ConstrainedBox(
+ constraints: const BoxConstraints(
+ maxWidth: 400
+ ),
+ child: content(),
),
- ),
- );
+ );
+ }
+ else {
+ return Padding(
+ padding: MediaQuery.of(context).viewInsets,
+ child: Container(
+ decoration: BoxDecoration(
+ color: Theme.of(context).dialogBackgroundColor,
+ borderRadius: const BorderRadius.only(
+ topLeft: Radius.circular(28),
+ topRight: Radius.circular(28)
+ )
+ ),
+ child: content()
+ ),
+ );
+ }
}
}
\ No newline at end of file
diff --git a/lib/screens/settings/dhcp/dhcp.dart b/lib/screens/settings/dhcp/dhcp.dart
index a4a8319..a12fe81 100644
--- a/lib/screens/settings/dhcp/dhcp.dart
+++ b/lib/screens/settings/dhcp/dhcp.dart
@@ -1,8 +1,10 @@
// ignore_for_file: use_build_context_synchronously
+import 'dart:io';
+
import 'package:flutter/material.dart';
+import 'package:flutter_split_view/flutter_split_view.dart';
import 'package:provider/provider.dart';
-import 'package:bottom_sheet/bottom_sheet.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:adguard_home_manager/widgets/section_label.dart';
@@ -204,7 +206,7 @@ class _DhcpWidgetState extends State {
@override
void initState() {
- loadDhcpStatus();
+ if (mounted) loadDhcpStatus();
super.initState();
}
@@ -213,6 +215,8 @@ class _DhcpWidgetState extends State {
final serversProvider = Provider.of(context);
final appConfigProvider = Provider.of(context);
+ final width = MediaQuery.of(context).size.width;
+
void saveSettings() async {
ProcessModal processModal = ProcessModal(context: context);
processModal.open(AppLocalizations.of(context)!.savingSettings);
@@ -354,24 +358,33 @@ class _DhcpWidgetState extends State {
void selectInterface() {
ScaffoldMessenger.of(context).clearSnackBars();
Future.delayed(const Duration(seconds: 0), () {
- showFlexibleBottomSheet(
- minHeight: 0.6,
- initHeight: 0.6,
- maxHeight: 0.95,
- isCollapsible: true,
- duration: const Duration(milliseconds: 250),
- anchors: [0.95],
- context: context,
- builder: (ctx, controller, offset) => SelectInterfaceModal(
- interfaces: serversProvider.dhcp.data!.networkInterfaces,
- scrollController: controller,
- onSelect: (interface) => setState(() {
- clearAll();
- selectedInterface = interface;
- })
- ),
- bottomSheetColor: Colors.transparent
- );
+ if (width > 900 || !(Platform.isAndroid || Platform.isIOS)) {
+ showDialog(
+ context: context,
+ builder: (context) => SelectInterfaceModal(
+ interfaces: serversProvider.dhcp.data!.networkInterfaces,
+ onSelect: (interface) => setState(() {
+ clearAll();
+ selectedInterface = interface;
+ }),
+ dialog: true,
+ )
+ );
+ }
+ else {
+ showModalBottomSheet(
+ context: context,
+ builder: (context) => SelectInterfaceModal(
+ interfaces: serversProvider.dhcp.data!.networkInterfaces,
+ onSelect: (i) => setState(() {
+ clearAll();
+ selectedInterface = i;
+ }),
+ dialog: false,
+ ),
+ isScrollControlled: true
+ );
+ }
});
}
@@ -399,334 +412,436 @@ class _DhcpWidgetState extends State {
case 1:
if (selectedInterface != null) {
- return ListView(
- children: [
- Padding(
- padding: const EdgeInsets.only(
- top: 10,
- left: 16,
- right: 16
- ),
- child: Material(
- color: Theme.of(context).colorScheme.primary.withOpacity(0.1),
- borderRadius: BorderRadius.circular(28),
- child: InkWell(
- onTap: selectedInterface != null
- ? () => setState(() => enabled = !enabled)
- : null,
+ return SingleChildScrollView(
+ child: Wrap(
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(
+ top: 10,
+ left: 16,
+ right: 16
+ ),
+ child: Material(
+ color: Theme.of(context).colorScheme.primary.withOpacity(0.1),
borderRadius: BorderRadius.circular(28),
- child: Padding(
- padding: const EdgeInsets.symmetric(
- horizontal: 20,
- vertical: 12
+ child: InkWell(
+ onTap: selectedInterface != null
+ ? () => setState(() => enabled = !enabled)
+ : null,
+ borderRadius: BorderRadius.circular(28),
+ child: Padding(
+ padding: const EdgeInsets.symmetric(
+ horizontal: 20,
+ vertical: 12
+ ),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ AppLocalizations.of(context)!.enableDhcpServer,
+ style: TextStyle(
+ fontSize: 16,
+ color: Theme.of(context).colorScheme.onSurface
+ ),
+ ),
+ if (selectedInterface != null) ...[
+ Text(
+ selectedInterface!.name,
+ style: TextStyle(
+ fontSize: 14,
+ color: Theme.of(context).listTileTheme.textColor
+ ),
+ )
+ ]
+ ],
+ ),
+ Switch(
+ value: enabled,
+ onChanged: selectedInterface != null
+ ? (value) => setState(() => enabled = value)
+ : null,
+ ),
+ ],
+ ),
),
+ ),
+ ),
+ ),
+ if (selectedInterface!.ipv4Addresses.isNotEmpty) ...[
+ SectionLabel(
+ label: AppLocalizations.of(context)!.ipv4settings,
+ padding: const EdgeInsets.only(
+ top: 24, left: 16, right: 16, bottom: 8
+ )
+ ),
+ FractionallySizedBox(
+ widthFactor: width > 900 ? 0.5 : 1,
+ child: Padding(
+ padding: width > 900
+ ? const EdgeInsets.only(top: 12, bottom: 12, left: 16, right: 8)
+ : const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
+ child: TextFormField(
+ controller: ipv4StartRangeController,
+ onChanged: (value) => validateIpV4(value, 'ipv4StartRangeError', AppLocalizations.of(context)!.ipNotValid),
+ decoration: InputDecoration(
+ prefixIcon: const Icon(Icons.skip_previous_rounded),
+ border: const OutlineInputBorder(
+ borderRadius: BorderRadius.all(
+ Radius.circular(10)
+ )
+ ),
+ errorText: ipv4StartRangeError,
+ labelText: AppLocalizations.of(context)!.startOfRange,
+ ),
+ keyboardType: TextInputType.number,
+ ),
+ ),
+ ),
+ FractionallySizedBox(
+ widthFactor: width > 900 ? 0.5 : 1,
+ child: Padding(
+ padding: width > 900
+ ? const EdgeInsets.only(top: 12, bottom: 12, left: 8, right: 16)
+ : const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
+ child: TextFormField(
+ controller: ipv4EndRangeController,
+ onChanged: (value) => validateIpV4(value, 'ipv4EndRangeError', AppLocalizations.of(context)!.ipNotValid),
+ decoration: InputDecoration(
+ prefixIcon: const Icon(Icons.skip_next_rounded),
+ border: const OutlineInputBorder(
+ borderRadius: BorderRadius.all(
+ Radius.circular(10)
+ )
+ ),
+ errorText: ipv4EndRangeError,
+ labelText: AppLocalizations.of(context)!.endOfRange,
+ ),
+ keyboardType: TextInputType.number,
+ ),
+ ),
+ ),
+ FractionallySizedBox(
+ widthFactor: width > 900 ? 0.5 : 1,
+ child: Padding(
+ padding: width > 900
+ ? const EdgeInsets.only(top: 12, bottom: 12, left: 16, right: 8)
+ : const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
+ child: TextFormField(
+ controller: ipv4SubnetMaskController,
+ onChanged: (value) => validateIpV4(value, 'ipv4SubnetMaskError', AppLocalizations.of(context)!.subnetMaskNotValid),
+ decoration: InputDecoration(
+ prefixIcon: const Icon(Icons.hub_rounded),
+ border: const OutlineInputBorder(
+ borderRadius: BorderRadius.all(
+ Radius.circular(10)
+ )
+ ),
+ errorText: ipv4SubnetMaskError,
+ labelText: AppLocalizations.of(context)!.subnetMask,
+ ),
+ keyboardType: TextInputType.number,
+ ),
+ ),
+ ),
+ FractionallySizedBox(
+ widthFactor: width > 900 ? 0.5 : 1,
+ child: Padding(
+ padding: width > 900
+ ? const EdgeInsets.only(top: 12, bottom: 12, left: 8, right: 16)
+ : const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
+ child: TextFormField(
+ controller: ipv4GatewayController,
+ onChanged: (value) => validateIpV4(value, 'ipv4GatewayError', AppLocalizations.of(context)!.gatewayNotValid),
+ decoration: InputDecoration(
+ prefixIcon: const Icon(Icons.router_rounded),
+ border: const OutlineInputBorder(
+ borderRadius: BorderRadius.all(
+ Radius.circular(10)
+ )
+ ),
+ errorText: ipv4GatewayError,
+ labelText: AppLocalizations.of(context)!.gateway,
+ ),
+ keyboardType: TextInputType.number,
+ ),
+ ),
+ ),
+ FractionallySizedBox(
+ widthFactor: 1,
+ child: Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
+ child: TextFormField(
+ controller: ipv4LeaseTimeController,
+ onChanged: (value) {
+ if (int.tryParse(value).runtimeType == int) {
+ setState(() => ipv4LeaseTimeError = null);
+ }
+ else {
+ setState(() => ipv4LeaseTimeError = AppLocalizations.of(context)!.leaseTimeNotValid);
+ }
+ },
+ decoration: InputDecoration(
+ prefixIcon: const Icon(Icons.timer),
+ border: const OutlineInputBorder(
+ borderRadius: BorderRadius.all(
+ Radius.circular(10)
+ )
+ ),
+ errorText: ipv4LeaseTimeError,
+ labelText: AppLocalizations.of(context)!.leaseTime,
+ ),
+ keyboardType: TextInputType.number,
+ ),
+ ),
+ ),
+ ],
+ if (selectedInterface!.ipv6Addresses.isNotEmpty) ...[
+ SectionLabel(
+ label: AppLocalizations.of(context)!.ipv6settings,
+ padding: const EdgeInsets.all(16)
+ ),
+ FractionallySizedBox(
+ widthFactor: width > 900 ? 0.5 : 1,
+ child: Padding(
+ padding: width > 900
+ ? const EdgeInsets.only(top: 8, bottom: 12, left: 16, right: 8)
+ : const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
+ child: TextFormField(
+ controller: ipv6StartRangeController,
+ onChanged: (value) => validateIpV4(value, 'ipv6StartRangeError', AppLocalizations.of(context)!.ipNotValid),
+ decoration: InputDecoration(
+ prefixIcon: const Icon(Icons.skip_next_rounded),
+ border: const OutlineInputBorder(
+ borderRadius: BorderRadius.all(
+ Radius.circular(10)
+ )
+ ),
+ errorText: ipv6StartRangeError,
+ labelText: AppLocalizations.of(context)!.startOfRange,
+ ),
+ keyboardType: TextInputType.number,
+ ),
+ ),
+ ),
+ FractionallySizedBox(
+ widthFactor: width > 900 ? 0.5 : 1,
+ child: Padding(
+ padding: width > 900
+ ? const EdgeInsets.only(top: 8, bottom: 12, left: 8, right: 16)
+ : const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
+ child: TextFormField(
+ controller: ipv6EndRangeController,
+ onChanged: (value) => validateIpV4(value, 'ipv6EndRangeError', AppLocalizations.of(context)!.ipNotValid),
+ decoration: InputDecoration(
+ prefixIcon: const Icon(Icons.skip_previous_rounded),
+ border: const OutlineInputBorder(
+ borderRadius: BorderRadius.all(
+ Radius.circular(10)
+ )
+ ),
+ errorText: ipv6EndRangeError,
+ labelText: AppLocalizations.of(context)!.endOfRange,
+ ),
+ keyboardType: TextInputType.number,
+ ),
+ ),
+ ),
+ FractionallySizedBox(
+ widthFactor: 1,
+ child: Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
+ child: TextFormField(
+ controller: ipv6LeaseTimeController,
+ onChanged: (value) {
+ if (int.tryParse(value).runtimeType == int) {
+ setState(() => ipv6LeaseTimeError = null);
+ }
+ else {
+ setState(() => ipv6LeaseTimeError = AppLocalizations.of(context)!.leaseTimeNotValid);
+ }
+ },
+ decoration: InputDecoration(
+ prefixIcon: const Icon(Icons.timer),
+ border: const OutlineInputBorder(
+ borderRadius: BorderRadius.all(
+ Radius.circular(10)
+ )
+ ),
+ errorText: ipv6LeaseTimeError,
+ labelText: AppLocalizations.of(context)!.leaseTime,
+ ),
+ keyboardType: TextInputType.number,
+ ),
+ ),
+ ),
+ ],
+ const SizedBox(height: 20),
+ SectionLabel(
+ label: AppLocalizations.of(context)!.dhcpLeases,
+ padding: const EdgeInsets.all(16),
+ ),
+ if (width <= 900) Material(
+ color: Colors.transparent,
+ child: InkWell(
+ onTap: () {
+ Navigator.push(context, MaterialPageRoute(
+ builder: (context) => DhcpLeases(
+ items: serversProvider.dhcp.data!.dhcpStatus.leases,
+ staticLeases: false,
+ )
+ ));
+ },
+ child: Container(
+ padding: const EdgeInsets.all(16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
- Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- AppLocalizations.of(context)!.enableDhcpServer,
- style: TextStyle(
- fontSize: 16,
- color: Theme.of(context).colorScheme.onSurface
- ),
- ),
- if (selectedInterface != null) ...[
- Text(
- selectedInterface!.name,
- style: TextStyle(
- fontSize: 14,
- color: Theme.of(context).listTileTheme.textColor
- ),
- )
- ]
- ],
- ),
- Switch(
- value: enabled,
- onChanged: selectedInterface != null
- ? (value) => setState(() => enabled = value)
- : null,
+ Text(
+ AppLocalizations.of(context)!.dhcpLeases,
+ textAlign: TextAlign.center,
+ style: TextStyle(
+ fontSize: 16,
+ color: Theme.of(context).colorScheme.onSurface,
+ ),
),
+ Icon(
+ Icons.arrow_forward_rounded,
+ color: Theme.of(context).colorScheme.onSurface,
+ )
],
),
),
),
),
- ),
- if (selectedInterface!.ipv4Addresses.isNotEmpty) ...[
- SectionLabel(
- label: AppLocalizations.of(context)!.ipv4settings,
- padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 24),
- ),
- Padding(
- padding: const EdgeInsets.symmetric(horizontal: 16),
- child: TextFormField(
- controller: ipv4StartRangeController,
- onChanged: (value) => validateIpV4(value, 'ipv4StartRangeError', AppLocalizations.of(context)!.ipNotValid),
- decoration: InputDecoration(
- prefixIcon: const Icon(Icons.skip_previous_rounded),
- border: const OutlineInputBorder(
- borderRadius: BorderRadius.all(
- Radius.circular(10)
+ if (width <= 900) Material(
+ color: Colors.transparent,
+ child: InkWell(
+ onTap: () {
+ Navigator.push(context, MaterialPageRoute(
+ builder: (context) => DhcpLeases(
+ items: serversProvider.dhcp.data!.dhcpStatus.staticLeases,
+ staticLeases: true,
)
- ),
- errorText: ipv4StartRangeError,
- labelText: AppLocalizations.of(context)!.startOfRange,
- ),
- keyboardType: TextInputType.number,
- ),
- ),
- const SizedBox(height: 30),
- Padding(
- padding: const EdgeInsets.symmetric(horizontal: 16),
- child: TextFormField(
- controller: ipv4EndRangeController,
- onChanged: (value) => validateIpV4(value, 'ipv4EndRangeError', AppLocalizations.of(context)!.ipNotValid),
- decoration: InputDecoration(
- prefixIcon: const Icon(Icons.skip_next_rounded),
- border: const OutlineInputBorder(
- borderRadius: BorderRadius.all(
- Radius.circular(10)
- )
- ),
- errorText: ipv4EndRangeError,
- labelText: AppLocalizations.of(context)!.endOfRange,
- ),
- keyboardType: TextInputType.number,
- ),
- ),
- const SizedBox(height: 30),
- Padding(
- padding: const EdgeInsets.symmetric(horizontal: 16),
- child: TextFormField(
- controller: ipv4SubnetMaskController,
- onChanged: (value) => validateIpV4(value, 'ipv4SubnetMaskError', AppLocalizations.of(context)!.subnetMaskNotValid),
- decoration: InputDecoration(
- prefixIcon: const Icon(Icons.hub_rounded),
- border: const OutlineInputBorder(
- borderRadius: BorderRadius.all(
- Radius.circular(10)
- )
- ),
- errorText: ipv4SubnetMaskError,
- labelText: AppLocalizations.of(context)!.subnetMask,
- ),
- keyboardType: TextInputType.number,
- ),
- ),
- const SizedBox(height: 30),
- Padding(
- padding: const EdgeInsets.symmetric(horizontal: 16),
- child: TextFormField(
- controller: ipv4GatewayController,
- onChanged: (value) => validateIpV4(value, 'ipv4GatewayError', AppLocalizations.of(context)!.gatewayNotValid),
- decoration: InputDecoration(
- prefixIcon: const Icon(Icons.router_rounded),
- border: const OutlineInputBorder(
- borderRadius: BorderRadius.all(
- Radius.circular(10)
- )
- ),
- errorText: ipv4GatewayError,
- labelText: AppLocalizations.of(context)!.gateway,
- ),
- keyboardType: TextInputType.number,
- ),
- ),
- const SizedBox(height: 30),
- Padding(
- padding: const EdgeInsets.symmetric(horizontal: 16),
- child: TextFormField(
- controller: ipv4LeaseTimeController,
- onChanged: (value) {
- if (int.tryParse(value).runtimeType == int) {
- setState(() => ipv4LeaseTimeError = null);
- }
- else {
- setState(() => ipv4LeaseTimeError = AppLocalizations.of(context)!.leaseTimeNotValid);
- }
+ ));
},
- decoration: InputDecoration(
- prefixIcon: const Icon(Icons.timer),
- border: const OutlineInputBorder(
- borderRadius: BorderRadius.all(
- Radius.circular(10)
- )
- ),
- errorText: ipv4LeaseTimeError,
- labelText: AppLocalizations.of(context)!.leaseTime,
- ),
- keyboardType: TextInputType.number,
- ),
- ),
- ],
- if (selectedInterface!.ipv6Addresses.isNotEmpty) ...[
- SectionLabel(
- label: AppLocalizations.of(context)!.ipv6settings,
- ),
- Padding(
- padding: const EdgeInsets.symmetric(horizontal: 16),
- child: TextFormField(
- controller: ipv6StartRangeController,
- onChanged: (value) => validateIpV4(value, 'ipv6StartRangeError', AppLocalizations.of(context)!.ipNotValid),
- decoration: InputDecoration(
- prefixIcon: const Icon(Icons.skip_next_rounded),
- border: const OutlineInputBorder(
- borderRadius: BorderRadius.all(
- Radius.circular(10)
- )
- ),
- errorText: ipv6StartRangeError,
- labelText: AppLocalizations.of(context)!.startOfRange,
- ),
- keyboardType: TextInputType.number,
- ),
- ),
- const SizedBox(height: 30),
- Padding(
- padding: const EdgeInsets.symmetric(horizontal: 16),
- child: TextFormField(
- controller: ipv6EndRangeController,
- onChanged: (value) => validateIpV4(value, 'ipv6EndRangeError', AppLocalizations.of(context)!.ipNotValid),
- decoration: InputDecoration(
- prefixIcon: const Icon(Icons.skip_previous_rounded),
- border: const OutlineInputBorder(
- borderRadius: BorderRadius.all(
- Radius.circular(10)
- )
- ),
- errorText: ipv6EndRangeError,
- labelText: AppLocalizations.of(context)!.endOfRange,
- ),
- keyboardType: TextInputType.number,
- ),
- ),
- const SizedBox(height: 30),
- Padding(
- padding: const EdgeInsets.symmetric(horizontal: 16),
- child: TextFormField(
- controller: ipv6LeaseTimeController,
- onChanged: (value) {
- if (int.tryParse(value).runtimeType == int) {
- setState(() => ipv6LeaseTimeError = null);
- }
- else {
- setState(() => ipv6LeaseTimeError = AppLocalizations.of(context)!.leaseTimeNotValid);
- }
- },
- decoration: InputDecoration(
- prefixIcon: const Icon(Icons.timer),
- border: const OutlineInputBorder(
- borderRadius: BorderRadius.all(
- Radius.circular(10)
- )
- ),
- errorText: ipv6LeaseTimeError,
- labelText: AppLocalizations.of(context)!.leaseTime,
- ),
- keyboardType: TextInputType.number,
- ),
- ),
- ],
- const SizedBox(height: 20),
- SectionLabel(
- label: AppLocalizations.of(context)!.dhcpLeases,
- ),
- Material(
- color: Colors.transparent,
- child: InkWell(
- onTap: () {
- Navigator.push(context, MaterialPageRoute(
- builder: (context) => DhcpLeases(
- items: serversProvider.dhcp.data!.dhcpStatus.leases,
- staticLeases: false,
- )
- ));
- },
- child: Container(
- padding: const EdgeInsets.all(16),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Text(
- AppLocalizations.of(context)!.dhcpLeases,
- textAlign: TextAlign.center,
- style: TextStyle(
- fontSize: 16,
- color: Theme.of(context).colorScheme.onSurface,
+ child: Container(
+ padding: const EdgeInsets.all(16),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Text(
+ AppLocalizations.of(context)!.dhcpStatic,
+ textAlign: TextAlign.center,
+ style: TextStyle(
+ fontSize: 16,
+ color: Theme.of(context).colorScheme.onSurface,
+ ),
),
- ),
- Icon(
- Icons.arrow_forward_rounded,
- color: Theme.of(context).colorScheme.onSurface,
- )
- ],
- ),
- ),
- ),
- ),
- Material(
- color: Colors.transparent,
- child: InkWell(
- onTap: () {
- Navigator.push(context, MaterialPageRoute(
- builder: (context) => DhcpLeases(
- items: serversProvider.dhcp.data!.dhcpStatus.staticLeases,
- staticLeases: true,
- )
- ));
- },
- child: Container(
- padding: const EdgeInsets.all(16),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Text(
- AppLocalizations.of(context)!.dhcpStatic,
- textAlign: TextAlign.center,
- style: TextStyle(
- fontSize: 16,
+ Icon(
+ Icons.arrow_forward_rounded,
color: Theme.of(context).colorScheme.onSurface,
- ),
- ),
- Icon(
- Icons.arrow_forward_rounded,
- color: Theme.of(context).colorScheme.onSurface,
- )
- ],
+ )
+ ],
+ ),
),
),
),
- ),
- const SizedBox(height: 10)
- ],
+ if (width > 900) Row(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ children: [
+ ElevatedButton(
+ onPressed: () {
+ if (!(Platform.isAndroid || Platform.isIOS)) {
+ SplitView.of(context).push(
+ DhcpLeases(
+ items: serversProvider.dhcp.data!.dhcpStatus.leases,
+ staticLeases: false,
+ )
+ );
+ }
+ else {
+ Navigator.push(context, MaterialPageRoute(
+ builder: (context) => DhcpLeases(
+ items: serversProvider.dhcp.data!.dhcpStatus.leases,
+ staticLeases: false,
+ )
+ ));
+ }
+ },
+ child: Row(
+ children: [
+ Text(AppLocalizations.of(context)!.dhcpLeases),
+ const SizedBox(width: 8),
+ const Icon(Icons.arrow_forward_rounded)
+ ],
+ )
+ ),
+ ElevatedButton(
+ onPressed: () {
+ if (!(Platform.isAndroid || Platform.isIOS)) {
+ SplitView.of(context).push(
+ DhcpLeases(
+ items: serversProvider.dhcp.data!.dhcpStatus.staticLeases,
+ staticLeases: true,
+ )
+ );
+ }
+ else {
+ Navigator.push(context, MaterialPageRoute(
+ builder: (context) => DhcpLeases(
+ items: serversProvider.dhcp.data!.dhcpStatus.staticLeases,
+ staticLeases: true,
+ )
+ ));
+ }
+ },
+ child: Row(
+ children: [
+ Text(AppLocalizations.of(context)!.dhcpStatic),
+ const SizedBox(width: 8),
+ const Icon(Icons.arrow_forward_rounded)
+ ],
+ )
+ ),
+ ],
+ ),
+ const SizedBox(height: 10)
+ ],
+ ),
);
}
else {
- return Column(
- mainAxisSize: MainAxisSize.max,
+ return Row(
mainAxisAlignment: MainAxisAlignment.center,
- crossAxisAlignment: CrossAxisAlignment.center,
children: [
- Padding(
- padding: const EdgeInsets.symmetric(horizontal: 20),
- child: Text(
- AppLocalizations.of(context)!.neededSelectInterface,
- textAlign: TextAlign.center,
- style: TextStyle(
- fontSize: 22,
- color: Theme.of(context).colorScheme.onSurface.withOpacity(0.5)
- ),
+ Flexible(
+ child: Column(
+ mainAxisSize: MainAxisSize.max,
+ mainAxisAlignment: MainAxisAlignment.center,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 20),
+ child: Text(
+ AppLocalizations.of(context)!.neededSelectInterface,
+ textAlign: TextAlign.center,
+ style: TextStyle(
+ fontSize: 22,
+ color: Theme.of(context).colorScheme.onSurface.withOpacity(0.5)
+ ),
+ ),
+ ),
+ const SizedBox(height: 30),
+ ElevatedButton(
+ onPressed: selectInterface,
+ child: Text(AppLocalizations.of(context)!.selectInterface)
+ ),
+ ],
),
),
- const SizedBox(height: 30),
- ElevatedButton(
- onPressed: selectInterface,
- child: Text(AppLocalizations.of(context)!.selectInterface)
- ),
],
);
}
diff --git a/lib/screens/settings/dhcp/dhcp_leases.dart b/lib/screens/settings/dhcp/dhcp_leases.dart
index aed4f47..79f478a 100644
--- a/lib/screens/settings/dhcp/dhcp_leases.dart
+++ b/lib/screens/settings/dhcp/dhcp_leases.dart
@@ -1,5 +1,7 @@
// ignore_for_file: use_build_context_synchronously
+import 'dart:io';
+
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:animations/animations.dart';
@@ -30,6 +32,8 @@ class DhcpLeases extends StatelessWidget {
final serversProvider = Provider.of(context);
final appConfigProvider = Provider.of(context);
+ final width = MediaQuery.of(context).size.width;
+
void deleteLease(Lease lease) async {
ProcessModal processModal = ProcessModal(context: context);
processModal.open(AppLocalizations.of(context)!.deleting);
@@ -119,14 +123,26 @@ class DhcpLeases extends StatelessWidget {
}
void openAddStaticLease() {
- showModalBottomSheet(
- context: context,
- builder: (context) => AddStaticLeaseModal(
- onConfirm: createLease
- ),
- backgroundColor: Colors.transparent,
- isScrollControlled: true
- );
+ if (width > 900 || !(Platform.isAndroid || Platform.isIOS)) {
+ showDialog(
+ context: context,
+ builder: (context) => AddStaticLeaseModal(
+ onConfirm: createLease,
+ dialog: true,
+ ),
+ );
+ }
+ else {
+ showModalBottomSheet(
+ context: context,
+ builder: (context) => AddStaticLeaseModal(
+ onConfirm: createLease,
+ dialog: false,
+ ),
+ backgroundColor: Colors.transparent,
+ isScrollControlled: true
+ );
+ }
}
return Scaffold(
diff --git a/lib/screens/settings/dhcp/select_interface_modal.dart b/lib/screens/settings/dhcp/select_interface_modal.dart
index 202281c..9827f11 100644
--- a/lib/screens/settings/dhcp/select_interface_modal.dart
+++ b/lib/screens/settings/dhcp/select_interface_modal.dart
@@ -7,173 +7,177 @@ import 'package:adguard_home_manager/models/dhcp.dart';
class SelectInterfaceModal extends StatelessWidget {
final List interfaces;
- final ScrollController scrollController;
final void Function(NetworkInterface) onSelect;
+ final bool dialog;
const SelectInterfaceModal({
Key? key,
required this.interfaces,
- required this.scrollController,
required this.onSelect,
+ required this.dialog
}) : super(key: key);
@override
Widget build(BuildContext context) {
- return Container(
- decoration: BoxDecoration(
- color: Theme.of(context).dialogBackgroundColor,
- borderRadius: const BorderRadius.only(
- topLeft: Radius.circular(28),
- topRight: Radius.circular(28)
- )
- ),
- child: Column(
+ Widget content() {
+ return Column(
+ mainAxisSize: MainAxisSize.min,
children: [
- Expanded(
- child: ListView(
- controller: scrollController,
- children: [
- Padding(
- padding: const EdgeInsets.only(top: 24),
- child: Icon(
- Icons.settings_ethernet_rounded,
- size: 24,
- color: Theme.of(context).listTileTheme.iconColor
- ),
- ),
- const SizedBox(height: 16),
- Text(
- AppLocalizations.of(context)!.selectInterface,
- textAlign: TextAlign.center,
- style: TextStyle(
- fontSize: 24,
- color: Theme.of(context).colorScheme.onSurface
- ),
- ),
- const SizedBox(height: 16),
- ListView.builder(
- primary: false,
- shrinkWrap: true,
- itemCount: interfaces.length,
- itemBuilder: (context, index) => Material(
- color: Colors.transparent,
- child: InkWell(
- onTap: () {
- Navigator.pop(context);
- onSelect(interfaces[index]);
- },
- child: Container(
- padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- interfaces[index].name,
- style: TextStyle(
- fontSize: 16,
- fontWeight: FontWeight.w400,
- color: Theme.of(context).colorScheme.onSurface
- ),
+ Flexible(
+ child: SingleChildScrollView(
+ child: Wrap(
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Column(
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(top: 24),
+ child: Icon(
+ Icons.settings_ethernet_rounded,
+ size: 24,
+ color: Theme.of(context).listTileTheme.iconColor
),
- Row(
- children: [
- Text(
- "${AppLocalizations.of(context)!.hardwareAddress}: ",
- style: TextStyle(
- fontSize: 14,
- color: Theme.of(context).colorScheme.onSurfaceVariant
- ),
+ ),
+ const SizedBox(height: 16),
+ Text(
+ AppLocalizations.of(context)!.selectInterface,
+ textAlign: TextAlign.center,
+ style: TextStyle(
+ fontSize: 24,
+ color: Theme.of(context).colorScheme.onSurface
+ ),
+ ),
+ ],
+ ),
+ ],
+ ),
+ const SizedBox(height: 16),
+ ListView.builder(
+ primary: false,
+ shrinkWrap: true,
+ itemCount: interfaces.length,
+ itemBuilder: (context, index) => Material(
+ color: Colors.transparent,
+ child: InkWell(
+ onTap: () {
+ Navigator.pop(context);
+ onSelect(interfaces[index]);
+ },
+ child: Container(
+ padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ interfaces[index].name,
+ style: TextStyle(
+ fontSize: 16,
+ fontWeight: FontWeight.w400,
+ color: Theme.of(context).colorScheme.onSurface
),
- Text(
- interfaces[index].hardwareAddress,
- style: TextStyle(
- fontSize: 14,
- color: Theme.of(context).colorScheme.onSurfaceVariant
+ ),
+ Row(
+ children: [
+ Text(
+ "${AppLocalizations.of(context)!.hardwareAddress}: ",
+ style: TextStyle(
+ fontSize: 14,
+ color: Theme.of(context).colorScheme.onSurfaceVariant
+ ),
),
+ Text(
+ interfaces[index].hardwareAddress,
+ style: TextStyle(
+ fontSize: 14,
+ color: Theme.of(context).colorScheme.onSurfaceVariant
+ ),
+ ),
+ ],
+ ),
+ const SizedBox(height: 5),
+ if (interfaces[index].flags.isNotEmpty) ...[
+ Row(
+ children: [
+ Text(
+ "Flags: ",
+ style: TextStyle(
+ fontSize: 14,
+ color: Theme.of(context).colorScheme.onSurfaceVariant
+ ),
+ ),
+ Text(
+ interfaces[index].flags.join(', '),
+ style: TextStyle(
+ fontSize: 14,
+ color: Theme.of(context).colorScheme.onSurfaceVariant
+ ),
+ ),
+ ],
),
+ const SizedBox(height: 5),
],
- ),
- const SizedBox(height: 5),
- if (interfaces[index].flags.isNotEmpty) ...[
- Row(
- children: [
- Text(
- "Flags: ",
- style: TextStyle(
- fontSize: 14,
- color: Theme.of(context).colorScheme.onSurfaceVariant
- ),
- ),
- Text(
- interfaces[index].flags.join(', '),
- style: TextStyle(
- fontSize: 14,
- color: Theme.of(context).colorScheme.onSurfaceVariant
- ),
- ),
- ],
- ),
- const SizedBox(height: 5),
- ],
- if (interfaces[index].gatewayIp != '') ...[
- Row(
- children: [
- Text(
- "${AppLocalizations.of(context)!.gatewayIp}: ",
- style: TextStyle(
- fontSize: 14,
- color: Theme.of(context).colorScheme.onSurfaceVariant
- ),
- ),
- Text(
- interfaces[index].gatewayIp,
- style: TextStyle(
- fontSize: 14,
- color: Theme.of(context).colorScheme.onSurfaceVariant
- ),
- ),
- ],
- ),
- const SizedBox(height: 5),
- ],
- if (interfaces[index].ipv4Addresses.isNotEmpty) ...[
- Row(
- children: [
- Flexible(
- child: Text(
- "${AppLocalizations.of(context)!.ipv4addresses}: ${interfaces[index].ipv4Addresses.join(', ')}",
+ if (interfaces[index].gatewayIp != '') ...[
+ Row(
+ children: [
+ Text(
+ "${AppLocalizations.of(context)!.gatewayIp}: ",
style: TextStyle(
fontSize: 14,
color: Theme.of(context).colorScheme.onSurfaceVariant
),
),
- )
- ],
- ),
- const SizedBox(height: 5),
- ],
- if (interfaces[index].ipv6Addresses.isNotEmpty) ...[
- Row(
- children: [
- Flexible(
- child: Text(
- "${AppLocalizations.of(context)!.ipv6addresses}: ${interfaces[index].ipv6Addresses.join(', ')}",
+ Text(
+ interfaces[index].gatewayIp,
style: TextStyle(
fontSize: 14,
color: Theme.of(context).colorScheme.onSurfaceVariant
),
),
- )
- ],
- ),
- ]
- ],
+ ],
+ ),
+ const SizedBox(height: 5),
+ ],
+ if (interfaces[index].ipv4Addresses.isNotEmpty) ...[
+ Row(
+ children: [
+ Flexible(
+ child: Text(
+ "${AppLocalizations.of(context)!.ipv4addresses}: ${interfaces[index].ipv4Addresses.join(', ')}",
+ style: TextStyle(
+ fontSize: 14,
+ color: Theme.of(context).colorScheme.onSurfaceVariant
+ ),
+ ),
+ )
+ ],
+ ),
+ const SizedBox(height: 5),
+ ],
+ if (interfaces[index].ipv6Addresses.isNotEmpty) ...[
+ Row(
+ children: [
+ Flexible(
+ child: Text(
+ "${AppLocalizations.of(context)!.ipv6addresses}: ${interfaces[index].ipv6Addresses.join(', ')}",
+ style: TextStyle(
+ fontSize: 14,
+ color: Theme.of(context).colorScheme.onSurfaceVariant
+ ),
+ ),
+ )
+ ],
+ ),
+ ]
+ ],
+ ),
),
),
- ),
- )
- ),
- ],
+ )
+ ),
+ ],
+ ),
),
),
Padding(
@@ -190,7 +194,30 @@ class SelectInterfaceModal extends StatelessWidget {
),
if (Platform.isIOS) const SizedBox(height: 16)
],
- ),
- );
+ );
+ }
+
+ if (dialog == true) {
+ return Dialog(
+ child: ConstrainedBox(
+ constraints: const BoxConstraints(
+ maxWidth: 500
+ ),
+ child: content()
+ ),
+ );
+ }
+ else {
+ return Container(
+ decoration: BoxDecoration(
+ color: Theme.of(context).dialogBackgroundColor,
+ borderRadius: const BorderRadius.only(
+ topLeft: Radius.circular(28),
+ topRight: Radius.circular(28)
+ )
+ ),
+ child: content()
+ );
+ }
}
}
\ No newline at end of file
diff --git a/lib/screens/settings/dns/bootstrap_dns.dart b/lib/screens/settings/dns/bootstrap_dns.dart
index 32f02ed..7658c4d 100644
--- a/lib/screens/settings/dns/bootstrap_dns.dart
+++ b/lib/screens/settings/dns/bootstrap_dns.dart
@@ -182,8 +182,7 @@ class _BootstrapDnsScreenState extends State {
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
- SizedBox(
- width: MediaQuery.of(context).size.width-74,
+ Expanded(
child: TextFormField(
controller: c['controller'],
onChanged: (value) => validateIp(c, value),
@@ -199,6 +198,7 @@ class _BootstrapDnsScreenState extends State {
)
),
),
+ const SizedBox(width: 8),
IconButton(
onPressed: () {
setState(() => bootstrapControllers = bootstrapControllers.where((con) => con != c).toList());
diff --git a/lib/screens/settings/dns/cache_config.dart b/lib/screens/settings/dns/cache_config.dart
index 16090fb..713f837 100644
--- a/lib/screens/settings/dns/cache_config.dart
+++ b/lib/screens/settings/dns/cache_config.dart
@@ -257,7 +257,8 @@ class _CacheConfigDnsScreenState extends State {
label: Text(AppLocalizations.of(context)!.clearDnsCache),
),
],
- )
+ ),
+ const SizedBox(height: 16)
],
),
);
diff --git a/lib/screens/settings/dns/comment_modal.dart b/lib/screens/settings/dns/comment_modal.dart
index 20ad859..a9dd60d 100644
--- a/lib/screens/settings/dns/comment_modal.dart
+++ b/lib/screens/settings/dns/comment_modal.dart
@@ -4,11 +4,13 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class CommentModal extends StatefulWidget {
final String? comment;
final void Function(String) onConfirm;
+ final bool dialog;
const CommentModal({
Key? key,
this.comment,
- required this.onConfirm
+ required this.onConfirm,
+ required this.dialog
}) : super(key: key);
@override
@@ -30,43 +32,41 @@ class _CommentModalState extends State {
@override
Widget build(BuildContext context) {
- return Padding(
- padding: MediaQuery.of(context).viewInsets,
- child: Container(
- height: 310,
- decoration: BoxDecoration(
- borderRadius: const BorderRadius.only(
- topLeft: Radius.circular(28),
- topRight: Radius.circular(28)
- ),
- color: Theme.of(context).dialogBackgroundColor
- ),
- child: Column(
- children: [
- Expanded(
- child: ListView(
- physics: MediaQuery.of(context).size.height >= 330 == true
- ? const NeverScrollableScrollPhysics()
- : null,
+ Widget content() {
+ return Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Flexible(
+ child: SingleChildScrollView(
+ child: Wrap(
children: [
- Padding(
- padding: const EdgeInsets.only(top: 24),
- child: Icon(
- Icons.comment_rounded,
- size: 24,
- color: Theme.of(context).colorScheme.secondary,
- ),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Column(
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(top: 24),
+ child: Icon(
+ Icons.comment_rounded,
+ size: 24,
+ color: Theme.of(context).colorScheme.secondary,
+ ),
+ ),
+ const SizedBox(height: 16),
+ Text(
+ AppLocalizations.of(context)!.comment,
+ textAlign: TextAlign.center,
+ style: TextStyle(
+ fontSize: 24,
+ color: Theme.of(context).colorScheme.onSurface
+ ),
+ ),
+ const SizedBox(height: 16),
+ ],
+ ),
+ ],
),
- const SizedBox(height: 16),
- Text(
- AppLocalizations.of(context)!.comment,
- textAlign: TextAlign.center,
- style: TextStyle(
- fontSize: 24,
- color: Theme.of(context).colorScheme.onSurface
- ),
- ),
- const SizedBox(height: 16),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: TextFormField(
@@ -95,38 +95,64 @@ class _CommentModalState extends State {
],
),
),
- Padding(
- padding: const EdgeInsets.all(24),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.end,
- children: [
- TextButton(
- onPressed: () => Navigator.pop(context),
- child: Text(AppLocalizations.of(context)!.cancel)
- ),
- const SizedBox(width: 20),
- TextButton(
- onPressed: validData == true
- ? () {
- Navigator.pop(context);
- widget.onConfirm("# ${commentController.text}");
- }
- : null,
- child: Text(
- AppLocalizations.of(context)!.confirm,
- style: TextStyle(
- color: validData == true
- ? Theme.of(context).colorScheme.primary
- : Colors.grey
- ),
- )
- ),
- ],
- ),
- )
- ],
+ ),
+ Padding(
+ padding: const EdgeInsets.all(24),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.end,
+ children: [
+ TextButton(
+ onPressed: () => Navigator.pop(context),
+ child: Text(AppLocalizations.of(context)!.cancel)
+ ),
+ const SizedBox(width: 20),
+ TextButton(
+ onPressed: validData == true
+ ? () {
+ Navigator.pop(context);
+ widget.onConfirm("# ${commentController.text}");
+ }
+ : null,
+ child: Text(
+ AppLocalizations.of(context)!.confirm,
+ style: TextStyle(
+ color: validData == true
+ ? Theme.of(context).colorScheme.primary
+ : Colors.grey
+ ),
+ )
+ ),
+ ],
+ ),
+ )
+ ],
+ );
+ }
+
+ if (widget.dialog == true) {
+ return Dialog(
+ child: ConstrainedBox(
+ constraints: const BoxConstraints(
+ maxWidth: 400
+ ),
+ child: content()
),
- ),
- );
+ );
+ }
+ else {
+ return Padding(
+ padding: MediaQuery.of(context).viewInsets,
+ child: Container(
+ decoration: BoxDecoration(
+ borderRadius: const BorderRadius.only(
+ topLeft: Radius.circular(28),
+ topRight: Radius.circular(28)
+ ),
+ color: Theme.of(context).dialogBackgroundColor
+ ),
+ child: content()
+ ),
+ );
+ }
}
}
\ No newline at end of file
diff --git a/lib/screens/settings/dns/dns.dart b/lib/screens/settings/dns/dns.dart
index 229c167..55cfa19 100644
--- a/lib/screens/settings/dns/dns.dart
+++ b/lib/screens/settings/dns/dns.dart
@@ -1,6 +1,9 @@
// ignore_for_file: use_build_context_synchronously
+import 'dart:io';
+
import 'package:flutter/material.dart';
+import 'package:flutter_split_view/flutter_split_view.dart';
import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
@@ -76,6 +79,19 @@ class _DnsSettingsWidgetState extends State {
final serversProvider = Provider.of(context);
final appConfigProvider = Provider.of(context);
+ final width = MediaQuery.of(context).size.width;
+
+ void navigate(Widget widget) {
+ if (width > 900 || !(Platform.isAndroid || Platform.isIOS)) {
+ SplitView.of(context).push(widget);
+ }
+ else {
+ Navigator.push(context, MaterialPageRoute(
+ builder: (context) => widget
+ ));
+ }
+ }
+
Widget generateBody() {
switch (widget.serversProvider.dnsInfo.loadStatus) {
case 0:
@@ -105,51 +121,51 @@ class _DnsSettingsWidgetState extends State {
CustomListTile(
title: AppLocalizations.of(context)!.upstreamDns,
subtitle: AppLocalizations.of(context)!.upstreamDnsDescription,
- onTap: () => Navigator.push(context, MaterialPageRoute(
- builder: (context) => UpstreamDnsScreen(
+ onTap: () => navigate(
+ UpstreamDnsScreen(
serversProvider: serversProvider
)
- )),
+ ),
icon: Icons.upload_rounded,
),
CustomListTile(
title: AppLocalizations.of(context)!.bootstrapDns,
subtitle: AppLocalizations.of(context)!.bootstrapDnsDescription,
- onTap: () => Navigator.push(context, MaterialPageRoute(
- builder: (context) => BootstrapDnsScreen(
+ onTap: () => navigate(
+ BootstrapDnsScreen(
serversProvider: serversProvider
)
- )),
+ ),
icon: Icons.dns_rounded,
),
CustomListTile(
title: AppLocalizations.of(context)!.privateReverseDnsServers,
subtitle: AppLocalizations.of(context)!.privateReverseDnsDescription,
- onTap: () => Navigator.push(context, MaterialPageRoute(
- builder: (context) => PrivateReverseDnsServersScreen(
+ onTap: () => navigate(
+ PrivateReverseDnsServersScreen(
serversProvider: serversProvider
)
- )),
+ ),
icon: Icons.person_rounded,
),
CustomListTile(
title: AppLocalizations.of(context)!.dnsServerSettings,
subtitle: AppLocalizations.of(context)!.dnsServerSettingsDescription,
- onTap: () => Navigator.push(context, MaterialPageRoute(
- builder: (context) => DnsServerSettingsScreen(
+ onTap: () => navigate(
+ DnsServerSettingsScreen(
serversProvider: serversProvider
)
- )),
+ ),
icon: Icons.settings,
),
CustomListTile(
title: AppLocalizations.of(context)!.dnsCacheConfig,
subtitle: AppLocalizations.of(context)!.dnsCacheConfigDescription,
- onTap: () => Navigator.push(context, MaterialPageRoute(
- builder: (context) => CacheConfigDnsScreen(
+ onTap: () => navigate(
+ CacheConfigDnsScreen(
serversProvider: serversProvider
)
- )),
+ ),
icon: Icons.storage_rounded,
),
],
diff --git a/lib/screens/settings/dns/private_reverse_servers.dart b/lib/screens/settings/dns/private_reverse_servers.dart
index a6c29bf..a4f8131 100644
--- a/lib/screens/settings/dns/private_reverse_servers.dart
+++ b/lib/screens/settings/dns/private_reverse_servers.dart
@@ -229,8 +229,7 @@ class _PrivateReverseDnsServersScreenState extends State validateAddress(c, value),
@@ -246,6 +245,7 @@ class _PrivateReverseDnsServersScreenState extends State reverseResolversControllers = reverseResolversControllers.where((con) => con != c).toList());
diff --git a/lib/screens/settings/dns/upstream_dns.dart b/lib/screens/settings/dns/upstream_dns.dart
index 0005252..66d8b8c 100644
--- a/lib/screens/settings/dns/upstream_dns.dart
+++ b/lib/screens/settings/dns/upstream_dns.dart
@@ -1,5 +1,7 @@
// ignore_for_file: use_build_context_synchronously
+import 'dart:io';
+
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
@@ -71,36 +73,73 @@ class _UpstreamDnsScreenState extends State {
Widget build(BuildContext context) {
final serversProvider = Provider.of(context);
final appConfigProvider = Provider.of(context);
+
+ final width = MediaQuery.of(context).size.width;
void openAddCommentModal() {
- showModalBottomSheet(
- context: context,
- builder: (context) => CommentModal(
- onConfirm: (value) {
- dnsServers.add({
- 'comment': value
- });
- },
- ),
- backgroundColor: Colors.transparent,
- isScrollControlled: true,
- isDismissible: true
- );
+ if (width > 900 || !(Platform.isAndroid || Platform.isIOS)) {
+ showDialog(
+ context: context,
+ builder: (context) => CommentModal(
+ onConfirm: (value) {
+ setState(() {
+ dnsServers.add({
+ 'comment': value
+ });
+ });
+ },
+ dialog: true,
+ ),
+ );
+ }
+ else {
+ showModalBottomSheet(
+ context: context,
+ builder: (context) => CommentModal(
+ onConfirm: (value) {
+ setState(() {
+ dnsServers.add({
+ 'comment': value
+ });
+ });
+ },
+ dialog: false,
+ ),
+ backgroundColor: Colors.transparent,
+ isScrollControlled: true,
+ isDismissible: true
+ );
+ }
}
void openEditCommentModal(Map item, int position) {
- showModalBottomSheet(
- context: context,
- builder: (context) => CommentModal(
- comment: item['comment'],
- onConfirm: (value) {
- setState(() => dnsServers[position] = { 'comment': value });
- },
- ),
- backgroundColor: Colors.transparent,
- isScrollControlled: true,
- isDismissible: true
- );
+ if (width > 900 || !(Platform.isAndroid || Platform.isIOS)) {
+ showDialog(
+ context: context,
+ builder: (context) => CommentModal(
+ comment: item['comment'],
+ onConfirm: (value) {
+ setState(() => dnsServers[position] = { 'comment': value });
+ },
+ dialog: true,
+ ),
+ );
+ }
+ else {
+ showModalBottomSheet(
+ context: context,
+ builder: (context) => CommentModal(
+ comment: item['comment'],
+ onConfirm: (value) {
+ setState(() => dnsServers[position] = { 'comment': value });
+ },
+ dialog: false,
+ ),
+ backgroundColor: Colors.transparent,
+ isScrollControlled: true,
+ isDismissible: true
+ );
+ }
}
void saveData() async {
@@ -185,13 +224,12 @@ class _UpstreamDnsScreenState extends State {
),
...dnsServers.map((item) => Padding(
padding: const EdgeInsets.only(
- left: 16, right: 6, bottom: 20
+ left: 16, right: 6, bottom: 24
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
- if (item['controller'] != null) SizedBox(
- width: MediaQuery.of(context).size.width-74,
+ if (item['controller'] != null) Expanded(
child: TextFormField(
controller: item['controller'],
onChanged: (_) => checkValidValues(),
@@ -206,6 +244,7 @@ class _UpstreamDnsScreenState extends State {
)
),
),
+ const SizedBox(width: 8),
if (item['comment'] != null) Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
@@ -232,10 +271,12 @@ class _UpstreamDnsScreenState extends State {
},
icon: const Icon(Icons.remove_circle_outline),
tooltip: AppLocalizations.of(context)!.remove,
- )
+ ),
+ const SizedBox(width: 4),
],
),
)).toList(),
+ const SizedBox(height: 12),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.min,
diff --git a/lib/screens/settings/dns_rewrites/add_dns_rewrite_modal.dart b/lib/screens/settings/dns_rewrites/add_dns_rewrite_modal.dart
index 53bc3b2..edff851 100644
--- a/lib/screens/settings/dns_rewrites/add_dns_rewrite_modal.dart
+++ b/lib/screens/settings/dns_rewrites/add_dns_rewrite_modal.dart
@@ -7,10 +7,12 @@ import 'package:adguard_home_manager/models/rewrite_rules.dart';
class AddDnsRewriteModal extends StatefulWidget {
final void Function(RewriteRulesData) onConfirm;
+ final bool dialog;
const AddDnsRewriteModal({
Key? key,
- required this.onConfirm
+ required this.onConfirm,
+ required this.dialog
}) : super(key: key);
@override
@@ -50,45 +52,45 @@ class _AddDnsRewriteModalState extends State {
@override
Widget build(BuildContext context) {
- return Padding(
- padding: MediaQuery.of(context).viewInsets,
- child: Container(
- height: Platform.isIOS ? 416 : 400,
- decoration: BoxDecoration(
- borderRadius: const BorderRadius.only(
- topLeft: Radius.circular(28),
- topRight: Radius.circular(28)
- ),
- color: Theme.of(context).dialogBackgroundColor,
- ),
- child: Column(
- children: [
- Expanded(
- child: ListView(
- physics: (Platform.isIOS ? 426 : 410) < MediaQuery.of(context).size.height
- ? const NeverScrollableScrollPhysics()
- : null,
+ Widget content() {
+ return Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Flexible(
+ child: SingleChildScrollView(
+ child: Wrap(
children: [
- Padding(
- padding: const EdgeInsets.only(top: 24),
- child: Icon(
- Icons.add,
- size: 24,
- color: Theme.of(context).listTileTheme.iconColor
- ),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Column(
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(top: 24),
+ child: Icon(
+ Icons.add,
+ size: 24,
+ color: Theme.of(context).listTileTheme.iconColor
+ ),
+ ),
+ const SizedBox(height: 16),
+ Text(
+ AppLocalizations.of(context)!.addDnsRewrite,
+ textAlign: TextAlign.center,
+ style: TextStyle(
+ fontSize: 24,
+ color: Theme.of(context).colorScheme.onSurface
+ ),
+ ),
+ const SizedBox(height: 16),
+ ],
+ ),
+ ],
),
- const SizedBox(height: 16),
- Text(
- AppLocalizations.of(context)!.addDnsRewrite,
- textAlign: TextAlign.center,
- style: TextStyle(
- fontSize: 24,
- color: Theme.of(context).colorScheme.onSurface
- ),
- ),
- const SizedBox(height: 16),
Padding(
- padding: const EdgeInsets.symmetric(horizontal: 24),
+ padding: const EdgeInsets.only(
+ left: 24, right: 24, bottom: 12
+ ),
child: TextFormField(
controller: domainController,
onChanged: validateDomain,
@@ -104,9 +106,10 @@ class _AddDnsRewriteModalState extends State {
),
),
),
- const SizedBox(height: 30),
Padding(
- padding: const EdgeInsets.symmetric(horizontal: 24),
+ padding: const EdgeInsets.only(
+ left: 24, right: 24, top: 12
+ ),
child: TextFormField(
controller: answerController,
onChanged: (_) => checkValidValues(),
@@ -124,44 +127,70 @@ class _AddDnsRewriteModalState extends State {
],
),
),
- Padding(
- padding: const EdgeInsets.all(24),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.end,
- children: [
- TextButton(
- onPressed: () => Navigator.pop(context),
- child: Text(AppLocalizations.of(context)!.cancel),
- ),
- const SizedBox(width: 20),
- TextButton(
- onPressed: validData == true
- ? () {
- Navigator.pop(context);
- widget.onConfirm(
- RewriteRulesData(
- domain: domainController.text,
- answer: answerController.text
- )
- );
- }
- : null,
- child: Text(
- AppLocalizations.of(context)!.confirm,
- style: TextStyle(
- color: validData == true
- ? Theme.of(context).colorScheme.primary
- : Theme.of(context).colorScheme.onSurface.withOpacity(0.38)
- ),
+ ),
+ Padding(
+ padding: const EdgeInsets.all(24),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.end,
+ children: [
+ TextButton(
+ onPressed: () => Navigator.pop(context),
+ child: Text(AppLocalizations.of(context)!.cancel),
+ ),
+ const SizedBox(width: 20),
+ TextButton(
+ onPressed: validData == true
+ ? () {
+ Navigator.pop(context);
+ widget.onConfirm(
+ RewriteRulesData(
+ domain: domainController.text,
+ answer: answerController.text
+ )
+ );
+ }
+ : null,
+ child: Text(
+ AppLocalizations.of(context)!.confirm,
+ style: TextStyle(
+ color: validData == true
+ ? Theme.of(context).colorScheme.primary
+ : Theme.of(context).colorScheme.onSurface.withOpacity(0.38)
),
),
- ],
- ),
+ ),
+ ],
),
- if (Platform.isIOS) const SizedBox(height: 16)
- ],
+ ),
+ if (Platform.isIOS) const SizedBox(height: 16)
+ ],
+ );
+ }
+
+ if (widget.dialog == true) {
+ return Dialog(
+ child: ConstrainedBox(
+ constraints: const BoxConstraints(
+ maxWidth: 400
+ ),
+ child: content()
),
- ),
- );
+ );
+ }
+ else {
+ return Padding(
+ padding: MediaQuery.of(context).viewInsets,
+ child: Container(
+ decoration: BoxDecoration(
+ borderRadius: const BorderRadius.only(
+ topLeft: Radius.circular(28),
+ topRight: Radius.circular(28)
+ ),
+ color: Theme.of(context).dialogBackgroundColor,
+ ),
+ child: content()
+ ),
+ );
+ }
}
}
\ No newline at end of file
diff --git a/lib/screens/settings/dns_rewrites/dns_rewrites.dart b/lib/screens/settings/dns_rewrites/dns_rewrites.dart
index 119da4c..d9fe68f 100644
--- a/lib/screens/settings/dns_rewrites/dns_rewrites.dart
+++ b/lib/screens/settings/dns_rewrites/dns_rewrites.dart
@@ -1,5 +1,7 @@
// ignore_for_file: use_build_context_synchronously
+import 'dart:io';
+
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
@@ -70,6 +72,8 @@ class _DnsRewritesWidgetState extends State {
final serversProvider = Provider.of(context);
final appConfigProvider = Provider.of(context);
+ final width = MediaQuery.of(context).size.width;
+
void deleteDnsRewrite(RewriteRulesData rule) async {
ProcessModal processModal = ProcessModal(context: context);
processModal.open(AppLocalizations.of(context)!.deleting);
@@ -288,14 +292,26 @@ class _DnsRewritesWidgetState extends State {
body: generateBody(),
floatingActionButton: FloatingActionButton(
onPressed: () => {
- showModalBottomSheet(
- context: context,
- builder: (context) => AddDnsRewriteModal(
- onConfirm: addDnsRewrite,
- ),
- backgroundColor: Colors.transparent,
- isScrollControlled: true
- )
+ if (width > 900 || !(Platform.isAndroid || Platform.isIOS)) {
+ showDialog(
+ context: context,
+ builder: (context) => AddDnsRewriteModal(
+ onConfirm: addDnsRewrite,
+ dialog: true,
+ ),
+ )
+ }
+ else {
+ showModalBottomSheet(
+ context: context,
+ builder: (context) => AddDnsRewriteModal(
+ onConfirm: addDnsRewrite,
+ dialog: false,
+ ),
+ backgroundColor: Colors.transparent,
+ isScrollControlled: true
+ )
+ }
},
child: const Icon(Icons.add),
),
diff --git a/lib/screens/settings/encryption/custom_text_field.dart b/lib/screens/settings/encryption/custom_text_field.dart
index c149873..6a1cac8 100644
--- a/lib/screens/settings/encryption/custom_text_field.dart
+++ b/lib/screens/settings/encryption/custom_text_field.dart
@@ -26,8 +26,12 @@ class EncryptionTextField extends StatelessWidget {
@override
Widget build(BuildContext context) {
+ final width = MediaQuery.of(context).size.width;
+
return Padding(
- padding: const EdgeInsets.symmetric(horizontal: 16),
+ padding: width > 900
+ ? const EdgeInsets.symmetric(horizontal: 8)
+ : const EdgeInsets.symmetric(horizontal: 16),
child: ConstrainedBox(
constraints: const BoxConstraints(
maxHeight: 200
diff --git a/lib/screens/settings/encryption/encryption.dart b/lib/screens/settings/encryption/encryption.dart
index e1f3686..2392287 100644
--- a/lib/screens/settings/encryption/encryption.dart
+++ b/lib/screens/settings/encryption/encryption.dart
@@ -234,6 +234,8 @@ class _EncryptionSettingsWidgetState extends State {
final serversProvider = Provider.of(context);
final appConfigProvider = Provider.of(context);
+ final width = MediaQuery.of(context).size.width;
+
void saveData() async {
ProcessModal processModal = ProcessModal(context: context);
processModal.open(AppLocalizations.of(context)!.savingConfig);
@@ -343,43 +345,59 @@ class _EncryptionSettingsWidgetState extends State {
disabled: !enabled,
),
const SizedBox(height: 10),
- EncryptionTextField(
- enabled: enabled,
- controller: httpsPortController,
- icon: Icons.numbers_rounded,
- onChanged: (value) {
- setState(() => httpsPortError = validatePort(context, value));
- onEditValidate();
- },
- errorText: httpsPortError,
- label: AppLocalizations.of(context)!.httpsPort,
- keyboardType: TextInputType.number,
- ),
- const SizedBox(height: 30),
- EncryptionTextField(
- enabled: enabled,
- controller: tlsPortController,
- icon: Icons.numbers_rounded,
- onChanged: (value) {
- setState(() => tlsPortError = validatePort(context, value));
- onEditValidate();
- },
- errorText: tlsPortError,
- label: AppLocalizations.of(context)!.tlsPort,
- keyboardType: TextInputType.number,
- ),
- const SizedBox(height: 30),
- EncryptionTextField(
- enabled: enabled,
- controller: dnsOverQuicPortController,
- icon: Icons.numbers_rounded,
- onChanged: (value) {
- setState(() => dnsOverQuicPortError = validatePort(context, value));
- onEditValidate();
- },
- errorText: dnsOverQuicPortError,
- label: AppLocalizations.of(context)!.dnsOverQuicPort,
- keyboardType: TextInputType.number,
+ Wrap(
+ children: [
+ FractionallySizedBox(
+ widthFactor: width > 900 ? 0.33 : 1,
+ child: EncryptionTextField(
+ enabled: enabled,
+ controller: httpsPortController,
+ icon: Icons.numbers_rounded,
+ onChanged: (value) {
+ setState(() => httpsPortError = validatePort(context, value));
+ onEditValidate();
+ },
+ errorText: httpsPortError,
+ label: AppLocalizations.of(context)!.httpsPort,
+ keyboardType: TextInputType.number,
+ ),
+ ),
+ Padding(
+ padding: width <= 900
+ ? const EdgeInsets.symmetric(vertical: 24)
+ : const EdgeInsets.all(0),
+ child: FractionallySizedBox(
+ widthFactor: width > 900 ? 0.33 : 1,
+ child: EncryptionTextField(
+ enabled: enabled,
+ controller: tlsPortController,
+ icon: Icons.numbers_rounded,
+ onChanged: (value) {
+ setState(() => tlsPortError = validatePort(context, value));
+ onEditValidate();
+ },
+ errorText: tlsPortError,
+ label: AppLocalizations.of(context)!.tlsPort,
+ keyboardType: TextInputType.number,
+ ),
+ ),
+ ),
+ FractionallySizedBox(
+ widthFactor: width > 900 ? 0.33 : 1,
+ child: EncryptionTextField(
+ enabled: enabled,
+ controller: dnsOverQuicPortController,
+ icon: Icons.numbers_rounded,
+ onChanged: (value) {
+ setState(() => dnsOverQuicPortError = validatePort(context, value));
+ onEditValidate();
+ },
+ errorText: dnsOverQuicPortError,
+ label: AppLocalizations.of(context)!.dnsOverQuicPort,
+ keyboardType: TextInputType.number,
+ ),
+ ),
+ ],
),
SectionLabel(
label: AppLocalizations.of(context)!.certificates,
diff --git a/lib/screens/settings/safe_search_settings.dart b/lib/screens/settings/safe_search_settings.dart
index cefd757..fe428fa 100644
--- a/lib/screens/settings/safe_search_settings.dart
+++ b/lib/screens/settings/safe_search_settings.dart
@@ -52,24 +52,26 @@ class _SafeSearchSettingsScreenWidgetState extends State 900) {
+ return SplitView.material(
+ hideDivider: true,
+ flexWidth: const FlexWidth(mainViewFlexWidth: 1, secondaryViewFlexWidth: 2),
+ placeholder: Center(
+ child: Padding(
+ padding: const EdgeInsets.all(24),
+ child: Text(
+ AppLocalizations.of(context)!.selectOptionLeftColumn,
+ textAlign: TextAlign.center,
+ style: TextStyle(
+ fontSize: 24,
+ color: Theme.of(context).colorScheme.onSurfaceVariant
+ ),
+ ),
+ ),
+ ),
+ child: const SettingsWidget(),
+ );
+ }
+ else {
+ return const SettingsWidget();
+ }
+ }
+}
+class SettingsWidget extends StatelessWidget {
+ const SettingsWidget({Key? key}) : super(key: key);
+
@override
Widget build(BuildContext context) {
final appConfigProvider = Provider.of(context);
final serversProvider = Provider.of(context);
- void navigateServers() {
- Future.delayed(const Duration(milliseconds: 0), (() {
- Navigator.of(context).push(
- MaterialPageRoute(builder: (context) => const Servers())
+ final width = MediaQuery.of(context).size.width;
+
+ if (width <= 900 && appConfigProvider.selectedSettingsScreen != null) {
+ appConfigProvider.setSelectedSettingsScreen(screen: null);
+ }
+
+ Widget settingsTile({
+ required String title,
+ required String subtitle,
+ required IconData icon,
+ Widget? trailing,
+ required Widget screenToNavigate,
+ required int thisItem
+ }) {
+ if (width > 900) {
+ return CustomSettingsTile(
+ title: title,
+ subtitle: subtitle,
+ icon: icon,
+ trailing: trailing,
+ thisItem: thisItem,
+ selectedItem: appConfigProvider.selectedSettingsScreen,
+ onTap: () {
+ appConfigProvider.setSelectedSettingsScreen(screen: thisItem, notify: true);
+ SplitView.of(context).setSecondary(screenToNavigate);
+ },
);
- }));
- }
+ }
+ else {
+ return CustomListTile(
+ title: title,
+ subtitle: subtitle,
+ icon: icon,
+ trailing: trailing,
+ onTap: () {
+ Navigator.of(context).push(
+ MaterialPageRoute(builder: (context) => screenToNavigate)
+ );
+ },
+ );
+ }
+ }
return Scaffold(
appBar: AppBar(
@@ -49,85 +120,55 @@ class Settings extends StatelessWidget {
),
body: ListView(
children: [
- if (serversProvider.selectedServer != null) ...[
+ if (serversProvider.selectedServer != null && serversProvider.serverStatus.data != null) ...[
SectionLabel(label: AppLocalizations.of(context)!.serverSettings),
if (serverVersionIsAhead(
currentVersion: serversProvider.serverStatus.data!.serverVersion,
referenceVersion: 'v0.107.28',
referenceVersionBeta: 'v0.108.0-b.33'
- ) == true) CustomListTile(
+ ) == true) settingsTile(
icon: Icons.search_rounded,
title: AppLocalizations.of(context)!.safeSearch,
subtitle: AppLocalizations.of(context)!.safeSearchSettings,
- onTap: () => {
- Navigator.of(context).push(
- MaterialPageRoute(
- builder: (context) => const SafeSearchSettingsScreen()
- )
- )
- },
+ thisItem: 0,
+ screenToNavigate: const SafeSearchSettingsScreen(),
),
- CustomListTile(
+ settingsTile(
icon: Icons.lock_rounded,
title: AppLocalizations.of(context)!.accessSettings,
subtitle: AppLocalizations.of(context)!.accessSettingsDescription,
- onTap: () => {
- Navigator.of(context).push(
- MaterialPageRoute(
- builder: (context) => const AccessSettings()
- )
- )
- },
+ thisItem: 1,
+ screenToNavigate: const AccessSettings(),
),
- CustomListTile(
+ settingsTile(
icon: Icons.install_desktop_rounded,
title: AppLocalizations.of(context)!.dhcpSettings,
subtitle: AppLocalizations.of(context)!.dhcpSettingsDescription,
- onTap: () => {
- Navigator.of(context).push(
- MaterialPageRoute(
- builder: (context) => const Dhcp()
- )
- )
- },
+ thisItem: 2,
+ screenToNavigate: const Dhcp(),
),
- CustomListTile(
+ settingsTile(
icon: Icons.dns_rounded,
title: AppLocalizations.of(context)!.dnsSettings,
subtitle: AppLocalizations.of(context)!.dnsSettingsDescription,
- onTap: () => {
- Navigator.of(context).push(
- MaterialPageRoute(
- builder: (context) => const DnsSettings()
- )
- )
- },
+ thisItem: 3,
+ screenToNavigate: const DnsSettings(),
),
- CustomListTile(
+ settingsTile(
icon: Icons.security_rounded,
title: AppLocalizations.of(context)!.encryptionSettings,
subtitle: AppLocalizations.of(context)!.encryptionSettingsDescription,
- onTap: () => {
- Navigator.of(context).push(
- MaterialPageRoute(
- builder: (context) => const EncryptionSettings()
- )
- )
- },
+ thisItem: 4,
+ screenToNavigate: const EncryptionSettings(),
),
- CustomListTile(
+ settingsTile(
icon: Icons.route_rounded,
title: AppLocalizations.of(context)!.dnsRewrites,
subtitle: AppLocalizations.of(context)!.dnsRewritesDescription,
- onTap: () => {
- Navigator.of(context).push(
- MaterialPageRoute(
- builder: (context) => const DnsRewrites()
- )
- )
- },
+ thisItem: 5,
+ screenToNavigate: const DnsRewrites(),
),
- if (serversProvider.updateAvailable.data != null) CustomListTile(
+ if (serversProvider.updateAvailable.data != null) settingsTile(
icon: Icons.system_update_rounded,
title: AppLocalizations.of(context)!.updates,
subtitle: AppLocalizations.of(context)!.updatesDescription,
@@ -144,37 +185,26 @@ class Settings extends StatelessWidget {
),
)
: null,
- onTap: () => {
- Navigator.of(context).push(
- MaterialPageRoute(
- builder: (context) => const UpdateScreen()
- )
- )
- },
+ thisItem: 6,
+ screenToNavigate: const UpdateScreen(),
),
- CustomListTile(
+ settingsTile(
icon: Icons.info_rounded,
title: AppLocalizations.of(context)!.serverInformation,
subtitle: AppLocalizations.of(context)!.serverInformationDescription,
- onTap: () => {
- Navigator.of(context).push(
- MaterialPageRoute(
- builder: (context) => const ServerInformation()
- )
- )
- },
+ thisItem: 7,
+ screenToNavigate: const ServerInformation(),
),
],
SectionLabel(label: AppLocalizations.of(context)!.appSettings),
- CustomListTile(
+ settingsTile(
icon: Icons.palette_rounded,
title: AppLocalizations.of(context)!.customization,
subtitle: AppLocalizations.of(context)!.customizationDescription,
- onTap: () => Navigator.push(context, MaterialPageRoute(
- builder: (context) => const Customization()
- ))
+ thisItem: 8,
+ screenToNavigate: const Customization(),
),
- CustomListTile(
+ settingsTile(
icon: Icons.storage_rounded,
title: AppLocalizations.of(context)!.servers,
subtitle: serversProvider.selectedServer != null
@@ -182,31 +212,22 @@ class Settings extends StatelessWidget {
? "${AppLocalizations.of(context)!.connectedTo} ${serversProvider.selectedServer!.name}"
: "${AppLocalizations.of(context)!.selectedServer} ${serversProvider.selectedServer!.name}"
: AppLocalizations.of(context)!.noServerSelected,
- onTap: navigateServers,
+ thisItem: 9,
+ screenToNavigate: const Servers(),
),
- CustomListTile(
+ settingsTile(
icon: Icons.settings,
title: AppLocalizations.of(context)!.generalSettings,
subtitle: AppLocalizations.of(context)!.generalSettingsDescription,
- onTap: () => {
- Navigator.of(context).push(
- MaterialPageRoute(
- builder: (context) => const GeneralSettings()
- )
- )
- },
+ thisItem: 10,
+ screenToNavigate: const GeneralSettings(),
),
- CustomListTile(
+ settingsTile(
icon: Icons.build_outlined,
title: AppLocalizations.of(context)!.advancedSettings,
subtitle: AppLocalizations.of(context)!.advancedSetupDescription,
- onTap: () => {
- Navigator.of(context).push(
- MaterialPageRoute(
- builder: (context) => const AdvancedSettings()
- )
- )
- },
+ thisItem: 11,
+ screenToNavigate: const AdvancedSettings(),
),
SectionLabel(label: AppLocalizations.of(context)!.aboutApp),
CustomListTile(
@@ -222,7 +243,7 @@ class Settings extends StatelessWidget {
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
- IconButton(
+ if (Platform.isAndroid) IconButton(
onPressed: () => openUrl(Urls.playStore),
icon: SvgPicture.asset(
'assets/resources/google-play.svg',
diff --git a/lib/screens/settings/update_server/update.dart b/lib/screens/settings/update_server/update.dart
index 5fa898e..f92292a 100644
--- a/lib/screens/settings/update_server/update.dart
+++ b/lib/screens/settings/update_server/update.dart
@@ -65,16 +65,18 @@ class UpdateScreen extends StatelessWidget {
Widget headerPortrait() {
return Column(
children: [
+ const SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
- IconButton(
+ if (Navigator.canPop(context)) IconButton(
icon: Icon(
Icons.arrow_back,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
onPressed: () => Navigator.pop(context),
),
+ if (!Navigator.canPop(context)) const SizedBox(),
IconButton(
icon: Icon(
Icons.refresh_rounded,
@@ -170,120 +172,6 @@ class UpdateScreen extends StatelessWidget {
);
}
- Widget headerLandscape() {
- return Column(
- mainAxisSize: MainAxisSize.max,
- children: [
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- IconButton(
- icon: Icon(
- Icons.arrow_back,
- color: Theme.of(context).colorScheme.onSurfaceVariant,
- ),
- onPressed: () => Navigator.pop(context),
- ),
- IconButton(
- icon: Icon(
- Icons.refresh_rounded,
- color: Theme.of(context).colorScheme.onSurfaceVariant,
- ),
- tooltip: AppLocalizations.of(context)!.checkUpdates,
- onPressed: () => serversProvider.checkServerUpdatesAvailable(serversProvider.selectedServer!)
- ),
- ],
- ),
- Expanded(
- child: Padding(
- padding: const EdgeInsets.only(
- top: 8, bottom: 16, left: 16, right: 16
- ),
- child: Column(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- serversProvider.updateAvailable.loadStatus == LoadStatus.loading
- ? Column(
- children: const [
- CircularProgressIndicator(),
- SizedBox(height: 4)
- ],
- )
- : Icon(
- serversProvider.updateAvailable.data!.updateAvailable != null
- ? serversProvider.updateAvailable.data!.updateAvailable == true
- ? Icons.system_update_rounded
- : Icons.system_security_update_good_rounded
- : Icons.system_security_update_warning_rounded,
- size: 40,
- color: Theme.of(context).colorScheme.primary,
- ),
- const SizedBox(height: 16),
- Text(
- serversProvider.updateAvailable.loadStatus == LoadStatus.loading
- ? AppLocalizations.of(context)!.checkingUpdates
- : serversProvider.updateAvailable.data!.updateAvailable != null
- ? serversProvider.updateAvailable.data!.updateAvailable == true
- ? AppLocalizations.of(context)!.updateAvailable
- : AppLocalizations.of(context)!.serverUpdated
- : AppLocalizations.of(context)!.unknownStatus,
- style: const TextStyle(
- fontSize: 24,
- fontWeight: FontWeight.w400
- ),
- ),
- const SizedBox(height: 40),
- Expanded(
- child: Column(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- if (serversProvider.updateAvailable.loadStatus == LoadStatus.loaded) Column(
- crossAxisAlignment: CrossAxisAlignment.center,
- children: [
- Text(
- serversProvider.updateAvailable.data!.updateAvailable != null && serversProvider.updateAvailable.data!.updateAvailable == true
- ? AppLocalizations.of(context)!.newVersion
- : AppLocalizations.of(context)!.currentVersion,
- style: const TextStyle(
- fontSize: 16,
- ),
- ),
- const SizedBox(height: 4),
- Text(
- serversProvider.updateAvailable.data!.updateAvailable != null
- ? serversProvider.updateAvailable.data!.updateAvailable == true
- ? serversProvider.updateAvailable.data!.newVersion ?? 'N/A'
- : serversProvider.updateAvailable.data!.currentVersion
- : "N/A",
- style: TextStyle(
- fontSize: 12,
- fontWeight: FontWeight.w700,
- color: Theme.of(context).colorScheme.onSurfaceVariant
- ),
- )
- ],
- ),
- if (serversProvider.updateAvailable.loadStatus != LoadStatus.loaded) const SizedBox(),
- FilledButton.icon(
- icon: const Icon(Icons.download_rounded),
- label: Text(AppLocalizations.of(context)!.updateNow),
- onPressed: serversProvider.updateAvailable.data!.updateAvailable != null && serversProvider.updateAvailable.data!.updateAvailable == true
- ? serversProvider.updateAvailable.data!.canAutoupdate == true
- ? () => update()
- : () => showAutoUpdateUnavailableModal()
- : null
- )
- ],
- ),
- ),
- ],
- ),
- ),
- ),
- ],
- );
- }
-
final changelog = serversProvider.updateAvailable.loadStatus == LoadStatus.loaded && serversProvider.updateAvailable.data!.changelog != null
? ListView(
children: [
@@ -313,51 +201,20 @@ class UpdateScreen extends StatelessWidget {
: null;
return Scaffold(
- body: MediaQuery.of(context).size.width > 700
- ? Row(
- children: [
- Expanded(
- flex: 2,
- child: Container(
- color: Theme.of(context).colorScheme.surfaceVariant,
- child: Column(
- mainAxisSize: MainAxisSize.max,
- children: [
- Container(
- height: MediaQuery.of(context).size.height,
- padding: EdgeInsets.only(
- top: MediaQuery.of(context).viewPadding.top
- ),
- child: headerLandscape(),
- )
- ],
- ),
- ),
- ),
- Expanded(
- flex: 3,
- child: SafeArea(
- child: SizedBox(
- width: MediaQuery.of(context).size.width*0.6,
- child: changelog ?? const SizedBox(),
- ),
- ),
- )
- ],
- )
- : Column(
- children: [
- Container(
- color: Theme.of(context).colorScheme.surfaceVariant,
- child: SafeArea(
- child: headerPortrait()
- )
- ),
- changelog != null
- ? Expanded(child: changelog)
- : const SizedBox(),
- ]
- )
+ body: Column(
+ children: [
+ Container(
+ color: Theme.of(context).colorScheme.surfaceVariant,
+ child: SafeArea(
+ child: headerPortrait()
+ )
+ ),
+ const SizedBox(height: 16),
+ changelog != null
+ ? Expanded(child: changelog)
+ : const SizedBox(),
+ ]
+ )
);
}
}
\ No newline at end of file
diff --git a/lib/screens/top_items/top_items_modal.dart b/lib/screens/top_items/top_items_modal.dart
new file mode 100644
index 0000000..55aae09
--- /dev/null
+++ b/lib/screens/top_items/top_items_modal.dart
@@ -0,0 +1,276 @@
+// ignore_for_file: use_build_context_synchronously
+
+import 'dart:io';
+
+import 'package:flutter/material.dart';
+import 'package:percent_indicator/percent_indicator.dart';
+import 'package:provider/provider.dart';
+import 'package:flutter_gen/gen_l10n/app_localizations.dart';
+
+import 'package:adguard_home_manager/screens/home/top_items_options_modal.dart';
+import 'package:adguard_home_manager/widgets/custom_list_tile.dart';
+
+import 'package:adguard_home_manager/models/applied_filters.dart';
+import 'package:adguard_home_manager/functions/copy_clipboard.dart';
+import 'package:adguard_home_manager/providers/logs_provider.dart';
+import 'package:adguard_home_manager/functions/snackbar.dart';
+import 'package:adguard_home_manager/functions/number_format.dart';
+import 'package:adguard_home_manager/functions/block_unblock_domain.dart';
+import 'package:adguard_home_manager/providers/app_config_provider.dart';
+import 'package:adguard_home_manager/providers/servers_provider.dart';
+
+class TopItemsModal extends StatefulWidget {
+ final String type;
+ final String title;
+ final bool? isClient;
+ final List