From 7bd10f2cec75c3c279d4aa220f9ca0dfefdbcdbc Mon Sep 17 00:00:00 2001 From: lstonussi Date: Mon, 19 May 2025 14:24:32 -0300 Subject: [PATCH 01/30] feat: Introduce custom color parsing functionality in StacRegistry --- packages/stac/lib/src/framework/stac.dart | 3 +++ packages/stac/lib/src/framework/stac_registry.dart | 3 +++ .../parsers/stac_card_theme_data/stac_card_theme_data.dart | 4 ++-- .../lib/src/parsers/stac_dialog_theme/stac_dialog_theme.dart | 4 ++-- .../stac_tab_bar_theme_data/stac_tab_bar_theme_data.dart | 4 ++-- packages/stac/lib/src/utils/color_utils.dart | 5 +++++ 6 files changed, 17 insertions(+), 6 deletions(-) diff --git a/packages/stac/lib/src/framework/stac.dart b/packages/stac/lib/src/framework/stac.dart index a907338fe..fb3069b0c 100644 --- a/packages/stac/lib/src/framework/stac.dart +++ b/packages/stac/lib/src/framework/stac.dart @@ -120,6 +120,9 @@ class Stac { StacNetworkService.initialize(dio ?? Dio()); } + static void setParseCustomColor(Color? Function(String?)? parseCustomColor) => + StacRegistry.instance.parseCustomColor = parseCustomColor; + static Widget? fromJson(Map? json, BuildContext context) { try { if (json != null) { diff --git a/packages/stac/lib/src/framework/stac_registry.dart b/packages/stac/lib/src/framework/stac_registry.dart index 5c1d81252..d6d4dc28c 100644 --- a/packages/stac/lib/src/framework/stac_registry.dart +++ b/packages/stac/lib/src/framework/stac_registry.dart @@ -1,3 +1,4 @@ +import 'package:flutter/material.dart'; import 'package:stac/src/utils/log.dart'; import 'package:stac_framework/stac_framework.dart'; @@ -14,6 +15,8 @@ class StacRegistry { static final _stacActionParsers = {}; + Color? Function(String?)? parseCustomColor; + bool register(StacParser parser, [bool override = false]) { final String type = parser.type; if (_stacParsers.containsKey(type)) { diff --git a/packages/stac/lib/src/parsers/stac_card_theme_data/stac_card_theme_data.dart b/packages/stac/lib/src/parsers/stac_card_theme_data/stac_card_theme_data.dart index 8f1026f25..222a296af 100644 --- a/packages/stac/lib/src/parsers/stac_card_theme_data/stac_card_theme_data.dart +++ b/packages/stac/lib/src/parsers/stac_card_theme_data/stac_card_theme_data.dart @@ -24,8 +24,8 @@ class StacCardThemeData with _$StacCardThemeData { } extension StacCardThemeDataParser on StacCardThemeData { - CardTheme? parse(BuildContext context) { - return CardTheme( + CardThemeData? parse(BuildContext context) { + return CardThemeData( clipBehavior: clipBehavior, color: color.toColor(context), shadowColor: shadowColor.toColor(context), diff --git a/packages/stac/lib/src/parsers/stac_dialog_theme/stac_dialog_theme.dart b/packages/stac/lib/src/parsers/stac_dialog_theme/stac_dialog_theme.dart index dec8c38a2..710f6fbc5 100644 --- a/packages/stac/lib/src/parsers/stac_dialog_theme/stac_dialog_theme.dart +++ b/packages/stac/lib/src/parsers/stac_dialog_theme/stac_dialog_theme.dart @@ -29,8 +29,8 @@ class StacDialogTheme with _$StacDialogTheme { } extension StacDialogThemeParser on StacDialogTheme { - DialogTheme? parse(BuildContext context) { - return DialogTheme( + DialogThemeData? parse(BuildContext context) { + return DialogThemeData( backgroundColor: backgroundColor.toColor(context), elevation: elevation, shadowColor: shadowColor.toColor(context), diff --git a/packages/stac/lib/src/parsers/stac_tab_bar_theme_data/stac_tab_bar_theme_data.dart b/packages/stac/lib/src/parsers/stac_tab_bar_theme_data/stac_tab_bar_theme_data.dart index 8093fb70b..16e43f29b 100644 --- a/packages/stac/lib/src/parsers/stac_tab_bar_theme_data/stac_tab_bar_theme_data.dart +++ b/packages/stac/lib/src/parsers/stac_tab_bar_theme_data/stac_tab_bar_theme_data.dart @@ -28,8 +28,8 @@ class StacTabBarThemeData with _$StacTabBarThemeData { } extension StacTabBarThemeDataParser on StacTabBarThemeData { - TabBarTheme? parse(BuildContext context) { - return TabBarTheme( + TabBarThemeData? parse(BuildContext context) { + return TabBarThemeData( indicator: indicator.parse(context), indicatorColor: indicatorColor.toColor(context), indicatorSize: indicatorSize, diff --git a/packages/stac/lib/src/utils/color_utils.dart b/packages/stac/lib/src/utils/color_utils.dart index 07af47fea..a2c43aec6 100644 --- a/packages/stac/lib/src/utils/color_utils.dart +++ b/packages/stac/lib/src/utils/color_utils.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:stac/src/utils/color_type.dart'; +import 'package:stac/stac.dart'; const String _hashtag = "#"; const String _empty = ""; @@ -91,6 +92,10 @@ Color? _parseThemeColor(String color, BuildContext context) { case StacColorType.scaffoldBackgroundColor: return Theme.of(context).scaffoldBackgroundColor; case StacColorType.none: + final customColor = StacRegistry.instance.parseCustomColor?.call(color); + if (customColor != null) { + return customColor; + } return null; } } From 9161ed83389a959001f77d29b6f2ae91cce25f8b Mon Sep 17 00:00:00 2001 From: Felipe Sales Date: Mon, 26 May 2025 14:15:55 -0300 Subject: [PATCH 02/30] feat: Implement version management in Stac with StacVersion class and app version registration --- examples/counter_example/pubspec.lock | 16 +- .../stac_dialog_action.g.dart | 1 + packages/stac/lib/src/framework/stac.dart | 16 +- .../stac/lib/src/framework/stac_registry.dart | 8 + .../stac/lib/src/framework/stac_version.dart | 0 .../lib/src/utils/version/stac_version.dart | 83 ++++++++ .../utils/version/stac_version.freezed.dart | 183 ++++++++++++++++++ .../lib/src/utils/version/stac_version.g.dart | 28 +++ 8 files changed, 326 insertions(+), 9 deletions(-) create mode 100644 packages/stac/lib/src/framework/stac_version.dart create mode 100644 packages/stac/lib/src/utils/version/stac_version.dart create mode 100644 packages/stac/lib/src/utils/version/stac_version.freezed.dart create mode 100644 packages/stac/lib/src/utils/version/stac_version.g.dart diff --git a/examples/counter_example/pubspec.lock b/examples/counter_example/pubspec.lock index 2906f7e42..5807ba437 100644 --- a/examples/counter_example/pubspec.lock +++ b/examples/counter_example/pubspec.lock @@ -34,10 +34,10 @@ packages: dependency: transitive description: name: async - sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" url: "https://pub.dev" source: hosted - version: "2.12.0" + version: "2.13.0" bloc: dependency: transitive description: @@ -210,10 +210,10 @@ packages: dependency: transitive description: name: fake_async - sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" url: "https://pub.dev" source: hosted - version: "1.3.2" + version: "1.3.3" file: dependency: transitive description: @@ -348,10 +348,10 @@ packages: dependency: transitive description: name: leak_tracker - sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec + sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" url: "https://pub.dev" source: hosted - version: "10.0.8" + version: "10.0.9" leak_tracker_flutter_testing: dependency: transitive description: @@ -623,10 +623,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" + sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 url: "https://pub.dev" source: hosted - version: "14.3.1" + version: "15.0.0" watcher: dependency: transitive description: diff --git a/packages/stac/lib/src/action_parsers/stac_dialog_action/stac_dialog_action.g.dart b/packages/stac/lib/src/action_parsers/stac_dialog_action/stac_dialog_action.g.dart index 4182e6342..9e96fb883 100644 --- a/packages/stac/lib/src/action_parsers/stac_dialog_action/stac_dialog_action.g.dart +++ b/packages/stac/lib/src/action_parsers/stac_dialog_action/stac_dialog_action.g.dart @@ -41,4 +41,5 @@ const _$TraversalEdgeBehaviorEnumMap = { TraversalEdgeBehavior.closedLoop: 'closedLoop', TraversalEdgeBehavior.leaveFlutterView: 'leaveFlutterView', TraversalEdgeBehavior.parentScope: 'parentScope', + TraversalEdgeBehavior.stop: 'stop', }; diff --git a/packages/stac/lib/src/framework/stac.dart b/packages/stac/lib/src/framework/stac.dart index fb3069b0c..7b89c51a7 100644 --- a/packages/stac/lib/src/framework/stac.dart +++ b/packages/stac/lib/src/framework/stac.dart @@ -10,6 +10,7 @@ import 'package:stac/src/framework/stac_registry.dart'; import 'package:stac/src/parsers/parsers.dart'; import 'package:stac/src/services/stac_network_service.dart'; import 'package:stac/src/utils/log.dart'; +import 'package:stac/src/utils/version/stac_version.dart'; import 'package:stac_framework/stac_framework.dart'; typedef ErrorWidgetBuilder = Widget Function( @@ -112,11 +113,13 @@ class Stac { List actionParsers = const [], Dio? dio, bool override = false, + String? appVersion, }) async { _parsers.addAll(parsers); _actionParsers.addAll(actionParsers); StacRegistry.instance.registerAll(_parsers, override); StacRegistry.instance.registerAllActions(_actionParsers, override); + StacRegistry.instance.registerAppVersion(appVersion); StacNetworkService.initialize(dio ?? Dio()); } @@ -126,8 +129,19 @@ class Stac { static Widget? fromJson(Map? json, BuildContext context) { try { if (json != null) { + final stacRegistry = StacRegistry.instance; + StacVersion? jsonVersion = json['version']; + + if (jsonVersion != null && stacRegistry.appVersion != null) { + final isSatisfied = jsonVersion.isSatisfied(stacRegistry.appVersion); + if (!isSatisfied) { + Log.w('Stac version ${jsonVersion.version} is not satisfied'); + return null; + } + } + String widgetType = json['type']; - StacParser? stacParser = StacRegistry.instance.getParser(widgetType); + StacParser? stacParser = stacRegistry.getParser(widgetType); if (stacParser != null) { final model = stacParser.getModel(json); return stacParser.parse(context, model); diff --git a/packages/stac/lib/src/framework/stac_registry.dart b/packages/stac/lib/src/framework/stac_registry.dart index d6d4dc28c..1329f23df 100644 --- a/packages/stac/lib/src/framework/stac_registry.dart +++ b/packages/stac/lib/src/framework/stac_registry.dart @@ -16,6 +16,8 @@ class StacRegistry { static final _stacActionParsers = {}; Color? Function(String?)? parseCustomColor; + String? _appVersion; + String? get appVersion => _appVersion; bool register(StacParser parser, [bool override = false]) { final String type = parser.type; @@ -71,6 +73,12 @@ class StacRegistry { ); } + void registerAppVersion(String? version) { + if (version != null) { + _appVersion = version; + } + } + StacParser? getParser(String type) { return _stacParsers[type]; } diff --git a/packages/stac/lib/src/framework/stac_version.dart b/packages/stac/lib/src/framework/stac_version.dart new file mode 100644 index 000000000..e69de29bb diff --git a/packages/stac/lib/src/utils/version/stac_version.dart b/packages/stac/lib/src/utils/version/stac_version.dart new file mode 100644 index 000000000..fc8bcd1d6 --- /dev/null +++ b/packages/stac/lib/src/utils/version/stac_version.dart @@ -0,0 +1,83 @@ +import 'dart:math' as math; +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'stac_version.freezed.dart'; +part 'stac_version.g.dart'; + +@freezed +class StacVersion with _$StacVersion { + const factory StacVersion({ + required String version, + required StacConditionVersion condition, + }) = _StacVersion; + + factory StacVersion.fromJson(Map json) => + _$StacVersionFromJson(json); + + bool isSatisfied(String? targetVersion) { + if (targetVersion == null) { + return false; + } + + final current = version.split('.').toIntList(); + final target = targetVersion.split('.').toIntList(); + + final maxLength = math.max(current.length, target.length); + current.length = maxLength; + target.length = maxLength; + current.fillRange(current.length, maxLength, 0); + target.fillRange(target.length, maxLength, 0); + + // Compara os números + for (int i = 0; i < maxLength; i++) { + if (current[i] != target[i]) { + final comp = current[i].compareTo(target[i]); + return switch (condition) { + StacConditionVersion.greaterThan => comp > 0, + StacConditionVersion.greaterThanOrEqual => comp >= 0, + StacConditionVersion.lessThan => comp < 0, + StacConditionVersion.lessThanOrEqual => comp <= 0, + StacConditionVersion.equal => false, + StacConditionVersion.notEqual => true, + }; + } + } + + return false; + } +} + +enum StacConditionVersion { + greaterThan, + greaterThanOrEqual, + lessThan, + lessThanOrEqual, + equal, + notEqual, +} + +extension StacConditionVersionX on String? { + StacConditionVersion toStacConditionVersion() => switch (this) { + '>' => StacConditionVersion.greaterThan, + '>=' => StacConditionVersion.greaterThanOrEqual, + '<' => StacConditionVersion.lessThan, + '<=' => StacConditionVersion.lessThanOrEqual, + '==' => StacConditionVersion.equal, + _ => StacConditionVersion.notEqual, + }; +} + +extension StacConditionVersionToStringX on StacConditionVersion { + String toTypeString() => switch (this) { + StacConditionVersion.greaterThan => '>', + StacConditionVersion.greaterThanOrEqual => '>=', + StacConditionVersion.lessThan => '<', + StacConditionVersion.lessThanOrEqual => '<=', + StacConditionVersion.equal => '==', + StacConditionVersion.notEqual => '!=', + }; +} + +extension ListStringX on List { + List toIntList() => map((e) => int.tryParse(e) ?? 0).toList(); +} diff --git a/packages/stac/lib/src/utils/version/stac_version.freezed.dart b/packages/stac/lib/src/utils/version/stac_version.freezed.dart new file mode 100644 index 000000000..212c7e41e --- /dev/null +++ b/packages/stac/lib/src/utils/version/stac_version.freezed.dart @@ -0,0 +1,183 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'stac_version.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +StacVersion _$StacVersionFromJson(Map json) { + return _StacVersion.fromJson(json); +} + +/// @nodoc +mixin _$StacVersion { + String get version => throw _privateConstructorUsedError; + StacConditionVersion get condition => throw _privateConstructorUsedError; + + /// Serializes this StacVersion to a JSON map. + Map toJson() => throw _privateConstructorUsedError; + + /// Create a copy of StacVersion + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $StacVersionCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $StacVersionCopyWith<$Res> { + factory $StacVersionCopyWith( + StacVersion value, $Res Function(StacVersion) then) = + _$StacVersionCopyWithImpl<$Res, StacVersion>; + @useResult + $Res call({String version, StacConditionVersion condition}); +} + +/// @nodoc +class _$StacVersionCopyWithImpl<$Res, $Val extends StacVersion> + implements $StacVersionCopyWith<$Res> { + _$StacVersionCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of StacVersion + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? version = null, + Object? condition = null, + }) { + return _then(_value.copyWith( + version: null == version + ? _value.version + : version // ignore: cast_nullable_to_non_nullable + as String, + condition: null == condition + ? _value.condition + : condition // ignore: cast_nullable_to_non_nullable + as StacConditionVersion, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$StacVersionImplCopyWith<$Res> + implements $StacVersionCopyWith<$Res> { + factory _$$StacVersionImplCopyWith( + _$StacVersionImpl value, $Res Function(_$StacVersionImpl) then) = + __$$StacVersionImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({String version, StacConditionVersion condition}); +} + +/// @nodoc +class __$$StacVersionImplCopyWithImpl<$Res> + extends _$StacVersionCopyWithImpl<$Res, _$StacVersionImpl> + implements _$$StacVersionImplCopyWith<$Res> { + __$$StacVersionImplCopyWithImpl( + _$StacVersionImpl _value, $Res Function(_$StacVersionImpl) _then) + : super(_value, _then); + + /// Create a copy of StacVersion + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? version = null, + Object? condition = null, + }) { + return _then(_$StacVersionImpl( + version: null == version + ? _value.version + : version // ignore: cast_nullable_to_non_nullable + as String, + condition: null == condition + ? _value.condition + : condition // ignore: cast_nullable_to_non_nullable + as StacConditionVersion, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$StacVersionImpl implements _StacVersion { + const _$StacVersionImpl({required this.version, required this.condition}); + + factory _$StacVersionImpl.fromJson(Map json) => + _$$StacVersionImplFromJson(json); + + @override + final String version; + @override + final StacConditionVersion condition; + + @override + String toString() { + return 'StacVersion(version: $version, condition: $condition)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$StacVersionImpl && + (identical(other.version, version) || other.version == version) && + (identical(other.condition, condition) || + other.condition == condition)); + } + + @JsonKey(includeFromJson: false, includeToJson: false) + @override + int get hashCode => Object.hash(runtimeType, version, condition); + + /// Create a copy of StacVersion + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$StacVersionImplCopyWith<_$StacVersionImpl> get copyWith => + __$$StacVersionImplCopyWithImpl<_$StacVersionImpl>(this, _$identity); + + @override + Map toJson() { + return _$$StacVersionImplToJson( + this, + ); + } +} + +abstract class _StacVersion implements StacVersion { + const factory _StacVersion( + {required final String version, + required final StacConditionVersion condition}) = _$StacVersionImpl; + + factory _StacVersion.fromJson(Map json) = + _$StacVersionImpl.fromJson; + + @override + String get version; + @override + StacConditionVersion get condition; + + /// Create a copy of StacVersion + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$StacVersionImplCopyWith<_$StacVersionImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/packages/stac/lib/src/utils/version/stac_version.g.dart b/packages/stac/lib/src/utils/version/stac_version.g.dart new file mode 100644 index 000000000..a7a44b873 --- /dev/null +++ b/packages/stac/lib/src/utils/version/stac_version.g.dart @@ -0,0 +1,28 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'stac_version.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$StacVersionImpl _$$StacVersionImplFromJson(Map json) => + _$StacVersionImpl( + version: json['version'] as String, + condition: $enumDecode(_$StacConditionVersionEnumMap, json['condition']), + ); + +Map _$$StacVersionImplToJson(_$StacVersionImpl instance) => + { + 'version': instance.version, + 'condition': _$StacConditionVersionEnumMap[instance.condition]!, + }; + +const _$StacConditionVersionEnumMap = { + StacConditionVersion.greaterThan: 'greaterThan', + StacConditionVersion.greaterThanOrEqual: 'greaterThanOrEqual', + StacConditionVersion.lessThan: 'lessThan', + StacConditionVersion.lessThanOrEqual: 'lessThanOrEqual', + StacConditionVersion.equal: 'equal', + StacConditionVersion.notEqual: 'notEqual', +}; From 94de42f2007dfc5c7209a162229c0dc39c5de439 Mon Sep 17 00:00:00 2001 From: Felipe Sales Date: Mon, 26 May 2025 14:16:00 -0300 Subject: [PATCH 03/30] auto --- examples/stac_gallery/pubspec.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/stac_gallery/pubspec.lock b/examples/stac_gallery/pubspec.lock index 865d912b0..32526f6ae 100644 --- a/examples/stac_gallery/pubspec.lock +++ b/examples/stac_gallery/pubspec.lock @@ -34,10 +34,10 @@ packages: dependency: transitive description: name: async - sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" url: "https://pub.dev" source: hosted - version: "2.12.0" + version: "2.13.0" bloc: dependency: transitive description: @@ -210,10 +210,10 @@ packages: dependency: transitive description: name: fake_async - sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" url: "https://pub.dev" source: hosted - version: "1.3.2" + version: "1.3.3" file: dependency: transitive description: @@ -348,10 +348,10 @@ packages: dependency: transitive description: name: leak_tracker - sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec + sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" url: "https://pub.dev" source: hosted - version: "10.0.8" + version: "10.0.9" leak_tracker_flutter_testing: dependency: transitive description: @@ -638,10 +638,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" + sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 url: "https://pub.dev" source: hosted - version: "14.3.1" + version: "15.0.0" watcher: dependency: transitive description: From fa82e682826dc9b3f67a9eda360f7951daf6e27e Mon Sep 17 00:00:00 2001 From: Felipe Sales Date: Mon, 26 May 2025 16:12:01 -0300 Subject: [PATCH 04/30] refactor: Update StacVersion class to use versionCode and improve version satisfaction check --- packages/stac/lib/src/framework/stac.dart | 8 +- .../stac/lib/src/framework/stac_version.dart | 0 .../lib/src/utils/version/stac_version.dart | 75 +++++++++++-------- .../utils/version/stac_version.freezed.dart | 52 ++++++++----- .../lib/src/utils/version/stac_version.g.dart | 6 +- 5 files changed, 85 insertions(+), 56 deletions(-) delete mode 100644 packages/stac/lib/src/framework/stac_version.dart diff --git a/packages/stac/lib/src/framework/stac.dart b/packages/stac/lib/src/framework/stac.dart index 7b89c51a7..d54538e23 100644 --- a/packages/stac/lib/src/framework/stac.dart +++ b/packages/stac/lib/src/framework/stac.dart @@ -130,12 +130,14 @@ class Stac { try { if (json != null) { final stacRegistry = StacRegistry.instance; - StacVersion? jsonVersion = json['version']; + Map? jsonVersion = json['version']; if (jsonVersion != null && stacRegistry.appVersion != null) { - final isSatisfied = jsonVersion.isSatisfied(stacRegistry.appVersion); + final stacVersion = StacVersion.fromJson(jsonVersion); + final isSatisfied = stacVersion.isSatisfied(); if (!isSatisfied) { - Log.w('Stac version ${jsonVersion.version} is not satisfied'); + Log.w( + 'Stac versionCode ${stacVersion.versionCode} is not satisfied; current version is: ${stacRegistry.appVersion}'); return null; } } diff --git a/packages/stac/lib/src/framework/stac_version.dart b/packages/stac/lib/src/framework/stac_version.dart deleted file mode 100644 index e69de29bb..000000000 diff --git a/packages/stac/lib/src/utils/version/stac_version.dart b/packages/stac/lib/src/utils/version/stac_version.dart index fc8bcd1d6..6f25d2ea1 100644 --- a/packages/stac/lib/src/utils/version/stac_version.dart +++ b/packages/stac/lib/src/utils/version/stac_version.dart @@ -1,5 +1,7 @@ import 'dart:math' as math; +import 'package:flutter/material.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:stac/stac.dart'; part 'stac_version.freezed.dart'; part 'stac_version.g.dart'; @@ -7,44 +9,17 @@ part 'stac_version.g.dart'; @freezed class StacVersion with _$StacVersion { const factory StacVersion({ - required String version, + required String versionCode, + // ignore: invalid_annotation_target + @JsonKey(fromJson: StacVersion.fromJsonCondition) required StacConditionVersion condition, }) = _StacVersion; factory StacVersion.fromJson(Map json) => _$StacVersionFromJson(json); - bool isSatisfied(String? targetVersion) { - if (targetVersion == null) { - return false; - } - - final current = version.split('.').toIntList(); - final target = targetVersion.split('.').toIntList(); - - final maxLength = math.max(current.length, target.length); - current.length = maxLength; - target.length = maxLength; - current.fillRange(current.length, maxLength, 0); - target.fillRange(target.length, maxLength, 0); - - // Compara os números - for (int i = 0; i < maxLength; i++) { - if (current[i] != target[i]) { - final comp = current[i].compareTo(target[i]); - return switch (condition) { - StacConditionVersion.greaterThan => comp > 0, - StacConditionVersion.greaterThanOrEqual => comp >= 0, - StacConditionVersion.lessThan => comp < 0, - StacConditionVersion.lessThanOrEqual => comp <= 0, - StacConditionVersion.equal => false, - StacConditionVersion.notEqual => true, - }; - } - } - - return false; - } + static StacConditionVersion fromJsonCondition(String? json) => + json?.toStacConditionVersion() ?? StacConditionVersion.notEqual; } enum StacConditionVersion { @@ -81,3 +56,39 @@ extension StacConditionVersionToStringX on StacConditionVersion { extension ListStringX on List { List toIntList() => map((e) => int.tryParse(e) ?? 0).toList(); } + +extension StacVersionX on StacVersion { + bool isSatisfied() { + final appVersion = StacRegistry.instance.appVersion; + + if (appVersion == null) { + return false; + } + + final current = appVersion.split('.').toIntList(); + final target = versionCode.split('.').toIntList(); + + final maxLength = math.max(current.length, target.length); + current.length = maxLength; + target.length = maxLength; + current.fillRange(current.length, maxLength, 0); + target.fillRange(target.length, maxLength, 0); + + // Compara os números + for (int i = 0; i < maxLength; i++) { + if (current[i] != target[i]) { + final comp = current[i].compareTo(target[i]); + return switch (condition) { + StacConditionVersion.greaterThan => comp > 0, + StacConditionVersion.greaterThanOrEqual => comp >= 0, + StacConditionVersion.lessThan => comp < 0, + StacConditionVersion.lessThanOrEqual => comp <= 0, + StacConditionVersion.equal => false, + StacConditionVersion.notEqual => true, + }; + } + } + + return false; + } +} diff --git a/packages/stac/lib/src/utils/version/stac_version.freezed.dart b/packages/stac/lib/src/utils/version/stac_version.freezed.dart index 212c7e41e..802e1b6d3 100644 --- a/packages/stac/lib/src/utils/version/stac_version.freezed.dart +++ b/packages/stac/lib/src/utils/version/stac_version.freezed.dart @@ -20,7 +20,9 @@ StacVersion _$StacVersionFromJson(Map json) { /// @nodoc mixin _$StacVersion { - String get version => throw _privateConstructorUsedError; + String get versionCode => + throw _privateConstructorUsedError; // ignore: invalid_annotation_target + @JsonKey(fromJson: StacVersion.fromJsonCondition) StacConditionVersion get condition => throw _privateConstructorUsedError; /// Serializes this StacVersion to a JSON map. @@ -39,7 +41,10 @@ abstract class $StacVersionCopyWith<$Res> { StacVersion value, $Res Function(StacVersion) then) = _$StacVersionCopyWithImpl<$Res, StacVersion>; @useResult - $Res call({String version, StacConditionVersion condition}); + $Res call( + {String versionCode, + @JsonKey(fromJson: StacVersion.fromJsonCondition) + StacConditionVersion condition}); } /// @nodoc @@ -57,13 +62,13 @@ class _$StacVersionCopyWithImpl<$Res, $Val extends StacVersion> @pragma('vm:prefer-inline') @override $Res call({ - Object? version = null, + Object? versionCode = null, Object? condition = null, }) { return _then(_value.copyWith( - version: null == version - ? _value.version - : version // ignore: cast_nullable_to_non_nullable + versionCode: null == versionCode + ? _value.versionCode + : versionCode // ignore: cast_nullable_to_non_nullable as String, condition: null == condition ? _value.condition @@ -81,7 +86,10 @@ abstract class _$$StacVersionImplCopyWith<$Res> __$$StacVersionImplCopyWithImpl<$Res>; @override @useResult - $Res call({String version, StacConditionVersion condition}); + $Res call( + {String versionCode, + @JsonKey(fromJson: StacVersion.fromJsonCondition) + StacConditionVersion condition}); } /// @nodoc @@ -97,13 +105,13 @@ class __$$StacVersionImplCopyWithImpl<$Res> @pragma('vm:prefer-inline') @override $Res call({ - Object? version = null, + Object? versionCode = null, Object? condition = null, }) { return _then(_$StacVersionImpl( - version: null == version - ? _value.version - : version // ignore: cast_nullable_to_non_nullable + versionCode: null == versionCode + ? _value.versionCode + : versionCode // ignore: cast_nullable_to_non_nullable as String, condition: null == condition ? _value.condition @@ -116,19 +124,24 @@ class __$$StacVersionImplCopyWithImpl<$Res> /// @nodoc @JsonSerializable() class _$StacVersionImpl implements _StacVersion { - const _$StacVersionImpl({required this.version, required this.condition}); + const _$StacVersionImpl( + {required this.versionCode, + @JsonKey(fromJson: StacVersion.fromJsonCondition) + required this.condition}); factory _$StacVersionImpl.fromJson(Map json) => _$$StacVersionImplFromJson(json); @override - final String version; + final String versionCode; +// ignore: invalid_annotation_target @override + @JsonKey(fromJson: StacVersion.fromJsonCondition) final StacConditionVersion condition; @override String toString() { - return 'StacVersion(version: $version, condition: $condition)'; + return 'StacVersion(versionCode: $versionCode, condition: $condition)'; } @override @@ -136,14 +149,15 @@ class _$StacVersionImpl implements _StacVersion { return identical(this, other) || (other.runtimeType == runtimeType && other is _$StacVersionImpl && - (identical(other.version, version) || other.version == version) && + (identical(other.versionCode, versionCode) || + other.versionCode == versionCode) && (identical(other.condition, condition) || other.condition == condition)); } @JsonKey(includeFromJson: false, includeToJson: false) @override - int get hashCode => Object.hash(runtimeType, version, condition); + int get hashCode => Object.hash(runtimeType, versionCode, condition); /// Create a copy of StacVersion /// with the given fields replaced by the non-null parameter values. @@ -163,15 +177,17 @@ class _$StacVersionImpl implements _StacVersion { abstract class _StacVersion implements StacVersion { const factory _StacVersion( - {required final String version, + {required final String versionCode, + @JsonKey(fromJson: StacVersion.fromJsonCondition) required final StacConditionVersion condition}) = _$StacVersionImpl; factory _StacVersion.fromJson(Map json) = _$StacVersionImpl.fromJson; @override - String get version; + String get versionCode; // ignore: invalid_annotation_target @override + @JsonKey(fromJson: StacVersion.fromJsonCondition) StacConditionVersion get condition; /// Create a copy of StacVersion diff --git a/packages/stac/lib/src/utils/version/stac_version.g.dart b/packages/stac/lib/src/utils/version/stac_version.g.dart index a7a44b873..af7043c4b 100644 --- a/packages/stac/lib/src/utils/version/stac_version.g.dart +++ b/packages/stac/lib/src/utils/version/stac_version.g.dart @@ -8,13 +8,13 @@ part of 'stac_version.dart'; _$StacVersionImpl _$$StacVersionImplFromJson(Map json) => _$StacVersionImpl( - version: json['version'] as String, - condition: $enumDecode(_$StacConditionVersionEnumMap, json['condition']), + versionCode: json['versionCode'] as String, + condition: StacVersion.fromJsonCondition(json['condition'] as String?), ); Map _$$StacVersionImplToJson(_$StacVersionImpl instance) => { - 'version': instance.version, + 'versionCode': instance.versionCode, 'condition': _$StacConditionVersionEnumMap[instance.condition]!, }; From 2840f5aaee3dd70aa465d225d37711a4927ed8b7 Mon Sep 17 00:00:00 2001 From: Felipe Sales Date: Mon, 26 May 2025 16:31:06 -0300 Subject: [PATCH 05/30] wip --- .../stac/lib/src/utils/version/stac_version.dart | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/stac/lib/src/utils/version/stac_version.dart b/packages/stac/lib/src/utils/version/stac_version.dart index 6f25d2ea1..3349e5f80 100644 --- a/packages/stac/lib/src/utils/version/stac_version.dart +++ b/packages/stac/lib/src/utils/version/stac_version.dart @@ -65,6 +65,9 @@ extension StacVersionX on StacVersion { return false; } + debugPrint( + 'stacteste: appVersion: $appVersion | versionCode: $versionCode'); + final current = appVersion.split('.').toIntList(); final target = versionCode.split('.').toIntList(); @@ -74,17 +77,22 @@ extension StacVersionX on StacVersion { current.fillRange(current.length, maxLength, 0); target.fillRange(target.length, maxLength, 0); + debugPrint( + 'stacteste: current: $current | target: $target | maxLength: $maxLength'); + // Compara os números for (int i = 0; i < maxLength; i++) { if (current[i] != target[i]) { final comp = current[i].compareTo(target[i]); + debugPrint( + 'stacteste: comp: $comp | condition: $condition | current: $current | target: $target'); return switch (condition) { StacConditionVersion.greaterThan => comp > 0, StacConditionVersion.greaterThanOrEqual => comp >= 0, StacConditionVersion.lessThan => comp < 0, StacConditionVersion.lessThanOrEqual => comp <= 0, - StacConditionVersion.equal => false, - StacConditionVersion.notEqual => true, + StacConditionVersion.equal => comp == 0, + StacConditionVersion.notEqual => comp != 0, }; } } From e260f19196f4c09d44f423229d354ea9459d980a Mon Sep 17 00:00:00 2001 From: Felipe Sales Date: Mon, 26 May 2025 16:52:27 -0300 Subject: [PATCH 06/30] test: Add comprehensive tests for StacVersion class and its conditions --- .../lib/src/utils/version/stac_version.dart | 31 ++-- .../src/utils/version/stac_version_test.dart | 162 ++++++++++++++++++ 2 files changed, 180 insertions(+), 13 deletions(-) create mode 100644 packages/stac/test/src/utils/version/stac_version_test.dart diff --git a/packages/stac/lib/src/utils/version/stac_version.dart b/packages/stac/lib/src/utils/version/stac_version.dart index 3349e5f80..d1377e1bc 100644 --- a/packages/stac/lib/src/utils/version/stac_version.dart +++ b/packages/stac/lib/src/utils/version/stac_version.dart @@ -71,32 +71,37 @@ extension StacVersionX on StacVersion { final current = appVersion.split('.').toIntList(); final target = versionCode.split('.').toIntList(); - final maxLength = math.max(current.length, target.length); - current.length = maxLength; - target.length = maxLength; - current.fillRange(current.length, maxLength, 0); - target.fillRange(target.length, maxLength, 0); + final maxLength = + [current.length, target.length].reduce((a, b) => a > b ? a : b); - debugPrint( - 'stacteste: current: $current | target: $target | maxLength: $maxLength'); + while (current.length < maxLength) { + current.add(0); + } + while (target.length < maxLength) { + target.add(0); + } - // Compara os números for (int i = 0; i < maxLength; i++) { if (current[i] != target[i]) { final comp = current[i].compareTo(target[i]); - debugPrint( - 'stacteste: comp: $comp | condition: $condition | current: $current | target: $target'); return switch (condition) { StacConditionVersion.greaterThan => comp > 0, StacConditionVersion.greaterThanOrEqual => comp >= 0, StacConditionVersion.lessThan => comp < 0, StacConditionVersion.lessThanOrEqual => comp <= 0, - StacConditionVersion.equal => comp == 0, - StacConditionVersion.notEqual => comp != 0, + StacConditionVersion.equal => false, + StacConditionVersion.notEqual => true, }; } } - return false; + return switch (condition) { + StacConditionVersion.greaterThan => false, + StacConditionVersion.greaterThanOrEqual => true, + StacConditionVersion.lessThan => false, + StacConditionVersion.lessThanOrEqual => true, + StacConditionVersion.equal => true, + StacConditionVersion.notEqual => false, + }; } } diff --git a/packages/stac/test/src/utils/version/stac_version_test.dart b/packages/stac/test/src/utils/version/stac_version_test.dart new file mode 100644 index 000000000..0a64f5e0e --- /dev/null +++ b/packages/stac/test/src/utils/version/stac_version_test.dart @@ -0,0 +1,162 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:stac/src/framework/stac_registry.dart'; +import 'package:stac/src/utils/version/stac_version.dart'; + +void main() { + group('StacVersion', () { + setUp(() { + // Reset the app version before each test + StacRegistry.instance.registerAppVersion(null); + }); + + test('creates StacVersion instance correctly', () { + final version = StacVersion( + versionCode: '1.0.0', + condition: StacConditionVersion.equal, + ); + + expect(version.versionCode, '1.0.0'); + expect(version.condition, StacConditionVersion.equal); + }); + + test('creates StacVersion from JSON correctly', () { + final json = { + 'versionCode': '1.0.0', + 'condition': '>', + }; + + final version = StacVersion.fromJson(json); + + expect(version.versionCode, '1.0.0'); + expect(version.condition, StacConditionVersion.greaterThan); + }); + + group('condition parsing', () { + test('parses all condition types correctly', () { + final conditions = { + '>': StacConditionVersion.greaterThan, + '>=': StacConditionVersion.greaterThanOrEqual, + '<': StacConditionVersion.lessThan, + '<=': StacConditionVersion.lessThanOrEqual, + '==': StacConditionVersion.equal, + 'invalid': StacConditionVersion.notEqual, + }; + + conditions.forEach((input, expected) { + expect(input.toStacConditionVersion(), expected); + }); + }); + + test('handles null input by returning notEqual', () { + String? nullString; + expect( + nullString.toStacConditionVersion(), StacConditionVersion.notEqual); + }); + }); + + group('isSatisfied', () { + setUp(() { + StacRegistry.instance.registerAppVersion('2.0.0'); + }); + + test('returns false when app version is null', () { + StacRegistry.instance.registerAppVersion(null); + final version = StacVersion( + versionCode: '1.0.0', + condition: StacConditionVersion.equal, + ); + expect(version.isSatisfied(), false); + }); + + test('handles equal condition correctly', () { + final version = StacVersion( + versionCode: '2.0.0', + condition: StacConditionVersion.equal, + ); + expect(version.isSatisfied(), true); + + final differentVersion = StacVersion( + versionCode: '1.0.0', + condition: StacConditionVersion.equal, + ); + expect(differentVersion.isSatisfied(), false); + }); + + test('handles greater than condition correctly', () { + final version = StacVersion( + versionCode: '1.0.0', + condition: StacConditionVersion.greaterThan, + ); + expect(version.isSatisfied(), true); + + final higherVersion = StacVersion( + versionCode: '3.0.0', + condition: StacConditionVersion.greaterThan, + ); + expect(higherVersion.isSatisfied(), false); + }); + + test('handles less than condition correctly', () { + final version = StacVersion( + versionCode: '3.0.0', + condition: StacConditionVersion.lessThan, + ); + expect(version.isSatisfied(), true); + + final lowerVersion = StacVersion( + versionCode: '1.0.0', + condition: StacConditionVersion.lessThan, + ); + expect(lowerVersion.isSatisfied(), false); + }); + + test('handles version components of different lengths', () { + StacRegistry.instance.registerAppVersion('2.0.0.1'); + + final version = StacVersion( + versionCode: '2.0.0', + condition: StacConditionVersion.equal, + ); + expect(version.isSatisfied(), false); + + final versionWithExtra = StacVersion( + versionCode: '2.0.0.0', + condition: StacConditionVersion.equal, + ); + expect(versionWithExtra.isSatisfied(), false); + + final exactVersion = StacVersion( + versionCode: '2.0.0.1', + condition: StacConditionVersion.equal, + ); + expect(exactVersion.isSatisfied(), true); + }); + + test('handles version with non-numeric components', () { + StacRegistry.instance.registerAppVersion('2.0.alpha'); + + final version = StacVersion( + versionCode: '2.0.0', + condition: StacConditionVersion.equal, + ); + // Non-numeric components should be treated as 0 + expect(version.isSatisfied(), true); + }); + }); + + test('converts condition to string correctly', () { + final conditions = { + StacConditionVersion.greaterThan: '>', + StacConditionVersion.greaterThanOrEqual: '>=', + StacConditionVersion.lessThan: '<', + StacConditionVersion.lessThanOrEqual: '<=', + StacConditionVersion.equal: '==', + StacConditionVersion.notEqual: '!=', + }; + + conditions.forEach((condition, expected) { + expect(condition.toTypeString(), expected); + }); + }); + }); +} From 9dcfc91aff93f5af21ac0670d28c146f07e6e1ab Mon Sep 17 00:00:00 2001 From: Felipe Sales Date: Tue, 27 May 2025 10:56:40 -0300 Subject: [PATCH 07/30] wip --- packages/stac/lib/src/framework/stac.dart | 3 +++ packages/stac/lib/src/utils/version/stac_version.dart | 11 +++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/stac/lib/src/framework/stac.dart b/packages/stac/lib/src/framework/stac.dart index d54538e23..79a30d024 100644 --- a/packages/stac/lib/src/framework/stac.dart +++ b/packages/stac/lib/src/framework/stac.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'dart:convert'; +import 'dart:io'; import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; @@ -130,6 +131,8 @@ class Stac { try { if (json != null) { final stacRegistry = StacRegistry.instance; + // Map? jsonVersionSpecific = json['version_${Platform.operatingSystem}']; + Map? jsonVersion = json['version']; if (jsonVersion != null && stacRegistry.appVersion != null) { diff --git a/packages/stac/lib/src/utils/version/stac_version.dart b/packages/stac/lib/src/utils/version/stac_version.dart index d1377e1bc..fb6212936 100644 --- a/packages/stac/lib/src/utils/version/stac_version.dart +++ b/packages/stac/lib/src/utils/version/stac_version.dart @@ -1,4 +1,3 @@ -import 'dart:math' as math; import 'package:flutter/material.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:stac/stac.dart'; @@ -9,7 +8,7 @@ part 'stac_version.g.dart'; @freezed class StacVersion with _$StacVersion { const factory StacVersion({ - required String versionCode, + required String versionCode,//buildNumber - int // ignore: invalid_annotation_target @JsonKey(fromJson: StacVersion.fromJsonCondition) required StacConditionVersion condition, @@ -20,6 +19,14 @@ class StacVersion with _$StacVersion { static StacConditionVersion fromJsonCondition(String? json) => json?.toStacConditionVersion() ?? StacConditionVersion.notEqual; + + // { + // "version": { + // "versionCode": "6936", + // "versionCode_ios": "6940", + // "condition": ">" + // } + // } } enum StacConditionVersion { From 68ac416ff9bb36b808033b7920aabf9b33d8b4b1 Mon Sep 17 00:00:00 2001 From: Felipe Sales Date: Tue, 27 May 2025 14:29:10 -0300 Subject: [PATCH 08/30] refactor: change to buildNumber and add support to platforms --- packages/stac/lib/src/framework/stac.dart | 8 ++-- .../stac/lib/src/framework/stac_registry.dart | 10 ++-- .../lib/src/utils/version/stac_version.dart | 48 +++++++++---------- 3 files changed, 32 insertions(+), 34 deletions(-) diff --git a/packages/stac/lib/src/framework/stac.dart b/packages/stac/lib/src/framework/stac.dart index 79a30d024..d75b7c257 100644 --- a/packages/stac/lib/src/framework/stac.dart +++ b/packages/stac/lib/src/framework/stac.dart @@ -114,13 +114,13 @@ class Stac { List actionParsers = const [], Dio? dio, bool override = false, - String? appVersion, + String? buildNumber, }) async { _parsers.addAll(parsers); _actionParsers.addAll(actionParsers); StacRegistry.instance.registerAll(_parsers, override); StacRegistry.instance.registerAllActions(_actionParsers, override); - StacRegistry.instance.registerAppVersion(appVersion); + StacRegistry.instance.registerBuildNumber(buildNumber); StacNetworkService.initialize(dio ?? Dio()); } @@ -135,12 +135,12 @@ class Stac { Map? jsonVersion = json['version']; - if (jsonVersion != null && stacRegistry.appVersion != null) { + if (jsonVersion != null && stacRegistry.buildNumber != null) { final stacVersion = StacVersion.fromJson(jsonVersion); final isSatisfied = stacVersion.isSatisfied(); if (!isSatisfied) { Log.w( - 'Stac versionCode ${stacVersion.versionCode} is not satisfied; current version is: ${stacRegistry.appVersion}'); + 'Stac buildNumber ${stacVersion.buildNumber} is not satisfied; current version is: ${stacRegistry.buildNumber}'); return null; } } diff --git a/packages/stac/lib/src/framework/stac_registry.dart b/packages/stac/lib/src/framework/stac_registry.dart index 1329f23df..364e6213f 100644 --- a/packages/stac/lib/src/framework/stac_registry.dart +++ b/packages/stac/lib/src/framework/stac_registry.dart @@ -16,8 +16,8 @@ class StacRegistry { static final _stacActionParsers = {}; Color? Function(String?)? parseCustomColor; - String? _appVersion; - String? get appVersion => _appVersion; + String? _buildNumber; + String? get buildNumber => _buildNumber; bool register(StacParser parser, [bool override = false]) { final String type = parser.type; @@ -73,9 +73,9 @@ class StacRegistry { ); } - void registerAppVersion(String? version) { - if (version != null) { - _appVersion = version; + void registerBuildNumber(String? buildNumber) { + if (buildNumber != null) { + _buildNumber = buildNumber; } } diff --git a/packages/stac/lib/src/utils/version/stac_version.dart b/packages/stac/lib/src/utils/version/stac_version.dart index fb6212936..079be21a8 100644 --- a/packages/stac/lib/src/utils/version/stac_version.dart +++ b/packages/stac/lib/src/utils/version/stac_version.dart @@ -1,32 +1,30 @@ +import 'dart:io'; + import 'package:flutter/material.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:stac/stac.dart'; -part 'stac_version.freezed.dart'; -part 'stac_version.g.dart'; +class StacVersion { + const StacVersion({ + required this.buildNumber, + required this.condition, + }); + + final int buildNumber; + final StacConditionVersion condition; -@freezed -class StacVersion with _$StacVersion { - const factory StacVersion({ - required String versionCode,//buildNumber - int - // ignore: invalid_annotation_target - @JsonKey(fromJson: StacVersion.fromJsonCondition) - required StacConditionVersion condition, - }) = _StacVersion; + factory StacVersion.fromJson(Map json) { + return StacVersion( + buildNumber: toPlatformJson(json, 'buildNumber').toInt(), + condition: toPlatformJson(json, 'condition').toStacConditionVersion(), + ); + } - factory StacVersion.fromJson(Map json) => - _$StacVersionFromJson(json); + static dynamic toPlatformJson(Map json, String key) => + json['${key}_${Platform.operatingSystem}'] ?? json[key]; static StacConditionVersion fromJsonCondition(String? json) => json?.toStacConditionVersion() ?? StacConditionVersion.notEqual; - - // { - // "version": { - // "versionCode": "6936", - // "versionCode_ios": "6940", - // "condition": ">" - // } - // } } enum StacConditionVersion { @@ -66,17 +64,17 @@ extension ListStringX on List { extension StacVersionX on StacVersion { bool isSatisfied() { - final appVersion = StacRegistry.instance.appVersion; + final buildNumber = StacRegistry.instance.buildNumber; - if (appVersion == null) { + if (buildNumber == null) { return false; } debugPrint( - 'stacteste: appVersion: $appVersion | versionCode: $versionCode'); + 'stacteste: buildNumber: $buildNumber | buildNumber: $buildNumber'); - final current = appVersion.split('.').toIntList(); - final target = versionCode.split('.').toIntList(); + final current = buildNumber.split('.').toIntList(); + final target = buildNumber.split('.').toIntList(); final maxLength = [current.length, target.length].reduce((a, b) => a > b ? a : b); From 8462d5ce610d09429bf49083cfd022f84c5894d5 Mon Sep 17 00:00:00 2001 From: Felipe Sales Date: Tue, 27 May 2025 14:39:38 -0300 Subject: [PATCH 09/30] refactor: change to buildNumber (and delete freezed specifics) --- packages/stac/lib/src/framework/stac.dart | 4 +- .../stac/lib/src/framework/stac_registry.dart | 6 +- .../utils/version/stac_version.freezed.dart | 199 ------------------ .../lib/src/utils/version/stac_version.g.dart | 28 --- .../src/utils/version/stac_version_test.dart | 40 ++-- 5 files changed, 25 insertions(+), 252 deletions(-) delete mode 100644 packages/stac/lib/src/utils/version/stac_version.freezed.dart delete mode 100644 packages/stac/lib/src/utils/version/stac_version.g.dart diff --git a/packages/stac/lib/src/framework/stac.dart b/packages/stac/lib/src/framework/stac.dart index d75b7c257..42c322b7d 100644 --- a/packages/stac/lib/src/framework/stac.dart +++ b/packages/stac/lib/src/framework/stac.dart @@ -114,7 +114,7 @@ class Stac { List actionParsers = const [], Dio? dio, bool override = false, - String? buildNumber, + int? buildNumber, }) async { _parsers.addAll(parsers); _actionParsers.addAll(actionParsers); @@ -140,7 +140,7 @@ class Stac { final isSatisfied = stacVersion.isSatisfied(); if (!isSatisfied) { Log.w( - 'Stac buildNumber ${stacVersion.buildNumber} is not satisfied; current version is: ${stacRegistry.buildNumber}'); + 'Stac versionCode ${stacVersion.versionCode} is not satisfied; current version is: ${stacRegistry.buildNumber}'); return null; } } diff --git a/packages/stac/lib/src/framework/stac_registry.dart b/packages/stac/lib/src/framework/stac_registry.dart index 364e6213f..661b4583b 100644 --- a/packages/stac/lib/src/framework/stac_registry.dart +++ b/packages/stac/lib/src/framework/stac_registry.dart @@ -16,8 +16,8 @@ class StacRegistry { static final _stacActionParsers = {}; Color? Function(String?)? parseCustomColor; - String? _buildNumber; - String? get buildNumber => _buildNumber; + int? _buildNumber; + int? get buildNumber => _buildNumber; bool register(StacParser parser, [bool override = false]) { final String type = parser.type; @@ -73,7 +73,7 @@ class StacRegistry { ); } - void registerBuildNumber(String? buildNumber) { + void registerBuildNumber(int? buildNumber) { if (buildNumber != null) { _buildNumber = buildNumber; } diff --git a/packages/stac/lib/src/utils/version/stac_version.freezed.dart b/packages/stac/lib/src/utils/version/stac_version.freezed.dart deleted file mode 100644 index 802e1b6d3..000000000 --- a/packages/stac/lib/src/utils/version/stac_version.freezed.dart +++ /dev/null @@ -1,199 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'stac_version.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); - -StacVersion _$StacVersionFromJson(Map json) { - return _StacVersion.fromJson(json); -} - -/// @nodoc -mixin _$StacVersion { - String get versionCode => - throw _privateConstructorUsedError; // ignore: invalid_annotation_target - @JsonKey(fromJson: StacVersion.fromJsonCondition) - StacConditionVersion get condition => throw _privateConstructorUsedError; - - /// Serializes this StacVersion to a JSON map. - Map toJson() => throw _privateConstructorUsedError; - - /// Create a copy of StacVersion - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $StacVersionCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $StacVersionCopyWith<$Res> { - factory $StacVersionCopyWith( - StacVersion value, $Res Function(StacVersion) then) = - _$StacVersionCopyWithImpl<$Res, StacVersion>; - @useResult - $Res call( - {String versionCode, - @JsonKey(fromJson: StacVersion.fromJsonCondition) - StacConditionVersion condition}); -} - -/// @nodoc -class _$StacVersionCopyWithImpl<$Res, $Val extends StacVersion> - implements $StacVersionCopyWith<$Res> { - _$StacVersionCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - /// Create a copy of StacVersion - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? versionCode = null, - Object? condition = null, - }) { - return _then(_value.copyWith( - versionCode: null == versionCode - ? _value.versionCode - : versionCode // ignore: cast_nullable_to_non_nullable - as String, - condition: null == condition - ? _value.condition - : condition // ignore: cast_nullable_to_non_nullable - as StacConditionVersion, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$StacVersionImplCopyWith<$Res> - implements $StacVersionCopyWith<$Res> { - factory _$$StacVersionImplCopyWith( - _$StacVersionImpl value, $Res Function(_$StacVersionImpl) then) = - __$$StacVersionImplCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {String versionCode, - @JsonKey(fromJson: StacVersion.fromJsonCondition) - StacConditionVersion condition}); -} - -/// @nodoc -class __$$StacVersionImplCopyWithImpl<$Res> - extends _$StacVersionCopyWithImpl<$Res, _$StacVersionImpl> - implements _$$StacVersionImplCopyWith<$Res> { - __$$StacVersionImplCopyWithImpl( - _$StacVersionImpl _value, $Res Function(_$StacVersionImpl) _then) - : super(_value, _then); - - /// Create a copy of StacVersion - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? versionCode = null, - Object? condition = null, - }) { - return _then(_$StacVersionImpl( - versionCode: null == versionCode - ? _value.versionCode - : versionCode // ignore: cast_nullable_to_non_nullable - as String, - condition: null == condition - ? _value.condition - : condition // ignore: cast_nullable_to_non_nullable - as StacConditionVersion, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$StacVersionImpl implements _StacVersion { - const _$StacVersionImpl( - {required this.versionCode, - @JsonKey(fromJson: StacVersion.fromJsonCondition) - required this.condition}); - - factory _$StacVersionImpl.fromJson(Map json) => - _$$StacVersionImplFromJson(json); - - @override - final String versionCode; -// ignore: invalid_annotation_target - @override - @JsonKey(fromJson: StacVersion.fromJsonCondition) - final StacConditionVersion condition; - - @override - String toString() { - return 'StacVersion(versionCode: $versionCode, condition: $condition)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$StacVersionImpl && - (identical(other.versionCode, versionCode) || - other.versionCode == versionCode) && - (identical(other.condition, condition) || - other.condition == condition)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash(runtimeType, versionCode, condition); - - /// Create a copy of StacVersion - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$StacVersionImplCopyWith<_$StacVersionImpl> get copyWith => - __$$StacVersionImplCopyWithImpl<_$StacVersionImpl>(this, _$identity); - - @override - Map toJson() { - return _$$StacVersionImplToJson( - this, - ); - } -} - -abstract class _StacVersion implements StacVersion { - const factory _StacVersion( - {required final String versionCode, - @JsonKey(fromJson: StacVersion.fromJsonCondition) - required final StacConditionVersion condition}) = _$StacVersionImpl; - - factory _StacVersion.fromJson(Map json) = - _$StacVersionImpl.fromJson; - - @override - String get versionCode; // ignore: invalid_annotation_target - @override - @JsonKey(fromJson: StacVersion.fromJsonCondition) - StacConditionVersion get condition; - - /// Create a copy of StacVersion - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$StacVersionImplCopyWith<_$StacVersionImpl> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/packages/stac/lib/src/utils/version/stac_version.g.dart b/packages/stac/lib/src/utils/version/stac_version.g.dart deleted file mode 100644 index af7043c4b..000000000 --- a/packages/stac/lib/src/utils/version/stac_version.g.dart +++ /dev/null @@ -1,28 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'stac_version.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -_$StacVersionImpl _$$StacVersionImplFromJson(Map json) => - _$StacVersionImpl( - versionCode: json['versionCode'] as String, - condition: StacVersion.fromJsonCondition(json['condition'] as String?), - ); - -Map _$$StacVersionImplToJson(_$StacVersionImpl instance) => - { - 'versionCode': instance.versionCode, - 'condition': _$StacConditionVersionEnumMap[instance.condition]!, - }; - -const _$StacConditionVersionEnumMap = { - StacConditionVersion.greaterThan: 'greaterThan', - StacConditionVersion.greaterThanOrEqual: 'greaterThanOrEqual', - StacConditionVersion.lessThan: 'lessThan', - StacConditionVersion.lessThanOrEqual: 'lessThanOrEqual', - StacConditionVersion.equal: 'equal', - StacConditionVersion.notEqual: 'notEqual', -}; diff --git a/packages/stac/test/src/utils/version/stac_version_test.dart b/packages/stac/test/src/utils/version/stac_version_test.dart index 0a64f5e0e..b28b706a6 100644 --- a/packages/stac/test/src/utils/version/stac_version_test.dart +++ b/packages/stac/test/src/utils/version/stac_version_test.dart @@ -6,28 +6,28 @@ void main() { group('StacVersion', () { setUp(() { // Reset the app version before each test - StacRegistry.instance.registerAppVersion(null); + StacRegistry.instance.registerBuildNumber(null); }); test('creates StacVersion instance correctly', () { final version = StacVersion( - versionCode: '1.0.0', + buildNumber: 1000, condition: StacConditionVersion.equal, ); - expect(version.versionCode, '1.0.0'); + expect(version.buildNumber, 1000); expect(version.condition, StacConditionVersion.equal); }); test('creates StacVersion from JSON correctly', () { final json = { - 'versionCode': '1.0.0', + 'buildNumber': 1000, 'condition': '>', }; final version = StacVersion.fromJson(json); - expect(version.versionCode, '1.0.0'); + expect(version.buildNumber, 1000); expect(version.condition, StacConditionVersion.greaterThan); }); @@ -56,13 +56,13 @@ void main() { group('isSatisfied', () { setUp(() { - StacRegistry.instance.registerAppVersion('2.0.0'); + StacRegistry.instance.registerBuildNumber(2000); }); test('returns false when app version is null', () { - StacRegistry.instance.registerAppVersion(null); + StacRegistry.instance.registerBuildNumber(null); final version = StacVersion( - versionCode: '1.0.0', + buildNumber: 1000, condition: StacConditionVersion.equal, ); expect(version.isSatisfied(), false); @@ -70,13 +70,13 @@ void main() { test('handles equal condition correctly', () { final version = StacVersion( - versionCode: '2.0.0', + buildNumber: 2000, condition: StacConditionVersion.equal, ); expect(version.isSatisfied(), true); final differentVersion = StacVersion( - versionCode: '1.0.0', + buildNumber: 1000, condition: StacConditionVersion.equal, ); expect(differentVersion.isSatisfied(), false); @@ -84,13 +84,13 @@ void main() { test('handles greater than condition correctly', () { final version = StacVersion( - versionCode: '1.0.0', + buildNumber: 1000, condition: StacConditionVersion.greaterThan, ); expect(version.isSatisfied(), true); final higherVersion = StacVersion( - versionCode: '3.0.0', + buildNumber: 3000, condition: StacConditionVersion.greaterThan, ); expect(higherVersion.isSatisfied(), false); @@ -98,45 +98,45 @@ void main() { test('handles less than condition correctly', () { final version = StacVersion( - versionCode: '3.0.0', + buildNumber: 3000, condition: StacConditionVersion.lessThan, ); expect(version.isSatisfied(), true); final lowerVersion = StacVersion( - versionCode: '1.0.0', + buildNumber: 1000, condition: StacConditionVersion.lessThan, ); expect(lowerVersion.isSatisfied(), false); }); test('handles version components of different lengths', () { - StacRegistry.instance.registerAppVersion('2.0.0.1'); + StacRegistry.instance.registerBuildNumber(20001); final version = StacVersion( - versionCode: '2.0.0', + buildNumber: 2000, condition: StacConditionVersion.equal, ); expect(version.isSatisfied(), false); final versionWithExtra = StacVersion( - versionCode: '2.0.0.0', + buildNumber: 2000, condition: StacConditionVersion.equal, ); expect(versionWithExtra.isSatisfied(), false); final exactVersion = StacVersion( - versionCode: '2.0.0.1', + buildNumber: 20001, condition: StacConditionVersion.equal, ); expect(exactVersion.isSatisfied(), true); }); test('handles version with non-numeric components', () { - StacRegistry.instance.registerAppVersion('2.0.alpha'); + StacRegistry.instance.registerBuildNumber(2000); final version = StacVersion( - versionCode: '2.0.0', + buildNumber: 2000, condition: StacConditionVersion.equal, ); // Non-numeric components should be treated as 0 From abb666c4a0b50691bd0954e8791d13543548c84e Mon Sep 17 00:00:00 2001 From: Felipe Sales Date: Tue, 27 May 2025 14:52:36 -0300 Subject: [PATCH 10/30] test: adjust test and behaviours --- packages/stac/lib/src/framework/stac.dart | 5 +- .../lib/src/utils/version/stac_version.dart | 58 ++++--------------- 2 files changed, 13 insertions(+), 50 deletions(-) diff --git a/packages/stac/lib/src/framework/stac.dart b/packages/stac/lib/src/framework/stac.dart index 42c322b7d..01fcc094f 100644 --- a/packages/stac/lib/src/framework/stac.dart +++ b/packages/stac/lib/src/framework/stac.dart @@ -1,6 +1,5 @@ import 'dart:async'; import 'dart:convert'; -import 'dart:io'; import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; @@ -131,8 +130,6 @@ class Stac { try { if (json != null) { final stacRegistry = StacRegistry.instance; - // Map? jsonVersionSpecific = json['version_${Platform.operatingSystem}']; - Map? jsonVersion = json['version']; if (jsonVersion != null && stacRegistry.buildNumber != null) { @@ -140,7 +137,7 @@ class Stac { final isSatisfied = stacVersion.isSatisfied(); if (!isSatisfied) { Log.w( - 'Stac versionCode ${stacVersion.versionCode} is not satisfied; current version is: ${stacRegistry.buildNumber}'); + 'Stac buildNumber ${stacVersion.buildNumber} is not satisfied; current build is: ${stacRegistry.buildNumber}'); return null; } } diff --git a/packages/stac/lib/src/utils/version/stac_version.dart b/packages/stac/lib/src/utils/version/stac_version.dart index 079be21a8..c2355dec4 100644 --- a/packages/stac/lib/src/utils/version/stac_version.dart +++ b/packages/stac/lib/src/utils/version/stac_version.dart @@ -1,7 +1,5 @@ import 'dart:io'; -import 'package:flutter/material.dart'; -import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:stac/stac.dart'; class StacVersion { @@ -16,15 +14,13 @@ class StacVersion { factory StacVersion.fromJson(Map json) { return StacVersion( buildNumber: toPlatformJson(json, 'buildNumber').toInt(), - condition: toPlatformJson(json, 'condition').toStacConditionVersion(), + condition: (toPlatformJson(json, 'condition') as String?) + .toStacConditionVersion(), ); } static dynamic toPlatformJson(Map json, String key) => json['${key}_${Platform.operatingSystem}'] ?? json[key]; - - static StacConditionVersion fromJsonCondition(String? json) => - json?.toStacConditionVersion() ?? StacConditionVersion.notEqual; } enum StacConditionVersion { @@ -43,6 +39,7 @@ extension StacConditionVersionX on String? { '<' => StacConditionVersion.lessThan, '<=' => StacConditionVersion.lessThanOrEqual, '==' => StacConditionVersion.equal, + '!=' => StacConditionVersion.notEqual, _ => StacConditionVersion.notEqual, }; } @@ -64,49 +61,18 @@ extension ListStringX on List { extension StacVersionX on StacVersion { bool isSatisfied() { - final buildNumber = StacRegistry.instance.buildNumber; - - if (buildNumber == null) { - return false; - } - - debugPrint( - 'stacteste: buildNumber: $buildNumber | buildNumber: $buildNumber'); - - final current = buildNumber.split('.').toIntList(); - final target = buildNumber.split('.').toIntList(); - - final maxLength = - [current.length, target.length].reduce((a, b) => a > b ? a : b); - - while (current.length < maxLength) { - current.add(0); - } - while (target.length < maxLength) { - target.add(0); - } - - for (int i = 0; i < maxLength; i++) { - if (current[i] != target[i]) { - final comp = current[i].compareTo(target[i]); - return switch (condition) { - StacConditionVersion.greaterThan => comp > 0, - StacConditionVersion.greaterThanOrEqual => comp >= 0, - StacConditionVersion.lessThan => comp < 0, - StacConditionVersion.lessThanOrEqual => comp <= 0, - StacConditionVersion.equal => false, - StacConditionVersion.notEqual => true, - }; - } + final appBuildNumber = StacRegistry.instance.buildNumber; + if (appBuildNumber == null) { + return true; } return switch (condition) { - StacConditionVersion.greaterThan => false, - StacConditionVersion.greaterThanOrEqual => true, - StacConditionVersion.lessThan => false, - StacConditionVersion.lessThanOrEqual => true, - StacConditionVersion.equal => true, - StacConditionVersion.notEqual => false, + StacConditionVersion.greaterThan => appBuildNumber > buildNumber, + StacConditionVersion.greaterThanOrEqual => appBuildNumber >= buildNumber, + StacConditionVersion.lessThan => appBuildNumber < buildNumber, + StacConditionVersion.lessThanOrEqual => appBuildNumber <= buildNumber, + StacConditionVersion.equal => appBuildNumber == buildNumber, + StacConditionVersion.notEqual => appBuildNumber != buildNumber, }; } } From 18e6993717afa87b14ba459bf81adf095adebc2b Mon Sep 17 00:00:00 2001 From: Felipe Sales Date: Tue, 27 May 2025 14:59:33 -0300 Subject: [PATCH 11/30] test: Platform tests --- .../src/utils/version/stac_version_test.dart | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/packages/stac/test/src/utils/version/stac_version_test.dart b/packages/stac/test/src/utils/version/stac_version_test.dart index b28b706a6..77afdc43c 100644 --- a/packages/stac/test/src/utils/version/stac_version_test.dart +++ b/packages/stac/test/src/utils/version/stac_version_test.dart @@ -1,7 +1,11 @@ +import 'dart:io'; + import 'package:flutter_test/flutter_test.dart'; import 'package:stac/src/framework/stac_registry.dart'; import 'package:stac/src/utils/version/stac_version.dart'; +String platform = Platform.operatingSystem; + void main() { group('StacVersion', () { setUp(() { @@ -159,4 +163,110 @@ void main() { }); }); }); + + /// platform tests + group('platform tests', () { + test('uses platform-specific buildNumber when available', () { + final json = { + 'buildNumber': 1000, + 'buildNumber_$platform': 2000, + 'condition': '>', + }; + + final version = StacVersion.fromJson(json); + + // Should use platform-specific buildNumber (`platform` in this case) + expect(version.buildNumber, 2000); + expect(version.condition, StacConditionVersion.greaterThan); + }); + + test( + 'falls back to generic buildNumber when platform-specific not available', + () { + final json = { + 'buildNumber': 1500, + 'buildNumber_ios': 2500, // Different platform + 'condition': '>=', + }; + + final version = StacVersion.fromJson(json); + + // Should use generic buildNumber since `platform`-specific is not available + expect(version.buildNumber, 1500); + expect(version.condition, StacConditionVersion.greaterThanOrEqual); + }); + + test('uses platform-specific condition when available', () { + final json = { + 'buildNumber': 1000, + 'condition': '>', + 'condition_$platform': '<=', + }; + + final version = StacVersion.fromJson(json); + + expect(version.buildNumber, 1000); + // Should use platform-specific condition + expect(version.condition, StacConditionVersion.lessThanOrEqual); + }); + + test('falls back to generic condition when platform-specific not available', + () { + final json = { + 'buildNumber': 1000, + 'condition': '<', + 'condition_windows': '!=', // Different platform + }; + + final version = StacVersion.fromJson(json); + + expect(version.buildNumber, 1000); + // Should use generic condition since `platform`-specific is not available + expect(version.condition, StacConditionVersion.lessThan); + }); + + test('uses both platform-specific values when available', () { + final json = { + 'buildNumber': 1000, + 'buildNumber_$platform': 3000, + 'condition': '>', + 'condition_$platform': '==', + }; + + final version = StacVersion.fromJson(json); + + // Should use both platform-specific values + expect(version.buildNumber, 3000); + expect(version.condition, StacConditionVersion.equal); + }); + + test('handles missing generic fallback gracefully', () { + final json = { + 'buildNumber_ios': + 2000, // Only platform-specific for different platform + 'condition_android': + '>=', // Only platform-specific for different platform + }; + + // This should throw or handle gracefully since there's no fallback + expect(() => StacVersion.fromJson(json), throwsA(isA())); + }); + + test('toPlatformJson method works correctly', () { + final json = { + 'key': 'generic_value', + 'key_$platform': 'platform_value', + 'other_key': 'other_generic', + }; + + // Should return platform-specific value when available + expect(StacVersion.toPlatformJson(json, 'key'), 'platform_value'); + + // Should return generic value when platform-specific not available + expect(StacVersion.toPlatformJson(json, 'other_key'), 'other_generic'); + + // Should return null when neither exists + expect(StacVersion.toPlatformJson(json, 'missing_key'), isNull); + }); + }); } From 6abc595134900f85acb81b6c5740286846f35954 Mon Sep 17 00:00:00 2001 From: Felipe Sales Date: Wed, 28 May 2025 16:18:50 -0300 Subject: [PATCH 12/30] fix: add missing import --- packages/stac/lib/src/utils/utils.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/stac/lib/src/utils/utils.dart b/packages/stac/lib/src/utils/utils.dart index 8998150f7..79f16e0e0 100644 --- a/packages/stac/lib/src/utils/utils.dart +++ b/packages/stac/lib/src/utils/utils.dart @@ -1,2 +1,3 @@ export 'package:stac/src/utils/color_utils.dart'; export 'package:stac/src/utils/stac_scroll_physics.dart'; +export 'package:stac/src/utils/version/stac_version.dart'; \ No newline at end of file From 8e380edce0f07e3e1ff9e3a96ab20c88500950f3 Mon Sep 17 00:00:00 2001 From: Felipe Sales Date: Thu, 29 May 2025 16:12:03 -0300 Subject: [PATCH 13/30] refactor: appBuildNumber as parameter --- packages/stac/lib/src/framework/stac.dart | 10 +++---- .../lib/src/utils/version/stac_version.dart | 3 +-- .../src/utils/version/stac_version_test.dart | 27 +++++++++++-------- 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/packages/stac/lib/src/framework/stac.dart b/packages/stac/lib/src/framework/stac.dart index 01fcc094f..13a9f5d85 100644 --- a/packages/stac/lib/src/framework/stac.dart +++ b/packages/stac/lib/src/framework/stac.dart @@ -129,21 +129,21 @@ class Stac { static Widget? fromJson(Map? json, BuildContext context) { try { if (json != null) { - final stacRegistry = StacRegistry.instance; Map? jsonVersion = json['version']; - if (jsonVersion != null && stacRegistry.buildNumber != null) { + if (jsonVersion != null && StacRegistry.instance.buildNumber != null) { final stacVersion = StacVersion.fromJson(jsonVersion); - final isSatisfied = stacVersion.isSatisfied(); + final isSatisfied = + stacVersion.isSatisfied(StacRegistry.instance.buildNumber!); if (!isSatisfied) { Log.w( - 'Stac buildNumber ${stacVersion.buildNumber} is not satisfied; current build is: ${stacRegistry.buildNumber}'); + 'Stac buildNumber ${stacVersion.buildNumber} is not satisfied; current build is: ${StacRegistry.instance.buildNumber}'); return null; } } String widgetType = json['type']; - StacParser? stacParser = stacRegistry.getParser(widgetType); + StacParser? stacParser = StacRegistry.instance.getParser(widgetType); if (stacParser != null) { final model = stacParser.getModel(json); return stacParser.parse(context, model); diff --git a/packages/stac/lib/src/utils/version/stac_version.dart b/packages/stac/lib/src/utils/version/stac_version.dart index c2355dec4..82a3e40bb 100644 --- a/packages/stac/lib/src/utils/version/stac_version.dart +++ b/packages/stac/lib/src/utils/version/stac_version.dart @@ -60,8 +60,7 @@ extension ListStringX on List { } extension StacVersionX on StacVersion { - bool isSatisfied() { - final appBuildNumber = StacRegistry.instance.buildNumber; + bool isSatisfied(int? appBuildNumber) { if (appBuildNumber == null) { return true; } diff --git a/packages/stac/test/src/utils/version/stac_version_test.dart b/packages/stac/test/src/utils/version/stac_version_test.dart index 77afdc43c..fa77289ad 100644 --- a/packages/stac/test/src/utils/version/stac_version_test.dart +++ b/packages/stac/test/src/utils/version/stac_version_test.dart @@ -69,7 +69,7 @@ void main() { buildNumber: 1000, condition: StacConditionVersion.equal, ); - expect(version.isSatisfied(), false); + expect(version.isSatisfied(StacRegistry.instance.buildNumber), false); }); test('handles equal condition correctly', () { @@ -77,13 +77,14 @@ void main() { buildNumber: 2000, condition: StacConditionVersion.equal, ); - expect(version.isSatisfied(), true); + expect(version.isSatisfied(StacRegistry.instance.buildNumber), true); final differentVersion = StacVersion( buildNumber: 1000, condition: StacConditionVersion.equal, ); - expect(differentVersion.isSatisfied(), false); + expect(differentVersion.isSatisfied(StacRegistry.instance.buildNumber), + false); }); test('handles greater than condition correctly', () { @@ -91,13 +92,14 @@ void main() { buildNumber: 1000, condition: StacConditionVersion.greaterThan, ); - expect(version.isSatisfied(), true); + expect(version.isSatisfied(StacRegistry.instance.buildNumber), true); final higherVersion = StacVersion( buildNumber: 3000, condition: StacConditionVersion.greaterThan, ); - expect(higherVersion.isSatisfied(), false); + expect(higherVersion.isSatisfied(StacRegistry.instance.buildNumber), + false); }); test('handles less than condition correctly', () { @@ -105,13 +107,14 @@ void main() { buildNumber: 3000, condition: StacConditionVersion.lessThan, ); - expect(version.isSatisfied(), true); + expect(version.isSatisfied(StacRegistry.instance.buildNumber), true); final lowerVersion = StacVersion( buildNumber: 1000, condition: StacConditionVersion.lessThan, ); - expect(lowerVersion.isSatisfied(), false); + expect( + lowerVersion.isSatisfied(StacRegistry.instance.buildNumber), false); }); test('handles version components of different lengths', () { @@ -121,19 +124,21 @@ void main() { buildNumber: 2000, condition: StacConditionVersion.equal, ); - expect(version.isSatisfied(), false); + expect(version.isSatisfied(StacRegistry.instance.buildNumber), false); final versionWithExtra = StacVersion( buildNumber: 2000, condition: StacConditionVersion.equal, ); - expect(versionWithExtra.isSatisfied(), false); + expect(versionWithExtra.isSatisfied(StacRegistry.instance.buildNumber), + false); final exactVersion = StacVersion( buildNumber: 20001, condition: StacConditionVersion.equal, ); - expect(exactVersion.isSatisfied(), true); + expect( + exactVersion.isSatisfied(StacRegistry.instance.buildNumber), true); }); test('handles version with non-numeric components', () { @@ -144,7 +149,7 @@ void main() { condition: StacConditionVersion.equal, ); // Non-numeric components should be treated as 0 - expect(version.isSatisfied(), true); + expect(version.isSatisfied(StacRegistry.instance.buildNumber), true); }); }); From ee098d6d68744f605c578787e5a78be620acef26 Mon Sep 17 00:00:00 2001 From: Felipe Sales Date: Thu, 29 May 2025 16:28:13 -0300 Subject: [PATCH 14/30] docs: added documentation --- packages/stac/lib/src/framework/stac.dart | 2 + .../lib/src/utils/version/stac_version.dart | 212 +++++++++++++++++- 2 files changed, 210 insertions(+), 4 deletions(-) diff --git a/packages/stac/lib/src/framework/stac.dart b/packages/stac/lib/src/framework/stac.dart index 13a9f5d85..82d8ccd6d 100644 --- a/packages/stac/lib/src/framework/stac.dart +++ b/packages/stac/lib/src/framework/stac.dart @@ -131,10 +131,12 @@ class Stac { if (json != null) { Map? jsonVersion = json['version']; + /// Check if has version and buildNumber is not null if (jsonVersion != null && StacRegistry.instance.buildNumber != null) { final stacVersion = StacVersion.fromJson(jsonVersion); final isSatisfied = stacVersion.isSatisfied(StacRegistry.instance.buildNumber!); + // If version is not satisfied, return null if (!isSatisfied) { Log.w( 'Stac buildNumber ${stacVersion.buildNumber} is not satisfied; current build is: ${StacRegistry.instance.buildNumber}'); diff --git a/packages/stac/lib/src/utils/version/stac_version.dart b/packages/stac/lib/src/utils/version/stac_version.dart index 82a3e40bb..5877a6149 100644 --- a/packages/stac/lib/src/utils/version/stac_version.dart +++ b/packages/stac/lib/src/utils/version/stac_version.dart @@ -2,15 +2,157 @@ import 'dart:io'; import 'package:stac/stac.dart'; +/// # STAC Version Management System +/// +/// This module provides a comprehensive version management system for STAC applications, +/// allowing for flexible version-based feature control and compatibility checking. +/// +/// ## Overview +/// +/// The STAC Version system enables developers to control feature availability and +/// application behavior based on build numbers and version conditions. This is +/// particularly useful for: +/// +/// - **Feature Flags**: Enable/disable features based on app version +/// - **Backward Compatibility**: Ensure compatibility across different app versions +/// - **Gradual Rollouts**: Control feature deployment to specific version ranges +/// - **Platform-Specific Versioning**: Handle different versioning schemes per platform +/// +/// ## Key Components +/// +/// - `StacVersion`: Main class representing a version constraint +/// - `StacConditionVersion`: Enum defining comparison operators +/// - Platform-aware JSON parsing for cross-platform compatibility +/// +/// ## Usage Examples +/// +/// ### Basic Version Checking +/// ```dart +/// // Check if current app version satisfies a minimum requirement +/// final minVersion = StacVersion( +/// buildNumber: 100, +/// condition: StacConditionVersion.greaterThanOrEqual, +/// ); +/// +/// final currentBuild = 120; +/// if (minVersion.isSatisfied(currentBuild)) { +/// // Enable new feature +/// showNewFeature(); +/// } +/// ``` +/// +/// ### JSON Configuration +/// ```dart +/// // Load version constraints from server/config +/// final json = { +/// 'buildNumber': 150, +/// 'condition': '>=', +/// // Platform-specific overrides +/// 'buildNumber_ios': 155, +/// 'buildNumber_android': 145, +/// }; +/// +/// final version = StacVersion.fromJson(json); +/// ``` +/// +/// ### Feature Gate Example +/// ```dart +/// class FeatureManager { +/// static bool isNewUIEnabled(int appBuildNumber) { +/// final requirement = StacVersion( +/// buildNumber: 200, +/// condition: StacConditionVersion.greaterThanOrEqual, +/// ); +/// return requirement.isSatisfied(appBuildNumber); +/// } +/// } +/// ``` +/// +/// ## Use Cases +/// +/// ### 1. **Progressive Feature Rollout** +/// Deploy features gradually by setting minimum version requirements: +/// ```dart +/// final premiumFeatureGate = StacVersion( +/// buildNumber: 300, +/// condition: StacConditionVersion.greaterThan, +/// ); +/// ``` +/// +/// ### 2. **Deprecation Management** +/// Handle deprecated features with maximum version limits: +/// ```dart +/// final legacyAPIGate = StacVersion( +/// buildNumber: 250, +/// condition: StacConditionVersion.lessThanOrEqual, +/// ); +/// ``` +/// +/// ### 3. **A/B Testing** +/// Control experiment participation based on version: +/// ```dart +/// final experimentGroup = StacVersion( +/// buildNumber: 180, +/// condition: StacConditionVersion.equal, +/// ); +/// ``` +/// +/// ### 4. **Emergency Fixes** +/// Quickly disable problematic features: +/// ```dart +/// final emergencyDisable = StacVersion( +/// buildNumber: 195, +/// condition: StacConditionVersion.notEqual, +/// ); +/// ``` + +/// Represents a version constraint with a build number and comparison condition. +/// +/// This class encapsulates version requirements that can be evaluated against +/// an application's current build number to determine feature availability +/// or compatibility. +/// +/// Example: +/// ```dart +/// final versionGate = StacVersion( +/// buildNumber: 150, +/// condition: StacConditionVersion.greaterThanOrEqual, +/// ); +/// +/// if (versionGate.isSatisfied(currentAppBuild)) { +/// // Feature is available +/// } +/// ``` class StacVersion { + /// Creates a new version constraint. + /// + /// [buildNumber] is the reference build number for comparison. + /// [condition] defines how the comparison should be performed. const StacVersion({ required this.buildNumber, required this.condition, }); + /// The reference build number used for version comparisons. final int buildNumber; + + /// The condition that defines how version comparison is performed. final StacConditionVersion condition; + /// Creates a [StacVersion] instance from JSON data. + /// + /// Supports platform-specific overrides by checking for keys with + /// platform suffixes (e.g., 'buildNumber_ios', 'condition_android'). + /// + /// Example JSON: + /// ```json + /// { + /// "buildNumber": 100, + /// "condition": ">=", + /// "buildNumber_ios": 110, + /// "buildNumber_android": 95 + /// } + /// ``` factory StacVersion.fromJson(Map json) { return StacVersion( buildNumber: toPlatformJson(json, 'buildNumber').toInt(), @@ -19,20 +161,60 @@ class StacVersion { ); } + /// Extracts platform-specific values from JSON, falling back to generic keys. + /// + /// First attempts to find a platform-specific key (e.g., 'key_ios'), + /// then falls back to the generic key if not found. + /// + /// This enables different version requirements per platform while + /// maintaining a single configuration source. static dynamic toPlatformJson(Map json, String key) => json['${key}_${Platform.operatingSystem}'] ?? json[key]; } +/// Defines the available comparison operators for version conditions. +/// +/// These operators determine how a [StacVersion] compares an application's +/// build number against the reference build number. enum StacConditionVersion { + /// Greater than (>) + /// App build must be strictly greater than the reference build. greaterThan, + + /// Greater than or equal (>=) + /// App build must be greater than or equal to the reference build. greaterThanOrEqual, + + /// Less than (<) + /// App build must be strictly less than the reference build. lessThan, + + /// Less than or equal (<=) + /// App build must be less than or equal to the reference build. lessThanOrEqual, + + /// Equal (==) + /// App build must exactly match the reference build. equal, + + /// Not equal (!=) + /// App build must not match the reference build. notEqual, } +/// Extension to convert string representations to [StacConditionVersion] enum values. +/// +/// Supports standard comparison operators: +/// - '>' -> greaterThan +/// - '>=' -> greaterThanOrEqual +/// - '<' -> lessThan +/// - '<=' -> lessThanOrEqual +/// - '==' -> equal +/// - '!=' -> notEqual +/// +/// Defaults to [StacConditionVersion.notEqual] for invalid input. extension StacConditionVersionX on String? { + /// Converts a string operator to its corresponding [StacConditionVersion]. StacConditionVersion toStacConditionVersion() => switch (this) { '>' => StacConditionVersion.greaterThan, '>=' => StacConditionVersion.greaterThanOrEqual, @@ -44,7 +226,11 @@ extension StacConditionVersionX on String? { }; } +/// Extension to convert [StacConditionVersion] enum values back to string operators. +/// +/// Useful for serialization, logging, or displaying version conditions in UI. extension StacConditionVersionToStringX on StacConditionVersion { + /// Returns the string representation of the condition operator. String toTypeString() => switch (this) { StacConditionVersion.greaterThan => '>', StacConditionVersion.greaterThanOrEqual => '>=', @@ -55,16 +241,34 @@ extension StacConditionVersionToStringX on StacConditionVersion { }; } -extension ListStringX on List { - List toIntList() => map((e) => int.tryParse(e) ?? 0).toList(); -} - +/// Extension providing version satisfaction checking functionality. +/// +/// This is the core functionality that determines whether a given +/// application build number satisfies the version constraint. extension StacVersionX on StacVersion { + /// Determines if the given app build number satisfies this version constraint. + /// + /// Returns `true` if [appBuildNumber] is null (indicating no version check required) + /// or if the build number satisfies the condition relative to the reference build number. + /// + /// Examples: + /// ```dart + /// final minVersion = StacVersion(buildNumber: 100, condition: StacConditionVersion.greaterThanOrEqual); + /// + /// minVersion.isSatisfied(120); // true - 120 >= 100 + /// minVersion.isSatisfied(90); // false - 90 < 100 + /// minVersion.isSatisfied(null); // true - no version constraint + /// ``` + /// + /// [appBuildNumber] The current application's build number to check. + /// Returns whether the version constraint is satisfied. bool isSatisfied(int? appBuildNumber) { + // If no app build number is provided, assume constraint is satisfied if (appBuildNumber == null) { return true; } + // Evaluate the condition against the reference build number return switch (condition) { StacConditionVersion.greaterThan => appBuildNumber > buildNumber, StacConditionVersion.greaterThanOrEqual => appBuildNumber >= buildNumber, From dc8a2b9896b37603fe9e6d28734f24de7595ff23 Mon Sep 17 00:00:00 2001 From: Alan Trope Date: Mon, 15 Sep 2025 11:13:46 -0300 Subject: [PATCH 15/30] chore: standardize quotes in pubspec.yaml files and remove unused imports in stac_registry.dart and stac.dart --- examples/counter_example/pubspec.yaml | 3 +-- examples/movie_app/pubspec.yaml | 3 +-- packages/stac/lib/src/framework/stac.dart | 1 - packages/stac/lib/src/framework/stac_registry.dart | 1 - packages/stac/lib/src/parsers/theme/stac_theme/stac_theme.dart | 2 +- packages/stac/pubspec.yaml | 2 +- pubspec.yaml | 3 +-- 7 files changed, 5 insertions(+), 10 deletions(-) diff --git a/examples/counter_example/pubspec.yaml b/examples/counter_example/pubspec.yaml index a0e3b43e2..a04040b2a 100644 --- a/examples/counter_example/pubspec.yaml +++ b/examples/counter_example/pubspec.yaml @@ -2,7 +2,7 @@ name: counter_example description: "A Stac example app showcasing a simple counter." # The following line prevents the package from being accidentally published to # pub.dev using `flutter pub publish`. This is preferred for private packages. -publish_to: 'none' # Remove this line if you wish to publish to pub.dev +publish_to: "none" # Remove this line if you wish to publish to pub.dev # The following defines the version and build number for your application. # A version number is three numbers separated by dots, like 1.2.43 @@ -58,7 +58,6 @@ dev_dependencies: # The following section is specific to Flutter packages. flutter: - # The following line ensures that the Material Icons font is # included with your application, so that you can use the icons in # the material Icons class. diff --git a/examples/movie_app/pubspec.yaml b/examples/movie_app/pubspec.yaml index d2e4755a7..b325c46f7 100644 --- a/examples/movie_app/pubspec.yaml +++ b/examples/movie_app/pubspec.yaml @@ -2,7 +2,7 @@ name: movie_app description: "A new Flutter project." # The following line prevents the package from being accidentally published to # pub.dev using `flutter pub publish`. This is preferred for private packages. -publish_to: 'none' # Remove this line if you wish to publish to pub.dev +publish_to: "none" # Remove this line if you wish to publish to pub.dev # The following defines the version and build number for your application. # A version number is three numbers separated by dots, like 1.2.43 @@ -54,7 +54,6 @@ dev_dependencies: # The following section is specific to Flutter packages. flutter: - # The following line ensures that the Material Icons font is # included with your application, so that you can use the icons in # the material Icons class. diff --git a/packages/stac/lib/src/framework/stac.dart b/packages/stac/lib/src/framework/stac.dart index 55b5ca007..28ca0039f 100644 --- a/packages/stac/lib/src/framework/stac.dart +++ b/packages/stac/lib/src/framework/stac.dart @@ -10,7 +10,6 @@ import 'package:stac/src/parsers/parsers.dart'; import 'package:stac/src/parsers/widgets/stac_inkwell/stac_inkwell_parser.dart'; import 'package:stac/src/parsers/widgets/stac_set_value/stac_set_value_parser.dart'; import 'package:stac/src/services/stac_network_service.dart'; -import 'package:stac/src/utils/log.dart'; import 'package:stac/src/utils/version/stac_version.dart'; import 'package:stac/src/utils/variable_resolver.dart'; import 'package:stac/src/utils/widget_type.dart'; diff --git a/packages/stac/lib/src/framework/stac_registry.dart b/packages/stac/lib/src/framework/stac_registry.dart index 892418614..7e6cb6b60 100644 --- a/packages/stac/lib/src/framework/stac_registry.dart +++ b/packages/stac/lib/src/framework/stac_registry.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:stac/src/utils/log.dart'; import 'package:stac_logger/stac_logger.dart'; import 'package:stac_framework/stac_framework.dart'; diff --git a/packages/stac/lib/src/parsers/theme/stac_theme/stac_theme.dart b/packages/stac/lib/src/parsers/theme/stac_theme/stac_theme.dart index 0fc1c1f01..58d1393a0 100644 --- a/packages/stac/lib/src/parsers/theme/stac_theme/stac_theme.dart +++ b/packages/stac/lib/src/parsers/theme/stac_theme/stac_theme.dart @@ -174,7 +174,7 @@ extension StacThemeParser on StacTheme { checkboxTheme: checkboxTheme?.parse(context), chipTheme: chipTheme?.parse(context), datePickerTheme: datePickerTheme?.parse(context), - dialogTheme: dialogTheme?.parse(context)?.data, + dialogTheme: dialogTheme?.parse(context), dividerTheme: dividerTheme?.parse(context), drawerTheme: drawerTheme?.parse(context), // DropdownMenuThemeData? dropdownMenuTheme, diff --git a/packages/stac/pubspec.yaml b/packages/stac/pubspec.yaml index 67db2c60b..99e7c9ab3 100644 --- a/packages/stac/pubspec.yaml +++ b/packages/stac/pubspec.yaml @@ -31,4 +31,4 @@ dev_dependencies: flutter_lints: ^5.0.0 build_runner: ^2.4.15 freezed: ^3.0.6 - json_serializable: ^6.9.5 \ No newline at end of file + json_serializable: ^6.9.5 diff --git a/pubspec.yaml b/pubspec.yaml index 580d8c098..63d19e9b8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,8 +1,7 @@ name: stac_workspace environment: - sdk: '>=3.1.0 <4.0.0' + sdk: ">=3.1.0 <4.0.0" dev_dependencies: melos: ^6.3.2 - \ No newline at end of file From 8f84f831f8133932ec3042b0a502f4fbd2a1f2bf Mon Sep 17 00:00:00 2001 From: Felipe Sales Date: Tue, 30 Dec 2025 12:29:09 -0300 Subject: [PATCH 16/30] feat: add platform validation for widget support in Stac class --- packages/stac/lib/src/framework/stac.dart | 26 +++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/packages/stac/lib/src/framework/stac.dart b/packages/stac/lib/src/framework/stac.dart index 28ca0039f..c558ec1f4 100644 --- a/packages/stac/lib/src/framework/stac.dart +++ b/packages/stac/lib/src/framework/stac.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'dart:convert'; +import 'dart:io'; import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; @@ -150,6 +151,7 @@ class Stac { try { if (json != null) { Map? jsonVersion = json['version']; + final platform = json['platform']; /// Check if has version and buildNumber is not null if (jsonVersion != null && StacRegistry.instance.buildNumber != null) { @@ -164,6 +166,30 @@ class Stac { } } + /// Check if platform is specified and validate + if (platform != null) { + final currentPlatform = Platform.operatingSystem; + bool isPlatformSupported = false; + List supportedPlatforms = []; + + // Check if platform is a list or a single string + if (platform is List) { + supportedPlatforms = platform.map((e) => e.toString()).toList(); + isPlatformSupported = supportedPlatforms.contains(currentPlatform); + } else if (platform is String) { + supportedPlatforms.add(platform); + isPlatformSupported = platform == currentPlatform; + } + + // If platform is not supported, log and return null + if (!isPlatformSupported) { + final platformsStr = supportedPlatforms.join(', '); + Log.w( + 'Widget not supported on platform [$currentPlatform]. Only available for: $platformsStr'); + return null; + } + } + String widgetType = json['type']; StacParser? stacParser = StacRegistry.instance.getParser(widgetType); From 80dac42e4062daf6d08931f9e863ababe3d348dc Mon Sep 17 00:00:00 2001 From: Felipe Sales Date: Tue, 6 Jan 2026 13:30:57 -0300 Subject: [PATCH 17/30] refactor: removed unnecessary imports and add parser import --- packages/stac/lib/src/framework/stac_service.dart | 6 ------ packages/stac/lib/src/parsers/widgets/widgets.dart | 1 + 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/stac/lib/src/framework/stac_service.dart b/packages/stac/lib/src/framework/stac_service.dart index e9c7fdfaf..4e1e3f124 100644 --- a/packages/stac/lib/src/framework/stac_service.dart +++ b/packages/stac/lib/src/framework/stac_service.dart @@ -6,24 +6,18 @@ import 'package:dio/dio.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart' hide ErrorWidgetBuilder; import 'package:flutter/services.dart'; -import 'package:stac/src/framework/stac.dart'; import 'package:stac/src/framework/stac_error.dart'; -import 'package:stac/src/framework/stac_registry.dart'; import 'package:stac/src/parsers/actions/stac_form_validate/stac_form_validate_parser.dart'; import 'package:stac/src/parsers/actions/stac_get_form_value/stac_get_form_value_parser.dart'; import 'package:stac/src/parsers/actions/stac_network_request/stac_network_request_parser.dart'; -import 'package:stac/src/parsers/parsers.dart'; import 'package:stac/src/parsers/widgets/stac_app_bar/stac_app_bar_parser.dart'; import 'package:stac/src/parsers/widgets/stac_inkwell/stac_inkwell_parser.dart'; import 'package:stac/src/parsers/widgets/stac_row/stac_row_parser.dart'; -import 'package:stac/src/parsers/widgets/stac_text/stac_text_parser.dart'; import 'package:stac/src/parsers/widgets/stac_tool_tip/stac_tool_tip_parser.dart'; -import 'package:stac/src/services/stac_network_service.dart'; import 'package:stac/src/utils/variable_resolver.dart'; import 'package:stac/stac.dart'; import 'package:stac_core/core/stac_options.dart'; import 'package:stac_core/stac_core.dart'; -import 'package:stac_framework/stac_framework.dart'; import 'package:stac_logger/stac_logger.dart'; /// Internal service that manages Stac parsers, actions, and rendering. diff --git a/packages/stac/lib/src/parsers/widgets/widgets.dart b/packages/stac/lib/src/parsers/widgets/widgets.dart index 64af1ef05..86b03bc93 100644 --- a/packages/stac/lib/src/parsers/widgets/widgets.dart +++ b/packages/stac/lib/src/parsers/widgets/widgets.dart @@ -58,6 +58,7 @@ export 'package:stac/src/parsers/widgets/stac_refresh_indicator/stac_refresh_ind export 'package:stac/src/parsers/widgets/stac_safe_area/stac_safe_area_parser.dart'; export 'package:stac/src/parsers/widgets/stac_scaffold/stac_scaffold_parser.dart'; export 'package:stac/src/parsers/widgets/stac_selectable_text/stac_selectable_text_parser.dart'; +export 'package:stac/src/parsers/widgets/stac_text/stac_text_parser.dart'; export 'package:stac/src/parsers/widgets/stac_set_value/stac_set_value_parser.dart'; export 'package:stac/src/parsers/widgets/stac_single_child_scroll_view/stac_single_child_scroll_view_parser.dart'; export 'package:stac/src/parsers/widgets/stac_sized_box/stac_sized_box_parser.dart'; From 2e8c130834c7a0c5fee5b412dc480af008c43198 Mon Sep 17 00:00:00 2001 From: Felipe Sales Date: Tue, 6 Jan 2026 14:45:07 -0300 Subject: [PATCH 18/30] chore: update --- .../stac/lib/src/framework/stac_service.dart | 16 +++++++++++----- .../stac/lib/src/parsers/widgets/widgets.dart | 1 + packages/stac/pubspec.yaml | 2 ++ 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/packages/stac/lib/src/framework/stac_service.dart b/packages/stac/lib/src/framework/stac_service.dart index 4e1e3f124..0efb0e4ef 100644 --- a/packages/stac/lib/src/framework/stac_service.dart +++ b/packages/stac/lib/src/framework/stac_service.dart @@ -168,13 +168,16 @@ class StacService { _actionParsers.addAll(actionParsers); StacRegistry.instance.registerAll(_parsers, override); StacRegistry.instance.registerAllActions(_actionParsers, override); + StacRegistry.instance.registerBuildNumber(buildNumber); StacNetworkService.initialize(dio ?? Dio()); _showErrorWidgets = showErrorWidgets; _logStackTraces = logStackTraces; _errorWidgetBuilder = errorWidgetBuilder; - StacRegistry.instance.registerBuildNumber(buildNumber); } + static void setParseCustomColor(Color? Function(String?)? parseCustomColor) => + StacRegistry.instance.parseCustomColor = parseCustomColor; + static Widget? fromJson(Map? json, BuildContext context) { try { if (json == null) { @@ -187,12 +190,14 @@ class StacService { /// Check if has version and buildNumber is not null if (jsonVersion != null && StacRegistry.instance.buildNumber != null) { final stacVersion = StacVersion.fromJson(jsonVersion); - final isSatisfied = - stacVersion.isSatisfied(StacRegistry.instance.buildNumber!); + final isSatisfied = stacVersion.isSatisfied( + StacRegistry.instance.buildNumber!, + ); // If version is not satisfied, return null if (!isSatisfied) { Log.w( - 'Stac buildNumber ${stacVersion.buildNumber} is not satisfied; current build is: ${StacRegistry.instance.buildNumber}'); + 'Stac buildNumber ${stacVersion.buildNumber} is not satisfied; current build is: ${StacRegistry.instance.buildNumber}', + ); return null; } } @@ -216,7 +221,8 @@ class StacService { if (!isPlatformSupported) { final platformsStr = supportedPlatforms.join(', '); Log.w( - 'Widget not supported on platform [$currentPlatform]. Only available for: $platformsStr'); + 'Widget not supported on platform [$currentPlatform]. Only available for: $platformsStr', + ); return null; } } diff --git a/packages/stac/lib/src/parsers/widgets/widgets.dart b/packages/stac/lib/src/parsers/widgets/widgets.dart index 86b03bc93..580734a76 100644 --- a/packages/stac/lib/src/parsers/widgets/widgets.dart +++ b/packages/stac/lib/src/parsers/widgets/widgets.dart @@ -20,6 +20,7 @@ export 'package:stac/src/parsers/widgets/stac_column/stac_column_parser.dart'; export 'package:stac/src/parsers/widgets/stac_conditional/stac_conditional_parser.dart'; export 'package:stac/src/parsers/widgets/stac_container/stac_container_parser.dart'; export 'package:stac/src/parsers/widgets/stac_custom_scroll_view/stac_custom_scroll_view_parser.dart'; +export 'package:stac/src/parsers/widgets/stac_double/stac_double.dart'; export 'package:stac/src/parsers/widgets/stac_default_bottom_navigation_controller/stac_default_bottom_navigation_controller_parser.dart'; export 'package:stac/src/parsers/widgets/stac_default_tab_controller/stac_default_tab_controller_parser.dart'; export 'package:stac/src/parsers/widgets/stac_divider/stac_divider_parser.dart'; diff --git a/packages/stac/pubspec.yaml b/packages/stac/pubspec.yaml index 8a7a2adac..51c5dd68d 100644 --- a/packages/stac/pubspec.yaml +++ b/packages/stac/pubspec.yaml @@ -24,6 +24,8 @@ dependencies: flutter_svg: ^2.2.2 stac_logger: ^1.1.0 stac_core: ^1.1.0 + # stac_core: + # path: ../stac_core shared_preferences: ^2.5.3 dev_dependencies: From 5ce86bb061b696b0fb03c87bf0820533712f2fdf Mon Sep 17 00:00:00 2001 From: Lucas Tonussi Date: Thu, 8 Jan 2026 10:57:30 -0300 Subject: [PATCH 19/30] chore: update stac_core dependency to use local path and add new layout parsers --- .../lib/src/parsers/foundation/layout/parsers.dart | 10 ++++++++++ packages/stac/lib/stac.dart | 2 ++ packages/stac/pubspec.yaml | 6 +++--- 3 files changed, 15 insertions(+), 3 deletions(-) create mode 100644 packages/stac/lib/src/parsers/foundation/layout/parsers.dart diff --git a/packages/stac/lib/src/parsers/foundation/layout/parsers.dart b/packages/stac/lib/src/parsers/foundation/layout/parsers.dart new file mode 100644 index 000000000..b3590a8d3 --- /dev/null +++ b/packages/stac/lib/src/parsers/foundation/layout/parsers.dart @@ -0,0 +1,10 @@ +export 'stac_axis_parser.dart'; +export 'stac_box_fit_parser.dart'; +export 'stac_box_shape_parser.dart'; +export 'stac_clip_parser.dart'; +export 'stac_flex_fit_parser.dart'; +export 'stac_material_tap_target_size_parser.dart'; +export 'stac_stack_fit_parser.dart'; +export 'stac_vertical_direction_parser.dart'; +export 'stac_wrap_alignment_parser.dart'; +export 'stac_wrap_cross_alignment_parser.dart'; diff --git a/packages/stac/lib/stac.dart b/packages/stac/lib/stac.dart index 749fd39aa..3a645ddce 100644 --- a/packages/stac/lib/stac.dart +++ b/packages/stac/lib/stac.dart @@ -4,6 +4,8 @@ export 'package:stac/src/parsers/actions/actions.dart'; export 'package:stac/src/parsers/parsers.dart'; export 'package:stac/src/services/services.dart'; export 'package:stac/src/utils/utils.dart'; +export 'package:stac/src/parsers/foundation/layout/parsers.dart'; // Theme exports export 'package:stac_core/stac_core.dart' show StacTheme; + export 'package:stac_framework/stac_framework.dart'; diff --git a/packages/stac/pubspec.yaml b/packages/stac/pubspec.yaml index 51c5dd68d..23f4f1c42 100644 --- a/packages/stac/pubspec.yaml +++ b/packages/stac/pubspec.yaml @@ -23,9 +23,9 @@ dependencies: cached_network_image: ^3.4.1 flutter_svg: ^2.2.2 stac_logger: ^1.1.0 - stac_core: ^1.1.0 - # stac_core: - # path: ../stac_core + # stac_core: ^1.1.0 + stac_core: + path: ../stac_core shared_preferences: ^2.5.3 dev_dependencies: From 9c25956b87ae7d4b27b338e31292088575668d9e Mon Sep 17 00:00:00 2001 From: Felipe Sales Date: Thu, 12 Feb 2026 17:08:13 -0300 Subject: [PATCH 20/30] autoreview --- examples/counter_example/pubspec.yaml | 3 ++- examples/movie_app/pubspec.yaml | 3 ++- packages/stac/pubspec.yaml | 3 +-- pubspec.yaml | 4 ++-- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/examples/counter_example/pubspec.yaml b/examples/counter_example/pubspec.yaml index 343789258..77949698e 100644 --- a/examples/counter_example/pubspec.yaml +++ b/examples/counter_example/pubspec.yaml @@ -2,7 +2,7 @@ name: counter_example description: "A Stac example app showcasing a simple counter." # The following line prevents the package from being accidentally published to # pub.dev using `flutter pub publish`. This is preferred for private packages. -publish_to: "none" # Remove this line if you wish to publish to pub.dev +publish_to: 'none' # Remove this line if you wish to publish to pub.dev # The following defines the version and build number for your application. # A version number is three numbers separated by dots, like 1.2.43 @@ -58,6 +58,7 @@ dev_dependencies: # The following section is specific to Flutter packages. flutter: + # The following line ensures that the Material Icons font is # included with your application, so that you can use the icons in # the material Icons class. diff --git a/examples/movie_app/pubspec.yaml b/examples/movie_app/pubspec.yaml index b2815f92e..35185504f 100644 --- a/examples/movie_app/pubspec.yaml +++ b/examples/movie_app/pubspec.yaml @@ -2,7 +2,7 @@ name: movie_app description: "A new Flutter project." # The following line prevents the package from being accidentally published to # pub.dev using `flutter pub publish`. This is preferred for private packages. -publish_to: "none" # Remove this line if you wish to publish to pub.dev +publish_to: 'none' # Remove this line if you wish to publish to pub.dev # The following defines the version and build number for your application. # A version number is three numbers separated by dots, like 1.2.43 @@ -29,6 +29,7 @@ environment: # versions available, run `flutter pub outdated`. dependencies: flutter: + sdk: flutter # The following adds the Cupertino Icons font to your application. diff --git a/packages/stac/pubspec.yaml b/packages/stac/pubspec.yaml index a0a4cf589..b04a3a45f 100644 --- a/packages/stac/pubspec.yaml +++ b/packages/stac/pubspec.yaml @@ -33,5 +33,4 @@ dev_dependencies: sdk: flutter flutter_lints: ^6.0.0 build_runner: ^2.10.4 - json_serializable: ^6.11.3 - + json_serializable: ^6.11.3 \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index 63d19e9b8..121d76e1d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: stac_workspace environment: - sdk: ">=3.1.0 <4.0.0" + sdk: '>=3.1.0 <4.0.0' dev_dependencies: - melos: ^6.3.2 + melos: ^6.3.2 \ No newline at end of file From 228e9b72fb0a66310c1c99537b4fa566446a4692 Mon Sep 17 00:00:00 2001 From: Felipe Sales Date: Thu, 12 Feb 2026 17:09:42 -0300 Subject: [PATCH 21/30] autoreview w --- examples/movie_app/pubspec.yaml | 2 +- pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/movie_app/pubspec.yaml b/examples/movie_app/pubspec.yaml index 35185504f..d58c90331 100644 --- a/examples/movie_app/pubspec.yaml +++ b/examples/movie_app/pubspec.yaml @@ -29,7 +29,6 @@ environment: # versions available, run `flutter pub outdated`. dependencies: flutter: - sdk: flutter # The following adds the Cupertino Icons font to your application. @@ -59,6 +58,7 @@ dev_dependencies: # The following section is specific to Flutter packages. flutter: + # The following line ensures that the Material Icons font is # included with your application, so that you can use the icons in # the material Icons class. diff --git a/pubspec.yaml b/pubspec.yaml index 121d76e1d..7b57af382 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,4 +4,4 @@ environment: sdk: '>=3.1.0 <4.0.0' dev_dependencies: - melos: ^6.3.2 \ No newline at end of file + melos: ^6.3.2 From 2d6b52e2397a93b36a71db59bd900d9e221d4fef Mon Sep 17 00:00:00 2001 From: Felipe Sales Date: Tue, 24 Feb 2026 16:55:44 -0300 Subject: [PATCH 22/30] feat: add copyWith generic --- .../text/stac_text_style/stac_text_style.dart | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/packages/stac_core/lib/foundation/text/stac_text_style/stac_text_style.dart b/packages/stac_core/lib/foundation/text/stac_text_style/stac_text_style.dart index 6c7c990ee..95577c4c5 100644 --- a/packages/stac_core/lib/foundation/text/stac_text_style/stac_text_style.dart +++ b/packages/stac_core/lib/foundation/text/stac_text_style/stac_text_style.dart @@ -705,3 +705,79 @@ class StacThemeTextStyle extends StacTextStyle { ); } } + +/// Extension so [StacTextStyle] can use [copyWith] without changing the plugin. +/// Delegates to [StacCustomTextStyle.copyWith] or [StacThemeTextStyle.copyWith]. +extension StacTextStyleCopyWith on StacTextStyle { + StacTextStyle copyWith({ + bool? inherit, + StacColor? color, + StacColor? backgroundColor, + double? fontSize, + StacFontWeight? fontWeight, + StacFontStyle? fontStyle, + double? letterSpacing, + double? wordSpacing, + StacTextBaseline? textBaseline, + double? height, + StacTextLeadingDistribution? leadingDistribution, + StacColor? decorationColor, + StacTextDecorationStyle? decorationStyle, + double? decorationThickness, + String? debugLabel, + String? fontFamily, + List? fontFamilyFallback, + String? package, + StacTextOverflow? overflow, + }) { + if (this is StacCustomTextStyle) { + return (this as StacCustomTextStyle).copyWith( + inherit: inherit, + color: color, + backgroundColor: backgroundColor, + fontSize: fontSize, + fontWeight: fontWeight, + fontStyle: fontStyle, + letterSpacing: letterSpacing, + wordSpacing: wordSpacing, + textBaseline: textBaseline, + height: height, + leadingDistribution: leadingDistribution, + decorationColor: decorationColor, + decorationStyle: decorationStyle, + decorationThickness: decorationThickness, + debugLabel: debugLabel, + fontFamily: fontFamily, + fontFamilyFallback: fontFamilyFallback, + package: package, + overflow: overflow, + ); + } + if (this is StacThemeTextStyle) { + return (this as StacThemeTextStyle).copyWith( + inherit: inherit, + color: color, + backgroundColor: backgroundColor, + fontSize: fontSize, + fontWeight: fontWeight, + fontStyle: fontStyle, + letterSpacing: letterSpacing, + wordSpacing: wordSpacing, + textBaseline: textBaseline, + height: height, + leadingDistribution: leadingDistribution, + decorationColor: decorationColor, + decorationStyle: decorationStyle, + decorationThickness: decorationThickness, + debugLabel: debugLabel, + fontFamily: fontFamily, + fontFamilyFallback: fontFamilyFallback, + package: package, + overflow: overflow, + ); + } + throw StateError( + 'copyWith only supported on StacCustomTextStyle and StacThemeTextStyle', + ); + } +} From 3ff750a89cfa31d230f4f80343a984bb5f5961d7 Mon Sep 17 00:00:00 2001 From: Felipe Sales Date: Wed, 25 Feb 2026 11:27:27 -0300 Subject: [PATCH 23/30] feat(stac_service): add platform validation and normalization - Introduced a new method to normalize and validate platform identifiers against a predefined list of supported platforms. - Updated platform checking logic to improve validation and logging of unsupported platforms. - Removed unused imports and cleaned up the code for better readability. - Added missing export for layout parsers in foundation.dart. --- .../stac/lib/src/framework/stac_service.dart | 65 ++++++++++++++----- .../src/parsers/foundation/foundation.dart | 1 + packages/stac/lib/stac.dart | 1 - 3 files changed, 48 insertions(+), 19 deletions(-) diff --git a/packages/stac/lib/src/framework/stac_service.dart b/packages/stac/lib/src/framework/stac_service.dart index 0f4c712bf..69caf03e8 100644 --- a/packages/stac/lib/src/framework/stac_service.dart +++ b/packages/stac/lib/src/framework/stac_service.dart @@ -1,13 +1,10 @@ import 'dart:async'; import 'dart:convert'; -import 'dart:io'; import 'package:dio/dio.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart' hide ErrorWidgetBuilder; import 'package:flutter/services.dart'; -import 'package:stac/src/framework/stac.dart'; -import 'package:stac/src/models/stac_cache_config.dart'; import 'package:stac/src/framework/stac_error.dart'; import 'package:stac/src/parsers/actions/stac_form_validate/stac_form_validate_parser.dart'; import 'package:stac/src/parsers/actions/stac_get_form_value/stac_get_form_value_parser.dart'; @@ -22,6 +19,33 @@ import 'package:stac_core/core/stac_options.dart'; import 'package:stac_core/stac_core.dart'; import 'package:stac_logger/stac_logger.dart'; +/// Canonical platform identifiers supported in JSON "platform" field. +/// Incoming values are normalized (e.g. lowercased) and validated against this list. +const List supportedPlatformStrings = [ + 'android', + 'ios', + 'linux', + 'macos', + 'windows', + 'web', +]; + +/// Returns the current platform as a canonical string (one of [supportedPlatformStrings]). +String _currentPlatformString() { + if (kIsWeb) { + return 'web'; + } + + return switch (defaultTargetPlatform) { + TargetPlatform.android => 'android', + TargetPlatform.iOS => 'ios', + TargetPlatform.linux => 'linux', + TargetPlatform.macOS => 'macos', + TargetPlatform.windows => 'windows', + _ => 'unknown', + }; +} + /// Internal service that manages Stac parsers, actions, and rendering. /// /// This service is the core of the Stac framework, responsible for: @@ -224,24 +248,29 @@ class StacService { /// Check if platform is specified and validate if (platform != null) { - final currentPlatform = Platform.operatingSystem; - bool isPlatformSupported = false; - List supportedPlatforms = []; - - // Check if platform is a list or a single string - if (platform is List) { - supportedPlatforms = platform.map((e) => e.toString()).toList(); - isPlatformSupported = supportedPlatforms.contains(currentPlatform); - } else if (platform is String) { - supportedPlatforms.add(platform); - isPlatformSupported = platform == currentPlatform; + final currentPlatform = _currentPlatformString(); + final rawList = platform is List + ? platform.map((e) => e.toString().toLowerCase()).toList() + : [platform.toString().toLowerCase()]; + final validatedPlatforms = rawList + .where((s) => supportedPlatformStrings.contains(s)) + .toList(); + final invalid = rawList + .where((s) => !supportedPlatformStrings.contains(s)) + .toList(); + if (invalid.isNotEmpty) { + Log.w( + 'Unknown platform identifier(s) in "platform": $invalid. Supported: $supportedPlatformStrings', + ); } - // If platform is not supported, log and return null - if (!isPlatformSupported) { - final platformsStr = supportedPlatforms.join(', '); + final isPlatformSupported = validatedPlatforms.contains( + currentPlatform, + ); + + if (!isPlatformSupported && validatedPlatforms.isNotEmpty) { Log.w( - 'Widget not supported on platform [$currentPlatform]. Only available for: $platformsStr', + 'Widget not supported on platform [$currentPlatform]. Only available for: ${validatedPlatforms.join(', ')}', ); return null; } diff --git a/packages/stac/lib/src/parsers/foundation/foundation.dart b/packages/stac/lib/src/parsers/foundation/foundation.dart index db690b1eb..7fe236077 100644 --- a/packages/stac/lib/src/parsers/foundation/foundation.dart +++ b/packages/stac/lib/src/parsers/foundation/foundation.dart @@ -69,6 +69,7 @@ export 'layout/stac_stack_fit_parser.dart'; export 'layout/stac_vertical_direction_parser.dart'; export 'layout/stac_wrap_alignment_parser.dart'; export 'layout/stac_wrap_cross_alignment_parser.dart'; +export 'layout/parsers.dart'; // Navigation parsers export 'navigation/stac_bottom_navigation_bar_landscape_layout_parser.dart'; export 'navigation/stac_bottom_navigation_bar_type_parser.dart'; diff --git a/packages/stac/lib/stac.dart b/packages/stac/lib/stac.dart index 3a645ddce..9e3d5334f 100644 --- a/packages/stac/lib/stac.dart +++ b/packages/stac/lib/stac.dart @@ -4,7 +4,6 @@ export 'package:stac/src/parsers/actions/actions.dart'; export 'package:stac/src/parsers/parsers.dart'; export 'package:stac/src/services/services.dart'; export 'package:stac/src/utils/utils.dart'; -export 'package:stac/src/parsers/foundation/layout/parsers.dart'; // Theme exports export 'package:stac_core/stac_core.dart' show StacTheme; From 65fe2df2f72518061399d84ef7901e1b7cbe7106 Mon Sep 17 00:00:00 2001 From: Felipe Sales Date: Wed, 25 Feb 2026 11:53:58 -0300 Subject: [PATCH 24/30] refactor(stac_service, stac_version): streamline platform handling and version management - Removed the deprecated platform identifier normalization logic from stac_service.dart. - Updated platform checks to utilize a new utility function for better consistency. - Simplified the StacVersion class constructor and adjusted platform-specific JSON handling to use the current platform string. - Added export for stac_platforms.dart in utils.dart to support the new platform handling logic. --- .../stac/lib/src/framework/stac_service.dart | 29 +------------ .../stac/lib/src/utils/stac_platforms.dart | 28 +++++++++++++ packages/stac/lib/src/utils/utils.dart | 1 + .../lib/src/utils/version/stac_version.dart | 42 +++++++++---------- .../src/utils/version/stac_version_test.dart | 12 ++++-- 5 files changed, 58 insertions(+), 54 deletions(-) create mode 100644 packages/stac/lib/src/utils/stac_platforms.dart diff --git a/packages/stac/lib/src/framework/stac_service.dart b/packages/stac/lib/src/framework/stac_service.dart index 69caf03e8..55a270187 100644 --- a/packages/stac/lib/src/framework/stac_service.dart +++ b/packages/stac/lib/src/framework/stac_service.dart @@ -19,33 +19,6 @@ import 'package:stac_core/core/stac_options.dart'; import 'package:stac_core/stac_core.dart'; import 'package:stac_logger/stac_logger.dart'; -/// Canonical platform identifiers supported in JSON "platform" field. -/// Incoming values are normalized (e.g. lowercased) and validated against this list. -const List supportedPlatformStrings = [ - 'android', - 'ios', - 'linux', - 'macos', - 'windows', - 'web', -]; - -/// Returns the current platform as a canonical string (one of [supportedPlatformStrings]). -String _currentPlatformString() { - if (kIsWeb) { - return 'web'; - } - - return switch (defaultTargetPlatform) { - TargetPlatform.android => 'android', - TargetPlatform.iOS => 'ios', - TargetPlatform.linux => 'linux', - TargetPlatform.macOS => 'macos', - TargetPlatform.windows => 'windows', - _ => 'unknown', - }; -} - /// Internal service that manages Stac parsers, actions, and rendering. /// /// This service is the core of the Stac framework, responsible for: @@ -248,7 +221,7 @@ class StacService { /// Check if platform is specified and validate if (platform != null) { - final currentPlatform = _currentPlatformString(); + final currentPlatform = currentPlatformString(); final rawList = platform is List ? platform.map((e) => e.toString().toLowerCase()).toList() : [platform.toString().toLowerCase()]; diff --git a/packages/stac/lib/src/utils/stac_platforms.dart b/packages/stac/lib/src/utils/stac_platforms.dart new file mode 100644 index 000000000..aee548d1d --- /dev/null +++ b/packages/stac/lib/src/utils/stac_platforms.dart @@ -0,0 +1,28 @@ +import 'package:flutter/foundation.dart'; + +/// Canonical platform identifiers supported in JSON "platform" field. +/// Incoming values are normalized (e.g. lowercased) and validated against this list. +const List supportedPlatformStrings = [ + 'android', + 'ios', + 'linux', + 'macos', + 'windows', + 'web', +]; + +/// Returns the current platform as a canonical string (one of [supportedPlatformStrings]). +String currentPlatformString() { + if (kIsWeb) { + return 'web'; + } + + return switch (defaultTargetPlatform) { + TargetPlatform.android => 'android', + TargetPlatform.iOS => 'ios', + TargetPlatform.linux => 'linux', + TargetPlatform.macOS => 'macos', + TargetPlatform.windows => 'windows', + _ => 'unknown', + }; +} \ No newline at end of file diff --git a/packages/stac/lib/src/utils/utils.dart b/packages/stac/lib/src/utils/utils.dart index e50c6a120..2b2845558 100644 --- a/packages/stac/lib/src/utils/utils.dart +++ b/packages/stac/lib/src/utils/utils.dart @@ -1,2 +1,3 @@ export 'package:stac/src/utils/color_utils.dart'; export 'package:stac/src/utils/version/stac_version.dart'; +export 'package:stac/src/utils/stac_platforms.dart'; \ No newline at end of file diff --git a/packages/stac/lib/src/utils/version/stac_version.dart b/packages/stac/lib/src/utils/version/stac_version.dart index 5877a6149..0ae407161 100644 --- a/packages/stac/lib/src/utils/version/stac_version.dart +++ b/packages/stac/lib/src/utils/version/stac_version.dart @@ -1,5 +1,3 @@ -import 'dart:io'; - import 'package:stac/stac.dart'; /// # STAC Version Management System @@ -128,10 +126,7 @@ class StacVersion { /// /// [buildNumber] is the reference build number for comparison. /// [condition] defines how the comparison should be performed. - const StacVersion({ - required this.buildNumber, - required this.condition, - }); + const StacVersion({required this.buildNumber, required this.condition}); /// The reference build number used for version comparisons. final int buildNumber; @@ -168,8 +163,9 @@ class StacVersion { /// /// This enables different version requirements per platform while /// maintaining a single configuration source. - static dynamic toPlatformJson(Map json, String key) => - json['${key}_${Platform.operatingSystem}'] ?? json[key]; + static dynamic toPlatformJson(Map json, String key) { + return json['${key}_${currentPlatformString()}'] ?? json[key]; + } } /// Defines the available comparison operators for version conditions. @@ -216,14 +212,14 @@ enum StacConditionVersion { extension StacConditionVersionX on String? { /// Converts a string operator to its corresponding [StacConditionVersion]. StacConditionVersion toStacConditionVersion() => switch (this) { - '>' => StacConditionVersion.greaterThan, - '>=' => StacConditionVersion.greaterThanOrEqual, - '<' => StacConditionVersion.lessThan, - '<=' => StacConditionVersion.lessThanOrEqual, - '==' => StacConditionVersion.equal, - '!=' => StacConditionVersion.notEqual, - _ => StacConditionVersion.notEqual, - }; + '>' => StacConditionVersion.greaterThan, + '>=' => StacConditionVersion.greaterThanOrEqual, + '<' => StacConditionVersion.lessThan, + '<=' => StacConditionVersion.lessThanOrEqual, + '==' => StacConditionVersion.equal, + '!=' => StacConditionVersion.notEqual, + _ => StacConditionVersion.notEqual, + }; } /// Extension to convert [StacConditionVersion] enum values back to string operators. @@ -232,13 +228,13 @@ extension StacConditionVersionX on String? { extension StacConditionVersionToStringX on StacConditionVersion { /// Returns the string representation of the condition operator. String toTypeString() => switch (this) { - StacConditionVersion.greaterThan => '>', - StacConditionVersion.greaterThanOrEqual => '>=', - StacConditionVersion.lessThan => '<', - StacConditionVersion.lessThanOrEqual => '<=', - StacConditionVersion.equal => '==', - StacConditionVersion.notEqual => '!=', - }; + StacConditionVersion.greaterThan => '>', + StacConditionVersion.greaterThanOrEqual => '>=', + StacConditionVersion.lessThan => '<', + StacConditionVersion.lessThanOrEqual => '<=', + StacConditionVersion.equal => '==', + StacConditionVersion.notEqual => '!=', + }; } /// Extension providing version satisfaction checking functionality. diff --git a/packages/stac/test/src/utils/version/stac_version_test.dart b/packages/stac/test/src/utils/version/stac_version_test.dart index fa77289ad..61add769e 100644 --- a/packages/stac/test/src/utils/version/stac_version_test.dart +++ b/packages/stac/test/src/utils/version/stac_version_test.dart @@ -1,10 +1,16 @@ -import 'dart:io'; - import 'package:flutter_test/flutter_test.dart'; +import 'package:flutter/foundation.dart'; import 'package:stac/src/framework/stac_registry.dart'; import 'package:stac/src/utils/version/stac_version.dart'; -String platform = Platform.operatingSystem; +String _platformSuffix() { + if (kIsWeb) { + return 'web'; + } + return defaultTargetPlatform.name.toLowerCase(); +} + +final String platform = _platformSuffix(); void main() { group('StacVersion', () { From 0b4038b5cc6ea81ec4d97a2d8df1b53ad247b73a Mon Sep 17 00:00:00 2001 From: Felipe Sales Date: Wed, 25 Feb 2026 11:58:03 -0300 Subject: [PATCH 25/30] test: update version satisfaction logic for null build number - Changed the test to return true when the app version is null, indicating that the constraint is satisfied in this case. - Renamed a test for clarity, focusing on build number equality handling. - Updated comments for better understanding of platform-specific JSON handling. --- .../test/src/utils/version/stac_version_test.dart | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/stac/test/src/utils/version/stac_version_test.dart b/packages/stac/test/src/utils/version/stac_version_test.dart index 61add769e..76afa2330 100644 --- a/packages/stac/test/src/utils/version/stac_version_test.dart +++ b/packages/stac/test/src/utils/version/stac_version_test.dart @@ -69,13 +69,14 @@ void main() { StacRegistry.instance.registerBuildNumber(2000); }); - test('returns false when app version is null', () { - StacRegistry.instance.registerBuildNumber(null); + test('returns true when app version is null', () { final version = StacVersion( buildNumber: 1000, condition: StacConditionVersion.equal, ); - expect(version.isSatisfied(StacRegistry.instance.buildNumber), false); + // When there is no app build number (e.g. StacRegistry.instance.registerBuildNumber(null)), + // StacVersion.isSatisfied should treat the constraint as satisfied. + expect(version.isSatisfied(null), true); }); test('handles equal condition correctly', () { @@ -147,14 +148,13 @@ void main() { exactVersion.isSatisfied(StacRegistry.instance.buildNumber), true); }); - test('handles version with non-numeric components', () { + test('handles build number equality', () { StacRegistry.instance.registerBuildNumber(2000); final version = StacVersion( buildNumber: 2000, condition: StacConditionVersion.equal, ); - // Non-numeric components should be treated as 0 expect(version.isSatisfied(StacRegistry.instance.buildNumber), true); }); }); @@ -196,7 +196,9 @@ void main() { () { final json = { 'buildNumber': 1500, - 'buildNumber_ios': 2500, // Different platform + // Use a platform key that will not match the current platform, + // so the generic buildNumber is always used. + 'buildNumber_fuchsia': 2500, 'condition': '>=', }; From 05bbf702dca7b0733971a32065556813dee02cd3 Mon Sep 17 00:00:00 2001 From: Felipe Sales Date: Wed, 25 Feb 2026 12:05:58 -0300 Subject: [PATCH 26/30] chore: import --- packages/stac/pubspec.yaml | 4 +--- .../stac/test/src/utils/version/stac_version_test.dart | 8 ++++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/stac/pubspec.yaml b/packages/stac/pubspec.yaml index b04a3a45f..08ae28f77 100644 --- a/packages/stac/pubspec.yaml +++ b/packages/stac/pubspec.yaml @@ -23,9 +23,7 @@ dependencies: cached_network_image: ^3.4.1 flutter_svg: ^2.2.3 stac_logger: ^1.1.0 - # stac_core: ^1.3.0 - stac_core: - path: ../stac_core + stac_core: ^1.3.0 shared_preferences: ^2.5.4 dev_dependencies: diff --git a/packages/stac/test/src/utils/version/stac_version_test.dart b/packages/stac/test/src/utils/version/stac_version_test.dart index 76afa2330..63e3130a6 100644 --- a/packages/stac/test/src/utils/version/stac_version_test.dart +++ b/packages/stac/test/src/utils/version/stac_version_test.dart @@ -49,7 +49,7 @@ void main() { '<': StacConditionVersion.lessThan, '<=': StacConditionVersion.lessThanOrEqual, '==': StacConditionVersion.equal, - 'invalid': StacConditionVersion.notEqual, + 'invalid': StacConditionVersion.greaterThanOrEqual, }; conditions.forEach((input, expected) { @@ -57,10 +57,10 @@ void main() { }); }); - test('handles null input by returning notEqual', () { + test('handles null input by returning greaterThanOrEqual', () { String? nullString; - expect( - nullString.toStacConditionVersion(), StacConditionVersion.notEqual); + expect(nullString.toStacConditionVersion(), + StacConditionVersion.greaterThanOrEqual); }); }); From 10fff993c6d02b1ca5112021c6350252d44f97e4 Mon Sep 17 00:00:00 2001 From: Felipe Sales Date: Wed, 25 Feb 2026 12:13:40 -0300 Subject: [PATCH 27/30] build: version --- packages/stac/pubspec.yaml | 7 ++++++- packages/stac_webview/pubspec.yaml | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/stac/pubspec.yaml b/packages/stac/pubspec.yaml index 08ae28f77..68d79859b 100644 --- a/packages/stac/pubspec.yaml +++ b/packages/stac/pubspec.yaml @@ -23,7 +23,12 @@ dependencies: cached_network_image: ^3.4.1 flutter_svg: ^2.2.3 stac_logger: ^1.1.0 - stac_core: ^1.3.0 + # when approved, use stac_core: ^1.3.x + stac_core: + git: + url: https://github.com/SuaMusica/stac.git + path: packages/stac_core + ref: devall shared_preferences: ^2.5.4 dev_dependencies: diff --git a/packages/stac_webview/pubspec.yaml b/packages/stac_webview/pubspec.yaml index 5d3867fd6..6253f50a3 100644 --- a/packages/stac_webview/pubspec.yaml +++ b/packages/stac_webview/pubspec.yaml @@ -19,7 +19,12 @@ dependencies: sdk: flutter webview_flutter: ^4.13.1 json_annotation: ^4.10.0 - stac_core: ^1.3.0 + # when approved, use stac_core: ^1.3.x + stac_core: + git: + url: https://github.com/SuaMusica/stac.git + path: packages/stac_core + ref: devall stac_framework: ^1.0.0 dev_dependencies: From bd752b61cc749e45917c104d59b2b2ea55653db9 Mon Sep 17 00:00:00 2001 From: Felipe Sales Date: Wed, 25 Feb 2026 12:21:42 -0300 Subject: [PATCH 28/30] test: fix default value --- packages/stac/test/src/utils/version/stac_version_test.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/stac/test/src/utils/version/stac_version_test.dart b/packages/stac/test/src/utils/version/stac_version_test.dart index 63e3130a6..8306295cb 100644 --- a/packages/stac/test/src/utils/version/stac_version_test.dart +++ b/packages/stac/test/src/utils/version/stac_version_test.dart @@ -49,7 +49,7 @@ void main() { '<': StacConditionVersion.lessThan, '<=': StacConditionVersion.lessThanOrEqual, '==': StacConditionVersion.equal, - 'invalid': StacConditionVersion.greaterThanOrEqual, + 'invalid': StacConditionVersion.notEqual, }; conditions.forEach((input, expected) { @@ -57,10 +57,10 @@ void main() { }); }); - test('handles null input by returning greaterThanOrEqual', () { + test('handles null input by returning notEqual', () { String? nullString; expect(nullString.toStacConditionVersion(), - StacConditionVersion.greaterThanOrEqual); + StacConditionVersion.notEqual); }); }); From b86190c36aca68a39c272fbf5212bf7db698a9fc Mon Sep 17 00:00:00 2001 From: Felipe Sales Date: Wed, 25 Feb 2026 15:34:19 -0300 Subject: [PATCH 29/30] platform and ci run --- .../stac/test/src/utils/version/stac_version_test.dart | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/packages/stac/test/src/utils/version/stac_version_test.dart b/packages/stac/test/src/utils/version/stac_version_test.dart index 8306295cb..59c77261a 100644 --- a/packages/stac/test/src/utils/version/stac_version_test.dart +++ b/packages/stac/test/src/utils/version/stac_version_test.dart @@ -2,15 +2,9 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:flutter/foundation.dart'; import 'package:stac/src/framework/stac_registry.dart'; import 'package:stac/src/utils/version/stac_version.dart'; +import 'package:stac/src/utils/stac_platforms.dart'; -String _platformSuffix() { - if (kIsWeb) { - return 'web'; - } - return defaultTargetPlatform.name.toLowerCase(); -} - -final String platform = _platformSuffix(); +final String platform = currentPlatformString(); void main() { group('StacVersion', () { From d82e83f2ac55d30f4febaeeddc4a054aa4b5edb2 Mon Sep 17 00:00:00 2001 From: Felipe Sales Date: Thu, 26 Feb 2026 14:15:58 -0300 Subject: [PATCH 30/30] feat(stac_text_span): enhance JSON parsing for text content - Introduced a utility function to read span content from either text or data keys in JSON. - Updated the StacTextSpan class to accept text from both keys, improving flexibility in JSON handling. - Enhanced documentation to clarify the new behavior for JSON input.update ref --- .../foundation/text/stac_text_span/stac_text_span.dart | 10 ++++++++++ .../text/stac_text_span/stac_text_span.g.dart | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/stac_core/lib/foundation/text/stac_text_span/stac_text_span.dart b/packages/stac_core/lib/foundation/text/stac_text_span/stac_text_span.dart index b3619d5d8..02b76b010 100644 --- a/packages/stac_core/lib/foundation/text/stac_text_span/stac_text_span.dart +++ b/packages/stac_core/lib/foundation/text/stac_text_span/stac_text_span.dart @@ -4,6 +4,10 @@ import 'package:stac_core/foundation/text/stac_text_style/stac_text_style.dart'; part 'stac_text_span.g.dart'; +/// Used by [StacTextSpan] to read span content from either `"text"` or `"data"`. +String? _readTextOrData(Map map, String key) => + map['text'] ?? map['data']; + /// A Stac representation of an immutable span of text. /// /// This class defines a piece of text with associated styling and @@ -33,6 +37,9 @@ part 'stac_text_span.g.dart'; /// ] /// } /// ``` +/// +/// The span text can be provided as either `"text"` or `"data"` in JSON; +/// both keys are accepted. /// {@end-tool} @JsonSerializable() class StacTextSpan implements StacElement { @@ -40,6 +47,7 @@ class StacTextSpan implements StacElement { StacTextSpan({this.text, this.style, this.children = const [], this.onTap}); /// The text content of this span. + @JsonKey(readValue: _readTextOrData) final String? text; /// The style to apply to the text in this span. @@ -55,6 +63,8 @@ class StacTextSpan implements StacElement { Map toJson() => _$StacTextSpanToJson(this); /// Creates a [StacTextSpan] from a JSON map. + /// + /// Accepts the span content under either `"text"` or `"data"` via @JsonKey(readValue: ...). factory StacTextSpan.fromJson(Map json) => _$StacTextSpanFromJson(json); } diff --git a/packages/stac_core/lib/foundation/text/stac_text_span/stac_text_span.g.dart b/packages/stac_core/lib/foundation/text/stac_text_span/stac_text_span.g.dart index 4bb088238..5f33ac950 100644 --- a/packages/stac_core/lib/foundation/text/stac_text_span/stac_text_span.g.dart +++ b/packages/stac_core/lib/foundation/text/stac_text_span/stac_text_span.g.dart @@ -7,7 +7,7 @@ part of 'stac_text_span.dart'; // ************************************************************************** StacTextSpan _$StacTextSpanFromJson(Map json) => StacTextSpan( - text: json['text'] as String?, + text: _readTextOrData(json, 'text') as String?, style: json['style'] == null ? null : StacTextStyle.fromJson(json['style']), children: (json['children'] as List?)