From ea1f110ff3fa3f66b603ab0046f5c524fc7a0a70 Mon Sep 17 00:00:00 2001 From: Mithul Nayagam Date: Fri, 5 Dec 2025 15:17:21 +0530 Subject: [PATCH 1/8] feat: Add theme support to Stac framework with cloud loading Add comprehensive theme support to the Stac framework, enabling themes to be loaded from Stac Cloud similar to screens. This includes: - Add StacArtifactType enum to support both screens and themes - Add StacCloudTheme annotation for marking theme builder methods - Add StacAppTheme helper class for loading themes from cloud, network, or JSON - Extend StacApp to support FutureOr for async theme loading - Refactor StacCacheService to be artifact-type agnostic (supports screens and themes) - Refactor StacCloud to support fetching both screens and themes with unified caching Themes can now be loaded asynchronously from Stac Cloud using the same caching strategies as screens (optimistic, cache-first, network-first, etc.), with proper loading states handled in StacApp. --- .../stac/lib/src/framework/framework.dart | 1 + packages/stac/lib/src/framework/stac_app.dart | 111 +++++- .../lib/src/framework/stac_app_theme.dart | 81 +++++ .../lib/src/models/stac_artifact_type.dart | 8 + .../lib/src/services/stac_cache_service.dart | 124 ++++--- .../stac/lib/src/services/stac_cloud.dart | 321 +++++++++++++----- .../lib/annotations/annotations.dart | 1 + .../lib/annotations/stac_cloud_theme.dart | 19 ++ 8 files changed, 519 insertions(+), 147 deletions(-) create mode 100644 packages/stac/lib/src/framework/stac_app_theme.dart create mode 100644 packages/stac/lib/src/models/stac_artifact_type.dart create mode 100644 packages/stac_core/lib/annotations/stac_cloud_theme.dart diff --git a/packages/stac/lib/src/framework/framework.dart b/packages/stac/lib/src/framework/framework.dart index 66a085da..5085f31d 100644 --- a/packages/stac/lib/src/framework/framework.dart +++ b/packages/stac/lib/src/framework/framework.dart @@ -2,3 +2,4 @@ export 'stac.dart'; export 'stac_app.dart'; export 'stac_registry.dart'; export 'stac_service.dart'; +export 'stac_app_theme.dart'; diff --git a/packages/stac/lib/src/framework/stac_app.dart b/packages/stac/lib/src/framework/stac_app.dart index 01e337a5..74b4cca3 100644 --- a/packages/stac/lib/src/framework/stac_app.dart +++ b/packages/stac/lib/src/framework/stac_app.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:stac/src/parsers/theme/themes.dart'; @@ -108,8 +110,8 @@ class StacApp extends StatelessWidget { final TransitionBuilder? builder; final String title; final GenerateAppTitle? onGenerateTitle; - final StacTheme? theme; - final StacTheme? darkTheme; + final FutureOr? theme; + final FutureOr? darkTheme; final ThemeData? highContrastTheme; final ThemeData? highContrastDarkTheme; final ThemeMode? themeMode; @@ -142,6 +144,22 @@ class StacApp extends StatelessWidget { } Widget _materialApp(BuildContext context) { + return _withResolvedThemes( + context, + (resolvedContext, resolved) => + _buildMaterialApp(resolvedContext, resolved), + ); + } + + Widget _materialRouterApp(BuildContext context) { + return _withResolvedThemes( + context, + (resolvedContext, resolved) => + _buildMaterialAppRouter(resolvedContext, resolved), + ); + } + + Widget _buildMaterialApp(BuildContext context, _ResolvedStacThemes themes) { return MaterialApp( navigatorKey: navigatorKey, scaffoldMessengerKey: scaffoldMessengerKey, @@ -162,8 +180,8 @@ class StacApp extends StatelessWidget { builder: builder, title: title, onGenerateTitle: onGenerateTitle, - theme: theme?.parse(context), - darkTheme: darkTheme?.parse(context), + theme: themes.theme?.parse(context), + darkTheme: themes.darkTheme?.parse(context), highContrastTheme: highContrastTheme, highContrastDarkTheme: highContrastDarkTheme, themeMode: themeMode, @@ -188,7 +206,10 @@ class StacApp extends StatelessWidget { ); } - Widget _materialRouterApp(BuildContext context) { + Widget _buildMaterialAppRouter( + BuildContext context, + _ResolvedStacThemes themes, + ) { return MaterialApp.router( scaffoldMessengerKey: scaffoldMessengerKey, routeInformationProvider: routeInformationProvider, @@ -200,8 +221,8 @@ class StacApp extends StatelessWidget { title: title, onGenerateTitle: onGenerateTitle, color: color, - theme: theme?.parse(context), - darkTheme: darkTheme?.parse(context), + theme: themes.theme?.parse(context), + darkTheme: themes.darkTheme?.parse(context), highContrastTheme: highContrastTheme, highContrastDarkTheme: highContrastDarkTheme, themeMode: themeMode, @@ -224,4 +245,80 @@ class StacApp extends StatelessWidget { scrollBehavior: scrollBehavior, ); } + + FutureOr<_ResolvedStacThemes> _resolveThemes() { + final themeInput = theme; + final darkThemeInput = darkTheme; + + final Future? themeFuture = themeInput is Future + ? themeInput + : null; + final Future? darkThemeFuture = + darkThemeInput is Future ? darkThemeInput : null; + + final StacTheme? themeValue = themeFuture == null + ? themeInput as StacTheme? + : null; + final StacTheme? darkThemeValue = darkThemeFuture == null + ? darkThemeInput as StacTheme? + : null; + + if (themeFuture == null && darkThemeFuture == null) { + return _ResolvedStacThemes(theme: themeValue, darkTheme: darkThemeValue); + } + + return Future<_ResolvedStacThemes>(() async { + final resolvedTheme = + await (themeFuture ?? Future.value(themeValue)); + final resolvedDarkTheme = + await (darkThemeFuture ?? Future.value(darkThemeValue)); + + return _ResolvedStacThemes( + theme: resolvedTheme, + darkTheme: resolvedDarkTheme, + ); + }); + } + + Widget _withResolvedThemes( + BuildContext context, + Widget Function(BuildContext, _ResolvedStacThemes) builder, + ) { + final resolved = _resolveThemes(); + if (resolved is Future<_ResolvedStacThemes>) { + return FutureBuilder<_ResolvedStacThemes>( + future: resolved, + builder: (futureContext, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const _ThemeFutureLoading(); + } + if (snapshot.hasError) { + return const _ThemeFutureLoading(); + } + final themes = snapshot.data; + if (themes == null) { + return const _ThemeFutureLoading(); + } + return builder(futureContext, themes); + }, + ); + } + return builder(context, resolved); + } +} + +class _ResolvedStacThemes { + const _ResolvedStacThemes({required this.theme, required this.darkTheme}); + + final StacTheme? theme; + final StacTheme? darkTheme; +} + +class _ThemeFutureLoading extends StatelessWidget { + const _ThemeFutureLoading(); + + @override + Widget build(BuildContext context) { + return const Material(child: Center(child: CircularProgressIndicator())); + } } diff --git a/packages/stac/lib/src/framework/stac_app_theme.dart b/packages/stac/lib/src/framework/stac_app_theme.dart new file mode 100644 index 00000000..bf78e314 --- /dev/null +++ b/packages/stac/lib/src/framework/stac_app_theme.dart @@ -0,0 +1,81 @@ +import 'dart:convert'; + +import 'package:flutter/widgets.dart'; +import 'package:stac/src/services/stac_cloud.dart'; +import 'package:stac/src/services/stac_network_service.dart'; +import 'package:stac_core/actions/network_request/stac_network_request.dart'; +import 'package:stac_core/foundation/theme/stac_theme/stac_theme.dart'; + +/// Provides helpers to load [StacTheme] definitions for [StacApp]. +class StacAppTheme { + const StacAppTheme._(); + + /// Fetches a theme from the `/themes` endpoint by [themeName]. + /// + /// Returns `null` if the network call fails or the payload is malformed. + static Future fromCloud({required String themeName}) async { + final response = await StacCloud.fetchTheme(themeName: themeName); + if (response == null) { + return null; + } + + final rawData = response.data; + if (rawData is! Map) { + return null; + } + + final themePayload = _normalizeThemeJson(rawData['stacJson']); + if (themePayload == null) { + return null; + } + + return StacTheme.fromJson(themePayload); + } + + /// Fetches a theme over HTTP using a [StacNetworkRequest]. + /// + /// Mirrors [Stac.fromNetwork], allowing callers to reuse existing request + /// builders and middleware. + static Future fromNetwork({ + required BuildContext context, + required StacNetworkRequest request, + }) async { + final response = await StacNetworkService.request(context, request); + if (response == null) { + return null; + } + + return fromJson(response.data); + } + + /// Creates a [StacTheme] from raw JSON payloads. + /// + /// Accepts either a `Map` or a JSON `String`. Returns `null` + /// when the payload cannot be parsed into a valid [StacTheme]. + static StacTheme? fromJson(dynamic payload) { + final themePayload = _normalizeThemeJson(payload); + if (themePayload == null) { + return null; + } + return StacTheme.fromJson(themePayload); + } + + static Map? _normalizeThemeJson(dynamic payload) { + if (payload == null) { + return null; + } + if (payload is Map && payload['stacJson'] != null) { + return _normalizeThemeJson(payload['stacJson']); + } + if (payload is Map) { + return payload; + } + if (payload is String) { + final decoded = jsonDecode(payload); + if (decoded is Map) { + return decoded; + } + } + return null; + } +} diff --git a/packages/stac/lib/src/models/stac_artifact_type.dart b/packages/stac/lib/src/models/stac_artifact_type.dart new file mode 100644 index 00000000..6bcc7683 --- /dev/null +++ b/packages/stac/lib/src/models/stac_artifact_type.dart @@ -0,0 +1,8 @@ +/// Type of artifact that can be fetched from Stac Cloud. +enum StacArtifactType { + /// A screen artifact. + screen, + + /// A theme artifact. + theme, +} diff --git a/packages/stac/lib/src/services/stac_cache_service.dart b/packages/stac/lib/src/services/stac_cache_service.dart index 4737ca20..20f2e4f3 100644 --- a/packages/stac/lib/src/services/stac_cache_service.dart +++ b/packages/stac/lib/src/services/stac_cache_service.dart @@ -1,15 +1,13 @@ import 'package:shared_preferences/shared_preferences.dart'; import 'package:stac/src/models/stac_screen_cache.dart'; -/// Service for managing cached Stac screens. +/// Service for managing cached Stac artifacts (screens, themes, etc.). /// -/// This service uses SharedPreferences to persist screen data locally, +/// This service uses SharedPreferences to persist artifact data locally, /// enabling offline access and reducing unnecessary network requests. class StacCacheService { StacCacheService._(); - static const String _cachePrefix = 'stac_screen_cache_'; - /// Cached SharedPreferences instance for better performance. static SharedPreferences? _prefs; @@ -18,13 +16,29 @@ class StacCacheService { return _prefs ??= await SharedPreferences.getInstance(); } - /// Gets a cached screen by its name. + /// Gets the cache prefix for a given artifact type. + static String _getCachePrefix(String artifactType) { + switch (artifactType) { + case 'screen': + return 'stac_screen_cache_'; + case 'theme': + return 'stac_theme_cache_'; + default: + throw ArgumentError('Unknown artifact type: $artifactType'); + } + } + + /// Gets a cached artifact by its name and type. /// - /// Returns `null` if the screen is not cached. - static Future getCachedScreen(String screenName) async { + /// Returns `null` if the artifact is not cached. + static Future getCachedArtifact( + String artifactName, + String artifactType, + ) async { try { final prefs = await _sharedPrefs; - final cacheKey = _getCacheKey(screenName); + final cachePrefix = _getCachePrefix(artifactType); + final cacheKey = '$cachePrefix$artifactName'; final cachedData = prefs.getString(cacheKey); if (cachedData == null) { @@ -33,86 +47,60 @@ class StacCacheService { return StacScreenCache.fromJsonString(cachedData); } catch (e) { - // If there's an error reading from cache, return null - // and let the app fetch from network return null; } } - /// Saves a screen to the cache. + /// Saves an artifact to the cache. /// - /// If a screen with the same name already exists, it will be overwritten. - static Future saveScreen({ + /// If an artifact with the same name already exists, it will be overwritten. + static Future saveArtifact({ required String name, required String stacJson, required int version, + required String artifactType, }) async { try { final prefs = await _sharedPrefs; - final cacheKey = _getCacheKey(name); + final cachePrefix = _getCachePrefix(artifactType); + final cacheKey = '$cachePrefix$name'; - final screenCache = StacScreenCache( + final artifactCache = StacScreenCache( name: name, stacJson: stacJson, version: version, cachedAt: DateTime.now(), ); - return prefs.setString(cacheKey, screenCache.toJsonString()); + return prefs.setString(cacheKey, artifactCache.toJsonString()); } catch (e) { - // If there's an error saving to cache, return false - // but don't throw - the app should still work without cache return false; } } - /// Checks if a cached screen is still valid based on its age. - /// - /// Returns `true` if the cache is valid (not expired). - /// Returns `false` if the cache is expired or doesn't exist. - /// - /// If [maxAge] is `null`, cache is considered valid (no time-based expiration). - static Future isCacheValid({ - required String screenName, - Duration? maxAge, - }) async { - final cachedScreen = await getCachedScreen(screenName); - return isCacheValidSync(cachedScreen, maxAge); - } - - /// Synchronous version of [isCacheValid] for when you already have the cache. - /// - /// Use this to avoid re-fetching the cache when you already have it. - static bool isCacheValidSync( - StacScreenCache? cachedScreen, - Duration? maxAge, - ) { - if (cachedScreen == null) return false; - if (maxAge == null) return true; - - final age = DateTime.now().difference(cachedScreen.cachedAt); - return age <= maxAge; - } - - /// Removes a specific screen from the cache. - static Future removeScreen(String screenName) async { + /// Removes a specific artifact from the cache. + static Future removeArtifact( + String artifactName, + String artifactType, + ) async { try { final prefs = await _sharedPrefs; - final cacheKey = _getCacheKey(screenName); + final cachePrefix = _getCachePrefix(artifactType); + final cacheKey = '$cachePrefix$artifactName'; return prefs.remove(cacheKey); } catch (e) { return false; } } - /// Clears all cached screens. - static Future clearAllScreens() async { + /// Clears all cached artifacts of a specific type. + static Future clearAllArtifacts(String artifactType) async { try { final prefs = await _sharedPrefs; final keys = prefs.getKeys(); - final cacheKeys = keys.where((key) => key.startsWith(_cachePrefix)); + final cachePrefix = _getCachePrefix(artifactType); + final cacheKeys = keys.where((key) => key.startsWith(cachePrefix)); - // Use Future.wait for parallel deletion instead of sequential awaits await Future.wait(cacheKeys.map(prefs.remove)); return true; @@ -121,8 +109,32 @@ class StacCacheService { } } - /// Generates a cache key for a screen name. - static String _getCacheKey(String screenName) { - return '$_cachePrefix$screenName'; + /// Checks if a cached artifact is still valid based on its age. + /// + /// Returns `true` if the cache is valid (not expired). + /// Returns `false` if the cache is expired or doesn't exist. + /// + /// If [maxAge] is `null`, cache is considered valid (no time-based expiration). + static Future isCacheValid({ + required String artifactName, + required String artifactType, + Duration? maxAge, + }) async { + final cachedArtifact = await getCachedArtifact(artifactName, artifactType); + return isCacheValidSync(cachedArtifact, maxAge); + } + + /// Synchronous version of [isCacheValid] for when you already have the cache. + /// + /// Use this to avoid re-fetching the cache when you already have it. + static bool isCacheValidSync( + StacScreenCache? cachedArtifact, + Duration? maxAge, + ) { + if (cachedArtifact == null) return false; + if (maxAge == null) return true; + + final age = DateTime.now().difference(cachedArtifact.cachedAt); + return age <= maxAge; } } diff --git a/packages/stac/lib/src/services/stac_cloud.dart b/packages/stac/lib/src/services/stac_cloud.dart index c95cf504..62b7142e 100644 --- a/packages/stac/lib/src/services/stac_cloud.dart +++ b/packages/stac/lib/src/services/stac_cloud.dart @@ -1,5 +1,6 @@ import 'package:dio/dio.dart'; import 'package:stac/src/framework/stac_service.dart'; +import 'package:stac/src/models/stac_artifact_type.dart'; import 'package:stac/src/models/stac_cache_config.dart'; import 'package:stac/src/models/stac_screen_cache.dart'; import 'package:stac/src/services/stac_cache_service.dart'; @@ -19,12 +20,41 @@ class StacCloud { ), ); - static const String _fetchUrl = 'https://api.stac.dev/screens'; + static const String _baseUrl = + 'https://us-central1-stac-dev-9eff0.cloudfunctions.net'; - /// Tracks screens currently being fetched in background to prevent duplicates. - static final Set _backgroundFetchInProgress = {}; + /// Gets the fetch URL for a given artifact type. + static String _getFetchUrl(StacArtifactType artifactType) { + switch (artifactType) { + case StacArtifactType.screen: + return '$_baseUrl/screens'; + case StacArtifactType.theme: + return '$_baseUrl/themes'; + } + } - /// Fetches a screen from Stac Cloud with intelligent caching. + /// Gets the query parameter name for a given artifact type. + static String _getQueryParamName(StacArtifactType artifactType) { + switch (artifactType) { + case StacArtifactType.screen: + return 'screenName'; + case StacArtifactType.theme: + return 'themeName'; + } + } + + /// Gets the artifact type string for cache operations. + static String _getArtifactTypeString(StacArtifactType artifactType) { + return artifactType.name; + } + + /// Tracks artifacts currently being fetched in background to prevent duplicates. + static final Map> _backgroundFetchInProgress = { + StacArtifactType.screen: {}, + StacArtifactType.theme: {}, + }; + + /// Fetches an artifact from Stac Cloud with intelligent caching. /// /// The [cacheConfig] parameter controls caching behavior: /// - Strategy: How to handle cache vs network @@ -33,8 +63,9 @@ class StacCloud { /// - staleWhileRevalidate: Use expired cache while fetching fresh data /// /// Defaults to [StacCacheConfig.optimistic] if not provided. - static Future fetchScreen({ - required String routeName, + static Future _fetchArtifact({ + required StacArtifactType artifactType, + required String artifactName, StacCacheConfig cacheConfig = const StacCacheConfig( strategy: StacCacheStrategy.optimistic, ), @@ -44,144 +75,221 @@ class StacCloud { throw Exception('StacOptions is not set'); } + final artifactTypeString = _getArtifactTypeString(artifactType); + // Handle network-only strategy if (cacheConfig.strategy == StacCacheStrategy.networkOnly) { - return _fetchFromNetwork(routeName, saveToCache: false); + return _fetchArtifactFromNetwork( + artifactType: artifactType, + artifactName: artifactName, + saveToCache: false, + ); } - // Get cached screen - final cachedScreen = await StacCacheService.getCachedScreen(routeName); + // Get cached artifact + final cachedArtifact = await StacCacheService.getCachedArtifact( + artifactName, + artifactTypeString, + ); // Handle cache-only strategy if (cacheConfig.strategy == StacCacheStrategy.cacheOnly) { - if (cachedScreen != null) { - return _buildCacheResponse(cachedScreen); + if (cachedArtifact != null) { + return _buildArtifactCacheResponse(artifactType, cachedArtifact); } throw Exception( - 'No cached data available for $routeName (cache-only mode)', + 'No cached data available for $artifactType $artifactName (cache-only mode)', ); } // Check if cache is valid based on maxAge (sync to avoid double cache read) final isCacheValid = StacCacheService.isCacheValidSync( - cachedScreen, + cachedArtifact, cacheConfig.maxAge, ); // Handle different strategies switch (cacheConfig.strategy) { case StacCacheStrategy.networkFirst: - return _handleNetworkFirst(routeName, cachedScreen); + return _handleArtifactNetworkFirst( + artifactType: artifactType, + artifactName: artifactName, + cachedArtifact: cachedArtifact, + ); case StacCacheStrategy.cacheFirst: - return _handleCacheFirst( - routeName, - cachedScreen, - isCacheValid, - cacheConfig, + return _handleArtifactCacheFirst( + artifactType: artifactType, + artifactName: artifactName, + cachedArtifact: cachedArtifact, + isCacheValid: isCacheValid, + config: cacheConfig, ); case StacCacheStrategy.optimistic: - return _handleOptimistic( - routeName, - cachedScreen, - isCacheValid, - cacheConfig, + return _handleArtifactOptimistic( + artifactType: artifactType, + artifactName: artifactName, + cachedArtifact: cachedArtifact, + isCacheValid: isCacheValid, + config: cacheConfig, ); case StacCacheStrategy.cacheOnly: case StacCacheStrategy.networkOnly: // Already handled above - return _fetchFromNetwork(routeName, saveToCache: false); + return _fetchArtifactFromNetwork( + artifactType: artifactType, + artifactName: artifactName, + saveToCache: false, + ); } } + /// Fetches a screen from Stac Cloud with intelligent caching. + /// + /// The [cacheConfig] parameter controls caching behavior: + /// - Strategy: How to handle cache vs network + /// - maxAge: How long cache is valid + /// - refreshInBackground: Whether to update stale cache in background + /// - staleWhileRevalidate: Use expired cache while fetching fresh data + /// + /// Defaults to [StacCacheConfig.optimistic] if not provided. + static Future fetchScreen({ + required String routeName, + StacCacheConfig cacheConfig = const StacCacheConfig( + strategy: StacCacheStrategy.optimistic, + ), + }) async { + return _fetchArtifact( + artifactType: StacArtifactType.screen, + artifactName: routeName, + cacheConfig: cacheConfig, + ); + } + /// Handles network-first strategy: Try network, fallback to cache. - static Future _handleNetworkFirst( - String routeName, - StacScreenCache? cachedScreen, - ) async { + static Future _handleArtifactNetworkFirst({ + required StacArtifactType artifactType, + required String artifactName, + StacScreenCache? cachedArtifact, + }) async { try { - return await _fetchFromNetwork(routeName, saveToCache: true); + return await _fetchArtifactFromNetwork( + artifactType: artifactType, + artifactName: artifactName, + saveToCache: true, + ); } catch (e) { // Network failed, use cache as fallback - if (cachedScreen != null) { - Log.d('StacCloud: Network failed, using cached data for $routeName'); - return _buildCacheResponse(cachedScreen); + if (cachedArtifact != null) { + Log.d( + 'StacCloud: Network failed, using cached data for ${artifactType.name} $artifactName', + ); + return _buildArtifactCacheResponse(artifactType, cachedArtifact); } rethrow; } } /// Handles cache-first strategy: Use valid cache, fallback to network. - static Future _handleCacheFirst( - String routeName, - StacScreenCache? cachedScreen, - bool isCacheValid, - StacCacheConfig config, - ) async { + static Future _handleArtifactCacheFirst({ + required StacArtifactType artifactType, + required String artifactName, + StacScreenCache? cachedArtifact, + required bool isCacheValid, + required StacCacheConfig config, + }) async { // If cache is valid and exists, use it - if (cachedScreen != null && isCacheValid) { + if (cachedArtifact != null && isCacheValid) { // Optionally refresh in background if (config.refreshInBackground) { - _fetchAndUpdateInBackground(routeName, cachedScreen.version); + _fetchAndUpdateArtifactInBackground( + artifactType: artifactType, + artifactName: artifactName, + cachedVersion: cachedArtifact.version, + ); } - return _buildCacheResponse(cachedScreen); + return _buildArtifactCacheResponse(artifactType, cachedArtifact); } // Cache invalid or doesn't exist, fetch from network try { - return await _fetchFromNetwork(routeName, saveToCache: true); + return await _fetchArtifactFromNetwork( + artifactType: artifactType, + artifactName: artifactName, + saveToCache: true, + ); } catch (e) { // Network failed, use stale cache if available and staleWhileRevalidate is true - if (cachedScreen != null && config.staleWhileRevalidate) { + if (cachedArtifact != null && config.staleWhileRevalidate) { Log.d( - 'StacCloud: Using stale cache for $routeName due to network error', + 'StacCloud: Using stale cache for ${artifactType.name} $artifactName due to network error', ); - return _buildCacheResponse(cachedScreen); + return _buildArtifactCacheResponse(artifactType, cachedArtifact); } rethrow; } } /// Handles optimistic strategy: Return cache immediately, update in background. - static Future _handleOptimistic( - String routeName, - StacScreenCache? cachedScreen, - bool isCacheValid, - StacCacheConfig config, - ) async { + static Future _handleArtifactOptimistic({ + required StacArtifactType artifactType, + required String artifactName, + StacScreenCache? cachedArtifact, + required bool isCacheValid, + required StacCacheConfig config, + }) async { // If cache exists and is valid (or staleWhileRevalidate is true) - if (cachedScreen != null && (isCacheValid || config.staleWhileRevalidate)) { + if (cachedArtifact != null && + (isCacheValid || config.staleWhileRevalidate)) { // Update in background if configured if (config.refreshInBackground || !isCacheValid) { - _fetchAndUpdateInBackground(routeName, cachedScreen.version); + _fetchAndUpdateArtifactInBackground( + artifactType: artifactType, + artifactName: artifactName, + cachedVersion: cachedArtifact.version, + ); } - return _buildCacheResponse(cachedScreen); + return _buildArtifactCacheResponse(artifactType, cachedArtifact); } // No valid cache, must fetch from network - return _fetchFromNetwork(routeName, saveToCache: true); + return _fetchArtifactFromNetwork( + artifactType: artifactType, + artifactName: artifactName, + saveToCache: true, + ); } - /// Makes a network request to fetch screen data. - static Future _makeRequest(String routeName) { + /// Makes a network request to fetch artifact data. + static Future _makeArtifactRequest({ + required StacArtifactType artifactType, + required String artifactName, + }) { final options = StacService.options!; + final fetchUrl = _getFetchUrl(artifactType); + final queryParamName = _getQueryParamName(artifactType); + return _dio.get( - _fetchUrl, + fetchUrl, queryParameters: { 'projectId': options.projectId, - 'screenName': routeName, + queryParamName: artifactName, }, ); } - /// Fetches screen data from network and optionally saves to cache. - static Future _fetchFromNetwork( - String routeName, { + /// Fetches artifact data from network and optionally saves to cache. + static Future _fetchArtifactFromNetwork({ + required StacArtifactType artifactType, + required String artifactName, required bool saveToCache, }) async { - final response = await _makeRequest(routeName); + final response = await _makeArtifactRequest( + artifactType: artifactType, + artifactName: artifactName, + ); // Save to cache if enabled and response is valid if (saveToCache && response.data != null) { @@ -190,10 +298,11 @@ class StacCloud { final name = response.data['name'] as String?; if (version != null && stacJson != null && name != null) { - await StacCacheService.saveScreen( + await StacCacheService.saveArtifact( name: name, stacJson: stacJson, version: version, + artifactType: _getArtifactTypeString(artifactType), ); } } @@ -201,14 +310,18 @@ class StacCloud { return response; } - /// Builds a Response from cached screen data. - static Response _buildCacheResponse(StacScreenCache cachedScreen) { + /// Builds a Response from cached artifact data. + static Response _buildArtifactCacheResponse( + StacArtifactType artifactType, + StacScreenCache cachedArtifact, + ) { + final fetchUrl = _getFetchUrl(artifactType); return Response( - requestOptions: RequestOptions(path: _fetchUrl), + requestOptions: RequestOptions(path: fetchUrl), data: { - 'name': cachedScreen.name, - 'stacJson': cachedScreen.stacJson, - 'version': cachedScreen.version, + 'name': cachedArtifact.name, + 'stacJson': cachedArtifact.stacJson, + 'version': cachedArtifact.version, }, ); } @@ -217,16 +330,21 @@ class StacCloud { /// /// This method runs asynchronously without blocking the UI. /// If a newer version is found, it updates the cache for the next load. - /// Prevents duplicate fetches for the same screen. - static Future _fetchAndUpdateInBackground( - String routeName, - int cachedVersion, - ) async { - // Prevent duplicate background fetches for the same screen - if (!_backgroundFetchInProgress.add(routeName)) return; + /// Prevents duplicate fetches for the same artifact. + static Future _fetchAndUpdateArtifactInBackground({ + required StacArtifactType artifactType, + required String artifactName, + required int cachedVersion, + }) async { + final inProgressSet = _backgroundFetchInProgress[artifactType]!; + // Prevent duplicate background fetches for the same artifact + if (!inProgressSet.add(artifactName)) return; try { - final response = await _makeRequest(routeName); + final response = await _makeArtifactRequest( + artifactType: artifactType, + artifactName: artifactName, + ); if (response.data != null) { final serverVersion = response.data['version'] as int?; @@ -239,28 +357,63 @@ class StacCloud { name != null && serverVersion > cachedVersion) { // Update cache with new version for next load - await StacCacheService.saveScreen( + await StacCacheService.saveArtifact( name: name, stacJson: serverStacJson, version: serverVersion, + artifactType: _getArtifactTypeString(artifactType), ); } } } catch (e) { // Silently fail - background update is optional - Log.d('StacCloud: Background update failed for $routeName: $e'); + Log.d( + 'StacCloud: Background update failed for ${artifactType.name} $artifactName: $e', + ); } finally { - _backgroundFetchInProgress.remove(routeName); + inProgressSet.remove(artifactName); } } + /// Fetches a theme from Stac Cloud with intelligent caching. + /// + /// The [cacheConfig] parameter controls caching behavior: + /// - Strategy: How to handle cache vs network + /// - maxAge: How long cache is valid + /// - refreshInBackground: Whether to update stale cache in background + /// - staleWhileRevalidate: Use expired cache while fetching fresh data + /// + /// Defaults to [StacCacheConfig.optimistic] if not provided. + static Future fetchTheme({ + required String themeName, + StacCacheConfig cacheConfig = const StacCacheConfig( + strategy: StacCacheStrategy.optimistic, + ), + }) async { + return _fetchArtifact( + artifactType: StacArtifactType.theme, + artifactName: themeName, + cacheConfig: cacheConfig, + ); + } + /// Clears the cache for a specific screen. static Future clearScreenCache(String routeName) { - return StacCacheService.removeScreen(routeName); + return StacCacheService.removeArtifact(routeName, 'screen'); } /// Clears all cached screens. static Future clearAllCache() { - return StacCacheService.clearAllScreens(); + return StacCacheService.clearAllArtifacts('screen'); + } + + /// Clears the cache for a specific theme. + static Future clearThemeCache(String themeName) { + return StacCacheService.removeArtifact(themeName, 'theme'); + } + + /// Clears all cached themes. + static Future clearAllThemeCache() { + return StacCacheService.clearAllArtifacts('theme'); } } diff --git a/packages/stac_core/lib/annotations/annotations.dart b/packages/stac_core/lib/annotations/annotations.dart index 0b3ece7b..3a83c0ec 100644 --- a/packages/stac_core/lib/annotations/annotations.dart +++ b/packages/stac_core/lib/annotations/annotations.dart @@ -1,3 +1,4 @@ library; export 'stac_screen.dart'; +export 'stac_cloud_theme.dart'; diff --git a/packages/stac_core/lib/annotations/stac_cloud_theme.dart b/packages/stac_core/lib/annotations/stac_cloud_theme.dart new file mode 100644 index 00000000..259921d7 --- /dev/null +++ b/packages/stac_core/lib/annotations/stac_cloud_theme.dart @@ -0,0 +1,19 @@ +/// Annotation to mark methods that return theme definitions. +/// +/// This annotation is used to identify Stac theme builders so the framework can +/// register them and apply the correct theme at runtime. +/// +/// Example usage: +/// ```dart +/// @StacThemeAnnotation(themeName: 'darkTheme') +/// ThemeData buildDarkTheme() { +/// return ThemeData.dark(); +/// } +/// ``` +class StacCloudTheme { + /// Creates a [StacCloudTheme] with the given theme name. + const StacCloudTheme({required this.themeName}); + + /// The identifier for this theme. + final String themeName; +} From 3538b9f41a26109cacaa184c54142025d7e1863c Mon Sep 17 00:00:00 2001 From: Mithul Nayagam Date: Wed, 24 Dec 2025 20:43:31 +0530 Subject: [PATCH 2/8] Fix: address PR comments fixes for PR review comments --- .../lib/src/framework/stac_app_theme.dart | 20 ++++++--- packages/stac/lib/src/models/models.dart | 2 +- ...en_cache.dart => stac_artifact_cache.dart} | 34 +++++++------- ...ache.g.dart => stac_artifact_cache.g.dart} | 8 ++-- .../lib/src/services/stac_cache_service.dart | 45 ++++++++----------- .../stac/lib/src/services/stac_cloud.dart | 33 ++++++-------- .../lib/annotations/annotations.dart | 2 +- ...loud_theme.dart => stac_theme_config.dart} | 8 ++-- 8 files changed, 71 insertions(+), 81 deletions(-) rename packages/stac/lib/src/models/{stac_screen_cache.dart => stac_artifact_cache.dart} (64%) rename packages/stac/lib/src/models/{stac_screen_cache.g.dart => stac_artifact_cache.g.dart} (74%) rename packages/stac_core/lib/annotations/{stac_cloud_theme.dart => stac_theme_config.dart} (67%) diff --git a/packages/stac/lib/src/framework/stac_app_theme.dart b/packages/stac/lib/src/framework/stac_app_theme.dart index bf78e314..cab19002 100644 --- a/packages/stac/lib/src/framework/stac_app_theme.dart +++ b/packages/stac/lib/src/framework/stac_app_theme.dart @@ -5,6 +5,7 @@ import 'package:stac/src/services/stac_cloud.dart'; import 'package:stac/src/services/stac_network_service.dart'; import 'package:stac_core/actions/network_request/stac_network_request.dart'; import 'package:stac_core/foundation/theme/stac_theme/stac_theme.dart'; +import 'package:stac_logger/stac_logger.dart'; /// Provides helpers to load [StacTheme] definitions for [StacApp]. class StacAppTheme { @@ -24,7 +25,7 @@ class StacAppTheme { return null; } - final themePayload = _normalizeThemeJson(rawData['stacJson']); + final themePayload = _themeJsonDynamicToMap(rawData['stacJson']); if (themePayload == null) { return null; } @@ -53,27 +54,32 @@ class StacAppTheme { /// Accepts either a `Map` or a JSON `String`. Returns `null` /// when the payload cannot be parsed into a valid [StacTheme]. static StacTheme? fromJson(dynamic payload) { - final themePayload = _normalizeThemeJson(payload); + final themePayload = _themeJsonDynamicToMap(payload); if (themePayload == null) { return null; } return StacTheme.fromJson(themePayload); } - static Map? _normalizeThemeJson(dynamic payload) { + static Map? _themeJsonDynamicToMap(dynamic payload) { if (payload == null) { return null; } if (payload is Map && payload['stacJson'] != null) { - return _normalizeThemeJson(payload['stacJson']); + return _themeJsonDynamicToMap(payload['stacJson']); } if (payload is Map) { return payload; } if (payload is String) { - final decoded = jsonDecode(payload); - if (decoded is Map) { - return decoded; + try { + final decoded = jsonDecode(payload); + if (decoded is Map) { + return decoded; + } + } catch (e) { + Log.w('Unexpected error parsing theme JSON: $e'); + return null; } } return null; diff --git a/packages/stac/lib/src/models/models.dart b/packages/stac/lib/src/models/models.dart index 845acf51..8ef3364e 100644 --- a/packages/stac/lib/src/models/models.dart +++ b/packages/stac/lib/src/models/models.dart @@ -1,2 +1,2 @@ export 'package:stac/src/models/stac_cache_config.dart'; -export 'package:stac/src/models/stac_screen_cache.dart'; +export 'package:stac/src/models/stac_artifact_cache.dart'; diff --git a/packages/stac/lib/src/models/stac_screen_cache.dart b/packages/stac/lib/src/models/stac_artifact_cache.dart similarity index 64% rename from packages/stac/lib/src/models/stac_screen_cache.dart rename to packages/stac/lib/src/models/stac_artifact_cache.dart index e63d5a38..7be7e1fb 100644 --- a/packages/stac/lib/src/models/stac_screen_cache.dart +++ b/packages/stac/lib/src/models/stac_artifact_cache.dart @@ -2,15 +2,15 @@ import 'dart:convert'; import 'package:json_annotation/json_annotation.dart'; -part 'stac_screen_cache.g.dart'; +part 'stac_artifact_cache.g.dart'; /// Model representing a cached screen from Stac Cloud. /// /// This model stores the screen data along with metadata for caching purposes. @JsonSerializable() -class StacScreenCache { - /// Creates a [StacScreenCache] instance. - const StacScreenCache({ +class StacArtifactCache { + /// Creates a [StacArtifactCache] instance. + const StacArtifactCache({ required this.name, required this.stacJson, required this.version, @@ -29,33 +29,33 @@ class StacScreenCache { /// The timestamp when this screen was cached. final DateTime cachedAt; - /// Creates a [StacScreenCache] from a JSON map. - factory StacScreenCache.fromJson(Map json) => - _$StacScreenCacheFromJson(json); + /// Creates a [StacArtifactCache] from a JSON map. + factory StacArtifactCache.fromJson(Map json) => + _$StacArtifactCacheFromJson(json); - /// Converts this [StacScreenCache] to a JSON map. - Map toJson() => _$StacScreenCacheToJson(this); + /// Converts this [StacArtifactCache] to a JSON map. + Map toJson() => _$StacArtifactCacheToJson(this); - /// Creates a [StacScreenCache] from a JSON string. - factory StacScreenCache.fromJsonString(String jsonString) { - return StacScreenCache.fromJson( + /// Creates a [StacArtifactCache] from a JSON string. + factory StacArtifactCache.fromJsonString(String jsonString) { + return StacArtifactCache.fromJson( jsonDecode(jsonString) as Map, ); } - /// Converts this [StacScreenCache] to a JSON string. + /// Converts this [StacArtifactCache] to a JSON string. String toJsonString() { return jsonEncode(toJson()); } - /// Creates a copy of this [StacScreenCache] with the given fields replaced. - StacScreenCache copyWith({ + /// Creates a copy of this [StacArtifactCache] with the given fields replaced. + StacArtifactCache copyWith({ String? name, String? stacJson, int? version, DateTime? cachedAt, }) { - return StacScreenCache( + return StacArtifactCache( name: name ?? this.name, stacJson: stacJson ?? this.stacJson, version: version ?? this.version, @@ -72,7 +72,7 @@ class StacScreenCache { bool operator ==(Object other) { if (identical(this, other)) return true; - return other is StacScreenCache && + return other is StacArtifactCache && other.name == name && other.stacJson == stacJson && other.version == version && diff --git a/packages/stac/lib/src/models/stac_screen_cache.g.dart b/packages/stac/lib/src/models/stac_artifact_cache.g.dart similarity index 74% rename from packages/stac/lib/src/models/stac_screen_cache.g.dart rename to packages/stac/lib/src/models/stac_artifact_cache.g.dart index be1b97e8..59021ccd 100644 --- a/packages/stac/lib/src/models/stac_screen_cache.g.dart +++ b/packages/stac/lib/src/models/stac_artifact_cache.g.dart @@ -1,20 +1,20 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'stac_screen_cache.dart'; +part of 'stac_artifact_cache.dart'; // ************************************************************************** // JsonSerializableGenerator // ************************************************************************** -StacScreenCache _$StacScreenCacheFromJson(Map json) => - StacScreenCache( +StacArtifactCache _$StacArtifactCacheFromJson(Map json) => + StacArtifactCache( name: json['name'] as String, stacJson: json['stacJson'] as String, version: (json['version'] as num).toInt(), cachedAt: DateTime.parse(json['cachedAt'] as String), ); -Map _$StacScreenCacheToJson(StacScreenCache instance) => +Map _$StacArtifactCacheToJson(StacArtifactCache instance) => { 'name': instance.name, 'stacJson': instance.stacJson, diff --git a/packages/stac/lib/src/services/stac_cache_service.dart b/packages/stac/lib/src/services/stac_cache_service.dart index 20f2e4f3..0f006024 100644 --- a/packages/stac/lib/src/services/stac_cache_service.dart +++ b/packages/stac/lib/src/services/stac_cache_service.dart @@ -1,5 +1,7 @@ import 'package:shared_preferences/shared_preferences.dart'; -import 'package:stac/src/models/stac_screen_cache.dart'; +import 'package:stac/src/models/stac_artifact_cache.dart'; +import 'package:stac/src/models/stac_artifact_type.dart'; +import 'package:stac_logger/stac_logger.dart'; /// Service for managing cached Stac artifacts (screens, themes, etc.). /// @@ -17,23 +19,21 @@ class StacCacheService { } /// Gets the cache prefix for a given artifact type. - static String _getCachePrefix(String artifactType) { + static String _getCachePrefix(StacArtifactType artifactType) { switch (artifactType) { - case 'screen': + case StacArtifactType.screen: return 'stac_screen_cache_'; - case 'theme': + case StacArtifactType.theme: return 'stac_theme_cache_'; - default: - throw ArgumentError('Unknown artifact type: $artifactType'); } } /// Gets a cached artifact by its name and type. /// /// Returns `null` if the artifact is not cached. - static Future getCachedArtifact( + static Future getCachedArtifact( String artifactName, - String artifactType, + StacArtifactType artifactType, ) async { try { final prefs = await _sharedPrefs; @@ -45,8 +45,11 @@ class StacCacheService { return null; } - return StacScreenCache.fromJsonString(cachedData); + return StacArtifactCache.fromJsonString(cachedData); } catch (e) { + Log.w( + 'Failed to get cached artifact $artifactName (${artifactType.name}): $e', + ); return null; } } @@ -58,14 +61,14 @@ class StacCacheService { required String name, required String stacJson, required int version, - required String artifactType, + required StacArtifactType artifactType, }) async { try { final prefs = await _sharedPrefs; final cachePrefix = _getCachePrefix(artifactType); final cacheKey = '$cachePrefix$name'; - final artifactCache = StacScreenCache( + final artifactCache = StacArtifactCache( name: name, stacJson: stacJson, version: version, @@ -81,7 +84,7 @@ class StacCacheService { /// Removes a specific artifact from the cache. static Future removeArtifact( String artifactName, - String artifactType, + StacArtifactType artifactType, ) async { try { final prefs = await _sharedPrefs; @@ -94,7 +97,7 @@ class StacCacheService { } /// Clears all cached artifacts of a specific type. - static Future clearAllArtifacts(String artifactType) async { + static Future clearAllArtifacts(StacArtifactType artifactType) async { try { final prefs = await _sharedPrefs; final keys = prefs.getKeys(); @@ -115,20 +118,8 @@ class StacCacheService { /// Returns `false` if the cache is expired or doesn't exist. /// /// If [maxAge] is `null`, cache is considered valid (no time-based expiration). - static Future isCacheValid({ - required String artifactName, - required String artifactType, - Duration? maxAge, - }) async { - final cachedArtifact = await getCachedArtifact(artifactName, artifactType); - return isCacheValidSync(cachedArtifact, maxAge); - } - - /// Synchronous version of [isCacheValid] for when you already have the cache. - /// - /// Use this to avoid re-fetching the cache when you already have it. - static bool isCacheValidSync( - StacScreenCache? cachedArtifact, + static bool isCacheValid( + StacArtifactCache? cachedArtifact, Duration? maxAge, ) { if (cachedArtifact == null) return false; diff --git a/packages/stac/lib/src/services/stac_cloud.dart b/packages/stac/lib/src/services/stac_cloud.dart index 62b7142e..b621ce98 100644 --- a/packages/stac/lib/src/services/stac_cloud.dart +++ b/packages/stac/lib/src/services/stac_cloud.dart @@ -2,7 +2,7 @@ import 'package:dio/dio.dart'; import 'package:stac/src/framework/stac_service.dart'; import 'package:stac/src/models/stac_artifact_type.dart'; import 'package:stac/src/models/stac_cache_config.dart'; -import 'package:stac/src/models/stac_screen_cache.dart'; +import 'package:stac/src/models/stac_artifact_cache.dart'; import 'package:stac/src/services/stac_cache_service.dart'; import 'package:stac_logger/stac_logger.dart'; @@ -43,11 +43,6 @@ class StacCloud { } } - /// Gets the artifact type string for cache operations. - static String _getArtifactTypeString(StacArtifactType artifactType) { - return artifactType.name; - } - /// Tracks artifacts currently being fetched in background to prevent duplicates. static final Map> _backgroundFetchInProgress = { StacArtifactType.screen: {}, @@ -75,8 +70,6 @@ class StacCloud { throw Exception('StacOptions is not set'); } - final artifactTypeString = _getArtifactTypeString(artifactType); - // Handle network-only strategy if (cacheConfig.strategy == StacCacheStrategy.networkOnly) { return _fetchArtifactFromNetwork( @@ -89,7 +82,7 @@ class StacCloud { // Get cached artifact final cachedArtifact = await StacCacheService.getCachedArtifact( artifactName, - artifactTypeString, + artifactType, ); // Handle cache-only strategy @@ -103,7 +96,7 @@ class StacCloud { } // Check if cache is valid based on maxAge (sync to avoid double cache read) - final isCacheValid = StacCacheService.isCacheValidSync( + final isCacheValid = StacCacheService.isCacheValid( cachedArtifact, cacheConfig.maxAge, ); @@ -172,7 +165,7 @@ class StacCloud { static Future _handleArtifactNetworkFirst({ required StacArtifactType artifactType, required String artifactName, - StacScreenCache? cachedArtifact, + StacArtifactCache? cachedArtifact, }) async { try { return await _fetchArtifactFromNetwork( @@ -196,7 +189,7 @@ class StacCloud { static Future _handleArtifactCacheFirst({ required StacArtifactType artifactType, required String artifactName, - StacScreenCache? cachedArtifact, + StacArtifactCache? cachedArtifact, required bool isCacheValid, required StacCacheConfig config, }) async { @@ -236,7 +229,7 @@ class StacCloud { static Future _handleArtifactOptimistic({ required StacArtifactType artifactType, required String artifactName, - StacScreenCache? cachedArtifact, + StacArtifactCache? cachedArtifact, required bool isCacheValid, required StacCacheConfig config, }) async { @@ -302,7 +295,7 @@ class StacCloud { name: name, stacJson: stacJson, version: version, - artifactType: _getArtifactTypeString(artifactType), + artifactType: artifactType, ); } } @@ -313,7 +306,7 @@ class StacCloud { /// Builds a Response from cached artifact data. static Response _buildArtifactCacheResponse( StacArtifactType artifactType, - StacScreenCache cachedArtifact, + StacArtifactCache cachedArtifact, ) { final fetchUrl = _getFetchUrl(artifactType); return Response( @@ -361,7 +354,7 @@ class StacCloud { name: name, stacJson: serverStacJson, version: serverVersion, - artifactType: _getArtifactTypeString(artifactType), + artifactType: artifactType, ); } } @@ -399,21 +392,21 @@ class StacCloud { /// Clears the cache for a specific screen. static Future clearScreenCache(String routeName) { - return StacCacheService.removeArtifact(routeName, 'screen'); + return StacCacheService.removeArtifact(routeName, StacArtifactType.screen); } /// Clears all cached screens. static Future clearAllCache() { - return StacCacheService.clearAllArtifacts('screen'); + return StacCacheService.clearAllArtifacts(StacArtifactType.screen); } /// Clears the cache for a specific theme. static Future clearThemeCache(String themeName) { - return StacCacheService.removeArtifact(themeName, 'theme'); + return StacCacheService.removeArtifact(themeName, StacArtifactType.theme); } /// Clears all cached themes. static Future clearAllThemeCache() { - return StacCacheService.clearAllArtifacts('theme'); + return StacCacheService.clearAllArtifacts(StacArtifactType.theme); } } diff --git a/packages/stac_core/lib/annotations/annotations.dart b/packages/stac_core/lib/annotations/annotations.dart index 3a83c0ec..b25ae4f0 100644 --- a/packages/stac_core/lib/annotations/annotations.dart +++ b/packages/stac_core/lib/annotations/annotations.dart @@ -1,4 +1,4 @@ library; export 'stac_screen.dart'; -export 'stac_cloud_theme.dart'; +export 'stac_theme_config.dart'; diff --git a/packages/stac_core/lib/annotations/stac_cloud_theme.dart b/packages/stac_core/lib/annotations/stac_theme_config.dart similarity index 67% rename from packages/stac_core/lib/annotations/stac_cloud_theme.dart rename to packages/stac_core/lib/annotations/stac_theme_config.dart index 259921d7..1b557390 100644 --- a/packages/stac_core/lib/annotations/stac_cloud_theme.dart +++ b/packages/stac_core/lib/annotations/stac_theme_config.dart @@ -5,14 +5,14 @@ /// /// Example usage: /// ```dart -/// @StacThemeAnnotation(themeName: 'darkTheme') +/// @StacThemeConfig(themeName: 'darkTheme') /// ThemeData buildDarkTheme() { /// return ThemeData.dark(); /// } /// ``` -class StacCloudTheme { - /// Creates a [StacCloudTheme] with the given theme name. - const StacCloudTheme({required this.themeName}); +class StacThemeConfig { + /// Creates a [StacThemeConfig] with the given theme name. + const StacThemeConfig({required this.themeName}); /// The identifier for this theme. final String themeName; From 2e1a97f759bf0e6af87d29c184008f1cf6659304 Mon Sep 17 00:00:00 2001 From: Mithul Nayagam Date: Fri, 26 Dec 2025 20:04:14 +0530 Subject: [PATCH 3/8] feat: Add theming support to documentation and examples - Introduced a new "Theming" concept in the documentation, detailing how to implement and manage themes in Stac applications. - Updated the main application example to utilize cloud-based themes instead of local theme definitions. - Removed the deprecated app_theme.dart file to streamline theme management. - Enhanced StacApp to support various theme loading methods, including cloud, network, and JSON sources. --- docs/concepts/theming.mdx | 427 ++++++++++++++++-- docs/docs.json | 3 +- examples/movie_app/lib/main.dart | 3 +- examples/movie_app/lib/themes/app_theme.dart | 120 ----- examples/movie_app/stac/app_theme.dart | 158 +++++++ examples/stac_gallery/lib/main.dart | 6 +- packages/stac/lib/src/framework/stac_app.dart | 74 ++- .../lib/src/framework/stac_app_theme.dart | 81 +++- .../lib/annotations/annotations.dart | 2 +- ..._theme_config.dart => stac_theme_ref.dart} | 8 +- 10 files changed, 675 insertions(+), 207 deletions(-) delete mode 100644 examples/movie_app/lib/themes/app_theme.dart create mode 100644 examples/movie_app/stac/app_theme.dart rename packages/stac_core/lib/annotations/{stac_theme_config.dart => stac_theme_ref.dart} (71%) diff --git a/docs/concepts/theming.mdx b/docs/concepts/theming.mdx index 1a1b65f8..397b3d3d 100644 --- a/docs/concepts/theming.mdx +++ b/docs/concepts/theming.mdx @@ -1,17 +1,22 @@ --- title: "Theming" -description: "Documentation for Theming" +description: "Learn how to implement and manage themes in Stac applications using cloud (recommended), DSL, network, or JSON sources" --- -Theming is an essential part of any application, ensuring a consistent look and feel across the entire app. Stac offers a powerful way to update the theme of your application dynamically using JSON. -Stac theming functions similarly to Flutter's built-in theming. You define the theme in JSON and apply it to your application using the StacTheme widget. This allows for a centralized and easily maintainable approach to managing your app's visual style. +Theming is an essential part of any application, ensuring a consistent look and feel across the entire app. Stac offers flexible theming options that allow you to fetch themes from Stac Cloud (recommended), define them using Dart DSL, load them over the network, or parse them from JSON. -## Implementing Stac Theming +## Overview -To implement theming in Stac, follow these steps: +Stac theming works similarly to Flutter's built-in theming system. You define themes using `StacTheme` objects and apply them to your application using `StacApp`. The framework supports multiple ways to load themes: -1. **Replace MaterialApp with StacApp**: Start by replacing your `MaterialApp` with `StacApp` -2. **Pass the StacTheme to StacApp**: Apply the theme by passing the `StacTheme` widget to the `StacApp`. The StacTheme widget takes a `StacTheme` object as a parameter, which is constructed from your JSON theme definition. +1. **Cloud Themes (Recommended)** - Fetch themes from Stac Cloud by name +2. **DSL Themes** - Define themes directly in Dart code using the `@StacThemeRef` annotation +3. **Network Themes** - Load themes over HTTP using `StacNetworkRequest` +4. **JSON Themes** - Parse themes from JSON data + +## Using StacApp with Themes + +To use themes in your Stac application, replace `MaterialApp` with `StacApp` and pass your theme using the `StacAppTheme` wrapper: ```dart import 'package:flutter/material.dart'; @@ -23,39 +28,389 @@ void main() async { } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); + const MyApp({super.key}); @override Widget build(BuildContext context) { return StacApp( - theme: StacTheme.fromJson(themeJson), + title: 'My App', + theme: StacAppTheme(name: "light_theme"), // Cloud theme (recommended) + darkTheme: StacAppTheme(name: "dark_theme"), // Cloud theme (recommended) homeBuilder: (context) => const HomeScreen(), ); } - - Map themeJson = { - "brightness": "light", - "disabledColor": "#60FEF7FF", - "fontFamily": "Handjet", - "colorScheme": { - "brightness": "light", - "primary": "#6750a4", - "onPrimary": "#FFFFFF", - "secondary": "#615B6F", - "onSecondary": "#FFFFFF", - "surface": "#FEFBFF", - "onSurface": "#1C1B1E", - "background": "#FEFBFF", - "onBackground": "#1C1B1E", - "surfaceVariant": "#E6E0EA", - "onSurfaceVariant": "#48454D", - "error": "#AB2D25", - "onError": "#FFFFFF", - "success": "#27BA62", - "onSuccess": "#FFFFFF" - } - }; -} -``` - -For more details check out [StacTheme](https://github.com/StacDev/stac/blob/dev/packages/stac/lib/src/parsers/theme/stac_theme/stac_theme.dart) class. +} +``` + +## Theme Sources + +### 1. Cloud Themes (Recommended) + +Fetch themes from Stac Cloud by name. This is the recommended approach as it allows themes to be managed server-side and updated without app updates. Themes are cached locally for offline access and performance. + +#### Workflow: Generate and Deploy Themes + +To use Cloud themes, you first need to: + +1. **Define your themes** in your `stac/` directory using `@StacThemeRef` annotation +2. **Generate JSON** from your DSL themes using the Stac CLI +3. **Deploy to Stac Cloud** using `stac deploy` + +Here's the complete workflow: + +**Step 1: Define your theme** in `stac/app_theme.dart`: + +```dart +import 'package:stac_core/stac_core.dart'; + +@StacThemeRef(name: "movie_app_dark") +StacTheme get darkTheme => _buildTheme( + brightness: StacBrightness.dark, + colorScheme: StacColorScheme( + brightness: StacBrightness.dark, + primary: '#95E183', + // ... other properties + ), +); +``` + +**Step 2: Generate and deploy** using the CLI: + +```bash +stac deploy +``` + +This command will: +- Build your project +- Process all `@StacScreen` annotated screens and `@StacThemeRef` annotated themes +- Generate JSON files for each screen and theme +- Upload all generated files to Stac Cloud + +Example output: +``` +[INFO] Building project before deployment... +[INFO] Found 4 @StacScreen annotated function(s) +[SUCCESS] ✓ Generated screen: onboarding_screen.json +[SUCCESS] ✓ Generated screen: home_screen.json +[SUCCESS] ✓ Generated screen: detail_screen.json +[INFO] Found 1 @StacThemeRef definition(s) in stac\app_theme.dart +[SUCCESS] ✓ Generated theme: movie_app_dark.json +[SUCCESS] Build completed successfully! +[INFO] Deploying screens/themes to cloud... +[SUCCESS] Uploaded screen: onboarding_screen.json +[SUCCESS] Uploaded screen: home_screen.json +[SUCCESS] Uploaded screen: detail_screen.json +[SUCCESS] Uploaded theme: movie_app_dark.json +[SUCCESS] Deployment completed successfully! +``` + +**Step 3: Use in your app**: + +```dart +StacApp( + theme: StacAppTheme(name: "movie_app_light"), + darkTheme: StacAppTheme(name: "movie_app_dark"), + // ... +) +``` + +The `StacAppTheme` wrapper automatically fetches the theme from Stac Cloud using the provided name. Themes are cached intelligently to minimize network requests. + +### 2. DSL Themes + +DSL themes are defined directly in your Dart code. This approach provides type safety, code completion, and easy refactoring. You can use DSL themes in two ways: + +1. **Direct usage** - Define themes without annotations and use them directly in your app +2. **Cloud deployment** - Add `@StacThemeRef` annotation to generate JSON and deploy to Stac Cloud + +#### Creating a DSL Theme + +Create a theme file in your `stac/` directory (e.g., `stac/app_theme.dart`): + +**For direct usage (no annotation needed):** + +```dart +import 'package:stac_core/stac_core.dart'; + +StacTheme get darkTheme => _buildTheme( + brightness: StacBrightness.dark, + colorScheme: StacColorScheme( + brightness: StacBrightness.dark, + primary: '#95E183', + onPrimary: '#050608', + secondary: '#95E183', + onSecondary: '#FFFFFF', + surface: '#050608', + onSurface: '#FFFFFF', + onSurfaceVariant: '#65FFFFFF', + error: '#FF6565', + onError: '#050608', + outline: '#08FFFFFF', + ), +); +``` + +**For cloud deployment (with annotation):** + +```dart +import 'package:stac_core/stac_core.dart'; + +@StacThemeRef(name: "movie_app_dark") +StacTheme get darkTheme => _buildTheme( + brightness: StacBrightness.dark, + colorScheme: StacColorScheme( + brightness: StacBrightness.dark, + primary: '#95E183', + onPrimary: '#050608', + secondary: '#95E183', + onSecondary: '#FFFFFF', + surface: '#050608', + onSurface: '#FFFFFF', + onSurfaceVariant: '#65FFFFFF', + error: '#FF6565', + onError: '#050608', + outline: '#08FFFFFF', + ), +); +``` + +**Note**: The `@StacThemeRef` annotation is only required if you want to generate JSON and deploy themes to Stac Cloud using `stac deploy`. For direct usage in your app, you can define themes without any annotations. + +Helper functions (shared for both approaches): + +```dart +StacTheme _buildTheme({ + required StacBrightness brightness, + required StacColorScheme colorScheme, +}) { + return StacTheme( + brightness: brightness, + colorScheme: colorScheme, + textTheme: _buildTextTheme(), + filledButtonTheme: _buildFilledButtonTheme(), + outlinedButtonTheme: _buildOutlinedButtonTheme(), + dividerTheme: _buildDividerTheme(), + ); +} +``` + +Helper functions (shared for both approaches): + +```dart +StacTextTheme _buildTextTheme() { + return StacTextTheme( + displayLarge: _textStyle( + fontSize: 48, + fontWeight: StacFontWeight.w700, + height: 1.1, + ), + // ... other text styles + ); +} + +StacButtonStyle _buildFilledButtonTheme() { + return StacButtonStyle( + minimumSize: StacSize(120, 40), + textStyle: _textStyle( + fontSize: 16, + fontWeight: StacFontWeight.w500, + height: 1.3, + ), + padding: StacEdgeInsets.only(left: 10, right: 10, top: 8, bottom: 8), + shape: StacRoundedRectangleBorder(borderRadius: StacBorderRadius.all(8)), + ); +} + +StacButtonStyle _buildOutlinedButtonTheme() { + return StacButtonStyle( + minimumSize: StacSize(120, 40), + textStyle: _textStyle( + fontSize: 16, + fontWeight: StacFontWeight.w500, + height: 1.3, + ), + padding: StacEdgeInsets.only(left: 10, right: 10, top: 8, bottom: 8), + side: StacBorderSide(color: '#95E183', width: 1.0), + shape: StacRoundedRectangleBorder(borderRadius: StacBorderRadius.all(8)), + ); +} + +StacDividerThemeData _buildDividerTheme() { + return StacDividerThemeData(color: '#24FFFFFF', thickness: 1); +} + +StacCustomTextStyle _textStyle({ + required double fontSize, + required StacFontWeight fontWeight, + required double height, + double? letterSpacing, +}) { + return StacCustomTextStyle( + fontSize: fontSize, + fontWeight: fontWeight, + height: height, + letterSpacing: letterSpacing, + ); +} +``` + +#### Using DSL Themes + +After defining your theme (with or without `@StacThemeRef`), you can use it directly by passing the `StacTheme` object to `StacAppTheme.dsl()`: + +```dart +// Import your theme file (adjust path based on your project structure) +import '../stac/app_theme.dart'; // or package:your_app/stac/app_theme.dart + +StacApp( + theme: StacAppTheme.dsl(theme: darkTheme), + // ... +) +``` + +**Important**: +- DSL themes take the `StacTheme` object directly, not a name string +- **No annotation required** for direct usage - you can define themes as simple getters +- `@StacThemeRef` annotation is only needed if you want to deploy themes to Stac Cloud +- Cloud themes use `StacAppTheme(name: "...")` to fetch by name from Stac Cloud +- DSL themes are compiled into your app and don't require network access + +**When to use `@StacThemeRef`:** +- Only when you want to generate JSON and deploy themes to Stac Cloud using `stac deploy` +- The annotation tells the CLI which themes to process and what name to use in the cloud +- For themes used only in your app code, annotations are optional + +### 3. Network Themes + +Load themes over HTTP using a `StacNetworkRequest`. This allows you to fetch themes from any API endpoint: + +```dart +StacApp( + theme: StacAppTheme.network( + context: context, + request: StacNetworkRequest( + url: 'https://api.example.com/themes/light', + method: Method.get, + ), + ), + // ... +) +``` + +### 4. JSON Themes + +Parse themes directly from JSON data. Useful when themes are stored locally or received from other sources: + +```dart +final themeJson = { + "brightness": "dark", + "colorScheme": { + "brightness": "dark", + "primary": "#95E183", + "onPrimary": "#050608", + // ... other color scheme properties + }, + "textTheme": { + // ... text theme properties + } +}; + +StacApp( + theme: StacAppTheme.json(payload: themeJson), + // ... +) +``` + +## Theme Structure + +A `StacTheme` consists of several components: + +### Color Scheme + +The color scheme defines the primary colors used throughout your app: + +```dart +StacColorScheme( + brightness: StacBrightness.dark, + primary: '#95E183', + onPrimary: '#050608', + secondary: '#95E183', + onSecondary: '#FFFFFF', + surface: '#050608', + onSurface: '#FFFFFF', + onSurfaceVariant: '#65FFFFFF', + error: '#FF6565', + onError: '#050608', + outline: '#08FFFFFF', +) +``` + +### Text Theme + +Define typography styles for different text elements: + +```dart +StacTextTheme( + displayLarge: StacCustomTextStyle( + fontSize: 48, + fontWeight: StacFontWeight.w700, + height: 1.1, + ), + headlineLarge: StacCustomTextStyle( + fontSize: 30, + fontWeight: StacFontWeight.w700, + height: 1.3, + ), + bodyLarge: StacCustomTextStyle( + fontSize: 18, + fontWeight: StacFontWeight.w400, + height: 1.5, + ), + // ... other text styles +) +``` + +### Button Themes + +Customize button appearances: + +```dart +// Filled button theme +StacButtonStyle( + minimumSize: StacSize(120, 40), + textStyle: StacCustomTextStyle( + fontSize: 16, + fontWeight: StacFontWeight.w500, + ), + padding: StacEdgeInsets.only(left: 10, right: 10, top: 8, bottom: 8), + shape: StacRoundedRectangleBorder(borderRadius: StacBorderRadius.all(8)), +) + +// Outlined button theme +StacButtonStyle( + minimumSize: StacSize(120, 40), + side: StacBorderSide(color: '#95E183', width: 1.0), + shape: StacRoundedRectangleBorder(borderRadius: StacBorderRadius.all(8)), +) +``` + +## Best Practices + +1. **Use Cloud Themes (Recommended)**: Cloud themes are the recommended approach as they allow themes to be managed server-side and updated without app updates. They also benefit from intelligent caching. + +2. **Use DSL Themes for Static Themes**: If your themes don't change frequently and you prefer type safety, use DSL themes with `@StacThemeRef`. + +3. **Organize Theme Files**: Keep theme definitions in your `stac/` directory alongside your screen definitions. + +4. **Extract Common Styles**: Use helper methods like `_textStyle()` and `_buildTheme()` to avoid code duplication. + +5. **Support Both Light and Dark Themes**: Always provide both `theme` and `darkTheme` parameters to `StacApp` for better user experience. + +## Error Handling + +If a theme fails to load (network error, malformed JSON, etc.), Stac will: +- Log a warning with details about the error +- Fall back to default Flutter theme +- Continue app execution without blocking + +This ensures your app remains functional even if theme loading fails. diff --git a/docs/docs.json b/docs/docs.json index 26a49426..f2c1724b 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -40,7 +40,8 @@ "concepts/rendering_stac_widgets", "concepts/caching", "concepts/custom_widgets", - "concepts/custom_actions" + "concepts/custom_actions", + "concepts/theming" ] } ] diff --git a/examples/movie_app/lib/main.dart b/examples/movie_app/lib/main.dart index 3ec8688a..df21e93a 100644 --- a/examples/movie_app/lib/main.dart +++ b/examples/movie_app/lib/main.dart @@ -1,7 +1,6 @@ import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:movie_app/default_stac_options.dart'; -import 'package:movie_app/themes/app_theme.dart'; import 'package:movie_app/widgets/movie_carousel/movie_carousel_parser.dart'; import 'package:stac/stac.dart'; @@ -35,7 +34,7 @@ class MyApp extends StatelessWidget { Widget build(BuildContext context) { return StacApp( title: 'Flutter Demo', - theme: darkTheme, + theme: StacAppTheme(name: "movie_app_dark"), homeBuilder: (_) { return Stac(routeName: 'onboarding_screen'); }, diff --git a/examples/movie_app/lib/themes/app_theme.dart b/examples/movie_app/lib/themes/app_theme.dart deleted file mode 100644 index 84e694bc..00000000 --- a/examples/movie_app/lib/themes/app_theme.dart +++ /dev/null @@ -1,120 +0,0 @@ -import 'package:stac_core/stac_core.dart'; // Hide StacTheme from stac_core to use the one from stac - -/// Dark theme for the Movie App. -StacTheme get darkTheme { - return StacTheme( - brightness: StacBrightness.dark, - colorScheme: StacColorScheme( - brightness: StacBrightness.dark, - primary: '#95E183', - onPrimary: '#050608', - secondary: '#95E183', - onSecondary: '#FFFFFF', - surface: '#050608', - onSurface: '#FFFFFF', - onSurfaceVariant: '#65FFFFFF', - error: '#FF6565', - onError: '#050608', - outline: '#08FFFFFF', - ), - textTheme: StacTextTheme( - displayLarge: StacCustomTextStyle( - fontSize: 48, - fontWeight: StacFontWeight.w700, - height: 1.1, - ), - displayMedium: StacCustomTextStyle( - fontSize: 40, - fontWeight: StacFontWeight.w700, - height: 1.1, - ), - displaySmall: StacCustomTextStyle( - fontSize: 34, - fontWeight: StacFontWeight.w700, - height: 1.1, - ), - headlineLarge: StacCustomTextStyle( - fontSize: 30, - fontWeight: StacFontWeight.w700, - height: 1.3, - ), - headlineMedium: StacCustomTextStyle( - fontSize: 26, - fontWeight: StacFontWeight.w700, - height: 1.3, - ), - headlineSmall: StacCustomTextStyle( - fontSize: 23, - fontWeight: StacFontWeight.w700, - height: 1.3, - ), - titleLarge: StacCustomTextStyle( - fontSize: 20, - fontWeight: StacFontWeight.w500, - height: 1.3, - ), - titleMedium: StacCustomTextStyle( - fontSize: 18, - fontWeight: StacFontWeight.w500, - height: 1.3, - ), - titleSmall: StacCustomTextStyle( - fontSize: 16, - fontWeight: StacFontWeight.w500, - height: 1.3, - ), - labelLarge: StacCustomTextStyle( - fontSize: 16, - fontWeight: StacFontWeight.w700, - height: 1.3, - ), - labelMedium: StacCustomTextStyle( - fontSize: 14, - fontWeight: StacFontWeight.w600, - height: 1.3, - ), - labelSmall: StacCustomTextStyle( - fontSize: 12, - fontWeight: StacFontWeight.w500, - height: 1.3, - ), - bodyLarge: StacCustomTextStyle( - fontSize: 18, - fontWeight: StacFontWeight.w400, - height: 1.5, - ), - bodyMedium: StacCustomTextStyle( - fontSize: 16, - fontWeight: StacFontWeight.w400, - height: 1.5, - ), - bodySmall: StacCustomTextStyle( - fontSize: 14, - fontWeight: StacFontWeight.w400, - height: 1.5, - ), - ), - filledButtonTheme: StacButtonStyle( - minimumSize: StacSize(120, 40), - textStyle: StacCustomTextStyle( - fontSize: 16, - fontWeight: StacFontWeight.w500, - height: 1.3, - ), - padding: StacEdgeInsets.only(left: 10, right: 10, top: 8, bottom: 8), - shape: StacRoundedRectangleBorder(borderRadius: StacBorderRadius.all(8)), - ), - outlinedButtonTheme: StacButtonStyle( - minimumSize: StacSize(120, 40), - textStyle: StacCustomTextStyle( - fontSize: 16, - fontWeight: StacFontWeight.w500, - height: 1.3, - ), - padding: StacEdgeInsets.only(left: 10, right: 10, top: 8, bottom: 8), - side: StacBorderSide(color: '#95E183', width: 1.0), - shape: StacRoundedRectangleBorder(borderRadius: StacBorderRadius.all(8)), - ), - dividerTheme: StacDividerThemeData(color: '#24FFFFFF', thickness: 1), - ); -} diff --git a/examples/movie_app/stac/app_theme.dart b/examples/movie_app/stac/app_theme.dart new file mode 100644 index 00000000..08ff2494 --- /dev/null +++ b/examples/movie_app/stac/app_theme.dart @@ -0,0 +1,158 @@ +import 'package:stac_core/stac_core.dart'; + +@StacThemeRef(name: "movie_app_dark") +StacTheme get darkTheme => _buildTheme( + brightness: StacBrightness.dark, + colorScheme: StacColorScheme( + brightness: StacBrightness.dark, + primary: '#95E183', + onPrimary: '#050608', + secondary: '#95E183', + onSecondary: '#FFFFFF', + surface: '#050608', + onSurface: '#FFFFFF', + onSurfaceVariant: '#65FFFFFF', + error: '#FF6565', + onError: '#050608', + outline: '#08FFFFFF', + ), +); + +StacTheme _buildTheme({ + required StacBrightness brightness, + required StacColorScheme colorScheme, +}) { + return StacTheme( + brightness: brightness, + colorScheme: colorScheme, + textTheme: _buildTextTheme(), + filledButtonTheme: _buildFilledButtonTheme(), + outlinedButtonTheme: _buildOutlinedButtonTheme(), + dividerTheme: _buildDividerTheme(), + ); +} + +StacTextTheme _buildTextTheme() { + return StacTextTheme( + displayLarge: _textStyle( + fontSize: 48, + fontWeight: StacFontWeight.w700, + height: 1.1, + ), + displayMedium: _textStyle( + fontSize: 40, + fontWeight: StacFontWeight.w700, + height: 1.1, + ), + displaySmall: _textStyle( + fontSize: 34, + fontWeight: StacFontWeight.w700, + height: 1.1, + ), + headlineLarge: _textStyle( + fontSize: 30, + fontWeight: StacFontWeight.w700, + height: 1.3, + ), + headlineMedium: _textStyle( + fontSize: 26, + fontWeight: StacFontWeight.w700, + height: 1.3, + ), + headlineSmall: _textStyle( + fontSize: 23, + fontWeight: StacFontWeight.w700, + height: 1.3, + ), + titleLarge: _textStyle( + fontSize: 20, + fontWeight: StacFontWeight.w500, + height: 1.3, + ), + titleMedium: _textStyle( + fontSize: 18, + fontWeight: StacFontWeight.w500, + height: 1.3, + ), + titleSmall: _textStyle( + fontSize: 16, + fontWeight: StacFontWeight.w500, + height: 1.3, + ), + labelLarge: _textStyle( + fontSize: 16, + fontWeight: StacFontWeight.w700, + height: 1.3, + ), + labelMedium: _textStyle( + fontSize: 14, + fontWeight: StacFontWeight.w600, + height: 1.3, + ), + labelSmall: _textStyle( + fontSize: 12, + fontWeight: StacFontWeight.w500, + height: 1.3, + ), + bodyLarge: _textStyle( + fontSize: 18, + fontWeight: StacFontWeight.w400, + height: 1.5, + ), + bodyMedium: _textStyle( + fontSize: 16, + fontWeight: StacFontWeight.w400, + height: 1.5, + ), + bodySmall: _textStyle( + fontSize: 14, + fontWeight: StacFontWeight.w400, + height: 1.5, + ), + ); +} + +StacButtonStyle _buildFilledButtonTheme() { + return StacButtonStyle( + minimumSize: StacSize(120, 40), + textStyle: _textStyle( + fontSize: 16, + fontWeight: StacFontWeight.w500, + height: 1.3, + ), + padding: StacEdgeInsets.only(left: 10, right: 10, top: 8, bottom: 8), + shape: StacRoundedRectangleBorder(borderRadius: StacBorderRadius.all(8)), + ); +} + +StacButtonStyle _buildOutlinedButtonTheme() { + return StacButtonStyle( + minimumSize: StacSize(120, 40), + textStyle: _textStyle( + fontSize: 16, + fontWeight: StacFontWeight.w500, + height: 1.3, + ), + padding: StacEdgeInsets.only(left: 10, right: 10, top: 8, bottom: 8), + side: StacBorderSide(color: '#95E183', width: 1.0), + shape: StacRoundedRectangleBorder(borderRadius: StacBorderRadius.all(8)), + ); +} + +StacDividerThemeData _buildDividerTheme() { + return StacDividerThemeData(color: '#24FFFFFF', thickness: 1); +} + +StacCustomTextStyle _textStyle({ + required double fontSize, + required StacFontWeight fontWeight, + required double height, + double? letterSpacing, +}) { + return StacCustomTextStyle( + fontSize: fontSize, + fontWeight: fontWeight, + height: height, + letterSpacing: letterSpacing, + ); +} diff --git a/examples/stac_gallery/lib/main.dart b/examples/stac_gallery/lib/main.dart index 15c7e44c..b25e6a20 100644 --- a/examples/stac_gallery/lib/main.dart +++ b/examples/stac_gallery/lib/main.dart @@ -2,7 +2,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:stac/stac.dart' show Stac, StacApp; +import 'package:stac/stac.dart'; import 'package:stac_core/stac_core.dart'; import 'package:stac_gallery/app/details/details_screen.dart'; import 'package:stac_gallery/app/example/example_screen_parser.dart'; @@ -41,8 +41,8 @@ class MyApp extends StatelessWidget { child: BlocBuilder( builder: (context, state) { return StacApp( - theme: state.lightTheme, - darkTheme: state.darkTheme, + theme: StacAppTheme.json(payload: state.lightTheme), + darkTheme: StacAppTheme.json(payload: state.darkTheme), themeMode: state.themeMode, homeBuilder: (context) => HomeScreen(), title: 'Stac Gallery', diff --git a/packages/stac/lib/src/framework/stac_app.dart b/packages/stac/lib/src/framework/stac_app.dart index 74b4cca3..f88a6f9b 100644 --- a/packages/stac/lib/src/framework/stac_app.dart +++ b/packages/stac/lib/src/framework/stac_app.dart @@ -1,7 +1,7 @@ -import 'dart:async'; - import 'package:flutter/material.dart'; +import 'package:stac/src/framework/stac_app_theme.dart'; import 'package:stac/src/parsers/theme/themes.dart'; +import 'package:stac_logger/stac_logger.dart'; class StacApp extends StatelessWidget { const StacApp({ @@ -110,8 +110,8 @@ class StacApp extends StatelessWidget { final TransitionBuilder? builder; final String title; final GenerateAppTitle? onGenerateTitle; - final FutureOr? theme; - final FutureOr? darkTheme; + final StacAppTheme? theme; + final StacAppTheme? darkTheme; final ThemeData? highContrastTheme; final ThemeData? highContrastDarkTheme; final ThemeMode? themeMode; @@ -246,32 +246,24 @@ class StacApp extends StatelessWidget { ); } - FutureOr<_ResolvedStacThemes> _resolveThemes() { + Future<_ResolvedStacThemes> _resolveThemes() { final themeInput = theme; final darkThemeInput = darkTheme; - final Future? themeFuture = themeInput is Future - ? themeInput - : null; - final Future? darkThemeFuture = - darkThemeInput is Future ? darkThemeInput : null; - - final StacTheme? themeValue = themeFuture == null - ? themeInput as StacTheme? - : null; - final StacTheme? darkThemeValue = darkThemeFuture == null - ? darkThemeInput as StacTheme? - : null; + // Both themes are optional, so we need to handle null cases + final Future? themeFuture = themeInput?.resolve(); + final Future? darkThemeFuture = darkThemeInput?.resolve(); + // If both are null, return immediately with null themes if (themeFuture == null && darkThemeFuture == null) { - return _ResolvedStacThemes(theme: themeValue, darkTheme: darkThemeValue); + return Future.value(_ResolvedStacThemes(theme: null, darkTheme: null)); } return Future<_ResolvedStacThemes>(() async { final resolvedTheme = - await (themeFuture ?? Future.value(themeValue)); + await (themeFuture ?? Future.value(null)); final resolvedDarkTheme = - await (darkThemeFuture ?? Future.value(darkThemeValue)); + await (darkThemeFuture ?? Future.value(null)); return _ResolvedStacThemes( theme: resolvedTheme, @@ -285,25 +277,29 @@ class StacApp extends StatelessWidget { Widget Function(BuildContext, _ResolvedStacThemes) builder, ) { final resolved = _resolveThemes(); - if (resolved is Future<_ResolvedStacThemes>) { - return FutureBuilder<_ResolvedStacThemes>( - future: resolved, - builder: (futureContext, snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - return const _ThemeFutureLoading(); - } - if (snapshot.hasError) { - return const _ThemeFutureLoading(); - } - final themes = snapshot.data; - if (themes == null) { - return const _ThemeFutureLoading(); - } - return builder(futureContext, themes); - }, - ); - } - return builder(context, resolved); + return FutureBuilder<_ResolvedStacThemes>( + future: resolved, + builder: (futureContext, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const _ThemeFutureLoading(); + } + if (snapshot.hasError) { + Log.w('Failed to resolve theme: ${snapshot.error}'); + return builder( + futureContext, + _ResolvedStacThemes(theme: null, darkTheme: null), + ); + } + final themes = snapshot.data; + if (themes == null) { + return builder( + futureContext, + _ResolvedStacThemes(theme: null, darkTheme: null), + ); + } + return builder(futureContext, themes); + }, + ); } } diff --git a/packages/stac/lib/src/framework/stac_app_theme.dart b/packages/stac/lib/src/framework/stac_app_theme.dart index cab19002..9ebac069 100644 --- a/packages/stac/lib/src/framework/stac_app_theme.dart +++ b/packages/stac/lib/src/framework/stac_app_theme.dart @@ -8,8 +8,85 @@ import 'package:stac_core/foundation/theme/stac_theme/stac_theme.dart'; import 'package:stac_logger/stac_logger.dart'; /// Provides helpers to load [StacTheme] definitions for [StacApp]. +/// +/// Can be used as a wrapper to fetch themes from different sources: +/// ```dart +/// // From DSL (StacTheme object) +/// StacAppTheme.dsl(theme: myTheme) +/// +/// // From cloud +/// StacAppTheme(name: "xyz") +/// +/// // From network +/// StacAppTheme.network(context: context, request: request) +/// +/// // From JSON +/// StacAppTheme.json(payload: jsonData) +/// ``` class StacAppTheme { - const StacAppTheme._(); + /// Creates a [StacAppTheme] wrapper for using a DSL theme. + /// + /// The [theme] should be a `StacTheme` object defined with `@StacThemeRef` annotation. + const StacAppTheme.dsl({required StacTheme theme}) + : _source = _ThemeSource.dsl, + name = null, + _context = null, + _request = null, + _jsonPayload = null, + _dslTheme = theme; + + /// Creates a [StacAppTheme] wrapper for fetching a theme from the cloud by [name]. + const StacAppTheme({required this.name}) + : _source = _ThemeSource.cloud, + _context = null, + _request = null, + _jsonPayload = null, + _dslTheme = null; + + /// Creates a [StacAppTheme] wrapper for fetching a theme from network. + const StacAppTheme.network({ + required BuildContext context, + required StacNetworkRequest request, + }) : _source = _ThemeSource.network, + name = null, + _context = context, + _request = request, + _jsonPayload = null, + _dslTheme = null; + + /// Creates a [StacAppTheme] wrapper for creating a theme from JSON. + const StacAppTheme.json({required dynamic payload}) + : _source = _ThemeSource.json, + name = null, + _context = null, + _request = null, + _jsonPayload = payload, + _dslTheme = null; + + /// The name of the theme to fetch from cloud (only used for cloud source). + final String? name; + + final _ThemeSource _source; + final BuildContext? _context; + final StacNetworkRequest? _request; + final dynamic _jsonPayload; + final StacTheme? _dslTheme; + + /// Resolves the theme based on the configured source. + /// + /// Returns `null` if the fetch/parse fails or the payload is malformed. + Future resolve() async { + switch (_source) { + case _ThemeSource.dsl: + return Future.value(_dslTheme); + case _ThemeSource.cloud: + return fromCloud(themeName: name!); + case _ThemeSource.network: + return fromNetwork(context: _context!, request: _request!); + case _ThemeSource.json: + return fromJson(_jsonPayload); + } + } /// Fetches a theme from the `/themes` endpoint by [themeName]. /// @@ -85,3 +162,5 @@ class StacAppTheme { return null; } } + +enum _ThemeSource { dsl, cloud, network, json } diff --git a/packages/stac_core/lib/annotations/annotations.dart b/packages/stac_core/lib/annotations/annotations.dart index b25ae4f0..cb160149 100644 --- a/packages/stac_core/lib/annotations/annotations.dart +++ b/packages/stac_core/lib/annotations/annotations.dart @@ -1,4 +1,4 @@ library; export 'stac_screen.dart'; -export 'stac_theme_config.dart'; +export 'stac_theme_ref.dart'; diff --git a/packages/stac_core/lib/annotations/stac_theme_config.dart b/packages/stac_core/lib/annotations/stac_theme_ref.dart similarity index 71% rename from packages/stac_core/lib/annotations/stac_theme_config.dart rename to packages/stac_core/lib/annotations/stac_theme_ref.dart index 1b557390..9eba335d 100644 --- a/packages/stac_core/lib/annotations/stac_theme_config.dart +++ b/packages/stac_core/lib/annotations/stac_theme_ref.dart @@ -10,10 +10,10 @@ /// return ThemeData.dark(); /// } /// ``` -class StacThemeConfig { - /// Creates a [StacThemeConfig] with the given theme name. - const StacThemeConfig({required this.themeName}); +class StacThemeRef { + /// Creates a [StacThemeRef] with the given theme name. + const StacThemeRef({required this.name}); /// The identifier for this theme. - final String themeName; + final String name; } From 3e98d2c68f071d93a2dbb542e3e8bedc3a7d4006 Mon Sep 17 00:00:00 2001 From: Mithul Nayagam Date: Sat, 27 Dec 2025 02:10:33 +0530 Subject: [PATCH 4/8] updated melos version --- examples/movie_app/pubspec.lock | 124 ++++++++---------- examples/stac_gallery/pubspec.lock | 204 +++++++++++------------------ pubspec.yaml | 2 +- 3 files changed, 137 insertions(+), 193 deletions(-) diff --git a/examples/movie_app/pubspec.lock b/examples/movie_app/pubspec.lock index 00d7faa4..b21c60c7 100644 --- a/examples/movie_app/pubspec.lock +++ b/examples/movie_app/pubspec.lock @@ -5,18 +5,18 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: c209688d9f5a5f26b2fb47a188131a6fb9e876ae9e47af3737c0b4f58a93470d + sha256: "5b7468c326d2f8a4f630056404ca0d291ade42918f4a3c6233618e724f39da8e" url: "https://pub.dev" source: hosted - version: "91.0.0" + version: "92.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: f51c8499b35f9b26820cfe914828a6a98a94efd5cc78b37bb7d03debae3a1d08 + sha256: "70e4b1ef8003c64793a9e268a551a82869a8a96f39deb73dea28084b0e8bf75e" url: "https://pub.dev" source: hosted - version: "8.4.1" + version: "9.0.0" args: dependency: transitive description: @@ -45,10 +45,10 @@ packages: dependency: transitive description: name: build - sha256: dfb67ccc9a78c642193e0c2d94cb9e48c2c818b3178a86097d644acdcde6a8d9 + sha256: c1668065e9ba04752570ad7e038288559d1e2ca5c6d0131c0f5f55e39e777413 url: "https://pub.dev" source: hosted - version: "4.0.2" + version: "4.0.3" build_config: dependency: transitive description: @@ -61,18 +61,18 @@ packages: dependency: transitive description: name: build_daemon - sha256: "409002f1adeea601018715d613115cfaf0e31f512cb80ae4534c79867ae2363d" + sha256: bf05f6e12cfea92d3c09308d7bcdab1906cd8a179b023269eed00c071004b957 url: "https://pub.dev" source: hosted - version: "4.1.0" + version: "4.1.1" build_runner: dependency: "direct dev" description: name: build_runner - sha256: a9461b8e586bf018dd4afd2e13b49b08c6a844a4b226c8d1d10f3a723cdd78c3 + sha256: "110c56ef29b5eb367b4d17fc79375fa8c18a6cd7acd92c05bb3986c17a079057" url: "https://pub.dev" source: hosted - version: "2.10.1" + version: "2.10.4" built_collection: dependency: transitive description: @@ -85,10 +85,10 @@ packages: dependency: transitive description: name: built_value - sha256: a30f0a0e38671e89a492c44d005b5545b830a961575bbd8336d42869ff71066d + sha256: "426cf75afdb23aa74bd4e471704de3f9393f3c7b04c1e2d9c6f1073ae0b8b139" url: "https://pub.dev" source: hosted - version: "8.12.0" + version: "8.12.1" cached_network_image: dependency: transitive description: @@ -165,10 +165,10 @@ packages: dependency: transitive description: name: crypto - sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" + sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf url: "https://pub.dev" source: hosted - version: "3.0.6" + version: "3.0.7" cupertino_icons: dependency: "direct main" description: @@ -181,10 +181,10 @@ packages: dependency: transitive description: name: dart_style - sha256: c87dfe3d56f183ffe9106a18aebc6db431fc7c98c31a54b952a77f3d54a85697 + sha256: a9c30492da18ff84efe2422ba2d319a89942d93e58eb0b73d32abe822ef54b7b url: "https://pub.dev" source: hosted - version: "3.1.2" + version: "3.1.3" dio: dependency: "direct main" description: @@ -258,10 +258,10 @@ packages: dependency: transitive description: name: flutter_svg - sha256: "055de8921be7b8e8b98a233c7a5ef84b3a6fcc32f46f1ebf5b9bb3576d108355" + sha256: "87fbd7c534435b6c5d9d98b01e1fd527812b82e68ddd8bd35fc45ed0fa8f0a95" url: "https://pub.dev" source: hosted - version: "2.2.2" + version: "2.2.3" flutter_test: dependency: "direct dev" description: flutter @@ -292,10 +292,10 @@ packages: dependency: transitive description: name: http - sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b" + sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.6.0" http_multi_server: dependency: transitive description: @@ -332,18 +332,18 @@ packages: dependency: "direct dev" description: name: json_serializable - sha256: "33a040668b31b320aafa4822b7b1e177e163fc3c1e835c6750319d4ab23aa6fe" + sha256: "6b253f7851cf1626a05c8b49c792e04a14897349798c03798137f2b5f7e0b5b1" url: "https://pub.dev" source: hosted - version: "6.11.1" + version: "6.11.3" leak_tracker: dependency: transitive description: name: leak_tracker - sha256: "8dcda04c3fc16c14f48a7bb586d4be1f0d1572731b6d81d51772ef47c02081e0" + sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" url: "https://pub.dev" source: hosted - version: "11.0.1" + version: "11.0.2" leak_tracker_flutter_testing: dependency: transitive description: @@ -372,10 +372,10 @@ packages: dependency: transitive description: name: logger - sha256: be4b23575aac7ebf01f225a241eb7f6b5641eeaf43c6a8613510fc2f8cf187d1 + sha256: a7967e31b703831a893bbc3c3dd11db08126fe5f369b5c648a36f821979f5be3 url: "https://pub.dev" source: hosted - version: "2.5.0" + version: "2.6.2" logging: dependency: transitive description: @@ -460,18 +460,18 @@ packages: dependency: transitive description: name: path_provider_android - sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9 + sha256: f2c65e21139ce2c3dad46922be8272bb5963516045659e71bb16e151c93b580e url: "https://pub.dev" source: hosted - version: "2.2.17" + version: "2.2.22" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942" + sha256: "6d13aece7b3f5c5a9731eaf553ff9dcbc2eff41087fd2df587fd0fed9a3eb0c4" url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.5.1" path_provider_linux: dependency: transitive description: @@ -500,10 +500,10 @@ packages: dependency: transitive description: name: petitparser - sha256: "07c8f0b1913bcde1ff0d26e57ace2f3012ccbf2b204e070290dad3bb22797646" + sha256: "1a97266a94f7350d30ae522c0af07890c70b8e62c71e8e3920d1db4d23c057d1" url: "https://pub.dev" source: hosted - version: "6.1.0" + version: "7.0.1" platform: dependency: transitive description: @@ -556,18 +556,18 @@ packages: dependency: transitive description: name: shared_preferences - sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5" + sha256: "2939ae520c9024cb197fc20dee269cd8cdbf564c8b5746374ec6cacdc5169e64" url: "https://pub.dev" source: hosted - version: "2.5.3" + version: "2.5.4" shared_preferences_android: dependency: transitive description: name: shared_preferences_android - sha256: "07d552dbe8e71ed720e5205e760438ff4ecfb76ec3b32ea664350e2ca4b0c43b" + sha256: "83af5c682796c0f7719c2bbf74792d113e40ae97981b8f266fa84574573556bc" url: "https://pub.dev" source: hosted - version: "2.4.16" + version: "2.4.18" shared_preferences_foundation: dependency: transitive description: @@ -641,18 +641,18 @@ packages: dependency: transitive description: name: source_gen - sha256: "9098ab86015c4f1d8af6486b547b11100e73b193e1899015033cb3e14ad20243" + sha256: "07b277b67e0096c45196cbddddf2d8c6ffc49342e88bf31d460ce04605ddac75" url: "https://pub.dev" source: hosted - version: "4.0.2" + version: "4.1.1" source_helper: dependency: transitive description: name: source_helper - sha256: "6a3c6cc82073a8797f8c4dc4572146114a39652851c157db37e964d9c7038723" + sha256: e82b1996c63da42aa3e6a34cc1ec17427728a1baf72ed017717a5669a7123f0d url: "https://pub.dev" source: hosted - version: "1.3.8" + version: "1.3.9" source_span: dependency: transitive description: @@ -661,14 +661,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.10.1" - sprintf: - dependency: transitive - description: - name: sprintf - sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" - url: "https://pub.dev" - source: hosted - version: "7.0.0" sqflite: dependency: transitive description: @@ -681,18 +673,18 @@ packages: dependency: transitive description: name: sqflite_android - sha256: "2b3070c5fa881839f8b402ee4a39c1b4d561704d4ebbbcfb808a119bc2a1701b" + sha256: ecd684501ebc2ae9a83536e8b15731642b9570dc8623e0073d227d0ee2bfea88 url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.2+2" sqflite_common: dependency: transitive description: name: sqflite_common - sha256: "84731e8bfd8303a3389903e01fb2141b6e59b5973cacbb0929021df08dddbe8b" + sha256: "6ef422a4525ecc601db6c0a2233ff448c731307906e92cabc9ba292afaae16a6" url: "https://pub.dev" source: hosted - version: "2.5.5" + version: "2.5.6" sqflite_darwin: dependency: transitive description: @@ -773,10 +765,10 @@ packages: dependency: transitive description: name: synchronized - sha256: "0669c70faae6270521ee4f05bffd2919892d42d1276e6c495be80174b6bc0ef6" + sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0 url: "https://pub.dev" source: hosted - version: "3.3.1" + version: "3.4.0" term_glyph: dependency: transitive description: @@ -805,18 +797,18 @@ packages: dependency: transitive description: name: uuid - sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff + sha256: a11b666489b1954e01d992f3d601b1804a33937b5a8fe677bd26b8a9f96f96e8 url: "https://pub.dev" source: hosted - version: "4.5.1" + version: "4.5.2" vector_graphics: dependency: transitive description: name: vector_graphics - sha256: "44cc7104ff32563122a929e4620cf3efd584194eec6d1d913eb5ba593dbcf6de" + sha256: a4f059dc26fc8295b5921376600a194c4ec7d55e72f2fe4c7d2831e103d461e6 url: "https://pub.dev" source: hosted - version: "1.1.18" + version: "1.1.19" vector_graphics_codec: dependency: transitive description: @@ -829,10 +821,10 @@ packages: dependency: transitive description: name: vector_graphics_compiler - sha256: "557a315b7d2a6dbb0aaaff84d857967ce6bdc96a63dc6ee2a57ce5a6ee5d3331" + sha256: d354a7ec6931e6047785f4db12a1f61ec3d43b207fc0790f863818543f8ff0dc url: "https://pub.dev" source: hosted - version: "1.1.17" + version: "1.1.19" vector_math: dependency: transitive description: @@ -845,18 +837,18 @@ packages: dependency: transitive description: name: vm_service - sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 + sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60" url: "https://pub.dev" source: hosted - version: "15.0.0" + version: "15.0.2" watcher: dependency: transitive description: name: watcher - sha256: "592ab6e2892f67760543fb712ff0177f4ec76c031f02f5b4ff8d3fc5eb9fb61a" + sha256: f52385d4f73589977c80797e60fe51014f7f2b957b5e9a62c3f6ada439889249 url: "https://pub.dev" source: hosted - version: "1.1.4" + version: "1.2.0" web: dependency: transitive description: @@ -893,10 +885,10 @@ packages: dependency: transitive description: name: xml - sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 + sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025" url: "https://pub.dev" source: hosted - version: "6.5.0" + version: "6.6.1" yaml: dependency: transitive description: diff --git a/examples/stac_gallery/pubspec.lock b/examples/stac_gallery/pubspec.lock index e4b11ea8..6f062e0a 100644 --- a/examples/stac_gallery/pubspec.lock +++ b/examples/stac_gallery/pubspec.lock @@ -5,18 +5,18 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: e55636ed79578b9abca5fecf9437947798f5ef7456308b5cb85720b793eac92f + sha256: c209688d9f5a5f26b2fb47a188131a6fb9e876ae9e47af3737c0b4f58a93470d url: "https://pub.dev" source: hosted - version: "82.0.0" + version: "91.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: "904ae5bb474d32c38fb9482e2d925d5454cda04ddd0e55d2e6826bc72f6ba8c0" + sha256: f51c8499b35f9b26820cfe914828a6a98a94efd5cc78b37bb7d03debae3a1d08 url: "https://pub.dev" source: hosted - version: "7.4.5" + version: "8.4.1" args: dependency: transitive description: @@ -37,10 +37,10 @@ packages: dependency: transitive description: name: bloc - sha256: "52c10575f4445c61dd9e0cafcc6356fdd827c4c64dd7945ef3c4105f6b6ac189" + sha256: a2cebb899f91d36eeeaa55c7b20b5915db5a9df1b8fd4a3c9c825e22e474537d url: "https://pub.dev" source: hosted - version: "9.0.0" + version: "9.1.0" boolean_selector: dependency: transitive description: @@ -53,50 +53,34 @@ packages: dependency: transitive description: name: build - sha256: cef23f1eda9b57566c81e2133d196f8e3df48f244b317368d65c5943d91148f0 + sha256: c1668065e9ba04752570ad7e038288559d1e2ca5c6d0131c0f5f55e39e777413 url: "https://pub.dev" source: hosted - version: "2.4.2" + version: "4.0.3" build_config: dependency: transitive description: name: build_config - sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33" + sha256: "4f64382b97504dc2fcdf487d5aae33418e08b4703fc21249e4db6d804a4d0187" url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.2.0" build_daemon: dependency: transitive description: name: build_daemon - sha256: "8e928697a82be082206edb0b9c99c5a4ad6bc31c9e9b8b2f291ae65cd4a25daa" - url: "https://pub.dev" - source: hosted - version: "4.0.4" - build_resolvers: - dependency: transitive - description: - name: build_resolvers - sha256: b9e4fda21d846e192628e7a4f6deda6888c36b5b69ba02ff291a01fd529140f0 + sha256: bf05f6e12cfea92d3c09308d7bcdab1906cd8a179b023269eed00c071004b957 url: "https://pub.dev" source: hosted - version: "2.4.4" + version: "4.1.1" build_runner: dependency: "direct dev" description: name: build_runner - sha256: "058fe9dce1de7d69c4b84fada934df3e0153dd000758c4d65964d0166779aa99" + sha256: "110c56ef29b5eb367b4d17fc79375fa8c18a6cd7acd92c05bb3986c17a079057" url: "https://pub.dev" source: hosted - version: "2.4.15" - build_runner_core: - dependency: transitive - description: - name: build_runner_core - sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021" - url: "https://pub.dev" - source: hosted - version: "8.0.0" + version: "2.10.4" built_collection: dependency: transitive description: @@ -109,10 +93,10 @@ packages: dependency: transitive description: name: built_value - sha256: ea90e81dc4a25a043d9bee692d20ed6d1c4a1662a28c03a96417446c093ed6b4 + sha256: "426cf75afdb23aa74bd4e471704de3f9393f3c7b04c1e2d9c6f1073ae0b8b139" url: "https://pub.dev" source: hosted - version: "8.9.5" + version: "8.12.1" cached_network_image: dependency: transitive description: @@ -149,10 +133,10 @@ packages: dependency: transitive description: name: checked_yaml - sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff + sha256: "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f" url: "https://pub.dev" source: hosted - version: "2.0.3" + version: "2.0.4" clock: dependency: transitive description: @@ -165,10 +149,10 @@ packages: dependency: transitive description: name: code_builder - sha256: "0ec10bf4a89e4c613960bf1e8b42c64127021740fb21640c29c909826a5eea3e" + sha256: "11654819532ba94c34de52ff5feb52bd81cba1de00ef2ed622fd50295f9d4243" url: "https://pub.dev" source: hosted - version: "4.10.1" + version: "4.11.0" collection: dependency: transitive description: @@ -189,10 +173,10 @@ packages: dependency: transitive description: name: crypto - sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" + sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf url: "https://pub.dev" source: hosted - version: "3.0.6" + version: "3.0.7" cupertino_icons: dependency: "direct main" description: @@ -205,10 +189,10 @@ packages: dependency: transitive description: name: dart_style - sha256: "27eb0ae77836989a3bc541ce55595e8ceee0992807f14511552a898ddd0d88ac" + sha256: a9c30492da18ff84efe2422ba2d319a89942d93e58eb0b73d32abe822ef54b7b url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.1.3" dio: dependency: transitive description: @@ -290,10 +274,10 @@ packages: dependency: transitive description: name: flutter_svg - sha256: "055de8921be7b8e8b98a233c7a5ef84b3a6fcc32f46f1ebf5b9bb3576d108355" + sha256: "87fbd7c534435b6c5d9d98b01e1fd527812b82e68ddd8bd35fc45ed0fa8f0a95" url: "https://pub.dev" source: hosted - version: "2.2.2" + version: "2.2.3" flutter_test: dependency: "direct dev" description: flutter @@ -308,10 +292,10 @@ packages: dependency: "direct dev" description: name: freezed - sha256: "6022db4c7bfa626841b2a10f34dd1e1b68e8f8f9650db6112dcdeeca45ca793c" + sha256: "13065f10e135263a4f5a4391b79a8efc5fb8106f8dd555a9e49b750b45393d77" url: "https://pub.dev" source: hosted - version: "3.0.6" + version: "3.2.3" freezed_annotation: dependency: "direct main" description: @@ -320,14 +304,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.0" - frontend_server_client: - dependency: transitive - description: - name: frontend_server_client - sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 - url: "https://pub.dev" - source: hosted - version: "4.0.0" glob: dependency: transitive description: @@ -348,10 +324,10 @@ packages: dependency: transitive description: name: http - sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b" + sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.6.0" http_multi_server: dependency: transitive description: @@ -376,14 +352,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.5" - js: - dependency: transitive - description: - name: js - sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc" - url: "https://pub.dev" - source: hosted - version: "0.7.2" json_annotation: dependency: "direct main" description: @@ -396,18 +364,18 @@ packages: dependency: "direct dev" description: name: json_serializable - sha256: c50ef5fc083d5b5e12eef489503ba3bf5ccc899e487d691584699b4bdefeea8c + sha256: c5b2ee75210a0f263c6c7b9eeea80553dbae96ea1bf57f02484e806a3ffdffa3 url: "https://pub.dev" source: hosted - version: "6.9.5" + version: "6.11.2" leak_tracker: dependency: transitive description: name: leak_tracker - sha256: "8dcda04c3fc16c14f48a7bb586d4be1f0d1572731b6d81d51772ef47c02081e0" + sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" url: "https://pub.dev" source: hosted - version: "11.0.1" + version: "11.0.2" leak_tracker_flutter_testing: dependency: transitive description: @@ -436,10 +404,10 @@ packages: dependency: transitive description: name: logger - sha256: be4b23575aac7ebf01f225a241eb7f6b5641eeaf43c6a8613510fc2f8cf187d1 + sha256: a7967e31b703831a893bbc3c3dd11db08126fe5f369b5c648a36f821979f5be3 url: "https://pub.dev" source: hosted - version: "2.5.0" + version: "2.6.2" logging: dependency: transitive description: @@ -532,18 +500,18 @@ packages: dependency: transitive description: name: path_provider_android - sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9 + sha256: f2c65e21139ce2c3dad46922be8272bb5963516045659e71bb16e151c93b580e url: "https://pub.dev" source: hosted - version: "2.2.17" + version: "2.2.22" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942" + sha256: "6d13aece7b3f5c5a9731eaf553ff9dcbc2eff41087fd2df587fd0fed9a3eb0c4" url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.5.1" path_provider_linux: dependency: transitive description: @@ -572,10 +540,10 @@ packages: dependency: transitive description: name: petitparser - sha256: "07c8f0b1913bcde1ff0d26e57ace2f3012ccbf2b204e070290dad3bb22797646" + sha256: "1a97266a94f7350d30ae522c0af07890c70b8e62c71e8e3920d1db4d23c057d1" url: "https://pub.dev" source: hosted - version: "6.1.0" + version: "7.0.1" platform: dependency: transitive description: @@ -596,18 +564,18 @@ packages: dependency: transitive description: name: pool - sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + sha256: "978783255c543aa3586a1b3c21f6e9d720eb315376a915872c61ef8b5c20177d" url: "https://pub.dev" source: hosted - version: "1.5.1" + version: "1.5.2" provider: dependency: transitive description: name: provider - sha256: "4abbd070a04e9ddc287673bf5a030c7ca8b685ff70218720abab8b092f53dd84" + sha256: "4e82183fa20e5ca25703ead7e05de9e4cceed1fbd1eadc1ac3cb6f565a09f272" url: "https://pub.dev" source: hosted - version: "6.1.5" + version: "6.1.5+1" pub_semver: dependency: transitive description: @@ -636,18 +604,18 @@ packages: dependency: transitive description: name: shared_preferences - sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5" + sha256: "2939ae520c9024cb197fc20dee269cd8cdbf564c8b5746374ec6cacdc5169e64" url: "https://pub.dev" source: hosted - version: "2.5.3" + version: "2.5.4" shared_preferences_android: dependency: transitive description: name: shared_preferences_android - sha256: "07d552dbe8e71ed720e5205e760438ff4ecfb76ec3b32ea664350e2ca4b0c43b" + sha256: "83af5c682796c0f7719c2bbf74792d113e40ae97981b8f266fa84574573556bc" url: "https://pub.dev" source: hosted - version: "2.4.16" + version: "2.4.18" shared_preferences_foundation: dependency: transitive description: @@ -713,18 +681,18 @@ packages: dependency: transitive description: name: source_gen - sha256: "35c8150ece9e8c8d263337a265153c3329667640850b9304861faea59fc98f6b" + sha256: "07b277b67e0096c45196cbddddf2d8c6ffc49342e88bf31d460ce04605ddac75" url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "4.1.1" source_helper: dependency: transitive description: name: source_helper - sha256: "86d247119aedce8e63f4751bd9626fc9613255935558447569ad42f9f5b48b3c" + sha256: "6a3c6cc82073a8797f8c4dc4572146114a39652851c157db37e964d9c7038723" url: "https://pub.dev" source: hosted - version: "1.3.5" + version: "1.3.8" source_span: dependency: transitive description: @@ -733,14 +701,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.10.1" - sprintf: - dependency: transitive - description: - name: sprintf - sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" - url: "https://pub.dev" - source: hosted - version: "7.0.0" sqflite: dependency: transitive description: @@ -753,18 +713,18 @@ packages: dependency: transitive description: name: sqflite_android - sha256: "2b3070c5fa881839f8b402ee4a39c1b4d561704d4ebbbcfb808a119bc2a1701b" + sha256: ecd684501ebc2ae9a83536e8b15731642b9570dc8623e0073d227d0ee2bfea88 url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.2+2" sqflite_common: dependency: transitive description: name: sqflite_common - sha256: "84731e8bfd8303a3389903e01fb2141b6e59b5973cacbb0929021df08dddbe8b" + sha256: "6ef422a4525ecc601db6c0a2233ff448c731307906e92cabc9ba292afaae16a6" url: "https://pub.dev" source: hosted - version: "2.5.5" + version: "2.5.6" sqflite_darwin: dependency: transitive description: @@ -852,10 +812,10 @@ packages: dependency: transitive description: name: synchronized - sha256: "0669c70faae6270521ee4f05bffd2919892d42d1276e6c495be80174b6bc0ef6" + sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0 url: "https://pub.dev" source: hosted - version: "3.3.1" + version: "3.4.0" term_glyph: dependency: transitive description: @@ -872,14 +832,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.7" - timing: - dependency: transitive - description: - name: timing - sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe" - url: "https://pub.dev" - source: hosted - version: "1.0.2" typed_data: dependency: transitive description: @@ -892,18 +844,18 @@ packages: dependency: transitive description: name: uuid - sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff + sha256: a11b666489b1954e01d992f3d601b1804a33937b5a8fe677bd26b8a9f96f96e8 url: "https://pub.dev" source: hosted - version: "4.5.1" + version: "4.5.2" vector_graphics: dependency: transitive description: name: vector_graphics - sha256: "44cc7104ff32563122a929e4620cf3efd584194eec6d1d913eb5ba593dbcf6de" + sha256: a4f059dc26fc8295b5921376600a194c4ec7d55e72f2fe4c7d2831e103d461e6 url: "https://pub.dev" source: hosted - version: "1.1.18" + version: "1.1.19" vector_graphics_codec: dependency: transitive description: @@ -916,10 +868,10 @@ packages: dependency: transitive description: name: vector_graphics_compiler - sha256: "557a315b7d2a6dbb0aaaff84d857967ce6bdc96a63dc6ee2a57ce5a6ee5d3331" + sha256: d354a7ec6931e6047785f4db12a1f61ec3d43b207fc0790f863818543f8ff0dc url: "https://pub.dev" source: hosted - version: "1.1.17" + version: "1.1.19" vector_math: dependency: transitive description: @@ -932,18 +884,18 @@ packages: dependency: transitive description: name: vm_service - sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 + sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60" url: "https://pub.dev" source: hosted - version: "15.0.0" + version: "15.0.2" watcher: dependency: transitive description: name: watcher - sha256: "69da27e49efa56a15f8afe8f4438c4ec02eff0a117df1b22ea4aad194fe1c104" + sha256: f52385d4f73589977c80797e60fe51014f7f2b957b5e9a62c3f6ada439889249 url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.2.0" web: dependency: transitive description: @@ -980,26 +932,26 @@ packages: dependency: transitive description: name: webview_flutter_android - sha256: f6e6afef6e234801da77170f7a1847ded8450778caf2fe13979d140484be3678 + sha256: eeeb3fcd5f0ff9f8446c9f4bbc18a99b809e40297528a3395597d03aafb9f510 url: "https://pub.dev" source: hosted - version: "4.7.0" + version: "4.10.11" webview_flutter_platform_interface: dependency: transitive description: name: webview_flutter_platform_interface - sha256: "7cb32b21825bd65569665c32bb00a34ded5779786d6201f5350979d2d529940d" + sha256: "63d26ee3aca7256a83ccb576a50272edd7cfc80573a4305caa98985feb493ee0" url: "https://pub.dev" source: hosted - version: "2.13.0" + version: "2.14.0" webview_flutter_wkwebview: dependency: transitive description: name: webview_flutter_wkwebview - sha256: a3d461fe3467014e05f3ac4962e5fdde2a4bf44c561cb53e9ae5c586600fdbc3 + sha256: e49f378ed066efb13fc36186bbe0bd2425630d4ea0dbc71a18fdd0e4d8ed8ebc url: "https://pub.dev" source: hosted - version: "3.22.0" + version: "3.23.5" xdg_directories: dependency: transitive description: @@ -1012,10 +964,10 @@ packages: dependency: transitive description: name: xml - sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 + sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025" url: "https://pub.dev" source: hosted - version: "6.5.0" + version: "6.6.1" yaml: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 580d8c09..2b22d99c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,5 +4,5 @@ environment: sdk: '>=3.1.0 <4.0.0' dev_dependencies: - melos: ^6.3.2 + melos: ^7.3.0 \ No newline at end of file From 0ab89dfde80889dba7622d3f693a214a3fd6561b Mon Sep 17 00:00:00 2001 From: Mithul Nayagam Date: Sat, 27 Dec 2025 02:23:12 +0530 Subject: [PATCH 5/8] Revert "updated melos version" This reverts commit 3e98d2c68f071d93a2dbb542e3e8bedc3a7d4006. --- examples/movie_app/pubspec.lock | 124 ++++++++++-------- examples/stac_gallery/pubspec.lock | 204 ++++++++++++++++++----------- pubspec.yaml | 2 +- 3 files changed, 193 insertions(+), 137 deletions(-) diff --git a/examples/movie_app/pubspec.lock b/examples/movie_app/pubspec.lock index b21c60c7..00d7faa4 100644 --- a/examples/movie_app/pubspec.lock +++ b/examples/movie_app/pubspec.lock @@ -5,18 +5,18 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: "5b7468c326d2f8a4f630056404ca0d291ade42918f4a3c6233618e724f39da8e" + sha256: c209688d9f5a5f26b2fb47a188131a6fb9e876ae9e47af3737c0b4f58a93470d url: "https://pub.dev" source: hosted - version: "92.0.0" + version: "91.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: "70e4b1ef8003c64793a9e268a551a82869a8a96f39deb73dea28084b0e8bf75e" + sha256: f51c8499b35f9b26820cfe914828a6a98a94efd5cc78b37bb7d03debae3a1d08 url: "https://pub.dev" source: hosted - version: "9.0.0" + version: "8.4.1" args: dependency: transitive description: @@ -45,10 +45,10 @@ packages: dependency: transitive description: name: build - sha256: c1668065e9ba04752570ad7e038288559d1e2ca5c6d0131c0f5f55e39e777413 + sha256: dfb67ccc9a78c642193e0c2d94cb9e48c2c818b3178a86097d644acdcde6a8d9 url: "https://pub.dev" source: hosted - version: "4.0.3" + version: "4.0.2" build_config: dependency: transitive description: @@ -61,18 +61,18 @@ packages: dependency: transitive description: name: build_daemon - sha256: bf05f6e12cfea92d3c09308d7bcdab1906cd8a179b023269eed00c071004b957 + sha256: "409002f1adeea601018715d613115cfaf0e31f512cb80ae4534c79867ae2363d" url: "https://pub.dev" source: hosted - version: "4.1.1" + version: "4.1.0" build_runner: dependency: "direct dev" description: name: build_runner - sha256: "110c56ef29b5eb367b4d17fc79375fa8c18a6cd7acd92c05bb3986c17a079057" + sha256: a9461b8e586bf018dd4afd2e13b49b08c6a844a4b226c8d1d10f3a723cdd78c3 url: "https://pub.dev" source: hosted - version: "2.10.4" + version: "2.10.1" built_collection: dependency: transitive description: @@ -85,10 +85,10 @@ packages: dependency: transitive description: name: built_value - sha256: "426cf75afdb23aa74bd4e471704de3f9393f3c7b04c1e2d9c6f1073ae0b8b139" + sha256: a30f0a0e38671e89a492c44d005b5545b830a961575bbd8336d42869ff71066d url: "https://pub.dev" source: hosted - version: "8.12.1" + version: "8.12.0" cached_network_image: dependency: transitive description: @@ -165,10 +165,10 @@ packages: dependency: transitive description: name: crypto - sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf + sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" url: "https://pub.dev" source: hosted - version: "3.0.7" + version: "3.0.6" cupertino_icons: dependency: "direct main" description: @@ -181,10 +181,10 @@ packages: dependency: transitive description: name: dart_style - sha256: a9c30492da18ff84efe2422ba2d319a89942d93e58eb0b73d32abe822ef54b7b + sha256: c87dfe3d56f183ffe9106a18aebc6db431fc7c98c31a54b952a77f3d54a85697 url: "https://pub.dev" source: hosted - version: "3.1.3" + version: "3.1.2" dio: dependency: "direct main" description: @@ -258,10 +258,10 @@ packages: dependency: transitive description: name: flutter_svg - sha256: "87fbd7c534435b6c5d9d98b01e1fd527812b82e68ddd8bd35fc45ed0fa8f0a95" + sha256: "055de8921be7b8e8b98a233c7a5ef84b3a6fcc32f46f1ebf5b9bb3576d108355" url: "https://pub.dev" source: hosted - version: "2.2.3" + version: "2.2.2" flutter_test: dependency: "direct dev" description: flutter @@ -292,10 +292,10 @@ packages: dependency: transitive description: name: http - sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" + sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b" url: "https://pub.dev" source: hosted - version: "1.6.0" + version: "1.4.0" http_multi_server: dependency: transitive description: @@ -332,18 +332,18 @@ packages: dependency: "direct dev" description: name: json_serializable - sha256: "6b253f7851cf1626a05c8b49c792e04a14897349798c03798137f2b5f7e0b5b1" + sha256: "33a040668b31b320aafa4822b7b1e177e163fc3c1e835c6750319d4ab23aa6fe" url: "https://pub.dev" source: hosted - version: "6.11.3" + version: "6.11.1" leak_tracker: dependency: transitive description: name: leak_tracker - sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" + sha256: "8dcda04c3fc16c14f48a7bb586d4be1f0d1572731b6d81d51772ef47c02081e0" url: "https://pub.dev" source: hosted - version: "11.0.2" + version: "11.0.1" leak_tracker_flutter_testing: dependency: transitive description: @@ -372,10 +372,10 @@ packages: dependency: transitive description: name: logger - sha256: a7967e31b703831a893bbc3c3dd11db08126fe5f369b5c648a36f821979f5be3 + sha256: be4b23575aac7ebf01f225a241eb7f6b5641eeaf43c6a8613510fc2f8cf187d1 url: "https://pub.dev" source: hosted - version: "2.6.2" + version: "2.5.0" logging: dependency: transitive description: @@ -460,18 +460,18 @@ packages: dependency: transitive description: name: path_provider_android - sha256: f2c65e21139ce2c3dad46922be8272bb5963516045659e71bb16e151c93b580e + sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9 url: "https://pub.dev" source: hosted - version: "2.2.22" + version: "2.2.17" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "6d13aece7b3f5c5a9731eaf553ff9dcbc2eff41087fd2df587fd0fed9a3eb0c4" + sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942" url: "https://pub.dev" source: hosted - version: "2.5.1" + version: "2.4.1" path_provider_linux: dependency: transitive description: @@ -500,10 +500,10 @@ packages: dependency: transitive description: name: petitparser - sha256: "1a97266a94f7350d30ae522c0af07890c70b8e62c71e8e3920d1db4d23c057d1" + sha256: "07c8f0b1913bcde1ff0d26e57ace2f3012ccbf2b204e070290dad3bb22797646" url: "https://pub.dev" source: hosted - version: "7.0.1" + version: "6.1.0" platform: dependency: transitive description: @@ -556,18 +556,18 @@ packages: dependency: transitive description: name: shared_preferences - sha256: "2939ae520c9024cb197fc20dee269cd8cdbf564c8b5746374ec6cacdc5169e64" + sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5" url: "https://pub.dev" source: hosted - version: "2.5.4" + version: "2.5.3" shared_preferences_android: dependency: transitive description: name: shared_preferences_android - sha256: "83af5c682796c0f7719c2bbf74792d113e40ae97981b8f266fa84574573556bc" + sha256: "07d552dbe8e71ed720e5205e760438ff4ecfb76ec3b32ea664350e2ca4b0c43b" url: "https://pub.dev" source: hosted - version: "2.4.18" + version: "2.4.16" shared_preferences_foundation: dependency: transitive description: @@ -641,18 +641,18 @@ packages: dependency: transitive description: name: source_gen - sha256: "07b277b67e0096c45196cbddddf2d8c6ffc49342e88bf31d460ce04605ddac75" + sha256: "9098ab86015c4f1d8af6486b547b11100e73b193e1899015033cb3e14ad20243" url: "https://pub.dev" source: hosted - version: "4.1.1" + version: "4.0.2" source_helper: dependency: transitive description: name: source_helper - sha256: e82b1996c63da42aa3e6a34cc1ec17427728a1baf72ed017717a5669a7123f0d + sha256: "6a3c6cc82073a8797f8c4dc4572146114a39652851c157db37e964d9c7038723" url: "https://pub.dev" source: hosted - version: "1.3.9" + version: "1.3.8" source_span: dependency: transitive description: @@ -661,6 +661,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.10.1" + sprintf: + dependency: transitive + description: + name: sprintf + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + url: "https://pub.dev" + source: hosted + version: "7.0.0" sqflite: dependency: transitive description: @@ -673,18 +681,18 @@ packages: dependency: transitive description: name: sqflite_android - sha256: ecd684501ebc2ae9a83536e8b15731642b9570dc8623e0073d227d0ee2bfea88 + sha256: "2b3070c5fa881839f8b402ee4a39c1b4d561704d4ebbbcfb808a119bc2a1701b" url: "https://pub.dev" source: hosted - version: "2.4.2+2" + version: "2.4.1" sqflite_common: dependency: transitive description: name: sqflite_common - sha256: "6ef422a4525ecc601db6c0a2233ff448c731307906e92cabc9ba292afaae16a6" + sha256: "84731e8bfd8303a3389903e01fb2141b6e59b5973cacbb0929021df08dddbe8b" url: "https://pub.dev" source: hosted - version: "2.5.6" + version: "2.5.5" sqflite_darwin: dependency: transitive description: @@ -765,10 +773,10 @@ packages: dependency: transitive description: name: synchronized - sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0 + sha256: "0669c70faae6270521ee4f05bffd2919892d42d1276e6c495be80174b6bc0ef6" url: "https://pub.dev" source: hosted - version: "3.4.0" + version: "3.3.1" term_glyph: dependency: transitive description: @@ -797,18 +805,18 @@ packages: dependency: transitive description: name: uuid - sha256: a11b666489b1954e01d992f3d601b1804a33937b5a8fe677bd26b8a9f96f96e8 + sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff url: "https://pub.dev" source: hosted - version: "4.5.2" + version: "4.5.1" vector_graphics: dependency: transitive description: name: vector_graphics - sha256: a4f059dc26fc8295b5921376600a194c4ec7d55e72f2fe4c7d2831e103d461e6 + sha256: "44cc7104ff32563122a929e4620cf3efd584194eec6d1d913eb5ba593dbcf6de" url: "https://pub.dev" source: hosted - version: "1.1.19" + version: "1.1.18" vector_graphics_codec: dependency: transitive description: @@ -821,10 +829,10 @@ packages: dependency: transitive description: name: vector_graphics_compiler - sha256: d354a7ec6931e6047785f4db12a1f61ec3d43b207fc0790f863818543f8ff0dc + sha256: "557a315b7d2a6dbb0aaaff84d857967ce6bdc96a63dc6ee2a57ce5a6ee5d3331" url: "https://pub.dev" source: hosted - version: "1.1.19" + version: "1.1.17" vector_math: dependency: transitive description: @@ -837,18 +845,18 @@ packages: dependency: transitive description: name: vm_service - sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60" + sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 url: "https://pub.dev" source: hosted - version: "15.0.2" + version: "15.0.0" watcher: dependency: transitive description: name: watcher - sha256: f52385d4f73589977c80797e60fe51014f7f2b957b5e9a62c3f6ada439889249 + sha256: "592ab6e2892f67760543fb712ff0177f4ec76c031f02f5b4ff8d3fc5eb9fb61a" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.1.4" web: dependency: transitive description: @@ -885,10 +893,10 @@ packages: dependency: transitive description: name: xml - sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025" + sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 url: "https://pub.dev" source: hosted - version: "6.6.1" + version: "6.5.0" yaml: dependency: transitive description: diff --git a/examples/stac_gallery/pubspec.lock b/examples/stac_gallery/pubspec.lock index 6f062e0a..e4b11ea8 100644 --- a/examples/stac_gallery/pubspec.lock +++ b/examples/stac_gallery/pubspec.lock @@ -5,18 +5,18 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: c209688d9f5a5f26b2fb47a188131a6fb9e876ae9e47af3737c0b4f58a93470d + sha256: e55636ed79578b9abca5fecf9437947798f5ef7456308b5cb85720b793eac92f url: "https://pub.dev" source: hosted - version: "91.0.0" + version: "82.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: f51c8499b35f9b26820cfe914828a6a98a94efd5cc78b37bb7d03debae3a1d08 + sha256: "904ae5bb474d32c38fb9482e2d925d5454cda04ddd0e55d2e6826bc72f6ba8c0" url: "https://pub.dev" source: hosted - version: "8.4.1" + version: "7.4.5" args: dependency: transitive description: @@ -37,10 +37,10 @@ packages: dependency: transitive description: name: bloc - sha256: a2cebb899f91d36eeeaa55c7b20b5915db5a9df1b8fd4a3c9c825e22e474537d + sha256: "52c10575f4445c61dd9e0cafcc6356fdd827c4c64dd7945ef3c4105f6b6ac189" url: "https://pub.dev" source: hosted - version: "9.1.0" + version: "9.0.0" boolean_selector: dependency: transitive description: @@ -53,34 +53,50 @@ packages: dependency: transitive description: name: build - sha256: c1668065e9ba04752570ad7e038288559d1e2ca5c6d0131c0f5f55e39e777413 + sha256: cef23f1eda9b57566c81e2133d196f8e3df48f244b317368d65c5943d91148f0 url: "https://pub.dev" source: hosted - version: "4.0.3" + version: "2.4.2" build_config: dependency: transitive description: name: build_config - sha256: "4f64382b97504dc2fcdf487d5aae33418e08b4703fc21249e4db6d804a4d0187" + sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.1.2" build_daemon: dependency: transitive description: name: build_daemon - sha256: bf05f6e12cfea92d3c09308d7bcdab1906cd8a179b023269eed00c071004b957 + sha256: "8e928697a82be082206edb0b9c99c5a4ad6bc31c9e9b8b2f291ae65cd4a25daa" url: "https://pub.dev" source: hosted - version: "4.1.1" + version: "4.0.4" + build_resolvers: + dependency: transitive + description: + name: build_resolvers + sha256: b9e4fda21d846e192628e7a4f6deda6888c36b5b69ba02ff291a01fd529140f0 + url: "https://pub.dev" + source: hosted + version: "2.4.4" build_runner: dependency: "direct dev" description: name: build_runner - sha256: "110c56ef29b5eb367b4d17fc79375fa8c18a6cd7acd92c05bb3986c17a079057" + sha256: "058fe9dce1de7d69c4b84fada934df3e0153dd000758c4d65964d0166779aa99" url: "https://pub.dev" source: hosted - version: "2.10.4" + version: "2.4.15" + build_runner_core: + dependency: transitive + description: + name: build_runner_core + sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021" + url: "https://pub.dev" + source: hosted + version: "8.0.0" built_collection: dependency: transitive description: @@ -93,10 +109,10 @@ packages: dependency: transitive description: name: built_value - sha256: "426cf75afdb23aa74bd4e471704de3f9393f3c7b04c1e2d9c6f1073ae0b8b139" + sha256: ea90e81dc4a25a043d9bee692d20ed6d1c4a1662a28c03a96417446c093ed6b4 url: "https://pub.dev" source: hosted - version: "8.12.1" + version: "8.9.5" cached_network_image: dependency: transitive description: @@ -133,10 +149,10 @@ packages: dependency: transitive description: name: checked_yaml - sha256: "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f" + sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff url: "https://pub.dev" source: hosted - version: "2.0.4" + version: "2.0.3" clock: dependency: transitive description: @@ -149,10 +165,10 @@ packages: dependency: transitive description: name: code_builder - sha256: "11654819532ba94c34de52ff5feb52bd81cba1de00ef2ed622fd50295f9d4243" + sha256: "0ec10bf4a89e4c613960bf1e8b42c64127021740fb21640c29c909826a5eea3e" url: "https://pub.dev" source: hosted - version: "4.11.0" + version: "4.10.1" collection: dependency: transitive description: @@ -173,10 +189,10 @@ packages: dependency: transitive description: name: crypto - sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf + sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" url: "https://pub.dev" source: hosted - version: "3.0.7" + version: "3.0.6" cupertino_icons: dependency: "direct main" description: @@ -189,10 +205,10 @@ packages: dependency: transitive description: name: dart_style - sha256: a9c30492da18ff84efe2422ba2d319a89942d93e58eb0b73d32abe822ef54b7b + sha256: "27eb0ae77836989a3bc541ce55595e8ceee0992807f14511552a898ddd0d88ac" url: "https://pub.dev" source: hosted - version: "3.1.3" + version: "3.0.1" dio: dependency: transitive description: @@ -274,10 +290,10 @@ packages: dependency: transitive description: name: flutter_svg - sha256: "87fbd7c534435b6c5d9d98b01e1fd527812b82e68ddd8bd35fc45ed0fa8f0a95" + sha256: "055de8921be7b8e8b98a233c7a5ef84b3a6fcc32f46f1ebf5b9bb3576d108355" url: "https://pub.dev" source: hosted - version: "2.2.3" + version: "2.2.2" flutter_test: dependency: "direct dev" description: flutter @@ -292,10 +308,10 @@ packages: dependency: "direct dev" description: name: freezed - sha256: "13065f10e135263a4f5a4391b79a8efc5fb8106f8dd555a9e49b750b45393d77" + sha256: "6022db4c7bfa626841b2a10f34dd1e1b68e8f8f9650db6112dcdeeca45ca793c" url: "https://pub.dev" source: hosted - version: "3.2.3" + version: "3.0.6" freezed_annotation: dependency: "direct main" description: @@ -304,6 +320,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.0" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 + url: "https://pub.dev" + source: hosted + version: "4.0.0" glob: dependency: transitive description: @@ -324,10 +348,10 @@ packages: dependency: transitive description: name: http - sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" + sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b" url: "https://pub.dev" source: hosted - version: "1.6.0" + version: "1.4.0" http_multi_server: dependency: transitive description: @@ -352,6 +376,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.5" + js: + dependency: transitive + description: + name: js + sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc" + url: "https://pub.dev" + source: hosted + version: "0.7.2" json_annotation: dependency: "direct main" description: @@ -364,18 +396,18 @@ packages: dependency: "direct dev" description: name: json_serializable - sha256: c5b2ee75210a0f263c6c7b9eeea80553dbae96ea1bf57f02484e806a3ffdffa3 + sha256: c50ef5fc083d5b5e12eef489503ba3bf5ccc899e487d691584699b4bdefeea8c url: "https://pub.dev" source: hosted - version: "6.11.2" + version: "6.9.5" leak_tracker: dependency: transitive description: name: leak_tracker - sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" + sha256: "8dcda04c3fc16c14f48a7bb586d4be1f0d1572731b6d81d51772ef47c02081e0" url: "https://pub.dev" source: hosted - version: "11.0.2" + version: "11.0.1" leak_tracker_flutter_testing: dependency: transitive description: @@ -404,10 +436,10 @@ packages: dependency: transitive description: name: logger - sha256: a7967e31b703831a893bbc3c3dd11db08126fe5f369b5c648a36f821979f5be3 + sha256: be4b23575aac7ebf01f225a241eb7f6b5641eeaf43c6a8613510fc2f8cf187d1 url: "https://pub.dev" source: hosted - version: "2.6.2" + version: "2.5.0" logging: dependency: transitive description: @@ -500,18 +532,18 @@ packages: dependency: transitive description: name: path_provider_android - sha256: f2c65e21139ce2c3dad46922be8272bb5963516045659e71bb16e151c93b580e + sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9 url: "https://pub.dev" source: hosted - version: "2.2.22" + version: "2.2.17" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "6d13aece7b3f5c5a9731eaf553ff9dcbc2eff41087fd2df587fd0fed9a3eb0c4" + sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942" url: "https://pub.dev" source: hosted - version: "2.5.1" + version: "2.4.1" path_provider_linux: dependency: transitive description: @@ -540,10 +572,10 @@ packages: dependency: transitive description: name: petitparser - sha256: "1a97266a94f7350d30ae522c0af07890c70b8e62c71e8e3920d1db4d23c057d1" + sha256: "07c8f0b1913bcde1ff0d26e57ace2f3012ccbf2b204e070290dad3bb22797646" url: "https://pub.dev" source: hosted - version: "7.0.1" + version: "6.1.0" platform: dependency: transitive description: @@ -564,18 +596,18 @@ packages: dependency: transitive description: name: pool - sha256: "978783255c543aa3586a1b3c21f6e9d720eb315376a915872c61ef8b5c20177d" + sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" url: "https://pub.dev" source: hosted - version: "1.5.2" + version: "1.5.1" provider: dependency: transitive description: name: provider - sha256: "4e82183fa20e5ca25703ead7e05de9e4cceed1fbd1eadc1ac3cb6f565a09f272" + sha256: "4abbd070a04e9ddc287673bf5a030c7ca8b685ff70218720abab8b092f53dd84" url: "https://pub.dev" source: hosted - version: "6.1.5+1" + version: "6.1.5" pub_semver: dependency: transitive description: @@ -604,18 +636,18 @@ packages: dependency: transitive description: name: shared_preferences - sha256: "2939ae520c9024cb197fc20dee269cd8cdbf564c8b5746374ec6cacdc5169e64" + sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5" url: "https://pub.dev" source: hosted - version: "2.5.4" + version: "2.5.3" shared_preferences_android: dependency: transitive description: name: shared_preferences_android - sha256: "83af5c682796c0f7719c2bbf74792d113e40ae97981b8f266fa84574573556bc" + sha256: "07d552dbe8e71ed720e5205e760438ff4ecfb76ec3b32ea664350e2ca4b0c43b" url: "https://pub.dev" source: hosted - version: "2.4.18" + version: "2.4.16" shared_preferences_foundation: dependency: transitive description: @@ -681,18 +713,18 @@ packages: dependency: transitive description: name: source_gen - sha256: "07b277b67e0096c45196cbddddf2d8c6ffc49342e88bf31d460ce04605ddac75" + sha256: "35c8150ece9e8c8d263337a265153c3329667640850b9304861faea59fc98f6b" url: "https://pub.dev" source: hosted - version: "4.1.1" + version: "2.0.0" source_helper: dependency: transitive description: name: source_helper - sha256: "6a3c6cc82073a8797f8c4dc4572146114a39652851c157db37e964d9c7038723" + sha256: "86d247119aedce8e63f4751bd9626fc9613255935558447569ad42f9f5b48b3c" url: "https://pub.dev" source: hosted - version: "1.3.8" + version: "1.3.5" source_span: dependency: transitive description: @@ -701,6 +733,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.10.1" + sprintf: + dependency: transitive + description: + name: sprintf + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + url: "https://pub.dev" + source: hosted + version: "7.0.0" sqflite: dependency: transitive description: @@ -713,18 +753,18 @@ packages: dependency: transitive description: name: sqflite_android - sha256: ecd684501ebc2ae9a83536e8b15731642b9570dc8623e0073d227d0ee2bfea88 + sha256: "2b3070c5fa881839f8b402ee4a39c1b4d561704d4ebbbcfb808a119bc2a1701b" url: "https://pub.dev" source: hosted - version: "2.4.2+2" + version: "2.4.1" sqflite_common: dependency: transitive description: name: sqflite_common - sha256: "6ef422a4525ecc601db6c0a2233ff448c731307906e92cabc9ba292afaae16a6" + sha256: "84731e8bfd8303a3389903e01fb2141b6e59b5973cacbb0929021df08dddbe8b" url: "https://pub.dev" source: hosted - version: "2.5.6" + version: "2.5.5" sqflite_darwin: dependency: transitive description: @@ -812,10 +852,10 @@ packages: dependency: transitive description: name: synchronized - sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0 + sha256: "0669c70faae6270521ee4f05bffd2919892d42d1276e6c495be80174b6bc0ef6" url: "https://pub.dev" source: hosted - version: "3.4.0" + version: "3.3.1" term_glyph: dependency: transitive description: @@ -832,6 +872,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.7" + timing: + dependency: transitive + description: + name: timing + sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe" + url: "https://pub.dev" + source: hosted + version: "1.0.2" typed_data: dependency: transitive description: @@ -844,18 +892,18 @@ packages: dependency: transitive description: name: uuid - sha256: a11b666489b1954e01d992f3d601b1804a33937b5a8fe677bd26b8a9f96f96e8 + sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff url: "https://pub.dev" source: hosted - version: "4.5.2" + version: "4.5.1" vector_graphics: dependency: transitive description: name: vector_graphics - sha256: a4f059dc26fc8295b5921376600a194c4ec7d55e72f2fe4c7d2831e103d461e6 + sha256: "44cc7104ff32563122a929e4620cf3efd584194eec6d1d913eb5ba593dbcf6de" url: "https://pub.dev" source: hosted - version: "1.1.19" + version: "1.1.18" vector_graphics_codec: dependency: transitive description: @@ -868,10 +916,10 @@ packages: dependency: transitive description: name: vector_graphics_compiler - sha256: d354a7ec6931e6047785f4db12a1f61ec3d43b207fc0790f863818543f8ff0dc + sha256: "557a315b7d2a6dbb0aaaff84d857967ce6bdc96a63dc6ee2a57ce5a6ee5d3331" url: "https://pub.dev" source: hosted - version: "1.1.19" + version: "1.1.17" vector_math: dependency: transitive description: @@ -884,18 +932,18 @@ packages: dependency: transitive description: name: vm_service - sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60" + sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 url: "https://pub.dev" source: hosted - version: "15.0.2" + version: "15.0.0" watcher: dependency: transitive description: name: watcher - sha256: f52385d4f73589977c80797e60fe51014f7f2b957b5e9a62c3f6ada439889249 + sha256: "69da27e49efa56a15f8afe8f4438c4ec02eff0a117df1b22ea4aad194fe1c104" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.1.1" web: dependency: transitive description: @@ -932,26 +980,26 @@ packages: dependency: transitive description: name: webview_flutter_android - sha256: eeeb3fcd5f0ff9f8446c9f4bbc18a99b809e40297528a3395597d03aafb9f510 + sha256: f6e6afef6e234801da77170f7a1847ded8450778caf2fe13979d140484be3678 url: "https://pub.dev" source: hosted - version: "4.10.11" + version: "4.7.0" webview_flutter_platform_interface: dependency: transitive description: name: webview_flutter_platform_interface - sha256: "63d26ee3aca7256a83ccb576a50272edd7cfc80573a4305caa98985feb493ee0" + sha256: "7cb32b21825bd65569665c32bb00a34ded5779786d6201f5350979d2d529940d" url: "https://pub.dev" source: hosted - version: "2.14.0" + version: "2.13.0" webview_flutter_wkwebview: dependency: transitive description: name: webview_flutter_wkwebview - sha256: e49f378ed066efb13fc36186bbe0bd2425630d4ea0dbc71a18fdd0e4d8ed8ebc + sha256: a3d461fe3467014e05f3ac4962e5fdde2a4bf44c561cb53e9ae5c586600fdbc3 url: "https://pub.dev" source: hosted - version: "3.23.5" + version: "3.22.0" xdg_directories: dependency: transitive description: @@ -964,10 +1012,10 @@ packages: dependency: transitive description: name: xml - sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025" + sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 url: "https://pub.dev" source: hosted - version: "6.6.1" + version: "6.5.0" yaml: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 2b22d99c..580d8c09 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,5 +4,5 @@ environment: sdk: '>=3.1.0 <4.0.0' dev_dependencies: - melos: ^7.3.0 + melos: ^6.3.2 \ No newline at end of file From 87fc8975e3316662d499ff31badaff36243ea167 Mon Sep 17 00:00:00 2001 From: Mithul Nayagam Date: Sat, 27 Dec 2025 23:19:19 +0530 Subject: [PATCH 6/8] refactor: Update StacApp to StatefulWidget and enhance theme resolution - Changed StacApp from StatelessWidget to StatefulWidget to manage theme resolution state. - Introduced a loading state while themes are being resolved. - Refactored theme resolution logic to improve clarity and efficiency. - Updated StacArtifactCache to StacCache for consistency across caching models. - Adjusted related methods and types to reflect the new StacCache naming convention. --- packages/stac/lib/src/framework/stac_app.dart | 236 ++++++++---------- .../lib/src/framework/stac_app_theme.dart | 4 +- packages/stac/lib/src/models/models.dart | 2 +- .../lib/src/models/stac_artifact_cache.g.dart | 23 -- ...ac_artifact_cache.dart => stac_cache.dart} | 38 ++- .../stac/lib/src/models/stac_cache.g.dart | 21 ++ .../lib/src/services/stac_cache_service.dart | 13 +- .../stac/lib/src/services/stac_cloud.dart | 10 +- 8 files changed, 160 insertions(+), 187 deletions(-) delete mode 100644 packages/stac/lib/src/models/stac_artifact_cache.g.dart rename packages/stac/lib/src/models/{stac_artifact_cache.dart => stac_cache.dart} (58%) create mode 100644 packages/stac/lib/src/models/stac_cache.g.dart diff --git a/packages/stac/lib/src/framework/stac_app.dart b/packages/stac/lib/src/framework/stac_app.dart index f88a6f9b..9c4a6b32 100644 --- a/packages/stac/lib/src/framework/stac_app.dart +++ b/packages/stac/lib/src/framework/stac_app.dart @@ -3,7 +3,7 @@ import 'package:stac/src/framework/stac_app_theme.dart'; import 'package:stac/src/parsers/theme/themes.dart'; import 'package:stac_logger/stac_logger.dart'; -class StacApp extends StatelessWidget { +class StacApp extends StatefulWidget { const StacApp({ super.key, this.navigatorKey, @@ -93,6 +93,9 @@ class StacApp extends StatelessWidget { routes = null, initialRoute = null; + @override + State createState() => _StacAppState(); + final GlobalKey? navigatorKey; final GlobalKey? scaffoldMessengerKey; final Widget? Function(BuildContext)? homeBuilder; @@ -134,75 +137,90 @@ class StacApp extends StatelessWidget { final ScrollBehavior? scrollBehavior; final bool debugShowMaterialGrid; final bool useInheritedMediaQuery; +} + +class _StacAppState extends State { + Future<_ResolvedStacThemes>? _themesFuture; + _ResolvedStacThemes? _resolvedThemes; @override - Widget build(BuildContext context) { - if (routerDelegate != null || routerConfig != null) { - return _materialRouterApp(context); - } - return _materialApp(context); + void initState() { + super.initState(); + _themesFuture = _resolveThemes(); + _themesFuture! + .then((themes) { + if (mounted) { + setState(() { + _resolvedThemes = themes; + }); + } + }) + .catchError((error) { + if (mounted) { + Log.w('Failed to resolve theme: $error'); + setState(() { + _resolvedThemes = (theme: null, darkTheme: null); + }); + } + }); } - Widget _materialApp(BuildContext context) { - return _withResolvedThemes( - context, - (resolvedContext, resolved) => - _buildMaterialApp(resolvedContext, resolved), - ); - } + @override + Widget build(BuildContext context) { + if (_resolvedThemes == null) { + return const _LoadingWidget(); + } - Widget _materialRouterApp(BuildContext context) { - return _withResolvedThemes( - context, - (resolvedContext, resolved) => - _buildMaterialAppRouter(resolvedContext, resolved), - ); + if (widget.routerDelegate != null || widget.routerConfig != null) { + return _buildMaterialAppRouter(context, _resolvedThemes!); + } + return _buildMaterialApp(context, _resolvedThemes!); } Widget _buildMaterialApp(BuildContext context, _ResolvedStacThemes themes) { return MaterialApp( - navigatorKey: navigatorKey, - scaffoldMessengerKey: scaffoldMessengerKey, + navigatorKey: widget.navigatorKey, + scaffoldMessengerKey: widget.scaffoldMessengerKey, home: Builder( builder: (context) { - if (homeBuilder != null) { - return homeBuilder!(context) ?? const SizedBox(); + if (widget.homeBuilder != null) { + return widget.homeBuilder!(context) ?? const SizedBox(); } return const SizedBox(); }, ), - routes: routes ?? {}, - initialRoute: initialRoute, - onGenerateRoute: onGenerateRoute, - onGenerateInitialRoutes: onGenerateInitialRoutes, - onUnknownRoute: onUnknownRoute, - navigatorObservers: navigatorObservers ?? [], - builder: builder, - title: title, - onGenerateTitle: onGenerateTitle, + routes: widget.routes ?? {}, + initialRoute: widget.initialRoute, + onGenerateRoute: widget.onGenerateRoute, + onGenerateInitialRoutes: widget.onGenerateInitialRoutes, + onUnknownRoute: widget.onUnknownRoute, + navigatorObservers: widget.navigatorObservers ?? [], + builder: widget.builder, + title: widget.title, + onGenerateTitle: widget.onGenerateTitle, theme: themes.theme?.parse(context), darkTheme: themes.darkTheme?.parse(context), - highContrastTheme: highContrastTheme, - highContrastDarkTheme: highContrastDarkTheme, - themeMode: themeMode, - themeAnimationDuration: themeAnimationDuration, - themeAnimationCurve: themeAnimationCurve, - color: color, - locale: locale, - localizationsDelegates: localizationsDelegates, - localeListResolutionCallback: localeListResolutionCallback, - localeResolutionCallback: localeResolutionCallback, - supportedLocales: supportedLocales, - showPerformanceOverlay: showPerformanceOverlay, - checkerboardRasterCacheImages: checkerboardRasterCacheImages, - checkerboardOffscreenLayers: checkerboardOffscreenLayers, - showSemanticsDebugger: showSemanticsDebugger, - debugShowCheckedModeBanner: debugShowCheckedModeBanner, - shortcuts: shortcuts, - actions: actions, - restorationScopeId: restorationScopeId, - scrollBehavior: scrollBehavior, - debugShowMaterialGrid: debugShowMaterialGrid, + highContrastTheme: widget.highContrastTheme, + highContrastDarkTheme: widget.highContrastDarkTheme, + themeMode: widget.themeMode, + themeAnimationDuration: widget.themeAnimationDuration, + themeAnimationCurve: widget.themeAnimationCurve, + color: widget.color, + locale: widget.locale, + localizationsDelegates: widget.localizationsDelegates, + localeListResolutionCallback: widget.localeListResolutionCallback, + localeResolutionCallback: widget.localeResolutionCallback, + supportedLocales: widget.supportedLocales, + showPerformanceOverlay: widget.showPerformanceOverlay, + checkerboardRasterCacheImages: widget.checkerboardRasterCacheImages, + checkerboardOffscreenLayers: widget.checkerboardOffscreenLayers, + showSemanticsDebugger: widget.showSemanticsDebugger, + debugShowCheckedModeBanner: widget.debugShowCheckedModeBanner, + shortcuts: widget.shortcuts, + actions: widget.actions, + restorationScopeId: widget.restorationScopeId, + scrollBehavior: widget.scrollBehavior, + debugShowMaterialGrid: widget.debugShowMaterialGrid, ); } @@ -211,44 +229,44 @@ class StacApp extends StatelessWidget { _ResolvedStacThemes themes, ) { return MaterialApp.router( - scaffoldMessengerKey: scaffoldMessengerKey, - routeInformationProvider: routeInformationProvider, - routeInformationParser: routeInformationParser, - routerDelegate: routerDelegate, - routerConfig: routerConfig, - backButtonDispatcher: backButtonDispatcher, - builder: builder, - title: title, - onGenerateTitle: onGenerateTitle, - color: color, + scaffoldMessengerKey: widget.scaffoldMessengerKey, + routeInformationProvider: widget.routeInformationProvider, + routeInformationParser: widget.routeInformationParser, + routerDelegate: widget.routerDelegate, + routerConfig: widget.routerConfig, + backButtonDispatcher: widget.backButtonDispatcher, + builder: widget.builder, + title: widget.title, + onGenerateTitle: widget.onGenerateTitle, + color: widget.color, theme: themes.theme?.parse(context), darkTheme: themes.darkTheme?.parse(context), - highContrastTheme: highContrastTheme, - highContrastDarkTheme: highContrastDarkTheme, - themeMode: themeMode, - themeAnimationDuration: themeAnimationDuration, - themeAnimationCurve: themeAnimationCurve, - locale: locale, - localizationsDelegates: localizationsDelegates, - localeListResolutionCallback: localeListResolutionCallback, - localeResolutionCallback: localeResolutionCallback, - supportedLocales: supportedLocales, - debugShowMaterialGrid: debugShowMaterialGrid, - showPerformanceOverlay: showPerformanceOverlay, - checkerboardRasterCacheImages: checkerboardRasterCacheImages, - checkerboardOffscreenLayers: checkerboardOffscreenLayers, - showSemanticsDebugger: showSemanticsDebugger, - debugShowCheckedModeBanner: debugShowCheckedModeBanner, - shortcuts: shortcuts, - actions: actions, - restorationScopeId: restorationScopeId, - scrollBehavior: scrollBehavior, + highContrastTheme: widget.highContrastTheme, + highContrastDarkTheme: widget.highContrastDarkTheme, + themeMode: widget.themeMode, + themeAnimationDuration: widget.themeAnimationDuration, + themeAnimationCurve: widget.themeAnimationCurve, + locale: widget.locale, + localizationsDelegates: widget.localizationsDelegates, + localeListResolutionCallback: widget.localeListResolutionCallback, + localeResolutionCallback: widget.localeResolutionCallback, + supportedLocales: widget.supportedLocales, + debugShowMaterialGrid: widget.debugShowMaterialGrid, + showPerformanceOverlay: widget.showPerformanceOverlay, + checkerboardRasterCacheImages: widget.checkerboardRasterCacheImages, + checkerboardOffscreenLayers: widget.checkerboardOffscreenLayers, + showSemanticsDebugger: widget.showSemanticsDebugger, + debugShowCheckedModeBanner: widget.debugShowCheckedModeBanner, + shortcuts: widget.shortcuts, + actions: widget.actions, + restorationScopeId: widget.restorationScopeId, + scrollBehavior: widget.scrollBehavior, ); } Future<_ResolvedStacThemes> _resolveThemes() { - final themeInput = theme; - final darkThemeInput = darkTheme; + final themeInput = widget.theme; + final darkThemeInput = widget.darkTheme; // Both themes are optional, so we need to handle null cases final Future? themeFuture = themeInput?.resolve(); @@ -256,7 +274,7 @@ class StacApp extends StatelessWidget { // If both are null, return immediately with null themes if (themeFuture == null && darkThemeFuture == null) { - return Future.value(_ResolvedStacThemes(theme: null, darkTheme: null)); + return Future.value((theme: null, darkTheme: null)); } return Future<_ResolvedStacThemes>(() async { @@ -265,53 +283,15 @@ class StacApp extends StatelessWidget { final resolvedDarkTheme = await (darkThemeFuture ?? Future.value(null)); - return _ResolvedStacThemes( - theme: resolvedTheme, - darkTheme: resolvedDarkTheme, - ); + return (theme: resolvedTheme, darkTheme: resolvedDarkTheme); }); } - - Widget _withResolvedThemes( - BuildContext context, - Widget Function(BuildContext, _ResolvedStacThemes) builder, - ) { - final resolved = _resolveThemes(); - return FutureBuilder<_ResolvedStacThemes>( - future: resolved, - builder: (futureContext, snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - return const _ThemeFutureLoading(); - } - if (snapshot.hasError) { - Log.w('Failed to resolve theme: ${snapshot.error}'); - return builder( - futureContext, - _ResolvedStacThemes(theme: null, darkTheme: null), - ); - } - final themes = snapshot.data; - if (themes == null) { - return builder( - futureContext, - _ResolvedStacThemes(theme: null, darkTheme: null), - ); - } - return builder(futureContext, themes); - }, - ); - } } -class _ResolvedStacThemes { - const _ResolvedStacThemes({required this.theme, required this.darkTheme}); - - final StacTheme? theme; - final StacTheme? darkTheme; -} +typedef _ResolvedStacThemes = ({StacTheme? theme, StacTheme? darkTheme}); -class _ThemeFutureLoading extends StatelessWidget { - const _ThemeFutureLoading(); +class _LoadingWidget extends StatelessWidget { + const _LoadingWidget(); @override Widget build(BuildContext context) { diff --git a/packages/stac/lib/src/framework/stac_app_theme.dart b/packages/stac/lib/src/framework/stac_app_theme.dart index 9ebac069..40484c32 100644 --- a/packages/stac/lib/src/framework/stac_app_theme.dart +++ b/packages/stac/lib/src/framework/stac_app_theme.dart @@ -69,7 +69,7 @@ class StacAppTheme { final _ThemeSource _source; final BuildContext? _context; final StacNetworkRequest? _request; - final dynamic _jsonPayload; + final Object? _jsonPayload; final StacTheme? _dslTheme; /// Resolves the theme based on the configured source. @@ -78,7 +78,7 @@ class StacAppTheme { Future resolve() async { switch (_source) { case _ThemeSource.dsl: - return Future.value(_dslTheme); + return _dslTheme; case _ThemeSource.cloud: return fromCloud(themeName: name!); case _ThemeSource.network: diff --git a/packages/stac/lib/src/models/models.dart b/packages/stac/lib/src/models/models.dart index 8ef3364e..34b2f513 100644 --- a/packages/stac/lib/src/models/models.dart +++ b/packages/stac/lib/src/models/models.dart @@ -1,2 +1,2 @@ export 'package:stac/src/models/stac_cache_config.dart'; -export 'package:stac/src/models/stac_artifact_cache.dart'; +export 'package:stac/src/models/stac_cache.dart'; diff --git a/packages/stac/lib/src/models/stac_artifact_cache.g.dart b/packages/stac/lib/src/models/stac_artifact_cache.g.dart deleted file mode 100644 index 59021ccd..00000000 --- a/packages/stac/lib/src/models/stac_artifact_cache.g.dart +++ /dev/null @@ -1,23 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'stac_artifact_cache.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -StacArtifactCache _$StacArtifactCacheFromJson(Map json) => - StacArtifactCache( - name: json['name'] as String, - stacJson: json['stacJson'] as String, - version: (json['version'] as num).toInt(), - cachedAt: DateTime.parse(json['cachedAt'] as String), - ); - -Map _$StacArtifactCacheToJson(StacArtifactCache instance) => - { - 'name': instance.name, - 'stacJson': instance.stacJson, - 'version': instance.version, - 'cachedAt': instance.cachedAt.toIso8601String(), - }; diff --git a/packages/stac/lib/src/models/stac_artifact_cache.dart b/packages/stac/lib/src/models/stac_cache.dart similarity index 58% rename from packages/stac/lib/src/models/stac_artifact_cache.dart rename to packages/stac/lib/src/models/stac_cache.dart index 7be7e1fb..8cc8ca60 100644 --- a/packages/stac/lib/src/models/stac_artifact_cache.dart +++ b/packages/stac/lib/src/models/stac_cache.dart @@ -2,15 +2,15 @@ import 'dart:convert'; import 'package:json_annotation/json_annotation.dart'; -part 'stac_artifact_cache.g.dart'; +part 'stac_cache.g.dart'; /// Model representing a cached screen from Stac Cloud. /// /// This model stores the screen data along with metadata for caching purposes. @JsonSerializable() -class StacArtifactCache { - /// Creates a [StacArtifactCache] instance. - const StacArtifactCache({ +class StacCache { + /// Creates a [StacCache] instance. + const StacCache({ required this.name, required this.stacJson, required this.version, @@ -29,33 +29,31 @@ class StacArtifactCache { /// The timestamp when this screen was cached. final DateTime cachedAt; - /// Creates a [StacArtifactCache] from a JSON map. - factory StacArtifactCache.fromJson(Map json) => - _$StacArtifactCacheFromJson(json); + /// Creates a [StacCache] from a JSON map. + factory StacCache.fromJson(Map json) => + _$StacCacheFromJson(json); - /// Converts this [StacArtifactCache] to a JSON map. - Map toJson() => _$StacArtifactCacheToJson(this); + /// Converts this [StacCache] to a JSON map. + Map toJson() => _$StacCacheToJson(this); - /// Creates a [StacArtifactCache] from a JSON string. - factory StacArtifactCache.fromJsonString(String jsonString) { - return StacArtifactCache.fromJson( - jsonDecode(jsonString) as Map, - ); + /// Creates a [StacCache] from a JSON string. + factory StacCache.fromJsonString(String jsonString) { + return StacCache.fromJson(jsonDecode(jsonString) as Map); } - /// Converts this [StacArtifactCache] to a JSON string. + /// Converts this [StacCache] to a JSON string. String toJsonString() { return jsonEncode(toJson()); } - /// Creates a copy of this [StacArtifactCache] with the given fields replaced. - StacArtifactCache copyWith({ + /// Creates a copy of this [StacCache] with the given fields replaced. + StacCache copyWith({ String? name, String? stacJson, int? version, DateTime? cachedAt, }) { - return StacArtifactCache( + return StacCache( name: name ?? this.name, stacJson: stacJson ?? this.stacJson, version: version ?? this.version, @@ -65,14 +63,14 @@ class StacArtifactCache { @override String toString() { - return 'StacScreenCache(name: $name, version: $version, cachedAt: $cachedAt)'; + return 'StacCache(name: $name, version: $version, cachedAt: $cachedAt)'; } @override bool operator ==(Object other) { if (identical(this, other)) return true; - return other is StacArtifactCache && + return other is StacCache && other.name == name && other.stacJson == stacJson && other.version == version && diff --git a/packages/stac/lib/src/models/stac_cache.g.dart b/packages/stac/lib/src/models/stac_cache.g.dart new file mode 100644 index 00000000..a5371ab8 --- /dev/null +++ b/packages/stac/lib/src/models/stac_cache.g.dart @@ -0,0 +1,21 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'stac_cache.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +StacCache _$StacCacheFromJson(Map json) => StacCache( + name: json['name'] as String, + stacJson: json['stacJson'] as String, + version: (json['version'] as num).toInt(), + cachedAt: DateTime.parse(json['cachedAt'] as String), +); + +Map _$StacCacheToJson(StacCache instance) => { + 'name': instance.name, + 'stacJson': instance.stacJson, + 'version': instance.version, + 'cachedAt': instance.cachedAt.toIso8601String(), +}; diff --git a/packages/stac/lib/src/services/stac_cache_service.dart b/packages/stac/lib/src/services/stac_cache_service.dart index 0f006024..6a5ea882 100644 --- a/packages/stac/lib/src/services/stac_cache_service.dart +++ b/packages/stac/lib/src/services/stac_cache_service.dart @@ -1,5 +1,5 @@ import 'package:shared_preferences/shared_preferences.dart'; -import 'package:stac/src/models/stac_artifact_cache.dart'; +import 'package:stac/src/models/stac_cache.dart'; import 'package:stac/src/models/stac_artifact_type.dart'; import 'package:stac_logger/stac_logger.dart'; @@ -31,7 +31,7 @@ class StacCacheService { /// Gets a cached artifact by its name and type. /// /// Returns `null` if the artifact is not cached. - static Future getCachedArtifact( + static Future getCachedArtifact( String artifactName, StacArtifactType artifactType, ) async { @@ -45,7 +45,7 @@ class StacCacheService { return null; } - return StacArtifactCache.fromJsonString(cachedData); + return StacCache.fromJsonString(cachedData); } catch (e) { Log.w( 'Failed to get cached artifact $artifactName (${artifactType.name}): $e', @@ -68,7 +68,7 @@ class StacCacheService { final cachePrefix = _getCachePrefix(artifactType); final cacheKey = '$cachePrefix$name'; - final artifactCache = StacArtifactCache( + final artifactCache = StacCache( name: name, stacJson: stacJson, version: version, @@ -118,10 +118,7 @@ class StacCacheService { /// Returns `false` if the cache is expired or doesn't exist. /// /// If [maxAge] is `null`, cache is considered valid (no time-based expiration). - static bool isCacheValid( - StacArtifactCache? cachedArtifact, - Duration? maxAge, - ) { + static bool isCacheValid(StacCache? cachedArtifact, Duration? maxAge) { if (cachedArtifact == null) return false; if (maxAge == null) return true; diff --git a/packages/stac/lib/src/services/stac_cloud.dart b/packages/stac/lib/src/services/stac_cloud.dart index b621ce98..9d4cc6e3 100644 --- a/packages/stac/lib/src/services/stac_cloud.dart +++ b/packages/stac/lib/src/services/stac_cloud.dart @@ -2,7 +2,7 @@ import 'package:dio/dio.dart'; import 'package:stac/src/framework/stac_service.dart'; import 'package:stac/src/models/stac_artifact_type.dart'; import 'package:stac/src/models/stac_cache_config.dart'; -import 'package:stac/src/models/stac_artifact_cache.dart'; +import 'package:stac/src/models/stac_cache.dart'; import 'package:stac/src/services/stac_cache_service.dart'; import 'package:stac_logger/stac_logger.dart'; @@ -165,7 +165,7 @@ class StacCloud { static Future _handleArtifactNetworkFirst({ required StacArtifactType artifactType, required String artifactName, - StacArtifactCache? cachedArtifact, + StacCache? cachedArtifact, }) async { try { return await _fetchArtifactFromNetwork( @@ -189,7 +189,7 @@ class StacCloud { static Future _handleArtifactCacheFirst({ required StacArtifactType artifactType, required String artifactName, - StacArtifactCache? cachedArtifact, + StacCache? cachedArtifact, required bool isCacheValid, required StacCacheConfig config, }) async { @@ -229,7 +229,7 @@ class StacCloud { static Future _handleArtifactOptimistic({ required StacArtifactType artifactType, required String artifactName, - StacArtifactCache? cachedArtifact, + StacCache? cachedArtifact, required bool isCacheValid, required StacCacheConfig config, }) async { @@ -306,7 +306,7 @@ class StacCloud { /// Builds a Response from cached artifact data. static Response _buildArtifactCacheResponse( StacArtifactType artifactType, - StacArtifactCache cachedArtifact, + StacCache cachedArtifact, ) { final fetchUrl = _getFetchUrl(artifactType); return Response( From 4293b1e2413f35e2b5fd91b4a1190c43fad31aa6 Mon Sep 17 00:00:00 2001 From: Mithul Nayagam Date: Fri, 2 Jan 2026 18:55:39 +0530 Subject: [PATCH 7/8] docs: Refine theming documentation for clarity and accuracy - Updated the description to clarify the use of DSL and Cloud sources for theme management. - Removed redundant sections on DSL themes and streamlined the explanation of theme loading methods. - Adjusted the numbering of theme loading methods for consistency. - Enhanced best practices and error handling sections for better guidance. --- docs/concepts/theming.mdx | 198 ++------------------------------------ 1 file changed, 7 insertions(+), 191 deletions(-) diff --git a/docs/concepts/theming.mdx b/docs/concepts/theming.mdx index 397b3d3d..e97589c0 100644 --- a/docs/concepts/theming.mdx +++ b/docs/concepts/theming.mdx @@ -1,18 +1,17 @@ --- title: "Theming" -description: "Learn how to implement and manage themes in Stac applications using cloud (recommended), DSL, network, or JSON sources" +description: "Learn how to implement and manage themes in Stac applications using DSL + Cloud (recommended), network, or JSON sources" --- -Theming is an essential part of any application, ensuring a consistent look and feel across the entire app. Stac offers flexible theming options that allow you to fetch themes from Stac Cloud (recommended), define them using Dart DSL, load them over the network, or parse them from JSON. +Theming is an essential part of any application, ensuring a consistent look and feel across the entire app. Stac offers flexible theming options that allow you to fetch themes from Stac Cloud (recommended), load them over the network, or parse them from JSON. ## Overview Stac theming works similarly to Flutter's built-in theming system. You define themes using `StacTheme` objects and apply them to your application using `StacApp`. The framework supports multiple ways to load themes: 1. **Cloud Themes (Recommended)** - Fetch themes from Stac Cloud by name -2. **DSL Themes** - Define themes directly in Dart code using the `@StacThemeRef` annotation -3. **Network Themes** - Load themes over HTTP using `StacNetworkRequest` -4. **JSON Themes** - Parse themes from JSON data +2. **Network Themes** - Load themes over HTTP using `StacNetworkRequest` +3. **JSON Themes** - Parse themes from JSON data ## Using StacApp with Themes @@ -116,171 +115,9 @@ StacApp( The `StacAppTheme` wrapper automatically fetches the theme from Stac Cloud using the provided name. Themes are cached intelligently to minimize network requests. -### 2. DSL Themes +> You can also use DSL themes directly by passing a `StacTheme` object to `StacAppTheme.dsl(theme: myTheme)`. When using themes directly, the `@StacThemeRef` annotation is not needed. The annotation is only required when you want to deploy themes to Stac Cloud using `stac deploy`. -DSL themes are defined directly in your Dart code. This approach provides type safety, code completion, and easy refactoring. You can use DSL themes in two ways: - -1. **Direct usage** - Define themes without annotations and use them directly in your app -2. **Cloud deployment** - Add `@StacThemeRef` annotation to generate JSON and deploy to Stac Cloud - -#### Creating a DSL Theme - -Create a theme file in your `stac/` directory (e.g., `stac/app_theme.dart`): - -**For direct usage (no annotation needed):** - -```dart -import 'package:stac_core/stac_core.dart'; - -StacTheme get darkTheme => _buildTheme( - brightness: StacBrightness.dark, - colorScheme: StacColorScheme( - brightness: StacBrightness.dark, - primary: '#95E183', - onPrimary: '#050608', - secondary: '#95E183', - onSecondary: '#FFFFFF', - surface: '#050608', - onSurface: '#FFFFFF', - onSurfaceVariant: '#65FFFFFF', - error: '#FF6565', - onError: '#050608', - outline: '#08FFFFFF', - ), -); -``` - -**For cloud deployment (with annotation):** - -```dart -import 'package:stac_core/stac_core.dart'; - -@StacThemeRef(name: "movie_app_dark") -StacTheme get darkTheme => _buildTheme( - brightness: StacBrightness.dark, - colorScheme: StacColorScheme( - brightness: StacBrightness.dark, - primary: '#95E183', - onPrimary: '#050608', - secondary: '#95E183', - onSecondary: '#FFFFFF', - surface: '#050608', - onSurface: '#FFFFFF', - onSurfaceVariant: '#65FFFFFF', - error: '#FF6565', - onError: '#050608', - outline: '#08FFFFFF', - ), -); -``` - -**Note**: The `@StacThemeRef` annotation is only required if you want to generate JSON and deploy themes to Stac Cloud using `stac deploy`. For direct usage in your app, you can define themes without any annotations. - -Helper functions (shared for both approaches): - -```dart -StacTheme _buildTheme({ - required StacBrightness brightness, - required StacColorScheme colorScheme, -}) { - return StacTheme( - brightness: brightness, - colorScheme: colorScheme, - textTheme: _buildTextTheme(), - filledButtonTheme: _buildFilledButtonTheme(), - outlinedButtonTheme: _buildOutlinedButtonTheme(), - dividerTheme: _buildDividerTheme(), - ); -} -``` - -Helper functions (shared for both approaches): - -```dart -StacTextTheme _buildTextTheme() { - return StacTextTheme( - displayLarge: _textStyle( - fontSize: 48, - fontWeight: StacFontWeight.w700, - height: 1.1, - ), - // ... other text styles - ); -} - -StacButtonStyle _buildFilledButtonTheme() { - return StacButtonStyle( - minimumSize: StacSize(120, 40), - textStyle: _textStyle( - fontSize: 16, - fontWeight: StacFontWeight.w500, - height: 1.3, - ), - padding: StacEdgeInsets.only(left: 10, right: 10, top: 8, bottom: 8), - shape: StacRoundedRectangleBorder(borderRadius: StacBorderRadius.all(8)), - ); -} - -StacButtonStyle _buildOutlinedButtonTheme() { - return StacButtonStyle( - minimumSize: StacSize(120, 40), - textStyle: _textStyle( - fontSize: 16, - fontWeight: StacFontWeight.w500, - height: 1.3, - ), - padding: StacEdgeInsets.only(left: 10, right: 10, top: 8, bottom: 8), - side: StacBorderSide(color: '#95E183', width: 1.0), - shape: StacRoundedRectangleBorder(borderRadius: StacBorderRadius.all(8)), - ); -} - -StacDividerThemeData _buildDividerTheme() { - return StacDividerThemeData(color: '#24FFFFFF', thickness: 1); -} - -StacCustomTextStyle _textStyle({ - required double fontSize, - required StacFontWeight fontWeight, - required double height, - double? letterSpacing, -}) { - return StacCustomTextStyle( - fontSize: fontSize, - fontWeight: fontWeight, - height: height, - letterSpacing: letterSpacing, - ); -} -``` - -#### Using DSL Themes - -After defining your theme (with or without `@StacThemeRef`), you can use it directly by passing the `StacTheme` object to `StacAppTheme.dsl()`: - -```dart -// Import your theme file (adjust path based on your project structure) -import '../stac/app_theme.dart'; // or package:your_app/stac/app_theme.dart - -StacApp( - theme: StacAppTheme.dsl(theme: darkTheme), - // ... -) -``` - -**Important**: -- DSL themes take the `StacTheme` object directly, not a name string -- **No annotation required** for direct usage - you can define themes as simple getters -- `@StacThemeRef` annotation is only needed if you want to deploy themes to Stac Cloud -- Cloud themes use `StacAppTheme(name: "...")` to fetch by name from Stac Cloud -- DSL themes are compiled into your app and don't require network access - -**When to use `@StacThemeRef`:** -- Only when you want to generate JSON and deploy themes to Stac Cloud using `stac deploy` -- The annotation tells the CLI which themes to process and what name to use in the cloud -- For themes used only in your app code, annotations are optional - -### 3. Network Themes +### 2. Network Themes Load themes over HTTP using a `StacNetworkRequest`. This allows you to fetch themes from any API endpoint: @@ -297,7 +134,7 @@ StacApp( ) ``` -### 4. JSON Themes +### 3. JSON Themes Parse themes directly from JSON data. Useful when themes are stored locally or received from other sources: @@ -393,24 +230,3 @@ StacButtonStyle( shape: StacRoundedRectangleBorder(borderRadius: StacBorderRadius.all(8)), ) ``` - -## Best Practices - -1. **Use Cloud Themes (Recommended)**: Cloud themes are the recommended approach as they allow themes to be managed server-side and updated without app updates. They also benefit from intelligent caching. - -2. **Use DSL Themes for Static Themes**: If your themes don't change frequently and you prefer type safety, use DSL themes with `@StacThemeRef`. - -3. **Organize Theme Files**: Keep theme definitions in your `stac/` directory alongside your screen definitions. - -4. **Extract Common Styles**: Use helper methods like `_textStyle()` and `_buildTheme()` to avoid code duplication. - -5. **Support Both Light and Dark Themes**: Always provide both `theme` and `darkTheme` parameters to `StacApp` for better user experience. - -## Error Handling - -If a theme fails to load (network error, malformed JSON, etc.), Stac will: -- Log a warning with details about the error -- Fall back to default Flutter theme -- Continue app execution without blocking - -This ensures your app remains functional even if theme loading fails. From dd9561a1aac7a33726f73d3686553950edd9e48c Mon Sep 17 00:00:00 2001 From: Mithul Nayagam Date: Sat, 3 Jan 2026 00:09:47 +0530 Subject: [PATCH 8/8] switched env var --- packages/stac/lib/src/services/stac_cloud.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/stac/lib/src/services/stac_cloud.dart b/packages/stac/lib/src/services/stac_cloud.dart index 9d4cc6e3..65f7a676 100644 --- a/packages/stac/lib/src/services/stac_cloud.dart +++ b/packages/stac/lib/src/services/stac_cloud.dart @@ -20,8 +20,7 @@ class StacCloud { ), ); - static const String _baseUrl = - 'https://us-central1-stac-dev-9eff0.cloudfunctions.net'; + static const String _baseUrl = 'https://api.stac.dev'; /// Gets the fetch URL for a given artifact type. static String _getFetchUrl(StacArtifactType artifactType) {