diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 954af07..7a1967e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,7 +10,11 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - flutter: [ 3.24.0, stable] + include: + - flutter-version: 3.24.0 + flutter-channel: stable + - flutter-version: latest + flutter-channel: stable steps: - uses: actions/checkout@v2 - uses: subosito/flutter-action@v2 diff --git a/CHANGELOG.md b/CHANGELOG.md index fa02402..912ae49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 0.7.1 + +- Updated dependencies. +- Added `isWasm` to `PlayxPlatform` to detect if the app is running in a WebAssembly environment. +- Introduced `WebUtils`, a new utility class providing common browser-side features for web apps: + - Update the browser URL and query parameters without navigation. + - Control fullscreen mode (enter, exit, toggle). + - Set the browser tab title and application switcher label. + - Change `` background color directly or by CSS class. + - Open links in new tabs. + - Reload the page. + + ## v0.7.0 > Note: This release has breaking changes. diff --git a/lib/playx_core.dart b/lib/playx_core.dart index c8f6b0c..4e29d19 100644 --- a/lib/playx_core.dart +++ b/lib/playx_core.dart @@ -32,6 +32,7 @@ export 'src/utils/mapper_utilities.dart'; export 'src/utils/playx_platform.dart'; export 'src/utils/safe_convert.dart'; export 'src/utils/safe_json_convert.dart'; +export 'src/utils/web/web_utils.dart'; export 'src/logger/base_logger.dart'; export 'src/logger/playx_logger.dart'; export 'src/logger/playx_logger_settings.dart'; diff --git a/lib/src/extensions/date_time_extensions.dart b/lib/src/extensions/date_time_extensions.dart index 97d402f..638a254 100644 --- a/lib/src/extensions/date_time_extensions.dart +++ b/lib/src/extensions/date_time_extensions.dart @@ -7,7 +7,7 @@ enum Weekday { saturday, sunday; - static fromDate(DateTime date) { + static Weekday fromDate(DateTime date) { return Weekday.values[date.weekday - 1]; } } @@ -26,7 +26,7 @@ enum Month { november, december; - static fromDate(DateTime date) { + static Month fromDate(DateTime date) { return Month.values[date.month - 1]; } } diff --git a/lib/src/utils/playx_platform.dart b/lib/src/utils/playx_platform.dart index 2b38aa8..eeb0598 100644 --- a/lib/src/utils/playx_platform.dart +++ b/lib/src/utils/playx_platform.dart @@ -1,11 +1,14 @@ import 'package:flutter/foundation.dart' - show kIsWeb, TargetPlatform, defaultTargetPlatform; + show kIsWeb, TargetPlatform, defaultTargetPlatform, kIsWasm; /// Utility class for determining the current platform type and characteristics. class PlayxPlatform { /// Returns `true` if the platform is the web. static bool get isWeb => kIsWeb; + /// Returns `true` if the platform is WebAssembly. + static bool get isWasm => kIsWasm; + /// Returns `true` if the platform is Android. static bool get isAndroid => !isWeb && defaultTargetPlatform == TargetPlatform.android; diff --git a/lib/src/utils/safe_convert.dart b/lib/src/utils/safe_convert.dart index 003d167..f80ee15 100644 --- a/lib/src/utils/safe_convert.dart +++ b/lib/src/utils/safe_convert.dart @@ -10,8 +10,9 @@ int? toIntOrNull(dynamic value) { if (value is int) return value; if (value is double) return value.toInt(); if (value is bool) return value ? 1 : 0; - if (value is String) + if (value is String) { return int.tryParse(value) ?? double.tryParse(value)?.toInt(); + } return null; } @@ -69,12 +70,13 @@ double toDouble(dynamic value) => bool? toBoolOrNull(dynamic value) { if (value == null) return null; if (value is bool) return value; - if (value is num) + if (value is num) { return value == 1 ? true : value == 0 ? false : null; + } if (value is String) { final lower = value.toLowerCase(); if (lower == 'true') return true; diff --git a/lib/src/utils/web/web_utils.dart b/lib/src/utils/web/web_utils.dart new file mode 100644 index 0000000..41edcc1 --- /dev/null +++ b/lib/src/utils/web/web_utils.dart @@ -0,0 +1 @@ +export 'web_utils_stub.dart' if (dart.library.html) 'web_utils_web.dart'; diff --git a/lib/src/utils/web/web_utils_stub.dart b/lib/src/utils/web/web_utils_stub.dart new file mode 100644 index 0000000..9133201 --- /dev/null +++ b/lib/src/utils/web/web_utils_stub.dart @@ -0,0 +1,50 @@ +import 'package:flutter/services.dart'; + +/// A stubbed version of [WebUtils] for non-web platforms (e.g. mobile, desktop). +/// +/// All methods are safe no-ops and will not affect the app outside the web environment. +class WebUtils { + const WebUtils._(); + + /// Stub: Does nothing on non-web platforms. + static void updateUrl(Uri uri) {} + + /// Stub: Does nothing on non-web platforms. + static void setBodyBackgroundColor(String colorHex) {} + + /// Stub: Sets only the app switcher title, as `document.title` is not supported. + static void setAppTitle({ + required String title, + Color? primaryColor, + bool webOnly = true, + }) { + if (webOnly) { + return; + } + SystemChrome.setApplicationSwitcherDescription( + ApplicationSwitcherDescription( + label: title, primaryColor: primaryColor?.toARGB32()), + ); + } + + /// Stub: Always returns `false` on non-web platforms. + static bool isFullScreen() => false; + + /// Stub: Does nothing on non-web platforms. + static void enterFullScreen() {} + + /// Stub: Does nothing on non-web platforms. + static void exitFullScreen() {} + + /// Stub: Does nothing on non-web platforms. + static void toggleFullScreen() {} + + /// Stub: Does nothing on non-web platforms. + static void reloadPage() {} + + /// Stub: Does nothing on non-web platforms. + static void openInNewTab(String url) {} + + /// Stub: Does nothing on non-web platforms. + static void updateQueryParameters(Map newParams) {} +} diff --git a/lib/src/utils/web/web_utils_web.dart b/lib/src/utils/web/web_utils_web.dart new file mode 100644 index 0000000..3ab9bf4 --- /dev/null +++ b/lib/src/utils/web/web_utils_web.dart @@ -0,0 +1,86 @@ +import 'package:web/web.dart'; +import 'package:flutter/services.dart'; + +/// A collection of static helper methods for performing common web-related tasks. +/// Only works when running on the web. +class WebUtils { + const WebUtils._(); + + /// Updates the browser's URL without navigating or reloading the page. + /// + /// This uses the HTML5 History API to replace the current URL in the address bar. + static void updateUrl(Uri uri) { + window.history.replaceState(null, '', uri.toString()); + } + + /// Sets the background color of the `` element directly via inline style. + /// + /// This overrides any background-color set by CSS classes. + static void setBodyBackgroundColor(String colorHex) { + final body = document.body; + if (body != null) { + body.style.backgroundColor = colorHex; + } + } + + /// Sets the title of the web tab and application switcher. + /// + /// This affects browser tab titles and some OS-level application switchers. + static void setAppTitle(String title) { + document.title = title; + SystemChrome.setApplicationSwitcherDescription( + ApplicationSwitcherDescription(label: title), + ); + } + + /// Checks if the browser is currently in fullscreen mode. + /// + /// Returns `true` if fullscreen mode is active, `false` otherwise. + static bool isFullScreen() { + return document.fullscreenElement != null; + } + + /// Requests the browser to enter fullscreen mode. + /// + /// This may require a user gesture (like a button press) to succeed. + static void enterFullScreen() { + document.documentElement?.requestFullscreen(); + } + + /// Requests the browser to exit fullscreen mode if it's currently active. + static void exitFullScreen() { + document.exitFullscreen(); + } + + /// Toggles fullscreen mode: enters if not in fullscreen, exits if already in fullscreen. + /// + /// This is useful for toggling fullscreen with a single button or gesture. + static void toggleFullScreen() { + if (isFullScreen()) { + exitFullScreen(); + } else { + enterFullScreen(); + } + } + + /// Reloads the current web page. + static void reloadPage() { + window.location.reload(); + } + + /// Opens a new browser tab or window with the given [url]. + /// + /// Uses `target = "_blank"` by default. + static void openInNewTab(String url) { + window.open(url, '_blank'); + } + + /// Updates only the query parameters in the browser URL without navigating. + /// + /// Preserves the current path and hash. + static void updateQueryParameters(Map newParams) { + final currentUri = Uri.parse(window.location.href); + final newUri = currentUri.replace(queryParameters: newParams); + window.history.replaceState(null, '', newUri.toString()); + } +} diff --git a/pubspec.lock b/pubspec.lock index a50d13a..cc03ebf 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -446,7 +446,7 @@ packages: source: hosted version: "15.0.0" web: - dependency: transitive + dependency: "direct main" description: name: web sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" diff --git a/pubspec.yaml b/pubspec.yaml index bd5b0d1..e3183f6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -26,6 +26,7 @@ dependencies: get_it: ^8.0.3 worker_manager: ^7.2.6 talker_logger: ^4.9.1 + web: ^1.1.1 dev_dependencies: flutter_test: