├── yx_scope ├── packages │ ├── yx_scope │ │ ├── example │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── CHANGELOG.md │ │ │ ├── analysis_options.yaml │ │ │ ├── pubspec.yaml │ │ │ ├── .metadata │ │ │ ├── bin │ │ │ │ └── deprecated_listeners_main.dart │ │ │ └── .gitignore │ │ ├── analysis_options.yaml │ │ ├── doc │ │ │ └── assets │ │ │ │ └── scope_anatomy.png │ │ ├── screenshots │ │ │ └── yx_scope_logo.webp │ │ ├── lib │ │ │ ├── src │ │ │ │ ├── test_utils │ │ │ │ │ ├── scope_container_test_utils.dart │ │ │ │ │ └── scope_state_test_utils.dart │ │ │ │ ├── monitoring │ │ │ │ │ ├── listeners.dart │ │ │ │ │ ├── models │ │ │ │ │ │ ├── value_meta.dart │ │ │ │ │ │ ├── dep_id.dart │ │ │ │ │ │ ├── scope_id.dart │ │ │ │ │ │ └── scope_meta.dart │ │ │ │ │ ├── scope_observatory_internal.dart │ │ │ │ │ ├── scope_observatory.dart │ │ │ │ │ ├── raw_listeners.dart │ │ │ │ │ ├── observers.dart │ │ │ │ │ └── raw_observers.dart │ │ │ │ ├── core │ │ │ │ │ ├── scope_exception.dart │ │ │ │ │ ├── async_lifecycle.dart │ │ │ │ │ └── scope_state.dart │ │ │ │ ├── scope_state_streamable.dart │ │ │ │ ├── scope_module.dart │ │ │ │ └── scope_container.dart │ │ │ ├── advanced.dart │ │ │ └── yx_scope.dart │ │ ├── .metadata │ │ ├── test │ │ │ ├── utils │ │ │ │ ├── utils.dart │ │ │ │ └── test_logger.dart │ │ │ ├── scope_state_test.dart │ │ │ └── deprecated_listeners_test.dart │ │ ├── pubspec.yaml │ │ ├── .gitignore │ │ ├── LICENSE │ │ └── CHANGELOG.md │ ├── yx_scope_linter │ │ ├── example │ │ │ ├── LICENSE │ │ │ ├── CHANGELOG.md │ │ │ ├── analysis_options.yaml │ │ │ ├── pubspec_overrides.yaml │ │ │ ├── pubspec.yaml │ │ │ ├── .metadata │ │ │ ├── lib │ │ │ │ ├── use_async_dep_for_async_lifecycle.dart │ │ │ │ ├── utils.dart │ │ │ │ ├── consider_dep_suffix.dart │ │ │ │ ├── pass_async_lifecycle_in_initialize_queue.dart │ │ │ │ ├── final_dep.dart │ │ │ │ └── avoid_async_dep_child_scope.dart │ │ │ ├── .gitignore │ │ │ └── README.md │ │ ├── analysis_options.yaml │ │ ├── pubspec_overrides.yaml │ │ ├── lib │ │ │ ├── src │ │ │ │ ├── names.dart │ │ │ │ ├── models │ │ │ │ │ ├── exceptions.dart │ │ │ │ │ └── dep.dart │ │ │ │ ├── priority.dart │ │ │ │ ├── extensions.dart │ │ │ │ ├── plugin.dart │ │ │ │ ├── types.dart │ │ │ │ ├── yx_scope_registry.dart │ │ │ │ ├── utils.dart │ │ │ │ ├── visitors │ │ │ │ │ ├── parse_visitor.dart │ │ │ │ │ ├── parse_initialize_queue_visitor.dart │ │ │ │ │ └── parse_scope_declaration_visitor.dart │ │ │ │ ├── lints │ │ │ │ │ ├── pass_async_lifecycle_in_initialize_queue.dart │ │ │ │ │ ├── avoid_async_dep_child_scope.dart │ │ │ │ │ ├── consider_dep_suffix.dart │ │ │ │ │ ├── dep_cycle.dart │ │ │ │ │ └── final_dep.dart │ │ │ │ ├── yx_scope_lint_rule.dart │ │ │ │ └── resolved_yx_scope_result.dart │ │ │ └── yx_scope_linter.dart │ │ ├── .metadata │ │ ├── pubspec.yaml │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── LICENSE │ │ └── doc │ │ │ └── manual_linter.md │ └── yx_scope_flutter │ │ ├── pubspec_overrides.yaml │ │ ├── test │ │ ├── test_utils.dart │ │ └── scope_listener_test.dart │ │ ├── example │ │ ├── lib │ │ │ ├── domain │ │ │ │ ├── auth │ │ │ │ │ ├── models │ │ │ │ │ │ ├── account.dart │ │ │ │ │ │ └── account_params.dart │ │ │ │ │ ├── register_state_holder.dart │ │ │ │ │ ├── account_holder.dart │ │ │ │ │ ├── register_manager.dart │ │ │ │ │ └── auth_manager.dart │ │ │ │ ├── order │ │ │ │ │ ├── online_order_state_holder.dart │ │ │ │ │ ├── models │ │ │ │ │ │ └── order.dart │ │ │ │ │ ├── order_position_manager.dart │ │ │ │ │ ├── orders_state_holder.dart │ │ │ │ │ ├── order_manager.dart │ │ │ │ │ └── accept_order_manager.dart │ │ │ │ ├── map │ │ │ │ │ ├── map_factory.dart │ │ │ │ │ └── map_manager.dart │ │ │ │ └── order_navigation │ │ │ │ │ └── order_navigation_delegate.dart │ │ │ ├── utils │ │ │ │ └── logger.dart │ │ │ ├── data │ │ │ │ ├── orders │ │ │ │ │ ├── models │ │ │ │ │ │ └── incoming_order_data.dart │ │ │ │ │ └── incoming_orders_provider.dart │ │ │ │ └── map │ │ │ │ │ ├── map.dart │ │ │ │ │ └── map_widget.dart │ │ │ ├── router │ │ │ │ ├── models │ │ │ │ │ └── app_state.dart │ │ │ │ ├── app_state_observer.dart │ │ │ │ └── router_delegate.dart │ │ │ ├── di │ │ │ │ ├── register │ │ │ │ │ └── register_scope.dart │ │ │ │ ├── app │ │ │ │ │ └── app_scope.dart │ │ │ │ ├── online_order │ │ │ │ │ └── online_scope.dart │ │ │ │ ├── map │ │ │ │ │ └── map_scope.dart │ │ │ │ └── map_navigation │ │ │ │ │ └── map_navigation_scope.dart │ │ │ ├── ui │ │ │ │ ├── auth │ │ │ │ │ └── auth_page.dart │ │ │ │ ├── account │ │ │ │ │ └── account_page.dart │ │ │ │ ├── register │ │ │ │ │ └── register_page.dart │ │ │ │ ├── orders │ │ │ │ │ └── accept_order_wrapper.dart │ │ │ │ └── tabbar │ │ │ │ │ └── tabbar_page.dart │ │ │ └── main.dart │ │ ├── pubspec_overrides.yaml │ │ ├── pubspec.yaml │ │ ├── analysis_options.yaml │ │ ├── README.md │ │ ├── .gitignore │ │ └── .metadata │ │ ├── lib │ │ ├── src │ │ │ ├── scope_widget_listener.dart │ │ │ ├── scope_widget_builder.dart │ │ │ └── core │ │ │ │ ├── scope_error.dart │ │ │ │ └── provider.dart │ │ └── yx_scope_flutter.dart │ │ ├── analysis_options.yaml │ │ ├── .metadata │ │ ├── pubspec.yaml │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── LICENSE │ │ └── README.md ├── assets │ └── logos │ │ └── yx_scope.webp ├── docs │ └── assets │ │ ├── scope_tree.png │ │ ├── scope_types.png │ │ └── scope_anatomy.png ├── AUTHORS ├── .gitignore ├── LICENSE ├── README.md └── CONTRIBUTING.md ├── yx_state ├── packages │ ├── yx_state │ │ ├── CHANGELOG.md │ │ ├── analysis_options.yaml │ │ ├── screenshots │ │ │ └── yx_state_logo.webp │ │ ├── AUTHORS │ │ ├── lib │ │ │ ├── yx_state.dart │ │ │ └── src │ │ │ │ ├── state_manager.dart │ │ │ │ ├── test_util │ │ │ │ └── state_manager_base_test_util.dart │ │ │ │ ├── mixin │ │ │ │ └── state_manager_listener_mixin.dart │ │ │ │ └── function_stream_handler │ │ │ │ └── handle_task_emitter.dart │ │ ├── test │ │ │ └── src │ │ │ │ └── mocks.dart │ │ ├── pubspec.yaml │ │ ├── .gitignore │ │ ├── LICENSE │ │ ├── CONTRIBUTING.md │ │ └── example │ │ │ └── main.dart │ ├── yx_state_flutter │ │ ├── CHANGELOG.md │ │ ├── example │ │ │ ├── README.md │ │ │ ├── analysis_options.yaml │ │ │ ├── pubspec_overrides.yaml │ │ │ ├── pubspec.yaml │ │ │ ├── .gitignore │ │ │ └── lib │ │ │ │ └── main.dart │ │ ├── analysis_options.yaml │ │ ├── pubspec_overrides.yaml │ │ ├── lib │ │ │ ├── yx_state_flutter.dart │ │ │ └── src │ │ │ │ └── typedefs.dart │ │ ├── AUTHORS │ │ ├── pubspec.yaml │ │ ├── .gitignore │ │ ├── LICENSE │ │ ├── CONTRIBUTING.md │ │ └── README.md │ └── yx_state_transformers │ │ ├── CHANGELOG.md │ │ ├── analysis_options.yaml │ │ ├── pubspec_overrides.yaml │ │ ├── lib │ │ ├── yx_state_transformers.dart │ │ └── src │ │ │ └── function_handlers.dart │ │ ├── AUTHORS │ │ ├── pubspec.yaml │ │ ├── .gitignore │ │ ├── LICENSE │ │ ├── example │ │ └── main.dart │ │ ├── README.md │ │ └── CONTRIBUTING.md ├── assets │ └── logos │ │ └── yx_state.webp ├── README.md ├── AUTHORS ├── .gitignore ├── LICENSE ├── CONTRIBUTING.md └── analysis_options.yaml ├── .piglet-meta.json ├── README.md ├── .github └── workflows │ └── yx_scope_pr.yaml └── CONTRIBUTING.md /yx_scope/packages/yx_scope/example/LICENSE: -------------------------------------------------------------------------------- 1 | TODO: Add your license here. 2 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope/example/README.md: -------------------------------------------------------------------------------- 1 | ## An example for yx_scope 2 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_linter/example/LICENSE: -------------------------------------------------------------------------------- 1 | TODO: Add your license here. 2 | -------------------------------------------------------------------------------- /yx_state/packages/yx_state/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.0.0 2 | 3 | - Initial version. 4 | -------------------------------------------------------------------------------- /yx_state/packages/yx_state_flutter/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.0.0 2 | 3 | - Initial version. 4 | -------------------------------------------------------------------------------- /.piglet-meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "project":"city-services-pub", 3 | "repository":"arcadia" 4 | } -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:lints/recommended.yaml 2 | -------------------------------------------------------------------------------- /yx_state/packages/yx_state/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: ../../analysis_options.yaml 2 | -------------------------------------------------------------------------------- /yx_state/packages/yx_state_flutter/example/README.md: -------------------------------------------------------------------------------- 1 | # An example for yx_state_flutter 2 | -------------------------------------------------------------------------------- /yx_state/packages/yx_state_transformers/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.0.0 2 | 3 | - Initial version. 4 | -------------------------------------------------------------------------------- /yx_state/packages/yx_state_flutter/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: ../../analysis_options.yaml 2 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope/example/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.0.1 2 | 3 | * TODO: Describe initial release. 4 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope/example/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:lints/recommended.yaml 2 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_linter/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:lints/recommended.yaml 2 | -------------------------------------------------------------------------------- /yx_state/packages/yx_state_transformers/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: ../../analysis_options.yaml 2 | -------------------------------------------------------------------------------- /yx_state/packages/yx_state_flutter/example/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: ../../../analysis_options.yaml 2 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_linter/example/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.0.1 2 | 3 | * TODO: Describe initial release. 4 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_linter/example/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | analyzer: 2 | plugins: 3 | - custom_lint 4 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/pubspec_overrides.yaml: -------------------------------------------------------------------------------- 1 | dependency_overrides: 2 | yx_scope: 3 | path: ../yx_scope 4 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_linter/pubspec_overrides.yaml: -------------------------------------------------------------------------------- 1 | dependency_overrides: 2 | yx_scope: 3 | path: ../yx_scope 4 | -------------------------------------------------------------------------------- /yx_state/packages/yx_state_flutter/pubspec_overrides.yaml: -------------------------------------------------------------------------------- 1 | dependency_overrides: 2 | yx_state: 3 | path: ../yx_state 4 | -------------------------------------------------------------------------------- /yx_scope/assets/logos/yx_scope.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yandex/city-services-pub/HEAD/yx_scope/assets/logos/yx_scope.webp -------------------------------------------------------------------------------- /yx_scope/docs/assets/scope_tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yandex/city-services-pub/HEAD/yx_scope/docs/assets/scope_tree.png -------------------------------------------------------------------------------- /yx_scope/docs/assets/scope_types.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yandex/city-services-pub/HEAD/yx_scope/docs/assets/scope_types.png -------------------------------------------------------------------------------- /yx_state/assets/logos/yx_state.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yandex/city-services-pub/HEAD/yx_state/assets/logos/yx_state.webp -------------------------------------------------------------------------------- /yx_state/packages/yx_state_transformers/pubspec_overrides.yaml: -------------------------------------------------------------------------------- 1 | dependency_overrides: 2 | yx_state: 3 | path: ../yx_state 4 | -------------------------------------------------------------------------------- /yx_scope/docs/assets/scope_anatomy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yandex/city-services-pub/HEAD/yx_scope/docs/assets/scope_anatomy.png -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_linter/example/pubspec_overrides.yaml: -------------------------------------------------------------------------------- 1 | dependency_overrides: 2 | yx_scope: 3 | path: ../../yx_scope 4 | -------------------------------------------------------------------------------- /yx_state/packages/yx_state_flutter/example/pubspec_overrides.yaml: -------------------------------------------------------------------------------- 1 | dependency_overrides: 2 | yx_state: 3 | path: ../../yx_state 4 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/test/test_utils.dart: -------------------------------------------------------------------------------- 1 | class CounterProvider { 2 | int count; 3 | 4 | CounterProvider(this.count); 5 | } 6 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope/doc/assets/scope_anatomy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yandex/city-services-pub/HEAD/yx_scope/packages/yx_scope/doc/assets/scope_anatomy.png -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_linter/lib/src/names.dart: -------------------------------------------------------------------------------- 1 | class MethodNames { 2 | static const initializeQueue = 'initializeQueue'; 3 | 4 | const MethodNames._(); 5 | } 6 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope/screenshots/yx_scope_logo.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yandex/city-services-pub/HEAD/yx_scope/packages/yx_scope/screenshots/yx_scope_logo.webp -------------------------------------------------------------------------------- /yx_state/packages/yx_state/screenshots/yx_state_logo.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yandex/city-services-pub/HEAD/yx_state/packages/yx_state/screenshots/yx_state_logo.webp -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/example/lib/domain/auth/models/account.dart: -------------------------------------------------------------------------------- 1 | class Account { 2 | final String uid; 3 | final String name; 4 | 5 | Account(this.uid, this.name); 6 | } 7 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_linter/lib/src/models/exceptions.dart: -------------------------------------------------------------------------------- 1 | class ScopeLinterError implements Exception { 2 | final String message; 3 | 4 | const ScopeLinterError(this.message); 5 | } 6 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/lib/src/scope_widget_listener.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | typedef ScopeWidgetListener = void Function(BuildContext context, T? scope); 4 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_linter/lib/src/priority.dart: -------------------------------------------------------------------------------- 1 | enum FixPriority { 2 | useAsyncDepForAsyncLifecycle, 3 | finalDep, 4 | considerDepSuffix; 5 | 6 | int get value => 100 - index; 7 | } 8 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/example/pubspec_overrides.yaml: -------------------------------------------------------------------------------- 1 | dependency_overrides: 2 | yx_scope: 3 | path: ../../yx_scope 4 | yx_scope_linter: 5 | path: ../../yx_scope_linter 6 | hotreloader: 4.3.0 7 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:flutter_lints/flutter.yaml 2 | 3 | # Additional information about this file can be found at 4 | # https://dart.dev/guides/language/analysis-options 5 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/example/lib/domain/auth/models/account_params.dart: -------------------------------------------------------------------------------- 1 | class AccountParams { 2 | final String login; 3 | final String password; 4 | 5 | AccountParams(this.login, this.password); 6 | } 7 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/example/lib/domain/order/online_order_state_holder.dart: -------------------------------------------------------------------------------- 1 | abstract class OnlineOrderStateHolder { 2 | bool get isOnline; 3 | Stream get isOnlineStream; 4 | Future toggle(); 5 | } 6 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/example/lib/domain/map/map_factory.dart: -------------------------------------------------------------------------------- 1 | import '../../data/map/map.dart'; 2 | 3 | abstract class MapInitializer { 4 | Future createMap(MapController mapController); 5 | 6 | Future dropMap(); 7 | } 8 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_linter/lib/yx_scope_linter.dart: -------------------------------------------------------------------------------- 1 | library yx_scope_linter; 2 | 3 | import 'package:custom_lint_builder/custom_lint_builder.dart'; 4 | 5 | import 'src/plugin.dart'; 6 | 7 | PluginBase createPlugin() => YXScopeLintsPlugin(); 8 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/example/lib/domain/auth/register_state_holder.dart: -------------------------------------------------------------------------------- 1 | abstract class RegisterHolder { 2 | bool get inProgress; 3 | 4 | Stream get inProgressStream; 5 | 6 | Future startRegister(); 7 | 8 | Future stopRegister(); 9 | } 10 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/example/lib/domain/order/models/order.dart: -------------------------------------------------------------------------------- 1 | import '../../../data/orders/models/incoming_order_data.dart'; 2 | 3 | class Order { 4 | final IncomingOrder incomingOrder; 5 | 6 | Order(this.incomingOrder); 7 | 8 | String get uid => incomingOrder.uid; 9 | } 10 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/lib/src/scope_widget_builder.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | typedef ScopeWidgetBuilder = Widget Function(BuildContext context, T? scope); 4 | 5 | typedef NonNullableScopeWidgetBuilder = Widget Function( 6 | BuildContext context, 7 | T scope, 8 | ); 9 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope/example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: yx_scope_example 2 | description: "An example for yx_scope" 3 | version: 1.0.0 4 | publish_to: 'none' 5 | 6 | environment: 7 | sdk: ">=2.19.0 <4.0.0" 8 | 9 | dependencies: 10 | yx_scope: 11 | path: .. 12 | 13 | dev_dependencies: 14 | lints: ^2.0.1 15 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/lib/yx_scope_flutter.dart: -------------------------------------------------------------------------------- 1 | library yx_scope_flutter; 2 | 3 | export 'src/scope_builder.dart'; 4 | export 'src/scope_consumer.dart'; 5 | export 'src/scope_listener.dart'; 6 | export 'src/scope_provider.dart'; 7 | export 'src/scope_widget_builder.dart'; 8 | export 'src/core/scope_error.dart'; 9 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/example/lib/domain/auth/account_holder.dart: -------------------------------------------------------------------------------- 1 | import 'models/account.dart'; 2 | 3 | abstract class AccountHolder { 4 | Account? get account; 5 | 6 | Stream get accountStream; 7 | 8 | Future setAccount(Account account); 9 | 10 | Future dropAccount(); 11 | } 12 | -------------------------------------------------------------------------------- /yx_state/packages/yx_state_flutter/lib/yx_state_flutter.dart: -------------------------------------------------------------------------------- 1 | /// Flutter widgets for [yx_state](https://pub.dev/packages/yx_state). 2 | library yx_state_flutter; 3 | 4 | export 'src/state_builder.dart'; 5 | export 'src/state_consumer.dart'; 6 | export 'src/state_listener.dart'; 7 | export 'src/state_selector.dart'; 8 | export 'src/typedefs.dart'; 9 | -------------------------------------------------------------------------------- /yx_state/packages/yx_state_transformers/lib/yx_state_transformers.dart: -------------------------------------------------------------------------------- 1 | /// Custom task transformers for [yx_state](https://pub.dev/packages/yx_state). 2 | /// 3 | /// This package provides various concurrency strategies for handling tasks 4 | /// in state management operations. 5 | library yx_state_transformers; 6 | 7 | export 'src/function_handlers.dart'; 8 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope/lib/src/test_utils/scope_container_test_utils.dart: -------------------------------------------------------------------------------- 1 | part of '../base_scope_container.dart'; 2 | 3 | class ScopeContainerTestUtils { 4 | /// Count of mounted providers. After dispose provider will be unmount. 5 | @visibleForTesting 6 | static int getDepCount(BaseScopeContainer container) => 7 | container._container.length; 8 | } 9 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/example/lib/domain/order/order_position_manager.dart: -------------------------------------------------------------------------------- 1 | import 'models/order.dart'; 2 | 3 | class OrderPositionHolder { 4 | int position; 5 | 6 | OrderPositionHolder(Order order) 7 | : position = order.incomingOrder.fromAddress.position; 8 | 9 | void updatePosition(int position) => this.position = position; 10 | } 11 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope/lib/advanced.dart: -------------------------------------------------------------------------------- 1 | export 'src/monitoring/raw_observers.dart'; 2 | export 'src/base_scope_container.dart' 3 | show 4 | CustomDep, 5 | CustomAsyncDep, 6 | DepBehavior, 7 | AsyncDepBehavior, 8 | DepAccess, 9 | AsyncDepAccess, 10 | CoreDepBehavior, 11 | CoreAsyncDepBehavior; 12 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: cfa03f886884e42676f1834864657721a1219624 8 | channel: stable 9 | 10 | project_type: package 11 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_linter/example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: example 2 | description: An example for yx_scope_linter rules 3 | version: 1.0.0 4 | publish_to: 'none' 5 | 6 | environment: 7 | sdk: '>=2.18.0 <4.0.0' 8 | 9 | dependencies: 10 | yx_scope: ^1.0.0 11 | 12 | dev_dependencies: 13 | lints: ^2.0.0 14 | custom_lint: ^0.6.8 15 | yx_scope_linter: 16 | path: .. 17 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope/example/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: "4cf269e36de2573851eaef3c763994f8f9be494d" 8 | channel: "stable" 9 | 10 | project_type: package 11 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: e2874b6e235bcae425ed3436c113ab4ac50ac707 8 | channel: unknown 9 | 10 | project_type: package 11 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_linter/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: "2f708eb8396e362e280fac22cf171c2cb467343c" 8 | channel: "stable" 9 | 10 | project_type: package 11 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_linter/example/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: "2f708eb8396e362e280fac22cf171c2cb467343c" 8 | channel: "stable" 9 | 10 | project_type: package 11 | -------------------------------------------------------------------------------- /yx_state/README.md: -------------------------------------------------------------------------------- 1 | # yx_state packages 2 | 3 | State management library for Dart / Flutter. 4 | 5 | ## Library Components 6 | 7 | The library group currently consists of: 8 | 9 | - **[yx_state](packages/yx_state)**: The core library for state management 10 | - **[yx_state_flutter](packages/yx_state_flutter)**: Flutter widgets for yx_state 11 | - **[yx_state_transformers](packages/yx_state_transformers)**: Transformers for yx_state 12 | -------------------------------------------------------------------------------- /yx_scope/AUTHORS: -------------------------------------------------------------------------------- 1 | The following authors have created the source code of "yx_scope" published and distributed by YANDEX LLC as the owner: 2 | kltsv 3 | FedorZavalnyj 4 | wupididu 5 | 6 | The following authors have licensed their contributions to YANDEX LLC and everyone who uses "yx_scope" under the licensing terms detailed in LICENSE available at https://github.com/yandex/yx_scope/blob/main/LICENSE. 7 | kltsv 8 | FedorZavalnyj 9 | wupididu 10 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope/lib/src/monitoring/listeners.dart: -------------------------------------------------------------------------------- 1 | part of 'observers.dart'; 2 | 3 | @Deprecated('Use ScopeObserver instead') 4 | abstract class ScopeListener implements ScopeObserver {} 5 | 6 | @Deprecated('Use DepObserver instead') 7 | abstract class DepListener implements DepObserver {} 8 | 9 | @Deprecated('Use AsyncDepObserver instead') 10 | abstract class AsyncDepListener extends DepListener 11 | implements AsyncDepObserver {} 12 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope/lib/src/test_utils/scope_state_test_utils.dart: -------------------------------------------------------------------------------- 1 | part of '../base_scope_container.dart'; 2 | 3 | /// The class that has access to private fields of the given [ScopeStateHolder] 4 | @visibleForTesting 5 | class TestableScopeStateHolder { 6 | final ScopeStateHolder holder; 7 | 8 | @visibleForTesting 9 | const TestableScopeStateHolder(this.holder); 10 | 11 | LinkedList> get listeners => holder._listeners; 12 | } 13 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope/lib/src/monitoring/models/value_meta.dart: -------------------------------------------------------------------------------- 1 | /// A ready-only meta info of created value in a [Dep]. 2 | class ValueMeta { 3 | final Type valueType; 4 | final int valueHashCode; 5 | 6 | const ValueMeta(this.valueType, this.valueHashCode); 7 | 8 | static ValueMeta? build(Object? object) => 9 | object == null ? null : ValueMeta(object.runtimeType, object.hashCode); 10 | 11 | @override 12 | String toString() => '$valueType[$valueHashCode]'; 13 | } 14 | -------------------------------------------------------------------------------- /yx_state/AUTHORS: -------------------------------------------------------------------------------- 1 | The following authors have created the source code of "yx_state" published and distributed by YANDEX LLC as the owner: 2 | kltsv 3 | guid-empty 4 | Vorkytaka 5 | sorokinDev 6 | wwwhttpru 7 | 8 | The following authors have licensed their contributions to YANDEX LLC and everyone who uses "yx_state" under the licensing terms detailed in LICENSE available at https://github.com/yandex/yx_state/blob/main/LICENSE. 9 | kltsv 10 | guid-empty 11 | Vorkytaka 12 | sorokinDev 13 | wwwhttpru 14 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope/lib/src/monitoring/models/dep_id.dart: -------------------------------------------------------------------------------- 1 | /// A ready-only unique identifier of a [Dep]. 2 | /// An instance of DepId is always the same for 3 | /// the same Dep instance. 4 | class DepId { 5 | final Type valueType; 6 | final int depHashCode; 7 | final String? name; 8 | 9 | const DepId(this.valueType, this.depHashCode, this.name); 10 | 11 | @override 12 | String toString() => 13 | '${name ?? valueType}${name != null ? '[$valueType]' : ''}[$depHashCode]'; 14 | } 15 | -------------------------------------------------------------------------------- /yx_state/packages/yx_state_flutter/example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: yx_state_flutter_example 2 | description: "An example for yx_state_flutter" 3 | version: 1.0.0 4 | publish_to: 'none' 5 | 6 | environment: 7 | sdk: ">=2.19.0 <4.0.0" 8 | 9 | dependencies: 10 | flutter: 11 | sdk: flutter 12 | yx_state: 13 | path: ../../yx_state 14 | yx_state_flutter: 15 | path: ../ 16 | 17 | dev_dependencies: 18 | flutter_test: 19 | sdk: flutter 20 | 21 | flutter: 22 | uses-material-design: true 23 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/example/lib/domain/order/orders_state_holder.dart: -------------------------------------------------------------------------------- 1 | import 'models/order.dart'; 2 | import 'order_manager.dart'; 3 | 4 | abstract class OrdersStateHolder implements OrdersStates, OrdersHandler {} 5 | 6 | abstract class OrdersStates { 7 | Stream> get ordersStream; 8 | 9 | List get orders; 10 | } 11 | 12 | abstract class OrdersHandler { 13 | Future addOrder(Order order); 14 | 15 | Future removeOrder(Order order); 16 | } 17 | -------------------------------------------------------------------------------- /yx_state/packages/yx_state/AUTHORS: -------------------------------------------------------------------------------- 1 | The following authors have created the source code of "yx_state" published and distributed by YANDEX LLC as the owner: 2 | kltsv 3 | guid-empty 4 | Vorkytaka 5 | sorokinDev 6 | wwwhttpru 7 | 8 | The following authors have licensed their contributions to YANDEX LLC and everyone who uses "yx_state" under the licensing terms detailed in LICENSE available at https://github.com/yandex/yx_state/blob/main/LICENSE. 9 | kltsv 10 | guid-empty 11 | Vorkytaka 12 | sorokinDev 13 | wwwhttpru 14 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope/lib/src/monitoring/models/scope_id.dart: -------------------------------------------------------------------------------- 1 | /// A ready-only unique identifier of a [ScopeContainer]. 2 | /// An instance of ScopeId is always the same for 3 | /// the same [ScopeContainer] instance. 4 | class ScopeId { 5 | final Type type; 6 | final int scopeHashCode; 7 | final String? name; 8 | 9 | const ScopeId(this.type, this.scopeHashCode, this.name); 10 | 11 | @override 12 | String toString() => 13 | '${name ?? type}${name != null ? '[$type]' : ''}[$scopeHashCode]'; 14 | } 15 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope/test/utils/utils.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:test/test.dart'; 4 | 5 | Future expectThrown( 6 | FutureOr Function() callback, 7 | ) async { 8 | try { 9 | await callback(); 10 | fail('There must be $T thrown'); 11 | // ignore: avoid_catching_errors 12 | } on T catch (_) {} 13 | } 14 | 15 | Future expectAssertion( 16 | FutureOr Function() callback, 17 | ) async => 18 | expectThrown(callback); 19 | -------------------------------------------------------------------------------- /yx_state/packages/yx_state_flutter/AUTHORS: -------------------------------------------------------------------------------- 1 | The following authors have created the source code of "yx_state" published and distributed by YANDEX LLC as the owner: 2 | kltsv 3 | guid-empty 4 | Vorkytaka 5 | sorokinDev 6 | wwwhttpru 7 | 8 | The following authors have licensed their contributions to YANDEX LLC and everyone who uses "yx_state" under the licensing terms detailed in LICENSE available at https://github.com/yandex/yx_state/blob/main/LICENSE. 9 | kltsv 10 | guid-empty 11 | Vorkytaka 12 | sorokinDev 13 | wwwhttpru 14 | -------------------------------------------------------------------------------- /yx_state/packages/yx_state_transformers/AUTHORS: -------------------------------------------------------------------------------- 1 | The following authors have created the source code of "yx_state" published and distributed by YANDEX LLC as the owner: 2 | kltsv 3 | guid-empty 4 | Vorkytaka 5 | sorokinDev 6 | wwwhttpru 7 | 8 | The following authors have licensed their contributions to YANDEX LLC and everyone who uses "yx_state" under the licensing terms detailed in LICENSE available at https://github.com/yandex/yx_state/blob/main/LICENSE. 9 | kltsv 10 | guid-empty 11 | Vorkytaka 12 | sorokinDev 13 | wwwhttpru 14 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_linter/lib/src/extensions.dart: -------------------------------------------------------------------------------- 1 | import 'package:custom_lint_builder/custom_lint_builder.dart'; 2 | 3 | extension LintCodeCopyWith on LintCode { 4 | LintCode copyWith({String? problemMessage, String? correctionMessage}) => 5 | LintCode( 6 | name: name, 7 | problemMessage: problemMessage ?? this.problemMessage, 8 | correctionMessage: correctionMessage ?? this.correctionMessage, 9 | uniqueName: uniqueName, 10 | url: url, 11 | errorSeverity: errorSeverity, 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/example/lib/domain/auth/register_manager.dart: -------------------------------------------------------------------------------- 1 | import 'account_holder.dart'; 2 | import 'models/account.dart'; 3 | import 'register_state_holder.dart'; 4 | 5 | class RegisterManager { 6 | final AccountHolder _accountHolder; 7 | final RegisterHolder _registerStateHolder; 8 | 9 | RegisterManager(this._accountHolder, this._registerStateHolder); 10 | 11 | Future createAccount() async { 12 | await _accountHolder.setAccount(Account('321', 'New Bob')); 13 | await _registerStateHolder.stopRegister(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /yx_state/packages/yx_state_transformers/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: yx_state_transformers 2 | description: Custom task transformers for yx_state 3 | version: 1.0.0 4 | repository: https://github.com/yandex/city-services-pub/tree/main/yx_state/packages/yx_state_transformers 5 | issue_tracker: https://github.com/yandex/city-services-pub/issues 6 | topics: 7 | - yx-state 8 | - state-management 9 | - concurrency 10 | 11 | environment: 12 | sdk: '>=2.19.0 <4.0.0' 13 | 14 | dependencies: 15 | yx_state: ^1.0.0 16 | stream_transform: ^2.1.0 17 | 18 | dev_dependencies: 19 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope/example/bin/deprecated_listeners_main.dart: -------------------------------------------------------------------------------- 1 | import 'app_listener.dart'; 2 | 3 | /// This file will be deleted in the next major version 4 | /// when Listeners will be completely removed 5 | void main() async { 6 | final appScopeHolderWithListener = AppScopeHolderWithListener(); 7 | 8 | await appScopeHolderWithListener.create(); 9 | 10 | print(appScopeHolderWithListener.scope?.routerDelegateDep.get); 11 | 12 | await appScopeHolderWithListener.drop(); 13 | 14 | print(appScopeHolderWithListener.scope?.routerDelegateDep.get); 15 | } 16 | -------------------------------------------------------------------------------- /yx_state/packages/yx_state_flutter/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: yx_state_flutter 2 | description: Flutter widgets for yx_state. 3 | version: 1.0.0 4 | repository: https://github.com/yandex/city-services-pub/tree/main/yx_state/packages/yx_state_flutter 5 | issue_tracker: https://github.com/yandex/city-services-pub/issues 6 | topics: 7 | - yx-state 8 | - state-management 9 | 10 | environment: 11 | sdk: '>=2.19.0 <4.0.0' 12 | 13 | dependencies: 14 | yx_state: ^1.0.0 15 | flutter: 16 | sdk: flutter 17 | 18 | dev_dependencies: 19 | flutter_test: 20 | sdk: flutter 21 | 22 | -------------------------------------------------------------------------------- /yx_state/packages/yx_state/lib/yx_state.dart: -------------------------------------------------------------------------------- 1 | /// A state management library for [Dart](https://dart.dev)/[Flutter](https://flutter.dev) applications. 2 | library yx_state; 3 | 4 | export 'src/base/interface.dart' 5 | hide StateManagerHandler, StateManagerListener, Closable; 6 | export 'src/base/state_manager_base.dart'; 7 | export 'src/function_stream_handler/handle_task.dart'; 8 | export 'src/function_stream_handler/stream_function_handler.dart'; 9 | export 'src/state_manager.dart'; 10 | export 'src/state_manager_observer.dart'; 11 | export 'src/state_manager_overrides.dart'; 12 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: yx_scope_flutter_example 2 | description: A new Flutter project. 3 | version: 1.0.0+1 4 | publish_to: 'none' 5 | 6 | environment: 7 | sdk: '>=2.19.6 <3.0.0' 8 | 9 | dependencies: 10 | flutter: 11 | sdk: flutter 12 | cupertino_icons: ^1.0.2 13 | yx_scope_flutter: 14 | path: ../ 15 | yx_scope: ^1.0.0 16 | 17 | dev_dependencies: 18 | flutter_test: 19 | sdk: flutter 20 | flutter_lints: ^2.0.0 21 | custom_lint: ^0.6.8 22 | yx_scope_linter: ^0.1.0 23 | 24 | flutter: 25 | uses-material-design: true 26 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/example/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:flutter_lints/flutter.yaml 2 | 3 | linter: 4 | rules: 5 | - implementation_imports # https://dart-lang.github.io/linter/lints/implementation_imports.html 6 | - prefer_relative_imports # https://dart-lang.github.io/linter/lints/prefer_relative_imports.html 7 | - directives_ordering # https://dart-lang.github.io/linter/lints/directives_ordering.html 8 | - unawaited_futures # https://dart-lang.github.io/linter/lints/unawaited_futures.html 9 | 10 | analyzer: 11 | plugins: 12 | - custom_lint 13 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/example/lib/utils/logger.dart: -------------------------------------------------------------------------------- 1 | import 'package:yx_scope/yx_scope.dart'; 2 | 3 | class Logger extends ScopeLogger { 4 | const Logger(); 5 | 6 | @override 7 | void log( 8 | LogType type, 9 | Object message, { 10 | Object? exception, 11 | StackTrace? stackTrace, 12 | }) { 13 | if (type != LogType.error) { 14 | // ignore: avoid_print 15 | print('[INTERNAL] ${type.name}: $message'); 16 | } else { 17 | // ignore: avoid_print 18 | print('[ERROR]: $message\n$exception\n$stackTrace'); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope/test/utils/test_logger.dart: -------------------------------------------------------------------------------- 1 | import 'package:yx_scope/src/monitoring/scope_observatory.dart'; 2 | 3 | class TestLogger extends ScopeLogger { 4 | const TestLogger(); 5 | 6 | @override 7 | void log( 8 | LogType type, 9 | Object message, { 10 | Object? exception, 11 | StackTrace? stackTrace, 12 | }) { 13 | if (type != LogType.error) { 14 | // ignore: avoid_print 15 | print('${type.name}: $message'); 16 | } else { 17 | // ignore: avoid_print 18 | print('ERROR: $message\n$exception\n$stackTrace'); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_linter/example/lib/use_async_dep_for_async_lifecycle.dart: -------------------------------------------------------------------------------- 1 | import 'package:yx_scope/yx_scope.dart'; 2 | 3 | class SomeScope extends BaseScopeContainer { 4 | late final syncDep = dep(() => '1'); 5 | 6 | // expect_lint: use_async_dep_for_async_lifecycle 7 | late final shouldBeAsyncDep = dep(() => AsyncLifecycleButSyncDeclaration()); 8 | } 9 | 10 | class AsyncLifecycleButSyncDeclaration implements AsyncLifecycle { 11 | const AsyncLifecycleButSyncDeclaration(); 12 | 13 | @override 14 | Future init() async {} 15 | 16 | @override 17 | Future dispose() async {} 18 | } 19 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_linter/example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | **/doc/api/ 26 | .dart_tool/ 27 | .packages 28 | build/ 29 | /pubspec.lock 30 | 31 | .arcignore 32 | -------------------------------------------------------------------------------- /yx_state/packages/yx_state/test/src/mocks.dart: -------------------------------------------------------------------------------- 1 | import 'package:mocktail/mocktail.dart'; 2 | import 'package:yx_state/src/function_stream_handler/handle_task_emitter.dart'; 3 | import 'package:yx_state/yx_state.dart'; 4 | 5 | class MockEmitter extends Mock 6 | implements Emitter {} 7 | 8 | class MockHandleTaskEmitter extends Mock 9 | implements HandleTaskEmitter {} 10 | 11 | class MockStateManagerObserver extends Mock implements StateManagerObserver {} 12 | 13 | class MockFunctionHandler extends Mock 14 | implements FunctionHandler {} 15 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope/lib/src/monitoring/models/scope_meta.dart: -------------------------------------------------------------------------------- 1 | part of '../../base_scope_container.dart'; 2 | 3 | /// Extract meta information about [BaseScopeContainer], 4 | /// e.x. ScopeId. 5 | /// This extra class helps to stay the interface of 6 | /// the main [BaseScopeContainer] clean and contain only dep-related method. 7 | /// 8 | /// This class must be used only locally and must not be 9 | /// assigned to any field or global final or variable. 10 | class ScopeMeta { 11 | final BaseScopeContainer _scope; 12 | 13 | const ScopeMeta(BaseScopeContainer scope) : _scope = scope; 14 | 15 | ScopeId get id => _scope._id; 16 | } 17 | -------------------------------------------------------------------------------- /yx_state/packages/yx_state/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: yx_state 2 | description: A state management library for Dart/Flutter applications. 3 | version: 1.0.0 4 | repository: https://github.com/yandex/city-services-pub/tree/main/yx_state/packages/yx_state 5 | issue_tracker: https://github.com/yandex/city-services-pub/issues 6 | topics: 7 | - yx-state 8 | - state-management 9 | 10 | environment: 11 | sdk: ">=2.19.0 <4.0.0" 12 | 13 | dependencies: 14 | meta: ^1.3.0 15 | 16 | dev_dependencies: 17 | test: ^1.18.2 18 | mocktail: ^1.0.0 19 | 20 | screenshots: 21 | - description: The yx_state package logo 22 | path: screenshots/yx_state_logo.webp 23 | -------------------------------------------------------------------------------- /yx_scope/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | # Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. 25 | /pubspec.lock 26 | **/doc/api/ 27 | .dart_tool/ 28 | .packages 29 | build/ 30 | .arcignore 31 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope/lib/src/monitoring/scope_observatory_internal.dart: -------------------------------------------------------------------------------- 1 | import 'scope_observatory.dart'; 2 | 3 | class Logger { 4 | static void debug(Object message) => 5 | ScopeObservatory.logger.log(LogType.debug, message); 6 | 7 | static void info(Object message) => 8 | ScopeObservatory.logger.log(LogType.info, message); 9 | 10 | static void warning(Object message) => 11 | ScopeObservatory.logger.log(LogType.warning, message); 12 | 13 | static void error(Object message, Object exception, StackTrace stackTrace) => 14 | ScopeObservatory.logger.log(LogType.error, message, 15 | exception: exception, stackTrace: stackTrace); 16 | } 17 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/example/README.md: -------------------------------------------------------------------------------- 1 | # yx_scope_flutter_example 2 | 3 | A new Flutter project. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) 13 | 14 | For help getting started with Flutter development, view the 15 | [online documentation](https://docs.flutter.dev/), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_linter/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: yx_scope_linter 2 | description: A package that contains static analysis rules for yx_scope DI framework 3 | version: 0.1.4 4 | repository: https://github.com/yandex/city-services-pub/tree/main/yx_scope/packages/yx_scope_linter 5 | issue_tracker: https://github.com/yandex/city-services-pub/issues 6 | topics: 7 | - di 8 | - scope 9 | - dependency-injection 10 | - dependency-management 11 | 12 | environment: 13 | sdk: '>=2.19.6 <4.0.0' 14 | 15 | dependencies: 16 | analyzer: ^6.4.1 17 | analyzer_plugin: ^0.11.2 18 | custom_lint_builder: ^0.6.2 19 | yx_scope: ^1.1.1 20 | 21 | dev_dependencies: 22 | lints: ^2.0.0 23 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/example/lib/data/orders/models/incoming_order_data.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | class IncomingOrder { 4 | static final _random = Random(); 5 | 6 | final DateTime createdTime; 7 | final String uid; 8 | final Address fromAddress; 9 | final Address toAddress; 10 | 11 | IncomingOrder({ 12 | required this.fromAddress, 13 | required this.toAddress, 14 | }) : createdTime = DateTime.now(), 15 | uid = _random.nextInt(100000).toString(); 16 | } 17 | 18 | class Address { 19 | final String name; 20 | final int position; 21 | 22 | const Address({ 23 | required this.name, 24 | required this.position, 25 | }); 26 | } 27 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope/lib/src/core/scope_exception.dart: -------------------------------------------------------------------------------- 1 | // This exception must be throw in cases when 2 | // a consumer of this package uses it 3 | // in some inappropriate way. 4 | class ScopeException implements Exception { 5 | final String message; 6 | 7 | const ScopeException(this.message); 8 | 9 | @override 10 | String toString() => 'ScopeException: $message'; 11 | } 12 | 13 | // This error must be throw in cases when 14 | // the problem happens because of 15 | // internal code problems of this package. 16 | class ScopeError extends ScopeException { 17 | const ScopeError(String message) : super(message); 18 | 19 | @override 20 | String toString() => 'ScopeError: $message'; 21 | } 22 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: yx_scope_flutter 2 | description: An adapter package that provides yx_scope widgets for Flutter UI integration 3 | version: 1.1.3 4 | repository: https://github.com/yandex/city-services-pub/tree/main/yx_scope/packages/yx_scope_flutter 5 | issue_tracker: https://github.com/yandex/city-services-pub/issues 6 | topics: 7 | - di 8 | - scope 9 | - dependency-injection 10 | - dependency-management 11 | 12 | environment: 13 | sdk: '>=2.19.0 <4.0.0' 14 | flutter: ">=1.17.0" 15 | 16 | dependencies: 17 | yx_scope: ^1.1.1 18 | flutter: 19 | sdk: flutter 20 | 21 | dev_dependencies: 22 | flutter_test: 23 | sdk: flutter 24 | flutter_lints: ^2.0.0 25 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: yx_scope 2 | description: A core package of the compile-safe DI framework with advanced scoping capabilities 3 | version: 1.1.4 4 | repository: https://github.com/yandex/city-services-pub/tree/main/yx_scope/packages/yx_scope 5 | issue_tracker: https://github.com/yandex/city-services-pub/issues 6 | topics: 7 | - di 8 | - scope 9 | - dependency-injection 10 | - dependency-management 11 | 12 | environment: 13 | sdk: ">=2.19.0 <4.0.0" 14 | 15 | dependencies: 16 | meta: ^1.8.0 17 | 18 | dev_dependencies: 19 | test: ^1.16.0 20 | lints: ^2.0.1 21 | 22 | screenshots: 23 | - description: The yx_scope package logo 24 | path: screenshots/yx_scope_logo.webp 25 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope/example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | # Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. 26 | /pubspec.lock 27 | **/doc/api/ 28 | .dart_tool/ 29 | build/ 30 | 31 | .arcignore 32 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_linter/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | # Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. 26 | /pubspec.lock 27 | **/doc/api/ 28 | .dart_tool/ 29 | .packages 30 | build/ 31 | 32 | .arcignore 33 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | # Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. 26 | /pubspec.lock 27 | **/doc/api/ 28 | .dart_tool/ 29 | .packages 30 | build/ 31 | 32 | .arcignore 33 | -------------------------------------------------------------------------------- /yx_state/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | # Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. 26 | **/doc/api/ 27 | .dart_tool/ 28 | build/ 29 | /pubspec.lock 30 | .arcignore 31 | .clineignore 32 | .cursorignore 33 | .rooignore 34 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope/lib/src/monitoring/scope_observatory.dart: -------------------------------------------------------------------------------- 1 | /// Class for diagnostic purposes. 2 | /// 3 | /// Logging, state of scopes, info about dependencies. 4 | class ScopeObservatory { 5 | // Set this logger in order to log event in scopes. 6 | // By default it logs nothing. 7 | static ScopeLogger logger = const ScopeLogger(); 8 | 9 | const ScopeObservatory._(); 10 | } 11 | 12 | class ScopeLogger { 13 | const ScopeLogger(); 14 | 15 | void log( 16 | LogType type, 17 | Object message, { 18 | Object? exception, 19 | StackTrace? stackTrace, 20 | }) { 21 | // Override in order to log event in scopes 22 | } 23 | } 24 | 25 | enum LogType { 26 | debug, 27 | info, 28 | warning, 29 | error, 30 | } 31 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_linter/example/lib/utils.dart: -------------------------------------------------------------------------------- 1 | import 'package:example/dep_cycle.dart'; 2 | import 'package:yx_scope/yx_scope.dart'; 3 | 4 | class UtilsScopeModule extends ScopeModule { 5 | UtilsScopeModule(super.container); 6 | 7 | late final some7Dep = rawAsyncDep( 8 | () { 9 | return container.createSome4Dep(); 10 | }, 11 | init: (dep) async => dep.init(), 12 | dispose: (dep) async => dep.dispose(), 13 | ); 14 | 15 | late final Dep someUtilsDep = dep(() => _createSomeUtilsDep()); 16 | 17 | SomeUtilsDep _createSomeUtilsDep() => SomeUtilsDep(container.some7Dep.get); 18 | } 19 | 20 | class SomeUtilsDep { 21 | final SomeDep7 some7; 22 | 23 | SomeUtilsDep(this.some7); 24 | } 25 | -------------------------------------------------------------------------------- /yx_state/packages/yx_state/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | # Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. 26 | **/doc/api/ 27 | .dart_tool/ 28 | build/ 29 | /pubspec.lock 30 | .arcignore 31 | .clineignore 32 | .cursorignore 33 | .rooignore 34 | -------------------------------------------------------------------------------- /yx_state/packages/yx_state_transformers/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | # Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. 26 | **/doc/api/ 27 | .dart_tool/ 28 | build/ 29 | /pubspec.lock 30 | .arcignore 31 | .clineignore 32 | .cursorignore 33 | .rooignore 34 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/lib/src/core/scope_error.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:flutter/widgets.dart'; 3 | 4 | // This exception must be throw in cases when 5 | // a consumer of this package uses it 6 | // in some inappropriate way. 7 | class FlutterScopeError extends FlutterError { 8 | FlutterScopeError.fromParts(List diagnostics) 9 | : super.fromParts(diagnostics); 10 | 11 | factory FlutterScopeError(String message) { 12 | final List lines = message.split('\n'); 13 | return FlutterScopeError.fromParts([ 14 | ErrorSummary(lines.first), 15 | ...lines 16 | .skip(1) 17 | .map((String line) => ErrorDescription(line)), 18 | ]); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/example/lib/domain/order/order_manager.dart: -------------------------------------------------------------------------------- 1 | import 'models/order.dart'; 2 | import 'order_position_manager.dart'; 3 | import 'orders_state_holder.dart'; 4 | 5 | typedef CancelOrderListener = void Function(); 6 | 7 | class OrderManager { 8 | final Order order; 9 | final OrdersHandler _ordersHandler; 10 | final OrderPositionHolder _orderPositionHolder; 11 | final void Function(Order order, int lastPosition) _onOrderNavigation; 12 | 13 | OrderManager( 14 | this.order, 15 | this._ordersHandler, 16 | this._orderPositionHolder, 17 | this._onOrderNavigation, 18 | ); 19 | 20 | void goToMapNavigation() { 21 | _onOrderNavigation(order, _orderPositionHolder.position); 22 | } 23 | 24 | void cancelOrder() => _ordersHandler.removeOrder(order); 25 | } 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # City Services Flutter™ Libraries 2 | 3 | This repository contains a collection of Flutter™ libraries developed by Yandex City Services. These libraries provide reusable components and utilities for building Flutter mobile applications. 4 | 5 | ## Usage 6 | 7 | Each library contains its own documentation and usage examples. Please refer to individual directories for specific implementation details. 8 | 9 | ## **Contributing** 10 | 11 | We welcome contributions from the community. Please read our contributing guidelines before submitting pull requests. 12 | 13 | ## **License** 14 | 15 | Please check individual package licenses for specific terms and conditions. 16 | 17 | --- 18 | 19 | **Disclaimer:** Flutter and the related logo are trademarks of Google LLC. We are not endorsed by or affiliated with Google LLC. -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/example/lib/router/models/app_state.dart: -------------------------------------------------------------------------------- 1 | enum TabbarPageType { 2 | map, 3 | orders, 4 | account; 5 | } 6 | 7 | class AppState { 8 | final bool hasAccount; 9 | final bool isOpenRegister; 10 | final TabbarPageType tabbarPage; 11 | 12 | const AppState._( 13 | this.hasAccount, 14 | this.isOpenRegister, 15 | this.tabbarPage, 16 | ); 17 | 18 | const AppState.init() 19 | : hasAccount = false, 20 | isOpenRegister = false, 21 | tabbarPage = TabbarPageType.orders; 22 | 23 | AppState copyWith({ 24 | bool? hasAccount, 25 | bool? isOpenRegister, 26 | TabbarPageType? tabbarPage, 27 | }) => 28 | AppState._( 29 | hasAccount ?? this.hasAccount, 30 | isOpenRegister ?? this.isOpenRegister, 31 | tabbarPage ?? this.tabbarPage, 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignoring native folders of the example as they can be re-generated easily using: 2 | # flutter create --platforms=android,ios,web,windows,macos . 3 | example/android/ 4 | example/ios/ 5 | example/web/ 6 | example/windows/ 7 | example/macos/ 8 | example/linux/ 9 | 10 | # Miscellaneous 11 | *.class 12 | *.log 13 | *.pyc 14 | *.swp 15 | .DS_Store 16 | .atom/ 17 | .buildlog/ 18 | .history 19 | .svn/ 20 | 21 | # IntelliJ related 22 | *.iml 23 | *.ipr 24 | *.iws 25 | .idea/ 26 | 27 | # The .vscode folder contains launch configuration and tasks you configure in 28 | # VS Code which you may wish to be included in version control, so this line 29 | # is commented out by default. 30 | #.vscode/ 31 | 32 | # Flutter/Dart/Pub related 33 | **/doc/api/ 34 | .dart_tool/ 35 | .packages 36 | build/ 37 | /pubspec.lock 38 | 39 | .arcignore 40 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/example/lib/domain/auth/auth_manager.dart: -------------------------------------------------------------------------------- 1 | import 'account_holder.dart'; 2 | import 'models/account.dart'; 3 | import 'models/account_params.dart'; 4 | import 'register_state_holder.dart'; 5 | 6 | class AuthManager { 7 | final AccountHolder _accountHolder; 8 | final RegisterHolder registerStateHolder; 9 | 10 | AuthManager(this._accountHolder, this.registerStateHolder); 11 | 12 | Future login(AccountParams loginParams) async { 13 | await _accountHolder.setAccount(Account('123', 'Bob')); 14 | } 15 | 16 | Future logout() async { 17 | await _accountHolder.dropAccount(); 18 | } 19 | 20 | Future startRegister() async { 21 | await registerStateHolder.startRegister(); 22 | } 23 | 24 | Future stopRegister() async { 25 | await registerStateHolder.stopRegister(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /yx_state/packages/yx_state_flutter/lib/src/typedefs.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | /// Builder function that builds a widget based on the current state. 4 | typedef StateWidgetBuilder = Widget Function( 5 | BuildContext context, 6 | S state, 7 | Widget? child, 8 | ); 9 | 10 | /// Listener function that is called when the state changes. 11 | typedef StateWidgetListener = void Function(BuildContext context, S state); 12 | 13 | /// Function that determines when a widget should rebuild. 14 | typedef StateBuilderCondition = bool Function(S previous, S current); 15 | 16 | /// Function that determines when a listener should be called. 17 | typedef StateListenerCondition = bool Function(S previous, S current); 18 | 19 | /// Function that extracts a value from the state. 20 | typedef StateWidgetSelector = T Function(S state); 21 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_linter/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.1.4 - 2025.07.08 2 | 3 | * Documentation links fixed 4 | 5 | ## 0.1.3 - 2025.07.07 6 | 7 | * Fix: dep_cycle rule support for ScopeModule 8 | * avoid_async_dep_child_scope lint rule added 9 | * Linter CI added to project 10 | * Analyzer version upgrade to v6.4.1 11 | * Documentation fixes and updates 12 | 13 | ## 0.1.2 - 2024.12.18 14 | 15 | * Two new manual lint rules: `avoid_child_scope_in_initialize_queue` 16 | and `avoid_conditions_in_initialize_queue` 17 | * Updated repository link 18 | * Library topics added 19 | 20 | ## 0.1.1 - 2024.10.23 21 | 22 | * Minor fixes in links 23 | 24 | ## 0.1.0 25 | 26 | * Ready to be open-source (but not finished yet) 27 | 28 | ## 0.0.1 29 | 30 | * Added lints: 31 | 32 | - consider_dep_suffix 33 | - final_dep 34 | - dep_cycle 35 | - pass_async_lifecycle_in_initialize_queue 36 | - use_async_dep_for_async_lifecycle 37 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_linter/example/lib/consider_dep_suffix.dart: -------------------------------------------------------------------------------- 1 | import 'package:yx_scope/yx_scope.dart'; 2 | 3 | class SomeScope extends ScopeContainer { 4 | @override 5 | List> get initializeQueue => [ 6 | {justMyString}, 7 | {rawAsync} 8 | ]; 9 | 10 | late final my1Dep = dep(() => '1'); 11 | 12 | // expect_lint: consider_dep_suffix 13 | late final myDep2 = dep(() => '2'); 14 | 15 | // expect_lint: consider_dep_suffix 16 | late final justMyString = asyncDep(() => SomeAsyncDep()); 17 | 18 | // expect_lint: consider_dep_suffix 19 | late final rawAsync = 20 | rawAsyncDep(() => '4', init: (value) async {}, dispose: (value) async {}); 21 | } 22 | 23 | class SomeAsyncDep implements AsyncLifecycle { 24 | const SomeAsyncDep(); 25 | 26 | @override 27 | Future init() async {} 28 | 29 | @override 30 | Future dispose() async {} 31 | } 32 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope/lib/src/core/async_lifecycle.dart: -------------------------------------------------------------------------------- 1 | /// An interface for entities that must be initialized and disposed. 2 | /// You can use this interface both for asynchronous and synchronous initialization. 3 | /// 4 | /// If your entity implements this interface and it's created in [ScopeContainer], 5 | /// you have to use [AsyncDep] for instance 6 | /// and pass this dep into [ScopeContainer.initializeQueue]. 7 | abstract class AsyncLifecycle { 8 | /// When you use [AsyncLifecycle] in [ScopeContainer], 9 | /// this method will be called during initialization of this [ScopeContainer]. 10 | Future init(); 11 | 12 | /// When you use [AsyncLifecycle] in [ScopeContainer], 13 | /// this method will be called during dispose of the [ScopeContainer]. 14 | Future dispose(); 15 | 16 | /// AsyncLifecycle can be used only as an interface via 'implements' 17 | const AsyncLifecycle._(); 18 | } 19 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope/lib/yx_scope.dart: -------------------------------------------------------------------------------- 1 | library yx_scope; 2 | 3 | export 'src/base_scope_container.dart' 4 | hide 5 | ScopeObserverInternal, 6 | DepObserverInternal, 7 | AsyncDepObserverInternal, 8 | Entry, 9 | TestableScopeStateHolder, 10 | CustomDep, 11 | CustomAsyncDep, 12 | DepBehavior, 13 | AsyncDepBehavior, 14 | DepAccess, 15 | AsyncDepAccess, 16 | CoreDepBehavior, 17 | CoreAsyncDepBehavior; 18 | export 'src/core/async_lifecycle.dart'; 19 | export 'src/core/scope_exception.dart'; 20 | export 'src/core/scope_state.dart'; 21 | export 'src/monitoring/observers.dart'; 22 | export 'src/monitoring/models/dep_id.dart'; 23 | export 'src/monitoring/models/scope_id.dart'; 24 | export 'src/monitoring/models/value_meta.dart'; 25 | export 'src/monitoring/scope_observatory.dart'; 26 | export 'src/scope_container.dart'; 27 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/lib/src/core/provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | class Provider extends InheritedWidget { 4 | final T data; 5 | const Provider({ 6 | required super.child, 7 | required this.data, 8 | super.key, 9 | }); 10 | 11 | static T of(BuildContext context, {bool listen = true}) { 12 | final provider = listen 13 | ? context.dependOnInheritedWidgetOfExactType>() 14 | : (context 15 | .getElementForInheritedWidgetOfExactType>() 16 | ?.widget as Provider?); 17 | 18 | if (provider == null) { 19 | throw NotFoundProviderException(); 20 | } else { 21 | return provider.data; 22 | } 23 | } 24 | 25 | @override 26 | bool updateShouldNotify(Provider oldWidget) => oldWidget.data != data; 27 | } 28 | 29 | class NotFoundProviderException implements Exception {} 30 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_linter/lib/src/plugin.dart: -------------------------------------------------------------------------------- 1 | import 'package:custom_lint_builder/custom_lint_builder.dart'; 2 | import 'package:yx_scope_linter/src/lints/avoid_async_dep_child_scope.dart'; 3 | import 'package:yx_scope_linter/src/lints/consider_dep_suffix.dart'; 4 | import 'package:yx_scope_linter/src/lints/dep_cycle.dart'; 5 | import 'package:yx_scope_linter/src/lints/pass_async_lifecycle_in_initialize_queue.dart'; 6 | import 'package:yx_scope_linter/src/lints/use_async_dep_for_async_lifecycle.dart'; 7 | 8 | import 'lints/final_dep.dart'; 9 | 10 | class YXScopeLintsPlugin extends PluginBase { 11 | @override 12 | List getLintRules(CustomLintConfigs configs) => const [ 13 | FinalDep(), 14 | DepCycle(), 15 | ConsiderDepSuffix(), 16 | PassAsyncLifecycleInInitializeQueue(), 17 | UseAsyncDepForAsyncLifecycle(), 18 | AvoidChildScopeInInitializeQueue(), 19 | ]; 20 | } 21 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | **/doc/api/ 26 | **/ios/Flutter/.last_build_id 27 | .dart_tool/ 28 | .flutter-plugins 29 | .flutter-plugins-dependencies 30 | .packages 31 | .pub-cache/ 32 | .pub/ 33 | /build/ 34 | /pubspec.lock 35 | 36 | # Symbolication related 37 | app.*.symbols 38 | 39 | # Obfuscation related 40 | app.*.map.json 41 | 42 | # Android Studio will place build artifacts here 43 | /android/app/debug 44 | /android/app/profile 45 | /android/app/release 46 | 47 | .arcignore 48 | -------------------------------------------------------------------------------- /yx_state/packages/yx_state_flutter/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignoring native folders of the example as they can be re-generated easily using: 2 | # flutter create --platforms=android,ios,web,windows,macos . 3 | example/android/ 4 | example/ios/ 5 | example/web/ 6 | example/windows/ 7 | example/macos/ 8 | example/linux/ 9 | 10 | # Miscellaneous 11 | *.class 12 | *.log 13 | *.pyc 14 | *.swp 15 | .DS_Store 16 | .atom/ 17 | .buildlog/ 18 | .history 19 | .svn/ 20 | migrate_working_dir/ 21 | 22 | # IntelliJ related 23 | *.iml 24 | *.ipr 25 | *.iws 26 | .idea/ 27 | 28 | # The .vscode folder contains launch configuration and tasks you configure in 29 | # VS Code which you may wish to be included in version control, so this line 30 | # is commented out by default. 31 | #.vscode/ 32 | 33 | # Flutter/Dart/Pub related 34 | # Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. 35 | **/doc/api/ 36 | .dart_tool/ 37 | build/ 38 | /pubspec.lock 39 | .arcignore 40 | .clineignore 41 | .cursorignore 42 | .rooignore 43 | -------------------------------------------------------------------------------- /yx_state/packages/yx_state_flutter/example/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignoring native folders of the example as they can be re-generated easily using: 2 | # flutter create --platforms=android,ios,web,windows,macos . 3 | example/android/ 4 | example/ios/ 5 | example/web/ 6 | example/windows/ 7 | example/macos/ 8 | example/linux/ 9 | 10 | # Miscellaneous 11 | *.class 12 | *.log 13 | *.pyc 14 | *.swp 15 | .DS_Store 16 | .atom/ 17 | .buildlog/ 18 | .history 19 | .svn/ 20 | migrate_working_dir/ 21 | 22 | # IntelliJ related 23 | *.iml 24 | *.ipr 25 | *.iws 26 | .idea/ 27 | 28 | # The .vscode folder contains launch configuration and tasks you configure in 29 | # VS Code which you may wish to be included in version control, so this line 30 | # is commented out by default. 31 | #.vscode/ 32 | 33 | # Flutter/Dart/Pub related 34 | # Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. 35 | **/doc/api/ 36 | .dart_tool/ 37 | build/ 38 | /pubspec.lock 39 | .arcignore 40 | .clineignore 41 | .cursorignore 42 | .rooignore 43 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_linter/lib/src/types.dart: -------------------------------------------------------------------------------- 1 | import 'package:custom_lint_builder/custom_lint_builder.dart'; 2 | 3 | const baseScopeContainerType = TypeChecker.fromName( 4 | 'BaseScopeContainer', 5 | packageName: 'yx_scope', 6 | ); 7 | 8 | const depValueType = TypeChecker.fromName( 9 | 'Dep', 10 | packageName: 'yx_scope', 11 | ); 12 | const asyncDepValueType = TypeChecker.fromName( 13 | 'AsyncDep', 14 | packageName: 'yx_scope', 15 | ); 16 | 17 | const asyncLifecycleType = TypeChecker.fromName( 18 | 'AsyncLifecycle', 19 | packageName: 'yx_scope', 20 | ); 21 | 22 | const childScopeHolderValueType = TypeChecker.fromName( 23 | 'ChildScopeHolder', 24 | packageName: 'yx_scope', 25 | ); 26 | 27 | const scopeContainerValueType = TypeChecker.fromName( 28 | 'ScopeContainer', 29 | packageName: 'yx_scope', 30 | ); 31 | 32 | const anyDepValueTypes = TypeChecker.any([ 33 | depValueType, 34 | asyncDepValueType, 35 | ]); 36 | 37 | const scopeModuleType = TypeChecker.fromName( 38 | 'ScopeModule', 39 | packageName: 'yx_scope', 40 | ); 41 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope/lib/src/monitoring/raw_listeners.dart: -------------------------------------------------------------------------------- 1 | part of 'raw_observers.dart'; 2 | 3 | @Deprecated('Use RawScopeObserver instead') 4 | abstract class RawScopeListener extends RawScopeObserver { 5 | static set override(RawScopeObserver? override) => 6 | RawScopeObserver.override = override; 7 | 8 | static RawScopeObserver? get override => RawScopeObserver.override; 9 | 10 | RawScopeListener._() : super._(); 11 | } 12 | 13 | @Deprecated('Use RawDepObserver instead') 14 | abstract class RawDepListener extends RawDepObserver { 15 | static set override(RawDepObserver? override) => 16 | RawDepObserver.override = override; 17 | 18 | static RawDepObserver? get override => RawDepObserver.override; 19 | } 20 | 21 | @Deprecated('Use RawAsyncDepObserver instead') 22 | abstract class RawAsyncDepListener extends RawAsyncDepObserver 23 | implements RawDepListener { 24 | static set override(RawAsyncDepObserver? override) => 25 | RawAsyncDepObserver.override = override; 26 | 27 | static RawAsyncDepObserver? get override => RawAsyncDepObserver.override; 28 | } 29 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/example/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled. 5 | 6 | version: 7 | revision: e2874b6e235bcae425ed3436c113ab4ac50ac707 8 | channel: unknown 9 | 10 | project_type: app 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: e2874b6e235bcae425ed3436c113ab4ac50ac707 17 | base_revision: e2874b6e235bcae425ed3436c113ab4ac50ac707 18 | - platform: android 19 | create_revision: e2874b6e235bcae425ed3436c113ab4ac50ac707 20 | base_revision: e2874b6e235bcae425ed3436c113ab4ac50ac707 21 | 22 | # User provided section 23 | 24 | # List of Local paths (relative to this file) that should be 25 | # ignored by the migrate tool. 26 | # 27 | # Files that are not part of the templates will be ignored by default. 28 | unmanaged_files: 29 | - 'lib/main.dart' 30 | - 'ios/Runner.xcodeproj/project.pbxproj' 31 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.1.3 - unpublished 2 | 3 | * Fix: handling of ScopeHolder state changes in ScopeListener subscription 4 | 5 | ## 1.1.2 - 2025.07.30 6 | 7 | * Fix: ScopeBuilder didUpdateWidget throw error if there is no 8 | parent ScopeProvider but scopeHolder passed as argument 9 | * ScopeBuilder test added 10 | 11 | ## 1.1.1 - 2025.07.08 12 | 13 | * Documentation links fixed 14 | 15 | ## 1.1.0 - 2025.07.07 16 | 17 | * Fix: ScopeBuilder and ScopeListener fixes, tests added 18 | * Fix: mounted check added to _subscribe method 19 | * Add test for checking rebuilds only for dependent widgets 20 | * New Flutter version added to CI 21 | * Documentation fixes and updates 22 | 23 | ## 1.0.2 - 2024.12.18 24 | 25 | * Updated repository link 26 | * Library topics added 27 | 28 | ## 1.0.1 - 2024.10.23 29 | 30 | * Minor fixes in links 31 | 32 | ## 1.0.0 — 2024.10.18 33 | 34 | * Ready to be open-source 35 | 36 | ## 0.0.2 — 2024.09.23 37 | 38 | * [ScopeBuilder] API changed: scopeStateHolder -> holder 39 | 40 | ## 0.0.1 41 | 42 | * Add [ScopeProvider], [ScopeConsumer], [ScopeBuilder], [ScopeListener] widgets 43 | -------------------------------------------------------------------------------- /yx_scope/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2024 YANDEX LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /yx_state/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2025 YANDEX LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2024 YANDEX LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /yx_state/packages/yx_state/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2025 YANDEX LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2024 YANDEX LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_linter/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2024 YANDEX LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /yx_state/packages/yx_state_flutter/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2025 YANDEX LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_linter/lib/src/yx_scope_registry.dart: -------------------------------------------------------------------------------- 1 | import 'models/dep.dart'; 2 | import 'resolved_yx_scope_result.dart'; 3 | 4 | /// An entity that provides the ability to track parsed data 5 | class YXScopeRegistry { 6 | /// This method provides the parsing result to subscribers 7 | void run(ResolvedYXScopeResult result) { 8 | result.accept(YXScopeRegistryVisitor(this)); 9 | } 10 | 11 | final List _forScopeDeclarations = []; 12 | 13 | /// This method provides the ability to track parsed scope classes 14 | void addScopeDeclarations(void Function(ScopeDeclaration scope) listener) { 15 | _forScopeDeclarations.add(listener); 16 | } 17 | } 18 | 19 | /// This visitor simply notifies all subscribers 20 | class YXScopeRegistryVisitor { 21 | final YXScopeRegistry _registry; 22 | 23 | YXScopeRegistryVisitor(this._registry); 24 | 25 | void visitResolvedUnits(ResolvedYXScopeResult result) { 26 | result.visitChildren(this); 27 | } 28 | 29 | void visitScopeDeclaration(ScopeDeclaration result) { 30 | for (final listener in _registry._forScopeDeclarations) { 31 | listener(result); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /yx_state/packages/yx_state_transformers/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2025 YANDEX LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /yx_scope/README.md: -------------------------------------------------------------------------------- 1 | # yx_scope packages 2 | 3 | yx_scope is a compile-safe DI framework with advanced scoping capabilities. 4 | 5 | ## Library Components 6 | 7 | The library group currently consists of: 8 | 9 | - **[yx_scope](packages/yx_scope)**: The core implementation of the framework 10 | - **[yx_scope_flutter](packages/yx_scope_flutter)**: An adapter library that allows embedding 11 | yx_scope containers into the widget tree 12 | - **[yx_scope_linter](packages/yx_scope_linter)**: A set of custom lint rules that provide 13 | additional protection against errors when working with yx_scope 14 | 15 | ## Features 16 | 17 | - Pure Dart 18 | - DI-like (not static and not ServiceLocator) 19 | - Compile-safe access to dependencies 20 | - No code generation 21 | - Flutter-friendly container management 22 | - Declarative description of the dependency tree 23 | - Non-reactive dependency tree 24 | - Unambiguous behavior and lifecycle of dependencies in containers 25 | - Ability to create scopes of any nesting level 26 | - Compile-safe check for the existence of active scopes 27 | - Support for asynchronous dependencies and their initialization 28 | - Compile-safe protection against circular dependencies 29 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_linter/example/lib/pass_async_lifecycle_in_initialize_queue.dart: -------------------------------------------------------------------------------- 1 | import 'package:yx_scope/yx_scope.dart'; 2 | 3 | class SomeScope extends BaseScopeContainer { 4 | static void someStaticMethod() {} 5 | 6 | @override 7 | List> get initializeQueue => [ 8 | { 9 | justMyString, 10 | } 11 | ]; 12 | 13 | void someMethod() {} 14 | 15 | void someGenericMethod() {} 16 | 17 | Future someAsyncMethod() async {} 18 | 19 | String get someGetter => ''; 20 | 21 | late final my1Dep = dep(() => '1'); 22 | 23 | // expect_lint: consider_dep_suffix 24 | late final myDep2 = dep(() => '2'); 25 | 26 | // expect_lint: consider_dep_suffix 27 | late final justMyString = asyncDep(() => SomeAsyncDep()); 28 | 29 | // expect_lint: consider_dep_suffix, pass_async_lifecycle_in_initialize_queue 30 | late final rawAsync = 31 | rawAsyncDep(() => '4', init: (value) async {}, dispose: (value) async {}); 32 | } 33 | 34 | class SomeAsyncDep implements AsyncLifecycle { 35 | const SomeAsyncDep(); 36 | 37 | @override 38 | Future init() async {} 39 | 40 | @override 41 | Future dispose() async {} 42 | } 43 | -------------------------------------------------------------------------------- /yx_state/packages/yx_state_transformers/example/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:yx_state/yx_state.dart'; 2 | import 'package:yx_state_transformers/yx_state_transformers.dart'; 3 | 4 | void main() async { 5 | /// Create a state manager with a concurrent handler. 6 | final counter = CounterStateManager(0); 7 | 8 | /// Subscribe to state changes and print each state. 9 | final subscription = counter.stream.listen(print); 10 | 11 | counter.increment(); 12 | counter.incrementBy(8); 13 | counter.increment(); 14 | 15 | // wait for 3 seconds 16 | await Future.delayed(const Duration(seconds: 3)); 17 | 18 | /// Close the state manager. 19 | await counter.close(); 20 | 21 | /// Unsubscribe from state changes. 22 | await subscription.cancel(); 23 | } 24 | 25 | class CounterStateManager extends StateManager { 26 | CounterStateManager(super.state) : super(handler: concurrent()); 27 | 28 | void increment() => handle( 29 | (emit) async { 30 | await Future.delayed(const Duration(seconds: 2)); 31 | emit(state + 1); 32 | }, 33 | ); 34 | 35 | void incrementBy(int value) => handle( 36 | (emit) async { 37 | await Future.delayed(const Duration(seconds: 1)); 38 | emit(state + value); 39 | }, 40 | ); 41 | } 42 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/example/lib/router/app_state_observer.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:yx_scope/yx_scope.dart'; 4 | 5 | import '../domain/auth/account_holder.dart'; 6 | import '../domain/auth/register_state_holder.dart'; 7 | import 'router_delegate.dart'; 8 | 9 | class AppStateObserver implements AsyncLifecycle { 10 | final AppRouterDelegate _routerDelegate; 11 | final AccountHolder _accountHolder; 12 | final RegisterHolder _registerHolder; 13 | 14 | late StreamSubscription _accountSubscription; 15 | late StreamSubscription _registerSubscription; 16 | 17 | AppStateObserver( 18 | this._routerDelegate, 19 | this._accountHolder, 20 | this._registerHolder, 21 | ); 22 | 23 | @override 24 | Future init() async { 25 | _accountSubscription = _accountHolder.accountStream.listen((account) { 26 | _routerDelegate.setHasAccount(hasAccount: account != null); 27 | }); 28 | _registerSubscription = 29 | _registerHolder.inProgressStream.listen((inProgress) { 30 | _routerDelegate.setOpenRegister(isOpenRegister: inProgress); 31 | }); 32 | } 33 | 34 | @override 35 | Future dispose() async { 36 | await Future.wait([ 37 | _accountSubscription.cancel(), 38 | _registerSubscription.cancel(), 39 | ]); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope/lib/src/scope_state_streamable.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'base_scope_container.dart'; 4 | import 'core/scope_state.dart'; 5 | 6 | mixin ScopeStateStreamable on ScopeStateHolder { 7 | Stream get stream { 8 | late StreamController controller; 9 | late RemoveStateListener removeStateListener; 10 | 11 | void onListen() { 12 | removeStateListener = listen((scope) { 13 | controller.add(scope); 14 | }); 15 | } 16 | 17 | void onCancel() async { 18 | removeStateListener(); 19 | await controller.close(); 20 | } 21 | 22 | controller = StreamController( 23 | onListen: onListen, 24 | onCancel: onCancel, 25 | ); 26 | 27 | return controller.stream; 28 | } 29 | 30 | Stream> get stateStream { 31 | late StreamController> controller; 32 | late RemoveStateListener removeStateListener; 33 | 34 | void onListen() { 35 | removeStateListener = listenState((state) { 36 | controller.add(state); 37 | }); 38 | } 39 | 40 | void onCancel() async { 41 | removeStateListener(); 42 | await controller.close(); 43 | } 44 | 45 | controller = StreamController>( 46 | onListen: onListen, 47 | onCancel: onCancel, 48 | ); 49 | 50 | return controller.stream; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/example/lib/domain/map/map_manager.dart: -------------------------------------------------------------------------------- 1 | import '../../data/map/map.dart'; 2 | 3 | import '../../data/orders/models/incoming_order_data.dart'; 4 | import '../map_navigation/map_navigation_manager.dart'; 5 | 6 | class MapManager { 7 | final MapController _mapController; 8 | final MapNavigationHolder _mapNavigationHolder; 9 | 10 | const MapManager( 11 | this._mapController, 12 | this._mapNavigationHolder, 13 | ); 14 | 15 | List get selectedItems => _mapController.items.values 16 | .where((item) => item.selected) 17 | .toList(growable: false); 18 | 19 | void focus(int position) { 20 | _mapController.focus(position); 21 | } 22 | 23 | void selectMapItem(int position, {required bool selected}) { 24 | _mapController.selectPosition(position, selected: selected); 25 | } 26 | 27 | void clear() { 28 | _mapController.clear(); 29 | } 30 | 31 | void startNavigation() { 32 | assert( 33 | selectedItems.length > 1, 34 | 'Incorrect number of selected items for map navigation', 35 | ); 36 | _mapNavigationHolder.startNavigation( 37 | MapNavigationParams( 38 | fromAddress: 39 | Address(name: 'Street', position: selectedItems[0].position), 40 | toAddress: Address( 41 | name: 'Street', 42 | position: selectedItems[selectedItems.length - 1].position), 43 | ), 44 | ); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_linter/lib/src/utils.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:analyzer/dart/element/element.dart'; 3 | 4 | import 'types.dart'; 5 | 6 | /// Utility class for working with class declarations and elements 7 | class ClassUtils { 8 | /// Checks if a class implements the specified interface by name 9 | static bool implementsInterface(ClassElement element, String ancestorName) => 10 | element.interfaces 11 | .map((e) => e.getDisplayString()) 12 | .contains(ancestorName); 13 | 14 | /// Determines if a class declaration is a scope container 15 | static bool isScopeContainer(ClassDeclaration node) { 16 | final element = node.declaredElement; 17 | return element != null 18 | ? baseScopeContainerType.isAssignableFrom(element) 19 | : false; 20 | } 21 | 22 | /// Gets all non-static field declarations from a class 23 | static Iterable getInstanceFields(ClassDeclaration node) { 24 | return node.members 25 | .whereType() 26 | .where((element) => !element.isStatic); 27 | } 28 | 29 | /// Gets all non-static method declarations from a class 30 | static Iterable getInstanceMethods(ClassDeclaration node) { 31 | return node.members 32 | .whereType() 33 | .where((element) => !element.isStatic); 34 | } 35 | 36 | const ClassUtils._(); 37 | } 38 | -------------------------------------------------------------------------------- /yx_state/packages/yx_state_transformers/README.md: -------------------------------------------------------------------------------- 1 | # yx_state_transformers 2 | 3 | Custom task transformers for [yx_state](https://pub.dev/packages/yx_state). 4 | 5 | ## Concurrency Strategies 6 | 7 | yx_state_transformers provides several concurrency strategies for handling tasks: 8 | 9 | 1. **Sequential** - Process tasks one after another in order: 10 | 11 | ```dart 12 | class CounterManager extends StateManager { 13 | CounterManager() 14 | : super( 15 | const CounterState(0), 16 | handler: sequential(), 17 | ); 18 | } 19 | ``` 20 | 21 | 2. **Concurrent** - Process tasks in parallel: 22 | 23 | ```dart 24 | class CounterManager extends StateManager { 25 | CounterManager() 26 | : super( 27 | const CounterState(0), 28 | handler: concurrent(), 29 | ); 30 | } 31 | ``` 32 | 33 | 3. **Droppable** - Ignore new tasks while processing: 34 | 35 | ```dart 36 | class CounterManager extends StateManager { 37 | CounterManager() 38 | : super( 39 | const CounterState(0), 40 | handler: droppable(), 41 | ); 42 | } 43 | ``` 44 | 45 | 4. **Restartable** - Cancel current task when a new one comes in: 46 | 47 | ```dart 48 | class CounterManager extends StateManager { 49 | CounterManager() 50 | : super( 51 | const CounterState(0), 52 | handler: restartable(), 53 | ); 54 | } 55 | ``` 56 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope/lib/src/core/scope_state.dart: -------------------------------------------------------------------------------- 1 | abstract class ScopeState { 2 | ScopeState._(); 3 | 4 | factory ScopeState.none() = ScopeStateNone; 5 | factory ScopeState.initializing() = ScopeStateInitializing; 6 | factory ScopeState.available({required Scope scope}) = ScopeStateAvailable; 7 | factory ScopeState.disposing() = ScopeStateDisposing; 8 | 9 | bool get none => this is ScopeStateNone; 10 | 11 | bool get initializing => this is ScopeStateInitializing; 12 | 13 | bool get available => this is ScopeStateAvailable; 14 | 15 | bool get disposing => this is ScopeStateDisposing; 16 | } 17 | 18 | class ScopeStateNone extends ScopeState { 19 | ScopeStateNone() : super._(); 20 | 21 | @override 22 | String toString() => 'ScopeState<$Scope>.none'; 23 | } 24 | 25 | class ScopeStateInitializing extends ScopeState { 26 | ScopeStateInitializing() : super._(); 27 | 28 | @override 29 | String toString() => 'ScopeState<$Scope>.initializing'; 30 | } 31 | 32 | class ScopeStateAvailable extends ScopeState { 33 | final Scope scope; 34 | 35 | ScopeStateAvailable({required this.scope}) : super._(); 36 | 37 | @override 38 | String toString() => 'ScopeState<$Scope>.available'; 39 | } 40 | 41 | class ScopeStateDisposing extends ScopeState { 42 | ScopeStateDisposing() : super._(); 43 | 44 | @override 45 | String toString() => 'ScopeState<$Scope>.disposing'; 46 | } 47 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/example/lib/di/register/register_scope.dart: -------------------------------------------------------------------------------- 1 | import 'package:yx_scope/yx_scope.dart'; 2 | 3 | import '../../domain/auth/register_manager.dart'; 4 | import '../../domain/auth/register_state_holder.dart'; 5 | import '../app/app_scope.dart'; 6 | import '../utils/listeners.dart'; 7 | 8 | class RegisterScopeContainer extends ChildScopeContainer { 9 | RegisterScopeContainer({required super.parent}); 10 | 11 | late final registerManagerDep = dep( 12 | () => RegisterManager( 13 | parent.accountScopeHolderDep.get, 14 | parent.registerScopeHolderDep.get, 15 | ), 16 | ); 17 | } 18 | 19 | class RegisterScopeHolder 20 | extends ChildScopeHolder 21 | implements RegisterHolder { 22 | RegisterScopeHolder(super.parent) 23 | : super( 24 | scopeObservers: [diObserver], 25 | depObservers: [diObserver], 26 | asyncDepObservers: [diObserver], 27 | ); 28 | 29 | @override 30 | RegisterScopeContainer createContainer(parent) => 31 | RegisterScopeContainer(parent: parent); 32 | 33 | @override 34 | bool get inProgress => scope != null; 35 | 36 | @override 37 | Stream get inProgressStream => stream.map((scope) => scope != null); 38 | 39 | @override 40 | Future startRegister() async => await create(); 41 | 42 | @override 43 | Future stopRegister() async => await drop(); 44 | } 45 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_linter/example/README.md: -------------------------------------------------------------------------------- 1 | 13 | 14 | TODO: Put a short description of the package here that helps potential users 15 | know whether this package might be useful for them. 16 | 17 | ## Features 18 | 19 | TODO: List what your package can do. Maybe include images, gifs, or videos. 20 | 21 | ## Getting started 22 | 23 | TODO: List prerequisites and provide or point to information on how to 24 | start using the package. 25 | 26 | ## Usage 27 | 28 | TODO: Include short and useful examples for package users. Add longer examples 29 | to `/example` folder. 30 | 31 | ```dart 32 | const like = 'sample'; 33 | ``` 34 | 35 | ## Additional information 36 | 37 | TODO: Tell users more about the package: where to find more information, how to 38 | contribute to the package, how to file issues, what response they can expect 39 | from the package authors, and more. 40 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_linter/lib/src/visitors/parse_visitor.dart: -------------------------------------------------------------------------------- 1 | part of '../resolved_yx_scope_result.dart'; 2 | 3 | /// Parser that stores parsed data in [result] 4 | class _ParseVisitor extends SimpleAstVisitor { 5 | final ResolvedYXScopeResult result; 6 | 7 | _ParseVisitor(this.result); 8 | 9 | @override 10 | Future visitCompilationUnit(CompilationUnit node) => 11 | Future.wait(node.declarations.map((e) async => e.accept(this))); 12 | 13 | @override 14 | Future visitClassDeclaration(ClassDeclaration node) async { 15 | final element = node.declaredElement; 16 | if (element == null) { 17 | return; 18 | } 19 | 20 | // Skip if not a scope container or module type 21 | if (!baseScopeContainerType.isAssignableFromType(element.thisType) && 22 | !scopeModuleType.isAssignableFromType(element.thisType)) { 23 | return; 24 | } 25 | 26 | // Parse the scope declaration 27 | final visitor = _ParseScopeDeclaration(ScopeDeclaration(node: node)); 28 | await visitor.visitClassDeclaration(node); 29 | 30 | // Parse initialization queue members 31 | node.members.accept( 32 | _ParseInitializeQueueVisitor(visitor.declaration as ScopeDeclaration)); 33 | 34 | // Parse dependencies 35 | await _ParseDependenciesForDepVisitor(visitor.declaration, null).run(); 36 | 37 | // Add the completed declaration to results 38 | result.scopeDeclarations.add(visitor.declaration as ScopeDeclaration); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/example/lib/ui/auth/auth_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:yx_scope_flutter/yx_scope_flutter.dart'; 3 | 4 | import '../../di/app/app_scope.dart'; 5 | import '../../domain/auth/models/account_params.dart'; 6 | 7 | class AuthPage extends StatefulWidget { 8 | const AuthPage({super.key}); 9 | 10 | @override 11 | State createState() => _AuthPageState(); 12 | } 13 | 14 | class _AuthPageState extends State { 15 | @override 16 | Widget build(BuildContext context) => Scaffold( 17 | appBar: AppBar(title: const Text('login')), 18 | body: ScopeBuilder.withPlaceholder( 19 | builder: (context, appScope) { 20 | final authManager = appScope.authManagerDep.get; 21 | 22 | return Center( 23 | child: Column( 24 | mainAxisAlignment: MainAxisAlignment.center, 25 | children: [ 26 | const Text('Login'), 27 | TextButton( 28 | onPressed: () => 29 | authManager.login(AccountParams('login', 'pass')), 30 | child: const Text('login'), 31 | ), 32 | TextButton( 33 | onPressed: () => authManager.startRegister(), 34 | child: const Text('start register'), 35 | ), 36 | ], 37 | ), 38 | ); 39 | }, 40 | ), 41 | ); 42 | } 43 | -------------------------------------------------------------------------------- /.github/workflows/yx_scope_pr.yaml: -------------------------------------------------------------------------------- 1 | name: CI for PRs 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - "**/yx_scope/**" 7 | - "**/.github/**" 8 | - "!**.md" 9 | branches: 10 | - main 11 | 12 | jobs: 13 | ci_checks: 14 | name: CI Checks 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | # Check the library against the most recent version and the previous minor version 20 | flutter-version: [3.32.4, 3.29.2, 3.27.1, 3.24.5] 21 | package: 22 | - yx_scope/packages/yx_scope 23 | - yx_scope/packages/yx_scope_flutter 24 | - yx_scope/packages/yx_scope_linter 25 | 26 | defaults: 27 | run: 28 | working-directory: ${{ matrix.package }} 29 | shell: bash 30 | 31 | steps: 32 | - name: Checkout code 33 | uses: actions/checkout@v4 34 | 35 | - name: Set up Flutter 36 | uses: subosito/flutter-action@v2 37 | with: 38 | flutter-version: ${{ matrix.flutter-version }} 39 | 40 | - name: Install dependencies 41 | run: flutter pub get 42 | 43 | - name: Check formatting 44 | run: dart format --set-exit-if-changed . 45 | 46 | - name: Run linter 47 | run: flutter analyze 48 | 49 | - name: Verify custom_lint 50 | working-directory: yx_scope/packages/yx_scope_linter/example 51 | run: dart run custom_lint --watch 52 | 53 | - name: Run tests 54 | if: matrix.package != 'yx_scope/packages/yx_scope_linter' 55 | run: flutter test 56 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:yx_scope/yx_scope.dart'; 3 | import 'package:yx_scope_flutter/yx_scope_flutter.dart'; 4 | 5 | import 'di/app/app_scope.dart'; 6 | import 'utils/logger.dart'; 7 | 8 | Future main() async { 9 | ScopeObservatory.logger = const Logger(); 10 | runApp(const App()); 11 | } 12 | 13 | class App extends StatefulWidget { 14 | const App({super.key}); 15 | 16 | @override 17 | State createState() => _AppState(); 18 | } 19 | 20 | class _AppState extends State { 21 | final _appScopeHolder = AppScopeHolder(); 22 | 23 | @override 24 | void initState() { 25 | super.initState(); 26 | _appScopeHolder.create(); 27 | } 28 | 29 | @override 30 | void dispose() { 31 | _appScopeHolder.drop(); 32 | super.dispose(); 33 | } 34 | 35 | @override 36 | Widget build(BuildContext context) { 37 | return ScopeProvider( 38 | holder: _appScopeHolder, 39 | child: ScopeBuilder.withPlaceholder( 40 | builder: (context, appScope) { 41 | return MaterialApp.router( 42 | title: 'YxScopedFlutter Demo', 43 | theme: ThemeData( 44 | primarySwatch: Colors.blue, 45 | ), 46 | routerDelegate: appScope.routerDelegateDep.get, 47 | ); 48 | }, 49 | 50 | // Shows this widget while [appScopeHolder] is loading 51 | placeholder: const Center(child: CircularProgressIndicator()), 52 | ), 53 | ); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/example/lib/di/app/app_scope.dart: -------------------------------------------------------------------------------- 1 | import 'package:yx_scope/yx_scope.dart'; 2 | 3 | import '../../domain/auth/auth_manager.dart'; 4 | import '../../router/app_state_observer.dart'; 5 | import '../../router/router_delegate.dart'; 6 | import '../account/account_scope.dart'; 7 | import '../register/register_scope.dart'; 8 | import '../utils/listeners.dart'; 9 | 10 | class AppScopeContainer extends ScopeContainer { 11 | @override 12 | List> get initializeQueue => [ 13 | { 14 | appStateObserverDep, 15 | } 16 | ]; 17 | 18 | late final accountScopeHolderDep = dep(() => AccountScopeHolder(this)); 19 | 20 | late final registerScopeHolderDep = dep(() => RegisterScopeHolder(this)); 21 | 22 | late final authManagerDep = dep( 23 | () => AuthManager( 24 | accountScopeHolderDep.get, 25 | registerScopeHolderDep.get, 26 | ), 27 | ); 28 | 29 | late final routerDelegateDep = dep(() => AppRouterDelegate()); 30 | 31 | late final appStateObserverDep = asyncDep( 32 | () => AppStateObserver( 33 | routerDelegateDep.get, 34 | accountScopeHolderDep.get, 35 | registerScopeHolderDep.get, 36 | ), 37 | ); 38 | } 39 | 40 | class AppScopeHolder extends ScopeHolder { 41 | AppScopeHolder() 42 | : super( 43 | scopeObservers: [diObserver], 44 | depObservers: [diObserver], 45 | asyncDepObservers: [diObserver], 46 | ); 47 | 48 | @override 49 | AppScopeContainer createContainer() => AppScopeContainer(); 50 | } 51 | -------------------------------------------------------------------------------- /yx_state/packages/yx_state_transformers/lib/src/function_handlers.dart: -------------------------------------------------------------------------------- 1 | import 'package:yx_state/yx_state.dart'; 2 | 3 | import 'handle_task_transformers.dart'; 4 | 5 | /// Process tasks one at a time by maintaining a queue of added tasks 6 | /// and processing the tasks sequentially. 7 | /// 8 | /// This is the default and safest handler to use in most cases. 9 | FunctionHandler sequential() => 10 | StreamFunctionHandler( 11 | handleTransformer: HandleTaskTransformers.sequential()); 12 | 13 | /// Process tasks concurrently without any restrictions. 14 | /// 15 | /// Use with caution as concurrent state updates may lead to race conditions. 16 | FunctionHandler concurrent() => 17 | StreamFunctionHandler( 18 | handleTransformer: HandleTaskTransformers.concurrent()); 19 | 20 | /// Process only one task and ignore (drop) any new tasks 21 | /// until the current task is done. 22 | /// 23 | /// Useful for preventing spamming of state updates during ongoing operations. 24 | FunctionHandler droppable() => 25 | StreamFunctionHandler( 26 | handleTransformer: HandleTaskTransformers.droppable()); 27 | 28 | /// Process only one task by cancelling any pending tasks and 29 | /// processing the new task immediately. 30 | /// 31 | /// Useful for operations where only the latest request matters. 32 | FunctionHandler restartable() => 33 | StreamFunctionHandler( 34 | handleTransformer: HandleTaskTransformers.restartable()); 35 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/example/lib/ui/account/account_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:yx_scope_flutter/yx_scope_flutter.dart'; 3 | 4 | import '../../di/account/account_scope.dart'; 5 | import '../../di/app/app_scope.dart'; 6 | 7 | class AccountPage extends StatefulWidget { 8 | const AccountPage({super.key}); 9 | 10 | @override 11 | State createState() => _AccountPageState(); 12 | } 13 | 14 | class _AccountPageState extends State { 15 | @override 16 | Widget build(BuildContext context) => 17 | ScopeBuilder.withPlaceholder( 18 | builder: (context, appScope) { 19 | return Center( 20 | child: Column( 21 | mainAxisAlignment: MainAxisAlignment.center, 22 | children: [ 23 | ScopeBuilder( 24 | builder: (context, scope) { 25 | return Text('account: ${scope?.account.name}'); 26 | }, 27 | ), 28 | TextButton( 29 | onPressed: () { 30 | appScope.authManagerDep.get.logout(); 31 | }, 32 | child: const Text('logout'), 33 | ), 34 | TextButton( 35 | onPressed: () { 36 | appScope.authManagerDep.get.startRegister(); 37 | }, 38 | child: const Text('start register'), 39 | ), 40 | ], 41 | ), 42 | ); 43 | }, 44 | ); 45 | } 46 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_linter/lib/src/lints/pass_async_lifecycle_in_initialize_queue.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/error/error.dart' hide LintCode; 2 | import 'package:analyzer/error/listener.dart'; 3 | import 'package:custom_lint_builder/custom_lint_builder.dart'; 4 | import 'package:yx_scope_linter/src/types.dart'; 5 | 6 | import '../yx_scope_lint_rule.dart'; 7 | 8 | class PassAsyncLifecycleInInitializeQueue extends YXScopeLintRule { 9 | static const _code = LintCode( 10 | name: 'pass_async_lifecycle_in_initialize_queue', 11 | problemMessage: 12 | 'asyncDep (or rawAsyncDep) must be passed to initializeQueue. ' 13 | 'Otherwise init/dispose methods will not be called.', 14 | correctionMessage: 'Override method initializeQueue in the current scope' 15 | ' and pass the Dep there', 16 | errorSeverity: ErrorSeverity.WARNING, 17 | ); 18 | 19 | const PassAsyncLifecycleInInitializeQueue() : super(code: _code); 20 | 21 | @override 22 | void run( 23 | CustomLintResolver resolver, 24 | ErrorReporter reporter, 25 | CustomLintContext context, 26 | ) { 27 | yxScopeRegistry(context).addScopeDeclarations((scope) { 28 | if (scopeModuleType.isAssignableFromType(scope.type)) { 29 | return; 30 | } 31 | for (final dep in scope.deps.values) { 32 | if (dep.isSync) { 33 | continue; 34 | } 35 | if (!scope.initializeQueue.expand((element) => element).contains(dep)) { 36 | reporter.atToken( 37 | dep.nameToken, 38 | _code, 39 | ); 40 | } 41 | } 42 | }); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/example/lib/data/orders/incoming_orders_provider.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:math'; 3 | 4 | import 'package:flutter/foundation.dart'; 5 | 6 | import 'models/incoming_order_data.dart'; 7 | 8 | class IncomingOrdersProvider { 9 | IncomingOrderSession get incomingOrderSession => 10 | IncomingOrderSession().._init(); 11 | } 12 | 13 | class IncomingOrderSession { 14 | final _controller = StreamController.broadcast(); 15 | late final StreamSubscription _subscription; 16 | 17 | Stream get incomingOrdersStream => _controller.stream; 18 | 19 | bool get isPaused => _subscription.isPaused; 20 | 21 | void _init() { 22 | _subscription = Stream.periodic( 23 | const Duration(seconds: 5), 24 | (_) => IncomingOrder( 25 | fromAddress: _getRandomAddress(), 26 | toAddress: _getRandomAddress(), 27 | ), 28 | ).listen((order) { 29 | _log('income order ${order.uid}'); 30 | _controller.add(order); 31 | }); 32 | } 33 | 34 | Future dispose() async => await _subscription.cancel(); 35 | 36 | void pause() { 37 | _log('pause to receive orders'); 38 | _subscription.pause(); 39 | } 40 | 41 | void resume() { 42 | _log('resume to receive orders'); 43 | _subscription.resume(); 44 | } 45 | 46 | void _log(String message) { 47 | if (kDebugMode) { 48 | print(message); 49 | } 50 | } 51 | } 52 | 53 | Address _getRandomAddress() { 54 | final position = Random().nextInt(100); 55 | final street = 'Street $position'; 56 | return Address(name: street, position: position); 57 | } 58 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/example/lib/ui/register/register_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:yx_scope_flutter/yx_scope_flutter.dart'; 3 | 4 | import '../../di/app/app_scope.dart'; 5 | 6 | class RegisterPage extends StatefulWidget { 7 | const RegisterPage({super.key}); 8 | 9 | @override 10 | State createState() => _RegisterPageState(); 11 | } 12 | 13 | class _RegisterPageState extends State { 14 | @override 15 | Widget build(BuildContext context) => 16 | ScopeBuilder.withPlaceholder( 17 | builder: (context, appScope) => Scaffold( 18 | appBar: AppBar( 19 | title: const Text('Register'), 20 | leading: IconButton( 21 | onPressed: () { 22 | appScope.authManagerDep.get.stopRegister(); 23 | }, 24 | icon: const Icon(Icons.arrow_back), 25 | ), 26 | ), 27 | body: ScopeBuilder.withPlaceholder( 28 | holder: appScope.registerScopeHolderDep.get, 29 | builder: (context, registerScope) => Center( 30 | child: Column( 31 | mainAxisAlignment: MainAxisAlignment.center, 32 | children: [ 33 | const Text('Register New Bob'), 34 | TextButton( 35 | onPressed: () { 36 | registerScope.registerManagerDep.get.createAccount(); 37 | }, 38 | child: const Text('Create'), 39 | ), 40 | ], 41 | ), 42 | ), 43 | ), 44 | ), 45 | ); 46 | } 47 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_linter/example/lib/final_dep.dart: -------------------------------------------------------------------------------- 1 | import 'package:yx_scope/yx_scope.dart'; 2 | 3 | class SomeScope extends BaseScopeContainer { 4 | static const someConstant = 'some_constant'; 5 | 6 | @override 7 | List> get initializeQueue => [ 8 | {lateFinalAsyncDep} 9 | ]; 10 | 11 | var someRandomField = 'random_field'; 12 | 13 | late final lateFinalDep = dep(() => '1'); 14 | 15 | // expect_lint: final_dep 16 | late var finalDep = dep(() => '2'); 17 | 18 | late final lateFinalAsyncDep = asyncDep(() => SomeAsyncDep()); 19 | 20 | // expect_lint: final_dep, pass_async_lifecycle_in_initialize_queue 21 | late var lateAsyncDep = asyncDep(() => SomeAsyncDep()); 22 | 23 | // expect_lint: final_dep, pass_async_lifecycle_in_initialize_queue 24 | late AsyncDep explicitTypeLateAsyncDep = 25 | asyncDep(() => SomeAsyncDep()); 26 | 27 | // expect_lint: pass_async_lifecycle_in_initialize_queue 28 | late final AsyncDep explicitTypeLateFinalAsyncDep = 29 | asyncDep(() => SomeAsyncDep()); 30 | 31 | // expect_lint: final_dep, pass_async_lifecycle_in_initialize_queue 32 | late var lateRawAsyncDep = 33 | rawAsyncDep(() => '3', init: (dep) async {}, dispose: (dep) async {}); 34 | 35 | // expect_lint: pass_async_lifecycle_in_initialize_queue 36 | late final lateFinalRawAsyncDep = 37 | rawAsyncDep(() => '4', init: (dep) async {}, dispose: (dep) async {}); 38 | 39 | void someMethod() {} 40 | } 41 | 42 | class SomeAsyncDep implements AsyncLifecycle { 43 | const SomeAsyncDep(); 44 | 45 | @override 46 | Future init() async {} 47 | 48 | @override 49 | Future dispose() async {} 50 | } 51 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_linter/lib/src/yx_scope_lint_rule.dart: -------------------------------------------------------------------------------- 1 | import 'package:custom_lint_builder/custom_lint_builder.dart'; 2 | 3 | import 'resolved_yx_scope_result.dart'; 4 | import 'yx_scope_registry.dart'; 5 | 6 | /// A base [DartLintRule] that parses required classes 7 | /// and provides data for analysis 8 | /// 9 | /// Check [YXScopeRegistry] to see what can be tracked 10 | abstract class YXScopeLintRule extends DartLintRule { 11 | const YXScopeLintRule({required super.code}); 12 | 13 | static final _contextKey = Object(); 14 | 15 | @override 16 | Future startUp( 17 | CustomLintResolver resolver, 18 | CustomLintContext context, 19 | ) async { 20 | await _setup(resolver, context); 21 | await super.startUp(resolver, context); 22 | } 23 | 24 | YXScopeRegistry yxScopeRegistry(CustomLintContext context) { 25 | final registry = context.sharedState[_contextKey] as YXScopeRegistry?; 26 | if (registry == null) { 27 | throw StateError('YXScopeRegistry not initialized'); 28 | } 29 | return registry; 30 | } 31 | 32 | Future _setup( 33 | CustomLintResolver resolver, 34 | CustomLintContext context, 35 | ) async { 36 | final registry = context.sharedState[_contextKey] = YXScopeRegistry(); 37 | 38 | final watch = Stopwatch()..start(); 39 | 40 | final unit = await resolver.getResolvedUnitResult(); 41 | // Here we parse everything we need 42 | final result = await ResolvedYXScopeResult.from([unit.unit]); 43 | 44 | watch.stop(); 45 | print(': ${watch.elapsedMilliseconds}ms'); 46 | 47 | context.addPostRunCallback(() { 48 | // Here we store the parsed data and notify subscribers 49 | registry.run(result); 50 | }); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope/test/scope_state_test.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:test/test.dart'; 4 | import 'package:yx_scope/yx_scope.dart'; 5 | 6 | void main() { 7 | test('scope state is valid according to updates', () async { 8 | final scopeHolder = _TestScopeHolder(); 9 | 10 | final createCompleter = Completer(); 11 | final dropCompleter = Completer(); 12 | 13 | expect(scopeHolder.state, isA>()); 14 | expect(scopeHolder.state.none, isTrue); 15 | scopeHolder.create().then((_) => createCompleter.complete()); 16 | 17 | expect( 18 | scopeHolder.state, isA>()); 19 | expect(scopeHolder.state.initializing, isTrue); 20 | await createCompleter.future; 21 | 22 | expect(scopeHolder.state, isA>()); 23 | expect(scopeHolder.state.available, isTrue); 24 | 25 | scopeHolder.drop().then((_) => dropCompleter.complete()); 26 | 27 | expect(scopeHolder.state, isA>()); 28 | expect(scopeHolder.state.disposing, isTrue); 29 | 30 | await dropCompleter.future; 31 | 32 | expect(scopeHolder.state, isA>()); 33 | expect(scopeHolder.state.none, isTrue); 34 | }); 35 | } 36 | 37 | class _TestScopeHolder extends ScopeHolder<_TestScopeContainer> { 38 | @override 39 | _TestScopeContainer createContainer() => _TestScopeContainer(); 40 | } 41 | 42 | class _TestScopeContainer extends ScopeContainer { 43 | @override 44 | List> get initializeQueue => [ 45 | {_asyncDep} 46 | ]; 47 | 48 | late final _asyncDep = rawAsyncDep( 49 | () => Future.delayed(Duration.zero), 50 | init: (dep) async => await dep, 51 | dispose: (dep) async {}, 52 | ); 53 | } 54 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/example/lib/data/map/map.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/scheduler.dart'; 3 | 4 | class MapController extends ChangeNotifier { 5 | static const totalPositions = 100; 6 | static const itemHeight = 56.0; 7 | 8 | final ScrollController _scrollController; 9 | 10 | final Map items = Map.fromEntries( 11 | List.generate( 12 | 100, 13 | (i) => MapItem(name: 'Street $i', position: i, selected: false), 14 | ).map( 15 | (item) => MapEntry(item.position, item), 16 | ), 17 | ); 18 | 19 | MapController(this._scrollController); 20 | 21 | void selectPosition(int position, {required bool selected}) { 22 | assert( 23 | position >= 0 && position < totalPositions, 24 | 'Your position is out of our simulated map', 25 | ); 26 | items[position] = items[position]!.copyWith(selected: selected); 27 | notifyListeners(); 28 | } 29 | 30 | void clear() { 31 | for (final position in items.keys) { 32 | items[position] = items[position]!.copyWith(selected: false); 33 | } 34 | notifyListeners(); 35 | } 36 | 37 | void focus(int position) { 38 | SchedulerBinding.instance.addPostFrameCallback((_) => 39 | _scrollController.animateTo((position - 2) * itemHeight, 40 | duration: const Duration(milliseconds: 200), curve: Curves.ease)); 41 | } 42 | } 43 | 44 | class MapItem { 45 | final String name; 46 | final int position; 47 | final bool selected; 48 | 49 | const MapItem({ 50 | required this.name, 51 | required this.position, 52 | required this.selected, 53 | }); 54 | 55 | MapItem copyWith({ 56 | bool? selected, 57 | }) { 58 | return MapItem( 59 | selected: selected ?? this.selected, 60 | name: name, 61 | position: position, 62 | ); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_linter/example/lib/avoid_async_dep_child_scope.dart: -------------------------------------------------------------------------------- 1 | import 'package:yx_scope/yx_scope.dart'; 2 | 3 | class _ParentScopeContainer extends ScopeContainer { 4 | @override 5 | List> get initializeQueue => [ 6 | {_childScopeHolderRawAsyncDep, _childScopeHolderAsyncDep} 7 | ]; 8 | 9 | // expect_lint: avoid_async_dep_child_scope 10 | late final _childScopeHolderRawAsyncDep = rawAsyncDep( 11 | () => _ChildScopeHolder(this), 12 | init: (dep) async => await dep.create(), 13 | dispose: (dep) async => await dep.drop(), 14 | ); 15 | 16 | // expect_lint: avoid_async_dep_child_scope 17 | late final _childScopeHolderAsyncDep = 18 | asyncDep(() => _ChildAsyncLifecycleScopeContainer()); 19 | 20 | late final parentModule = ParentScopeModule(this); 21 | } 22 | 23 | class ParentScopeModule extends ScopeModule<_ParentScopeContainer> { 24 | ParentScopeModule(super.container); 25 | 26 | // expect_lint: avoid_async_dep_child_scope 27 | late final childScopeHolderRawAsyncDep = rawAsyncDep( 28 | () => _ChildScopeHolder(this.container), 29 | init: (dep) async => await dep.create(), 30 | dispose: (dep) async => await dep.drop(), 31 | ); 32 | } 33 | 34 | class _ChildScopeHolder 35 | extends ChildScopeHolder<_ChildScopeContainer, _ParentScopeContainer> { 36 | _ChildScopeHolder(super.parent); 37 | 38 | @override 39 | _ChildScopeContainer createContainer(_ParentScopeContainer parent) => 40 | _ChildScopeContainer(parent: parent); 41 | } 42 | 43 | class _ChildScopeContainer extends ChildScopeContainer<_ParentScopeContainer> { 44 | _ChildScopeContainer({required super.parent}); 45 | } 46 | 47 | class _ChildAsyncLifecycleScopeContainer extends ScopeContainer 48 | implements AsyncLifecycle { 49 | @override 50 | Future dispose() async {} 51 | 52 | @override 53 | Future init() async {} 54 | } 55 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope/lib/src/scope_module.dart: -------------------------------------------------------------------------------- 1 | part of 'base_scope_container.dart'; 2 | 3 | /// This class helps to decompose [ScopeContainer] into a number of features. 4 | /// 5 | /// For example we want to extract all monitoring entities into a separate feature: 6 | /// 7 | /// class SomeScopeContainer extends ScopeContainer { 8 | /// // We declare [ScopeModule] inside it's [ScopeContainer] 9 | /// late final monitorScopeModule = MonitorScopeModule(this); 10 | /// 11 | /// late final appManagerDep = dep(() => AppManager()); 12 | /// 13 | /// late final navigationDep = dep(() => Navigation()); 14 | /// } 15 | /// 16 | /// class MonitorScopeModule extends ScopeModule { 17 | /// MonitorScopeModule(super.container); 18 | /// 19 | /// late final reporterDep = dep(() => Reporter()); 20 | /// 21 | /// late final loggerDep = dep(() => Logger()); 22 | /// } 23 | abstract class ScopeModule { 24 | final Container container; 25 | 26 | const ScopeModule(this.container); 27 | 28 | @protected 29 | Dep dep( 30 | DepBuilder builder, { 31 | String? name, 32 | }) => 33 | container.dep(builder, name: name); 34 | 35 | @protected 36 | AsyncDep asyncDep( 37 | DepBuilder builder, { 38 | String? name, 39 | }) => 40 | container.rawAsyncDep( 41 | builder, 42 | init: (value) => value.init(), 43 | dispose: (value) => value.dispose(), 44 | name: name, 45 | ); 46 | 47 | @protected 48 | AsyncDep rawAsyncDep( 49 | DepBuilder builder, { 50 | required AsyncDepCallback init, 51 | required AsyncDepCallback dispose, 52 | String? name, 53 | }) => 54 | container.rawAsyncDep( 55 | builder, 56 | init: init, 57 | dispose: dispose, 58 | name: name, 59 | ); 60 | } 61 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Notice to external contributors 2 | ### General info 3 | Hello! In order for us (YANDEX LLC) to accept patches and other contributions from you, you will have to adopt our Contributor License Agreement (the “CLA”). The current version of the CLA you may find here: 4 | 5 | * https://yandex.ru/legal/cla/?lang=en (in English) 6 | * https://yandex.ru/legal/cla/?lang=ru (in Russian). 7 | 8 | By adopting the CLA, you state the following: 9 | 10 | * You obviously wish and are willingly licensing your contributions to us for our open source projects under the terms of the CLA, 11 | * You have read the terms and conditions of the CLA and agree with them in full, 12 | * You are legally able to provide and license your contributions as stated, 13 | * We may use your contributions for our open source projects and for any other our project too, 14 | * We rely on your assurances concerning the rights of third parties in relation to your contributions. 15 | 16 | If you agree with these principles, please read and adopt our CLA. By providing us your contributions, you hereby declare that you have read and adopted our CLA, and we may freely merge your contributions with our corresponding open source project and use it in further in accordance with terms and conditions of the CLA. 17 | 18 | ### Provide contributions 19 | If you have adopted terms and conditions of the CLA, you are able to provide your contributions. When you submit your pull request, please add the following information into it: 20 | 21 | ``` 22 | I hereby agree to the terms of the CLA available at: [link]. 23 | ``` 24 | 25 | Replace the bracketed text as follows: 26 | 27 | * [link] is the link at the current version of the CLA (you may add here a link https://yandex.ru/legal/cla/?lang=en (in English) or a link https://yandex.ru/legal/cla/?lang=ru (in Russian). 28 | It is enough to provide us with such notification once. 29 | 30 | ### Other questions 31 | If you have any questions, please write us at opensource@yandex-team.ru. -------------------------------------------------------------------------------- /yx_scope/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Notice to external contributors 2 | ### General info 3 | Hello! In order for us (YANDEX LLC) to accept patches and other contributions from you, you will have to adopt our Contributor License Agreement (the “CLA”). The current version of the CLA you may find here: 4 | 5 | * https://yandex.ru/legal/cla/?lang=en (in English) 6 | * https://yandex.ru/legal/cla/?lang=ru (in Russian). 7 | 8 | By adopting the CLA, you state the following: 9 | 10 | * You obviously wish and are willingly licensing your contributions to us for our open source projects under the terms of the CLA, 11 | * You have read the terms and conditions of the CLA and agree with them in full, 12 | * You are legally able to provide and license your contributions as stated, 13 | * We may use your contributions for our open source projects and for any other our project too, 14 | * We rely on your assurances concerning the rights of third parties in relation to your contributions. 15 | 16 | If you agree with these principles, please read and adopt our CLA. By providing us your contributions, you hereby declare that you have read and adopted our CLA, and we may freely merge your contributions with our corresponding open source project and use it in further in accordance with terms and conditions of the CLA. 17 | 18 | ### Provide contributions 19 | If you have adopted terms and conditions of the CLA, you are able to provide your contributions. When you submit your pull request, please add the following information into it: 20 | 21 | ``` 22 | I hereby agree to the terms of the CLA available at: [link]. 23 | ``` 24 | 25 | Replace the bracketed text as follows: 26 | 27 | * [link] is the link at the current version of the CLA (you may add here a link https://yandex.ru/legal/cla/?lang=en (in English) or a link https://yandex.ru/legal/cla/?lang=ru (in Russian). 28 | It is enough to provide us with such notification once. 29 | 30 | ### Other questions 31 | If you have any questions, please write us at opensource@yandex-team.ru. 32 | -------------------------------------------------------------------------------- /yx_state/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Notice to external contributors 2 | ### General info 3 | Hello! In order for us (YANDEX LLC) to accept patches and other contributions from you, you will have to adopt our Contributor License Agreement (the “CLA”). The current version of the CLA you may find here: 4 | 5 | * https://yandex.ru/legal/cla/?lang=en (in English) 6 | * https://yandex.ru/legal/cla/?lang=ru (in Russian). 7 | 8 | By adopting the CLA, you state the following: 9 | 10 | * You obviously wish and are willingly licensing your contributions to us for our open source projects under the terms of the CLA, 11 | * You have read the terms and conditions of the CLA and agree with them in full, 12 | * You are legally able to provide and license your contributions as stated, 13 | * We may use your contributions for our open source projects and for any other our project too, 14 | * We rely on your assurances concerning the rights of third parties in relation to your contributions. 15 | 16 | If you agree with these principles, please read and adopt our CLA. By providing us your contributions, you hereby declare that you have read and adopted our CLA, and we may freely merge your contributions with our corresponding open source project and use it in further in accordance with terms and conditions of the CLA. 17 | 18 | ### Provide contributions 19 | If you have adopted terms and conditions of the CLA, you are able to provide your contributions. When you submit your pull request, please add the following information into it: 20 | 21 | ``` 22 | I hereby agree to the terms of the CLA available at: [link]. 23 | ``` 24 | 25 | Replace the bracketed text as follows: 26 | 27 | * [link] is the link at the current version of the CLA (you may add here a link https://yandex.ru/legal/cla/?lang=en (in English) or a link https://yandex.ru/legal/cla/?lang=ru (in Russian). 28 | It is enough to provide us with such notification once. 29 | 30 | ### Other questions 31 | If you have any questions, please write us at opensource@yandex-team.ru. 32 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_linter/lib/src/visitors/parse_initialize_queue_visitor.dart: -------------------------------------------------------------------------------- 1 | part of '../resolved_yx_scope_result.dart'; 2 | 3 | /// Visitor that parses the dependency initialization queue 4 | /// for a [scope] declaration by analyzing the initializeQueue method. 5 | class _ParseInitializeQueueVisitor extends SimpleAstVisitor { 6 | final ScopeDeclaration scope; 7 | 8 | /// Tracks the current scope during traversal 9 | late BaseScopeDeclaration _curScope = scope; 10 | 11 | _ParseInitializeQueueVisitor(this.scope); 12 | 13 | @override 14 | void visitListLiteral(ListLiteral node) { 15 | // Process all elements in a list literal 16 | node.visitChildren(this); 17 | } 18 | 19 | @override 20 | void visitSetOrMapLiteral(SetOrMapLiteral node) { 21 | // Create a new queue section for each set/map literal 22 | scope.addScopeQueue(); 23 | node.visitChildren(this); 24 | } 25 | 26 | @override 27 | void visitSimpleIdentifier(SimpleIdentifier node) { 28 | final type = node.staticType; 29 | if (type == null) { 30 | return; 31 | } 32 | 33 | // Handle module references - update current scope context 34 | if (scopeModuleType.isAssignableFromType(type)) { 35 | final curScope = _curScope.modules[node.name]; 36 | if (curScope != null) { 37 | _curScope = curScope; 38 | } 39 | } 40 | 41 | // Handle dependency references - add to initialization queue 42 | if (anyDepValueTypes.isAssignableFromType(type)) { 43 | final dep = _curScope.deps[node.name]; 44 | if (dep != null) { 45 | scope.addDepToQueue(dep); 46 | } 47 | _curScope = scope; 48 | } 49 | } 50 | 51 | @override 52 | void visitMethodDeclaration(MethodDeclaration node) { 53 | // Only process the initializeQueue method 54 | if (node.declaredElement?.name != MethodNames.initializeQueue) { 55 | return; 56 | } 57 | // Process the method body to find dependencies 58 | node.body.visitChildren(this); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /yx_state/packages/yx_state/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Notice to external contributors 2 | ### General info 3 | Hello! In order for us (YANDEX LLC) to accept patches and other contributions from you, you will have to adopt our Contributor License Agreement (the “CLA”). The current version of the CLA you may find here: 4 | 5 | * https://yandex.ru/legal/cla/?lang=en (in English) 6 | * https://yandex.ru/legal/cla/?lang=ru (in Russian). 7 | 8 | By adopting the CLA, you state the following: 9 | 10 | * You obviously wish and are willingly licensing your contributions to us for our open source projects under the terms of the CLA, 11 | * You have read the terms and conditions of the CLA and agree with them in full, 12 | * You are legally able to provide and license your contributions as stated, 13 | * We may use your contributions for our open source projects and for any other our project too, 14 | * We rely on your assurances concerning the rights of third parties in relation to your contributions. 15 | 16 | If you agree with these principles, please read and adopt our CLA. By providing us your contributions, you hereby declare that you have read and adopted our CLA, and we may freely merge your contributions with our corresponding open source project and use it in further in accordance with terms and conditions of the CLA. 17 | 18 | ### Provide contributions 19 | If you have adopted terms and conditions of the CLA, you are able to provide your contributions. When you submit your pull request, please add the following information into it: 20 | 21 | ``` 22 | I hereby agree to the terms of the CLA available at: [link]. 23 | ``` 24 | 25 | Replace the bracketed text as follows: 26 | 27 | * [link] is the link at the current version of the CLA (you may add here a link https://yandex.ru/legal/cla/?lang=en (in English) or a link https://yandex.ru/legal/cla/?lang=ru (in Russian). 28 | It is enough to provide us with such notification once. 29 | 30 | ### Other questions 31 | If you have any questions, please write us at opensource@yandex-team.ru. 32 | -------------------------------------------------------------------------------- /yx_state/packages/yx_state_flutter/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Notice to external contributors 2 | ### General info 3 | Hello! In order for us (YANDEX LLC) to accept patches and other contributions from you, you will have to adopt our Contributor License Agreement (the “CLA”). The current version of the CLA you may find here: 4 | 5 | * https://yandex.ru/legal/cla/?lang=en (in English) 6 | * https://yandex.ru/legal/cla/?lang=ru (in Russian). 7 | 8 | By adopting the CLA, you state the following: 9 | 10 | * You obviously wish and are willingly licensing your contributions to us for our open source projects under the terms of the CLA, 11 | * You have read the terms and conditions of the CLA and agree with them in full, 12 | * You are legally able to provide and license your contributions as stated, 13 | * We may use your contributions for our open source projects and for any other our project too, 14 | * We rely on your assurances concerning the rights of third parties in relation to your contributions. 15 | 16 | If you agree with these principles, please read and adopt our CLA. By providing us your contributions, you hereby declare that you have read and adopted our CLA, and we may freely merge your contributions with our corresponding open source project and use it in further in accordance with terms and conditions of the CLA. 17 | 18 | ### Provide contributions 19 | If you have adopted terms and conditions of the CLA, you are able to provide your contributions. When you submit your pull request, please add the following information into it: 20 | 21 | ``` 22 | I hereby agree to the terms of the CLA available at: [link]. 23 | ``` 24 | 25 | Replace the bracketed text as follows: 26 | 27 | * [link] is the link at the current version of the CLA (you may add here a link https://yandex.ru/legal/cla/?lang=en (in English) or a link https://yandex.ru/legal/cla/?lang=ru (in Russian). 28 | It is enough to provide us with such notification once. 29 | 30 | ### Other questions 31 | If you have any questions, please write us at opensource@yandex-team.ru. 32 | -------------------------------------------------------------------------------- /yx_state/packages/yx_state_transformers/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Notice to external contributors 2 | ### General info 3 | Hello! In order for us (YANDEX LLC) to accept patches and other contributions from you, you will have to adopt our Contributor License Agreement (the “CLA”). The current version of the CLA you may find here: 4 | 5 | * https://yandex.ru/legal/cla/?lang=en (in English) 6 | * https://yandex.ru/legal/cla/?lang=ru (in Russian). 7 | 8 | By adopting the CLA, you state the following: 9 | 10 | * You obviously wish and are willingly licensing your contributions to us for our open source projects under the terms of the CLA, 11 | * You have read the terms and conditions of the CLA and agree with them in full, 12 | * You are legally able to provide and license your contributions as stated, 13 | * We may use your contributions for our open source projects and for any other our project too, 14 | * We rely on your assurances concerning the rights of third parties in relation to your contributions. 15 | 16 | If you agree with these principles, please read and adopt our CLA. By providing us your contributions, you hereby declare that you have read and adopted our CLA, and we may freely merge your contributions with our corresponding open source project and use it in further in accordance with terms and conditions of the CLA. 17 | 18 | ### Provide contributions 19 | If you have adopted terms and conditions of the CLA, you are able to provide your contributions. When you submit your pull request, please add the following information into it: 20 | 21 | ``` 22 | I hereby agree to the terms of the CLA available at: [link]. 23 | ``` 24 | 25 | Replace the bracketed text as follows: 26 | 27 | * [link] is the link at the current version of the CLA (you may add here a link https://yandex.ru/legal/cla/?lang=en (in English) or a link https://yandex.ru/legal/cla/?lang=ru (in Russian). 28 | It is enough to provide us with such notification once. 29 | 30 | ### Other questions 31 | If you have any questions, please write us at opensource@yandex-team.ru. 32 | -------------------------------------------------------------------------------- /yx_state/packages/yx_state/lib/src/state_manager.dart: -------------------------------------------------------------------------------- 1 | import 'base/interface.dart'; 2 | import 'base/state_manager_base.dart'; 3 | import 'mixin/state_manager_listener_mixin.dart'; 4 | import 'state_manager_overrides.dart'; 5 | 6 | /// Main class for state management that should be extended by users. 7 | /// 8 | /// This class combines the core state management functionality from 9 | /// [StateManagerBase] with the default listener implementation from 10 | /// [StateManagerListenerMixin]. 11 | /// 12 | /// Usage example: 13 | /// ```dart 14 | /// class CounterState { 15 | /// final int count; 16 | /// 17 | /// CounterState(this.count); 18 | /// } 19 | /// 20 | /// class CounterManager extends StateManager { 21 | /// CounterManager() : super(CounterState(0)); 22 | /// 23 | /// Future increment() => handle((emit) async { 24 | /// emit(CounterState(state.count + 1)); 25 | /// }); 26 | /// 27 | /// Future decrement() => handle((emit) async { 28 | /// emit(CounterState(state.count - 1)); 29 | /// }); 30 | /// } 31 | /// ``` 32 | abstract class StateManager 33 | extends StateManagerBase with StateManagerListenerMixin { 34 | /// Creates a new [StateManager] with the provided initial [state] 35 | /// and optional function [handler]. 36 | /// 37 | /// If no [handler] is provided, the default handler from [StateManagerOverrides.defaultHandlerFactory] 38 | /// will be used, which is a sequential handler by default. 39 | StateManager( 40 | super.state, { 41 | FunctionHandler? handler, 42 | }) : super(handler: handler ?? _createDefaultHandler()); 43 | 44 | /// Creates a default handler for the given state type. 45 | /// 46 | /// This method is used to create a default handler for the given state type. 47 | /// It uses the [StateManagerOverrides.defaultHandlerFactory] to create a new handler. 48 | static FunctionHandler _createDefaultHandler() { 49 | return StateManagerOverrides.defaultHandlerFactory(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /yx_state/packages/yx_state/lib/src/test_util/state_manager_base_test_util.dart: -------------------------------------------------------------------------------- 1 | part of '../base/state_manager_base.dart'; 2 | 3 | /// {@template state_manager_base_test_util} 4 | /// A utility class for testing [StateManagerBase] instances. 5 | /// 6 | /// This class provides a method to set the state of the state manager without 7 | /// going through the normal handler flow. 8 | /// 9 | /// {@tool snippet} 10 | /// ```dart 11 | /// import 'package:test/test.dart'; 12 | /// import 'package:yx_state/yx_state.dart'; 13 | /// 14 | /// class TestStateManager extends StateManager { 15 | /// TestStateManager() : super(0); 16 | /// } 17 | /// 18 | /// void main() { 19 | /// late TestStateManager stateManager; 20 | /// late StateManagerBaseTestUtil testUtil; 21 | /// 22 | /// setUp(() { 23 | /// stateManager = TestStateManager(); 24 | /// testUtil = StateManagerBaseTestUtil(stateManager); 25 | /// }); 26 | /// 27 | /// tearDown(() => stateManager.close()); 28 | /// 29 | /// test('util usage example', () { 30 | /// // arrange 31 | /// const expectedState = 1; 32 | /// 33 | /// // act 34 | /// testUtil.emit(expectedState); 35 | /// 36 | /// // assert 37 | /// expect(stateManager.state, expectedState); 38 | /// }); 39 | /// } 40 | /// ``` 41 | /// {@end-tool} 42 | /// {@endtemplate} 43 | @visibleForTesting 44 | class StateManagerBaseTestUtil { 45 | final StateManagerBase _stateManager; 46 | 47 | /// The identifier for the test util. 48 | String get identifier => _identifier; 49 | 50 | /// {@macro state_manager_base_test_util} 51 | const StateManagerBaseTestUtil(this._stateManager); 52 | 53 | /// Base constant identifier for the test util. 54 | static const String _identifier = 'test_util'; 55 | 56 | /// Sets the state to the provided value without going through the normal 57 | /// handler flow. 58 | /// 59 | /// This method is primarily intended for testing purposes. It is not 60 | /// recommended to use this method in production code. 61 | @visibleForTesting 62 | void emit(State state) => _stateManager._emit(state, identifier); 63 | } 64 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/README.md: -------------------------------------------------------------------------------- 1 | yx_scope_flutter package is an adapter for yx_scope package for using it with Flutter. 2 | 3 | ## Features 4 | 5 | `ScopeProvider` is an `InheritedWidget` that passes your scope down to any widget in the subtree. 6 | 7 | `ScopeBuilder` handles building a widget in response to `scope`. 8 | 9 | `ScopeListener` is a widget that should be used for functionality that needs to occur only in 10 | response to a `scope` change such as navigation, showing a `SnackBar`, showing a `Dialog`, etc... 11 | 12 | `ScopeConsumer` is analogous to a nested `ScopeListener` and `ScopeBuilder` but reduces the amount 13 | of boilerplate needed. 14 | 15 | ## Usage 16 | 17 | Lets take a look at how to use `ScopeProvider` to provide `RootScopeHolder` and react to scope 18 | changes with `ScopeBuilder` 19 | 20 | 1. Create instance of `RootScopeHolder` and run app 21 | 22 | ```dart 23 | void main() { 24 | final rootScopeHolder = RootScopeHolder(); 25 | rootScopeHolder.create(); 26 | runApp(App(scopeHolder: rootScopeHolder)); 27 | } 28 | ``` 29 | 30 | 2. Use `ScopeProvider` to provide `RootScopeContainer` 31 | 32 | ```dart 33 | class App extends StatelessWidget { 34 | final RootScopeHolder scopeHolder; 35 | 36 | const App({required this.scopeHolder, super.key}); 37 | 38 | @override 39 | Widget build(BuildContext context) { 40 | return ScopeProvider( 41 | holder: scopeHolder, 42 | child: MaterialApp( 43 | title: 'YxScopedFlutter Demo', 44 | home: const HomePage(), 45 | ), 46 | ); 47 | } 48 | } 49 | ``` 50 | 51 | 3. Use `ScopeBuilder` to react to scope changes for build widget 52 | 53 | ```dart 54 | // HomePage widget 55 | //... 56 | Widget build() => 57 | ScopeBuilder.withPlaceholder( 58 | builder: (context, rootScope) => SomeWidget(rootScope), 59 | placeholder: const SizedBox.shrink(), 60 | ); 61 | // ... 62 | ``` 63 | 64 | ## More details 65 | 66 | Read full introduction to the library in [the documentation](https://github.com/yandex/city-services-pub/blob/main/yx_scope/packages/yx_scope_flutter/doc/introduction.md). 67 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/example/lib/domain/order/accept_order_manager.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:yx_scope/yx_scope.dart'; 4 | 5 | import '../../data/orders/incoming_orders_provider.dart'; 6 | import 'models/order.dart'; 7 | import 'orders_state_holder.dart'; 8 | 9 | /// This manager listens for incoming orders. 10 | /// Has a [_maxCountOfOrders] limit. 11 | class AcceptOrderManager implements AsyncLifecycle { 12 | static const _maxCountOfOrders = 3; 13 | 14 | final OrdersStateHolder _ordersStateHolder; 15 | final IncomingOrdersProvider _incomingOrdersProvider; 16 | 17 | final _controller = StreamController.broadcast(); 18 | late IncomingOrderSession _session; 19 | StreamSubscription? _maxCountOfOrdersSub; 20 | 21 | AcceptOrderManager( 22 | this._ordersStateHolder, 23 | this._incomingOrdersProvider, 24 | ); 25 | 26 | Stream get toAcceptOrdersStream => _controller.stream; 27 | 28 | Future acceptOrder(Order order) async { 29 | await _ordersStateHolder.addOrder(order); 30 | _resumeIfNeeded(); 31 | } 32 | 33 | Future cancelOrder() async { 34 | _resumeIfNeeded(); 35 | } 36 | 37 | @override 38 | Future init() async { 39 | _session = _incomingOrdersProvider.incomingOrderSession; 40 | _session.incomingOrdersStream.listen((incomingOrder) { 41 | _controller.add(Order(incomingOrder)); 42 | _session.pause(); 43 | }); 44 | _maxCountOfOrdersSub = _ordersStateHolder.ordersStream 45 | .map((orders) => orders.length) 46 | .listen((countOfOrders) { 47 | if (countOfOrders >= _maxCountOfOrders) { 48 | _session.pause(); 49 | } else { 50 | _resumeIfNeeded(); 51 | } 52 | }); 53 | } 54 | 55 | @override 56 | Future dispose() async { 57 | await _controller.close(); 58 | await _maxCountOfOrdersSub?.cancel(); 59 | await _session.dispose(); 60 | } 61 | 62 | void _resumeIfNeeded() { 63 | final session = _session; 64 | if (session.isPaused && 65 | _ordersStateHolder.orders.length < _maxCountOfOrders) { 66 | session.resume(); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/example/lib/di/online_order/online_scope.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:yx_scope/yx_scope.dart'; 4 | 5 | import '../../data/orders/incoming_orders_provider.dart'; 6 | import '../../domain/order/accept_order_manager.dart'; 7 | import '../../domain/order/online_order_state_holder.dart'; 8 | import '../../domain/order/orders_state_holder.dart'; 9 | import '../utils/listeners.dart'; 10 | 11 | abstract class OnlineScope implements Scope { 12 | AcceptOrderManager get acceptOrderManager; 13 | } 14 | 15 | abstract class OnlineScopeParent implements Scope { 16 | OrdersStateHolder get ordersStateHolder; 17 | } 18 | 19 | class OnlineScopeContainer extends ChildScopeContainer 20 | implements OnlineScope { 21 | OnlineScopeContainer({required super.parent}); 22 | 23 | @override 24 | List> get initializeQueue => [ 25 | {_acceptOrderManagerDep} 26 | ]; 27 | 28 | late final _incomingOrdersProviderDep = dep(() => IncomingOrdersProvider()); 29 | 30 | late final _acceptOrderManagerDep = asyncDep(() => AcceptOrderManager( 31 | parent.ordersStateHolder, 32 | _incomingOrdersProviderDep.get, 33 | )); 34 | 35 | @override 36 | AcceptOrderManager get acceptOrderManager => _acceptOrderManagerDep.get; 37 | } 38 | 39 | class OnlineScopeHolder extends BaseChildScopeHolder implements OnlineOrderStateHolder { 41 | OnlineScopeHolder(super.parent) 42 | : super( 43 | scopeObservers: [diObserver], 44 | depObservers: [diObserver], 45 | asyncDepObservers: [diObserver], 46 | ); 47 | 48 | @override 49 | OnlineScopeContainer createContainer(OnlineScopeParent parent) => 50 | OnlineScopeContainer(parent: parent); 51 | 52 | @override 53 | Stream get isOnlineStream => stream.map((scope) => scope != null); 54 | 55 | @override 56 | bool get isOnline => scope != null; 57 | 58 | @override 59 | Future toggle() async { 60 | if (scope == null) { 61 | await create(); 62 | return; 63 | } 64 | await drop(); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/example/lib/di/map/map_scope.dart: -------------------------------------------------------------------------------- 1 | import 'package:yx_scope/yx_scope.dart'; 2 | 3 | import '../../data/map/map.dart'; 4 | import '../../domain/map/map_factory.dart'; 5 | import '../../domain/map/map_manager.dart'; 6 | import '../../domain/map_navigation/map_navigation_manager.dart'; 7 | import '../map_navigation/map_navigation_scope.dart'; 8 | import '../utils/listeners.dart'; 9 | 10 | abstract class MapScope implements Scope { 11 | MapNavigationScopeHolder get mapNavigationScopeHolder; 12 | 13 | MapController get controller; 14 | 15 | MapManager get mapManager; 16 | 17 | MapNavigationHolder get mapNavigationHolder; 18 | } 19 | 20 | class MapScopeContainer extends DataScopeContainer 21 | implements MapScope, MapNavigationParent { 22 | MapScopeContainer({required super.data}); 23 | 24 | late final _mapNavigationScopeHolderDep = 25 | dep(() => MapNavigationScopeHolder(this)); 26 | 27 | late final _mapManagerDep = 28 | dep(() => MapManager(data, _mapNavigationScopeHolderDep.get)); 29 | 30 | @override 31 | MapNavigationScopeHolder get mapNavigationScopeHolder => 32 | _mapNavigationScopeHolderDep.get; 33 | 34 | @override 35 | MapManager get mapManager => _mapManagerDep.get; 36 | 37 | @override 38 | MapController get controller => data; 39 | 40 | @override 41 | MapNavigationHolder get mapNavigationHolder => 42 | _mapNavigationScopeHolderDep.get; 43 | 44 | @override 45 | void onNavigationStop() => _mapNavigationScopeHolderDep.get.drop(); 46 | } 47 | 48 | class MapScopeHolder 49 | extends BaseDataScopeHolder 50 | implements MapInitializer { 51 | MapScopeHolder() 52 | : super( 53 | scopeObservers: [diObserver], 54 | depObservers: [diObserver], 55 | asyncDepObservers: [diObserver], 56 | ); 57 | 58 | @override 59 | MapScopeContainer createContainer(MapController data) => 60 | MapScopeContainer(data: data); 61 | 62 | @override 63 | Future createMap(MapController mapController) => create(mapController); 64 | 65 | @override 66 | Future dropMap() => drop(); 67 | } 68 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/example/lib/di/map_navigation/map_navigation_scope.dart: -------------------------------------------------------------------------------- 1 | import 'package:yx_scope/yx_scope.dart'; 2 | 3 | import '../../domain/map/map_manager.dart'; 4 | import '../../domain/map_navigation/map_navigation_manager.dart'; 5 | import '../utils/listeners.dart'; 6 | 7 | abstract class MapNavigationScope { 8 | MapNavigationManager get mapNavigationManager; 9 | } 10 | 11 | abstract class MapNavigationParent implements Scope { 12 | MapManager get mapManager; 13 | 14 | void onNavigationStop(); 15 | } 16 | 17 | class MapNavigationScopeContainer 18 | extends ChildDataScopeContainer 19 | implements MapNavigationScope { 20 | MapNavigationScopeContainer({required super.parent, required super.data}); 21 | 22 | @override 23 | List> get initializeQueue => [ 24 | {_mapNavigationManagerDep} 25 | ]; 26 | 27 | late final _mapNavigationManagerDep = rawAsyncDep( 28 | () => MapNavigationManager( 29 | data, 30 | parent.mapManager, 31 | parent.onNavigationStop, 32 | ), 33 | init: (dep) async => dep.prepare(), 34 | dispose: (dep) async {}, 35 | ); 36 | 37 | @override 38 | MapNavigationManager get mapNavigationManager => _mapNavigationManagerDep.get; 39 | } 40 | 41 | class MapNavigationScopeHolder extends BaseChildDataScopeHolder< 42 | MapNavigationScope, 43 | MapNavigationScopeContainer, 44 | MapNavigationParent, 45 | MapNavigationParams> implements MapNavigationHolder { 46 | MapNavigationScopeHolder(super.parent) 47 | : super( 48 | scopeObservers: [diObserver], 49 | depObservers: [diObserver], 50 | asyncDepObservers: [diObserver], 51 | ); 52 | 53 | @override 54 | MapNavigationScopeContainer createContainer( 55 | MapNavigationParent parent, 56 | MapNavigationParams data, 57 | ) => 58 | MapNavigationScopeContainer(parent: parent, data: data); 59 | 60 | @override 61 | Future startNavigation(MapNavigationParams navigationParams) => 62 | create(navigationParams); 63 | 64 | @override 65 | Future stopNavigation() => drop(); 66 | } 67 | -------------------------------------------------------------------------------- /yx_state/packages/yx_state_flutter/README.md: -------------------------------------------------------------------------------- 1 | # yx_state_flutter 2 | 3 | Flutter widgets for [yx_state](https://github.com/yandex/yx_state). 4 | 5 | ## Installation 6 | 7 | Add this package to your `pubspec.yaml` file: 8 | 9 | ```yaml 10 | dependencies: 11 | yx_state_flutter: 12 | ``` 13 | 14 | ## Widgets 15 | 16 | The package provides several widgets to help you manage state in your Flutter application: 17 | 18 | ### StateBuilder 19 | 20 | A widget that rebuilds its UI in response to state changes. 21 | 22 | ```dart 23 | StateBuilder( 24 | stateReadable: loginController, 25 | builder: (context, state, child) { 26 | return Text('Current status: ${state.status}'); 27 | }, 28 | ) 29 | ``` 30 | 31 | ### StateListener 32 | 33 | A widget that performs side effects in response to state changes without rebuilding the UI. 34 | 35 | ```dart 36 | StateListener( 37 | stateReadable: loginController, 38 | listener: (context, state) { 39 | if (state.status == LoginStatus.error) { 40 | ScaffoldMessenger.of(context).showSnackBar( 41 | SnackBar(content: Text(state.errorMessage ?? 'Error')), 42 | ); 43 | } 44 | }, 45 | ... 46 | ) 47 | ``` 48 | 49 | ### StateConsumer 50 | 51 | A widget that combines both StateBuilder and StateListener functionality. 52 | 53 | ```dart 54 | StateConsumer( 55 | stateReadable: loginController, 56 | listener: (context, state) { 57 | if (state.status == LoginStatus.success) { 58 | Navigator.of(context).pushReplacementNamed('/home'); 59 | } 60 | }, 61 | builder: (context, state, child) { 62 | return LoginForm(isLoading: state.status == LoginStatus.loading); 63 | }, 64 | ) 65 | ``` 66 | 67 | ### StateSelector 68 | 69 | A widget that rebuilds only when specific parts of the state change. 70 | 71 | ```dart 72 | StateSelector( 73 | stateReadable: loginController, 74 | selector: (state) => state.errorMessage, 75 | builder: (context, errorMessage, child) { 76 | return errorMessage != null 77 | ? Text(errorMessage, style: TextStyle(color: Colors.red)) 78 | : SizedBox.shrink(); 79 | }, 80 | ) 81 | ``` 82 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope/test/deprecated_listeners_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:test/test.dart'; 2 | import 'package:yx_scope/yx_scope.dart'; 3 | 4 | class AppScopeContainer extends ScopeContainer {} 5 | 6 | class AppScopeHolder extends ScopeHolder { 7 | AppScopeHolder({ 8 | // ignore: deprecated_member_use_from_same_package 9 | List? scopeListeners, 10 | // ignore: deprecated_member_use_from_same_package 11 | List? depListeners, 12 | // ignore: deprecated_member_use_from_same_package 13 | List? asyncDepListeners, 14 | List? scopeObservers, 15 | List? depObservers, 16 | List? asyncDepObservers, 17 | }) : super( 18 | // ignore: deprecated_member_use_from_same_package 19 | scopeListeners: scopeListeners, 20 | scopeObservers: scopeObservers, 21 | // ignore: deprecated_member_use_from_same_package 22 | depListeners: depListeners, 23 | depObservers: depObservers, 24 | // ignore: deprecated_member_use_from_same_package 25 | asyncDepListeners: asyncDepListeners, 26 | asyncDepObservers: asyncDepObservers, 27 | ); 28 | @override 29 | AppScopeContainer createContainer() => AppScopeContainer(); 30 | } 31 | 32 | void main() { 33 | test( 34 | 'Fail assert if both scopeListeners and scopeObservers passed to ScopeHolder', 35 | () { 36 | expect( 37 | () => AppScopeHolder(scopeListeners: [], scopeObservers: []), 38 | throwsA( 39 | isA(), 40 | ), 41 | ); 42 | }); 43 | 44 | test( 45 | 'Fail assert if both depListeners and depObservers passed to ScopeHolder', 46 | () { 47 | expect( 48 | () => AppScopeHolder(depListeners: [], depObservers: []), 49 | throwsA( 50 | isA(), 51 | ), 52 | ); 53 | }); 54 | 55 | test( 56 | 'Fail assert if both asyncDepListeners and asyncDepObservers passed to ScopeHolder', 57 | () { 58 | expect( 59 | () => AppScopeHolder(asyncDepListeners: [], asyncDepObservers: []), 60 | throwsA( 61 | isA(), 62 | ), 63 | ); 64 | }); 65 | } 66 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_linter/lib/src/models/dep.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:analyzer/dart/ast/token.dart'; 3 | import 'package:analyzer/dart/element/type.dart'; 4 | import 'package:yx_scope_linter/src/types.dart'; 5 | 6 | class DepDeclaration { 7 | final FieldDeclaration field; 8 | final Token nameToken; 9 | final DartType type; 10 | final BaseScopeDeclaration parent; 11 | final Map deps = {}; 12 | 13 | DepDeclaration({ 14 | required this.field, 15 | required this.nameToken, 16 | required this.type, 17 | required this.parent, 18 | }); 19 | 20 | String get name => nameToken.lexeme; 21 | 22 | void addDep(DepDeclaration dep) => deps[dep.name] = dep; 23 | 24 | bool get isSync => depValueType.isExactlyType(type); 25 | 26 | bool get isAsync => asyncDepValueType.isExactlyType(type); 27 | 28 | @override 29 | String toString() => name; 30 | } 31 | 32 | abstract class BaseScopeDeclaration { 33 | final ClassDeclaration node; 34 | 35 | final Map deps = {}; 36 | final Map modules = {}; 37 | 38 | BaseScopeDeclaration({required this.node}); 39 | 40 | DartType get type => node.declaredElement!.thisType; 41 | 42 | addDep(DepDeclaration dep) => deps[dep.name] = dep; 43 | addModule(ModuleDeclaration module) => modules[module.name] = module; 44 | 45 | bool get isRoot => this is ScopeDeclaration; 46 | bool get isModule => this is ModuleDeclaration; 47 | } 48 | 49 | class ScopeDeclaration extends BaseScopeDeclaration { 50 | ScopeDeclaration({required super.node}); 51 | final List> initializeQueue = []; 52 | 53 | void addScopeQueue() => initializeQueue.add({}); 54 | void addDepToQueue(DepDeclaration dep) => initializeQueue.last.add(dep); 55 | 56 | @override 57 | String toString() => ''; 58 | } 59 | 60 | class ModuleDeclaration extends BaseScopeDeclaration { 61 | final BaseScopeDeclaration parent; 62 | final Token? nameToken; 63 | 64 | ModuleDeclaration({ 65 | required this.parent, 66 | required Token this.nameToken, 67 | required super.node, 68 | }); 69 | 70 | String get name => nameToken?.lexeme ?? ''; 71 | 72 | @override 73 | String toString() => '$parent$name.'; 74 | } 75 | -------------------------------------------------------------------------------- /yx_state/packages/yx_state_flutter/example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:yx_state/yx_state.dart'; 3 | import 'package:yx_state_flutter/yx_state_flutter.dart'; 4 | 5 | void main() => runApp(const MyApp()); 6 | 7 | class CounterStateManager extends StateManager { 8 | CounterStateManager(super.state); 9 | 10 | void increment() => handle((emit) async { 11 | emit(state + 1); 12 | }); 13 | } 14 | 15 | class MyApp extends StatelessWidget { 16 | const MyApp({super.key}); 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return MaterialApp( 21 | title: 'Flutter Demo', 22 | debugShowCheckedModeBanner: false, 23 | theme: ThemeData(colorSchemeSeed: Colors.blue), 24 | home: const MyHomePage(title: 'Flutter Demo Home Page'), 25 | ); 26 | } 27 | } 28 | 29 | class MyHomePage extends StatefulWidget { 30 | final String title; 31 | 32 | const MyHomePage({super.key, required this.title}); 33 | 34 | @override 35 | State createState() => _MyHomePageState(); 36 | } 37 | 38 | class _MyHomePageState extends State { 39 | late final CounterStateManager _stateManager; 40 | 41 | @override 42 | void initState() { 43 | super.initState(); 44 | _stateManager = CounterStateManager(0); 45 | } 46 | 47 | @override 48 | void dispose() { 49 | _stateManager.close(); 50 | super.dispose(); 51 | } 52 | 53 | @override 54 | Widget build(BuildContext context) { 55 | return Scaffold( 56 | appBar: AppBar(title: Text(widget.title)), 57 | body: Center( 58 | child: Column( 59 | mainAxisAlignment: MainAxisAlignment.center, 60 | children: [ 61 | const Text('You have pushed the button this many times:'), 62 | StateBuilder( 63 | stateReadable: _stateManager, 64 | builder: (context, state, _) => Text( 65 | '$state', 66 | style: Theme.of(context).textTheme.headlineMedium, 67 | ), 68 | ), 69 | ], 70 | ), 71 | ), 72 | floatingActionButton: FloatingActionButton( 73 | onPressed: _stateManager.increment, 74 | tooltip: 'Increment', 75 | child: const Icon(Icons.add), 76 | ), 77 | ); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_linter/lib/src/lints/avoid_async_dep_child_scope.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:analyzer/dart/element/element.dart'; 3 | import 'package:analyzer/dart/element/type.dart'; 4 | import 'package:analyzer/error/error.dart' hide LintCode; 5 | import 'package:analyzer/error/listener.dart'; 6 | import 'package:custom_lint_builder/custom_lint_builder.dart'; 7 | import 'package:yx_scope_linter/src/types.dart'; 8 | 9 | import '../yx_scope_lint_rule.dart'; 10 | 11 | class AvoidChildScopeInInitializeQueue extends YXScopeLintRule { 12 | static const _code = LintCode( 13 | name: 'avoid_async_dep_child_scope', 14 | problemMessage: 'Child scope should not have the same lifecycle as its ' 15 | 'parent, and therefore the child scope should be neither ' 16 | 'an asyncDep nor a rawAsyncDep', 17 | correctionMessage: 'If you need some dependencies with the same lifecycle ' 18 | 'but grouped together, use ScopeModule instead', 19 | errorSeverity: ErrorSeverity.WARNING, 20 | ); 21 | 22 | const AvoidChildScopeInInitializeQueue() : super(code: _code); 23 | 24 | @override 25 | void run( 26 | CustomLintResolver resolver, 27 | ErrorReporter reporter, 28 | CustomLintContext context, 29 | ) { 30 | yxScopeRegistry(context).addScopeDeclarations((module) { 31 | for (final dep in module.deps.values) { 32 | final methodInvocation = dep.field.fields.childEntities 33 | .whereType() 34 | .expand((e) => e.childEntities.whereType()) 35 | .first; 36 | final depClass = (methodInvocation.staticType as InterfaceType) 37 | .typeArguments 38 | .map((e) => e.element) 39 | .whereType() 40 | .first; 41 | final isScopeHolder = childScopeHolderValueType.isSuperOf(depClass); 42 | final isScopeContainer = 43 | scopeContainerValueType.isAssignableFrom(depClass); 44 | final isAsyncLifecycle = asyncLifecycleType.isAssignableFrom(depClass); 45 | if (dep.isAsync && 46 | (isScopeHolder || (isScopeContainer && isAsyncLifecycle))) { 47 | reporter.atToken( 48 | dep.nameToken, 49 | _code, 50 | ); 51 | } 52 | } 53 | }); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_linter/lib/src/resolved_yx_scope_result.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:analyzer/dart/analysis/results.dart'; 4 | import 'package:analyzer/dart/ast/ast.dart'; 5 | import 'package:analyzer/dart/ast/visitor.dart'; 6 | import 'package:analyzer/dart/element/element.dart'; 7 | import 'package:yx_scope_linter/src/names.dart'; 8 | 9 | import 'models/dep.dart'; 10 | import 'types.dart'; 11 | import 'yx_scope_registry.dart'; 12 | 13 | part 'visitors/parse_visitor.dart'; 14 | part 'visitors/parse_dependencies_for_dep_visitor.dart'; 15 | part 'visitors/parse_scope_declaration_visitor.dart'; 16 | part 'visitors/parse_initialize_queue_visitor.dart'; 17 | 18 | /// Class that analyzes all files and stores parsed data for analysis 19 | class ResolvedYXScopeResult { 20 | final List units; 21 | ResolvedYXScopeResult._(this.units); 22 | 23 | static Future from( 24 | List units, 25 | ) async { 26 | final result = ResolvedYXScopeResult._(units); 27 | final visitor = _ParseVisitor(result); 28 | 29 | for (final unit in units) { 30 | /// Skip generated files during parsing 31 | const generatedExtensions = {'.freezed.dart', '.g.dart'}; 32 | final shortName = unit.declaredElement?.source.shortName ?? ''; 33 | if (generatedExtensions.any(shortName.endsWith)) { 34 | continue; 35 | } 36 | await unit.accept(visitor); 37 | } 38 | 39 | return result; 40 | } 41 | 42 | /// Units contain resolved data that can be used to extract ClassDeclaration 43 | /// from [element] 44 | ClassDeclaration? classDeclarationByElement(Element element) { 45 | for (final unit in units) { 46 | final declarations = unit.declarations 47 | .where((declaration) => declaration.declaredElement == element) 48 | .whereType(); 49 | if (declarations.isEmpty) { 50 | return null; 51 | } 52 | return declarations.first; 53 | } 54 | return null; 55 | } 56 | 57 | final scopeDeclarations = {}; 58 | 59 | void accept(YXScopeRegistryVisitor visitor) { 60 | visitor.visitResolvedUnits(this); 61 | } 62 | 63 | void visitChildren(YXScopeRegistryVisitor visitor) { 64 | for (final scopeDeclaration in scopeDeclarations) { 65 | visitor.visitScopeDeclaration(scopeDeclaration); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_linter/lib/src/lints/consider_dep_suffix.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/error/error.dart' hide LintCode; 2 | import 'package:analyzer/error/listener.dart'; 3 | import 'package:custom_lint_builder/custom_lint_builder.dart'; 4 | import 'package:yx_scope_linter/src/extensions.dart'; 5 | 6 | import '../models/dep.dart'; 7 | import '../priority.dart'; 8 | import '../yx_scope_lint_rule.dart'; 9 | 10 | const _suffix = 'Dep'; 11 | 12 | class ConsiderDepSuffix extends YXScopeLintRule { 13 | static const _code = LintCode( 14 | name: 'consider_dep_suffix', 15 | problemMessage: 'Consider using suffix `$_suffix` for the name of your Dep', 16 | correctionMessage: 'Add suffix `$_suffix` like this: `entityName$_suffix`', 17 | errorSeverity: ErrorSeverity.INFO, 18 | ); 19 | 20 | const ConsiderDepSuffix() : super(code: _code); 21 | 22 | @override 23 | void run( 24 | CustomLintResolver resolver, 25 | ErrorReporter reporter, 26 | CustomLintContext context, 27 | ) { 28 | yxScopeRegistry(context).addScopeDeclarations((module) { 29 | void checkSuffix(BaseScopeDeclaration module) { 30 | for (final dep in module.deps.values) { 31 | if (dep.name.endsWith(_suffix)) { 32 | continue; 33 | } 34 | 35 | reporter.atToken( 36 | dep.nameToken, 37 | _code.copyWith( 38 | correctionMessage: 'Change the name to `${dep.name}$_suffix`', 39 | ), 40 | ); 41 | } 42 | 43 | for (final module in module.modules.values) { 44 | checkSuffix(module); 45 | } 46 | } 47 | 48 | checkSuffix(module); 49 | }); 50 | } 51 | 52 | @override 53 | List getFixes() => [ConsiderDepSuffixAssist()]; 54 | } 55 | 56 | class ConsiderDepSuffixAssist extends DartFix { 57 | @override 58 | void run( 59 | CustomLintResolver resolver, 60 | ChangeReporter reporter, 61 | CustomLintContext context, 62 | AnalysisError analysisError, 63 | List others, 64 | ) { 65 | final changeBuilder = reporter.createChangeBuilder( 66 | message: analysisError.correctionMessage!, 67 | priority: FixPriority.considerDepSuffix.value, 68 | ); 69 | 70 | changeBuilder.addDartFileEdit((builder) { 71 | builder.addSimpleInsertion( 72 | analysisError.sourceRange.end, 73 | _suffix, 74 | ); 75 | }); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/example/lib/domain/order_navigation/order_navigation_delegate.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/foundation.dart'; 4 | 5 | import '../../di/map/map_scope.dart'; 6 | import '../../di/map_navigation/map_navigation_scope.dart'; 7 | import '../../di/order/order_scope.dart'; 8 | import '../../domain/map_navigation/map_navigation_manager.dart'; 9 | import '../../domain/order/models/order.dart'; 10 | import '../../router/models/app_state.dart'; 11 | import '../../router/router_delegate.dart'; 12 | 13 | class OrderNavigationDelegate { 14 | final AppRouterDelegate _appRouterDelegate; 15 | final MapScopeHolder _mapScopeHolder; 16 | final OrderScopesHolder _orderScopesHolder; 17 | 18 | StreamSubscription? _subscription; 19 | 20 | OrderNavigationDelegate( 21 | this._appRouterDelegate, 22 | this._mapScopeHolder, 23 | this._orderScopesHolder, 24 | ); 25 | 26 | Future onOrderNavigation(Order order, int lastPosition) async { 27 | if (_appRouterDelegate.state.tabbarPage != TabbarPageType.map) { 28 | _appRouterDelegate.setTabBarPage(TabbarPageType.map); 29 | } 30 | _subscription = _mapScopeHolder.stream 31 | .asyncExpand((scope) => scope == null 32 | ? Stream.value(null) 33 | : scope.mapNavigationScopeHolder.stream) 34 | .asyncExpand((scope) => scope == null 35 | ? Stream.value(null) 36 | : scope.mapNavigationManager.stream) 37 | .listen((params) { 38 | final position = params?.currentPosition; 39 | if (position != null) { 40 | _orderScopesHolder.orderScopes[order]?.scope?.orderPositionHolder 41 | .updatePosition(position); 42 | _log('Order position updated: $position'); 43 | } else { 44 | _log('Order position update canceled'); 45 | _subscription?.cancel(); 46 | } 47 | }); 48 | 49 | // wait for MapScope appears 50 | final mapScope = await _mapScopeHolder.stream 51 | .firstWhere((scope) => scope != null) as MapScope; 52 | 53 | await mapScope.mapNavigationHolder.startNavigation( 54 | MapNavigationParams( 55 | fromAddress: order.incomingOrder.fromAddress, 56 | toAddress: order.incomingOrder.toAddress, 57 | currentPosition: lastPosition, 58 | ), 59 | ); 60 | } 61 | 62 | void _log(String message) { 63 | if (kDebugMode) { 64 | print(message); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/example/lib/router/router_delegate.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:yx_scope_flutter/yx_scope_flutter.dart'; 3 | 4 | import '../di/app/app_scope.dart'; 5 | import '../ui/auth/auth_page.dart'; 6 | import '../ui/register/register_page.dart'; 7 | import '../ui/tabbar/tabbar_page.dart'; 8 | import 'models/app_state.dart'; 9 | 10 | class AppRouterDelegate extends RouterDelegate 11 | with ChangeNotifier, PopNavigatorRouterDelegateMixin { 12 | var state = const AppState.init(); 13 | @override 14 | late final navigatorKey = GlobalKey(); 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | final pages = []; 19 | if (!state.hasAccount) { 20 | pages.add( 21 | const MaterialPage( 22 | key: ValueKey('LoginPage'), 23 | child: AuthPage(), 24 | ), 25 | ); 26 | } else { 27 | pages.add( 28 | MaterialPage( 29 | key: const ValueKey('TabBarPage'), 30 | child: ScopeBuilder.withPlaceholder( 31 | builder: (context, appScope) { 32 | return ScopeProvider( 33 | holder: appScope.accountScopeHolderDep.get, 34 | child: TabbarPage(page: state.tabbarPage), 35 | ); 36 | }, 37 | ), 38 | ), 39 | ); 40 | } 41 | 42 | if (state.isOpenRegister) { 43 | pages.add( 44 | const MaterialPage( 45 | key: ValueKey('RegisterPage'), 46 | child: RegisterPage(), 47 | ), 48 | ); 49 | } 50 | return Navigator( 51 | key: navigatorKey, 52 | pages: pages, 53 | // TODO: migrate to the new API 54 | // ignore: deprecated_member_use 55 | onPopPage: (route, result) { 56 | if (!route.didPop(result)) { 57 | return false; 58 | } 59 | 60 | return true; 61 | }, 62 | ); 63 | } 64 | 65 | @override 66 | Future setNewRoutePath(AppState configuration) async {} 67 | 68 | void setHasAccount({required bool hasAccount}) { 69 | state = state.copyWith(hasAccount: hasAccount); 70 | notifyListeners(); 71 | } 72 | 73 | void setOpenRegister({required bool isOpenRegister}) { 74 | state = state.copyWith(isOpenRegister: isOpenRegister); 75 | notifyListeners(); 76 | } 77 | 78 | void setTabBarPage(TabbarPageType page) { 79 | state = state.copyWith(tabbarPage: page); 80 | notifyListeners(); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope/lib/src/monitoring/observers.dart: -------------------------------------------------------------------------------- 1 | import 'models/dep_id.dart'; 2 | import 'models/scope_id.dart'; 3 | import 'models/value_meta.dart'; 4 | 5 | part 'listeners.dart'; 6 | 7 | /// Interface for observing [Scope] state. 8 | /// 9 | /// To observe [Scope] state create your implementation of this interface 10 | /// and pass it to your [ScopeHolder]. 11 | abstract class ScopeObserver { 12 | void onScopeStartInitialize(ScopeId scope); 13 | 14 | void onScopeInitialized(ScopeId scope); 15 | 16 | void onScopeInitializeFailed( 17 | ScopeId scope, 18 | Object exception, 19 | StackTrace stackTrace, 20 | ); 21 | 22 | void onScopeStartDispose(ScopeId scope); 23 | 24 | void onScopeDisposed(ScopeId scope); 25 | 26 | /// The method is called when dispose of the [dep] has failed 27 | /// during the dispose phase of the [scope]. 28 | /// 29 | /// This method can be called many times during dispose phase. 30 | void onScopeDisposeDepFailed( 31 | ScopeId scope, 32 | DepId dep, 33 | Object exception, 34 | StackTrace stackTrace, 35 | ); 36 | 37 | const ScopeObserver._(); 38 | } 39 | 40 | /// Interface for observing [Dep] state. 41 | /// 42 | /// To observe [Dep] state create your implementation of this interface 43 | /// and pass it to your [ScopeHolder]. 44 | abstract class DepObserver { 45 | void onValueStartCreate(ScopeId scope, DepId dep); 46 | 47 | void onValueCreated(ScopeId scope, DepId dep, ValueMeta? valueMeta); 48 | 49 | void onValueCreateFailed( 50 | ScopeId scope, 51 | DepId dep, 52 | Object exception, 53 | StackTrace stackTrace, 54 | ); 55 | 56 | void onValueCleared(ScopeId scope, DepId dep, ValueMeta? valueMeta); 57 | 58 | const DepObserver._(); 59 | } 60 | 61 | /// Interface for observing [AsyncDep] state. 62 | /// 63 | /// To observe [AsyncDep] state create your implementation of this interface 64 | /// and pass it to your [ScopeHolder]. 65 | abstract class AsyncDepObserver implements DepObserver { 66 | void onDepStartInitialize(ScopeId scope, DepId dep); 67 | 68 | void onDepInitialized(ScopeId scope, DepId dep); 69 | 70 | void onDepStartDispose(ScopeId scope, DepId dep); 71 | 72 | void onDepDisposed(ScopeId scope, DepId dep); 73 | 74 | void onDepInitializeFailed( 75 | ScopeId scope, 76 | DepId dep, 77 | Object exception, 78 | StackTrace stackTrace, 79 | ); 80 | 81 | void onDepDisposeFailed( 82 | ScopeId scope, 83 | DepId dep, 84 | Object exception, 85 | StackTrace stackTrace, 86 | ); 87 | 88 | const AsyncDepObserver._(); 89 | } 90 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope/lib/src/scope_container.dart: -------------------------------------------------------------------------------- 1 | import 'base_scope_container.dart'; 2 | 3 | /// Root [BaseScopeContainer] without parent scope. 4 | /// 5 | /// {@macro base_scope_container} 6 | abstract class ScopeContainer extends BaseScopeContainer { 7 | ScopeContainer({String? name}) : super(name: name) { 8 | _initializeQueueNoDuplications(this); 9 | } 10 | } 11 | 12 | /// {@macro child_scope_container} 13 | abstract class ChildScopeContainer 14 | extends BaseScopeContainer with ChildScopeContainerMixin { 15 | ChildScopeContainer({ 16 | required Parent parent, 17 | String? name, 18 | }) : super(name: name) { 19 | this.parent = parent; 20 | _initializeQueueNoDuplications(this); 21 | } 22 | } 23 | 24 | /// {@macro data_scope_container} 25 | abstract class DataScopeContainer 26 | extends BaseScopeContainer with DataScopeContainerMixin { 27 | DataScopeContainer({ 28 | required Data data, 29 | String? name, 30 | }) : super(name: name) { 31 | this.data = data; 32 | _initializeQueueNoDuplications(this); 33 | } 34 | } 35 | 36 | /// Combines [ChildScopeContainer] and [DataScopeContainer]. 37 | /// 38 | /// [ChildScopeContainer]: 39 | /// {@macro child_scope_container} 40 | /// 41 | /// [DataScopeContainer]: 42 | /// {@macro data_scope_container} 43 | abstract class ChildDataScopeContainer extends BaseScopeContainer 45 | with ChildScopeContainerMixin, DataScopeContainerMixin { 46 | ChildDataScopeContainer({ 47 | required Parent parent, 48 | required Data data, 49 | String? name, 50 | }) : super(name: name) { 51 | this.data = data; 52 | this.parent = parent; 53 | _initializeQueueNoDuplications(this); 54 | } 55 | } 56 | 57 | /// Checks if no duplicated async dependency instances 58 | /// added into initializeQueue. 59 | void _initializeQueueNoDuplications( 60 | Container scope) { 61 | // ignore: invalid_use_of_protected_member 62 | final deps = scope.initializeQueue.expand((depSet) => depSet); 63 | final counter = {}; 64 | for (final dep in deps) { 65 | counter[dep] = (counter[dep] ?? 0) + 1; 66 | } 67 | final duplications = counter.entries.where((entry) => entry.value > 1); 68 | assert( 69 | duplications.isEmpty, 70 | '(${scope.runtimeType}) Following async dependencies has been added ' 71 | 'to initializeQueue multiple times: ' 72 | '[${duplications.map((e) => '${e.key.runtimeType}: ${e.value}').join(', ')}]', 73 | ); 74 | } 75 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_linter/lib/src/lints/dep_cycle.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/error/error.dart' hide LintCode; 2 | import 'package:analyzer/error/listener.dart'; 3 | import 'package:custom_lint_builder/custom_lint_builder.dart'; 4 | import 'package:yx_scope_linter/src/extensions.dart'; 5 | 6 | import '../models/dep.dart'; 7 | import '../yx_scope_lint_rule.dart'; 8 | 9 | class DepCycle extends YXScopeLintRule { 10 | static const _name = 'dep_cycle'; 11 | static const _message = 'The cycle is detected'; 12 | static const _code = LintCode( 13 | name: _name, 14 | problemMessage: _message, 15 | errorSeverity: ErrorSeverity.ERROR, 16 | ); 17 | 18 | const DepCycle() : super(code: _code); 19 | 20 | @override 21 | void run( 22 | CustomLintResolver resolver, 23 | ErrorReporter reporter, 24 | CustomLintContext context, 25 | ) { 26 | yxScopeRegistry(context).addScopeDeclarations((scope) { 27 | final cycles = detectCycles(scope); 28 | for (final cycle in cycles) { 29 | final errorCode = _code.copyWith( 30 | problemMessage: '$_message: ${cycle.map((e) => e).join(' <- ')}' 31 | ' <- ${cycle.first}', 32 | ); 33 | 34 | for (final dep in cycle) { 35 | if (dep.parent == scope) { 36 | reporter.atToken( 37 | dep.nameToken, 38 | errorCode, 39 | ); 40 | } else { 41 | reporter.atToken( 42 | (dep.parent as ModuleDeclaration).nameToken!, 43 | errorCode, 44 | ); 45 | } 46 | } 47 | } 48 | }); 49 | } 50 | 51 | List> detectCycles(BaseScopeDeclaration module) { 52 | final cycles = >[]; 53 | final visited = {}; 54 | final inStack = {}; 55 | 56 | void dfs(DepDeclaration entity, List currentCycle) { 57 | visited.add(entity); 58 | inStack.add(entity); 59 | currentCycle.add(entity); 60 | 61 | for (final dependency in entity.deps.values) { 62 | if (!visited.contains(dependency)) { 63 | dfs(dependency, currentCycle); 64 | } else if (inStack.contains(dependency)) { 65 | cycles.add(currentCycle.sublist(currentCycle.indexOf(dependency))); 66 | } 67 | } 68 | 69 | inStack.remove(entity); 70 | currentCycle.remove(entity); 71 | } 72 | 73 | for (final entity in module.deps.values) { 74 | if (!visited.contains(entity)) { 75 | dfs(entity, []); 76 | } 77 | } 78 | 79 | return cycles; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_linter/lib/src/visitors/parse_scope_declaration_visitor.dart: -------------------------------------------------------------------------------- 1 | part of '../resolved_yx_scope_result.dart'; 2 | 3 | /// Visitor that parses scope declarations and their dependencies 4 | /// and put them into [declaration] 5 | class _ParseScopeDeclaration extends SimpleAstVisitor { 6 | final BaseScopeDeclaration declaration; 7 | 8 | _ParseScopeDeclaration(this.declaration); 9 | 10 | @override 11 | Future visitClassDeclaration(ClassDeclaration node) => 12 | Future.wait(node.members.map((e) async => e.accept(this))); 13 | 14 | @override 15 | Future visitFieldDeclaration(FieldDeclaration node) => 16 | Future.wait(node.fields.variables.map((e) async => e.accept(this))); 17 | 18 | @override 19 | Future visitVariableDeclaration(VariableDeclaration node) async { 20 | final element = node.declaredElement; 21 | if (element == null) { 22 | return; 23 | } 24 | 25 | // Handle dependency declarations 26 | if (anyDepValueTypes.isExactlyType(element.type)) { 27 | declaration.addDep( 28 | DepDeclaration( 29 | field: node.thisOrAncestorOfType()!, 30 | nameToken: node.name, 31 | type: element.type, 32 | parent: declaration, 33 | ), 34 | ); 35 | } 36 | 37 | // Handle scope module references 38 | if (scopeModuleType.isAssignableFromType(element.type)) { 39 | await Future.wait(node.childEntities 40 | .whereType() 41 | .map((e) async => e.accept(this))); 42 | } 43 | } 44 | 45 | @override 46 | Future visitInstanceCreationExpression( 47 | InstanceCreationExpression node) async { 48 | final library = node.constructorName.type.element?.library; 49 | if (library == null) { 50 | return; 51 | } 52 | 53 | // Resolve the module declaration from its library 54 | final result = (await library.session.getResolvedLibraryByElement(library)) 55 | as ResolvedLibraryResult; 56 | 57 | final moduleNode = result 58 | .getElementDeclaration( 59 | node.constructorName.type.element as ClassElement)! 60 | .node as ClassDeclaration; 61 | 62 | // Create and register the module declaration 63 | final moduleDeclaration = ModuleDeclaration( 64 | parent: declaration, 65 | nameToken: node.thisOrAncestorOfType()!.name, 66 | node: moduleNode, 67 | ); 68 | 69 | declaration.addModule(moduleDeclaration); 70 | 71 | // Recursively parse the module's contents 72 | await moduleNode.accept(_ParseScopeDeclaration(moduleDeclaration)); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/test/scope_listener_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | import 'package:yx_scope/yx_scope.dart'; 4 | import 'package:yx_scope_flutter/yx_scope_flutter.dart' as yx_flutter; 5 | 6 | import 'test_utils.dart'; 7 | 8 | class TestScopeStateHolder extends ScopeHolder { 9 | TestScopeStateHolder(); 10 | 11 | @override 12 | TestScopeContainer createContainer() => TestScopeContainer(); 13 | } 14 | 15 | class TestScopeContainer extends ScopeContainer {} 16 | 17 | class TestListenerApp extends StatelessWidget { 18 | final ScopeStateHolder holder; 19 | final CounterProvider listenerCounter; 20 | final Widget child; 21 | 22 | const TestListenerApp({ 23 | super.key, 24 | required this.holder, 25 | required this.listenerCounter, 26 | required this.child, 27 | }); 28 | 29 | @override 30 | Widget build(BuildContext context) => yx_flutter.ScopeProvider( 31 | holder: holder, 32 | child: MaterialApp( 33 | home: yx_flutter.ScopeListener( 34 | listener: (context, scope) { 35 | listenerCounter.count++; 36 | }, 37 | child: child, 38 | ), 39 | ), 40 | ); 41 | } 42 | 43 | void main() { 44 | testWidgets('Listener is not called after disposal', (tester) async { 45 | final holder = TestScopeStateHolder(); 46 | final counter = CounterProvider(0); 47 | 48 | await tester.pumpWidget(TestListenerApp( 49 | holder: holder, 50 | listenerCounter: counter, 51 | child: const SizedBox.shrink(), 52 | )); 53 | 54 | await tester.pumpWidget(const SizedBox.shrink()); // Dispose widget tree 55 | await holder.create(); 56 | 57 | expect(counter.count, 0); 58 | }); 59 | 60 | testWidgets('Listener handles holder change correctly', (tester) async { 61 | final holder1 = TestScopeStateHolder(); 62 | final holder2 = TestScopeStateHolder(); 63 | final counter = CounterProvider(0); 64 | 65 | await tester.pumpWidget(TestListenerApp( 66 | holder: holder1, 67 | listenerCounter: counter, 68 | child: const SizedBox.shrink(), 69 | )); 70 | 71 | await holder1.create(); 72 | expect(counter.count, 1); 73 | 74 | // Change holder 75 | await tester.pumpWidget(TestListenerApp( 76 | holder: holder2, 77 | listenerCounter: counter, 78 | child: const SizedBox.shrink(), 79 | )); 80 | 81 | await holder2.create(); 82 | expect(counter.count, 2); 83 | }); 84 | } 85 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_linter/lib/src/lints/final_dep.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:analyzer/error/error.dart' hide LintCode; 3 | import 'package:analyzer/error/listener.dart'; 4 | import 'package:custom_lint_builder/custom_lint_builder.dart'; 5 | 6 | import '../models/dep.dart'; 7 | import '../priority.dart'; 8 | import '../yx_scope_lint_rule.dart'; 9 | 10 | class FinalDep extends YXScopeLintRule { 11 | static const _code = LintCode( 12 | name: 'final_dep', 13 | problemMessage: 'A dep field must be `late final`', 14 | correctionMessage: 'Make your dep field `final`', 15 | errorSeverity: ErrorSeverity.WARNING, 16 | ); 17 | 18 | const FinalDep() : super(code: _code); 19 | 20 | @override 21 | void run( 22 | CustomLintResolver resolver, 23 | ErrorReporter reporter, 24 | CustomLintContext context, 25 | ) { 26 | yxScopeRegistry(context).addScopeDeclarations((module) { 27 | void checkFinal(BaseScopeDeclaration module) { 28 | for (final dep in module.deps.values) { 29 | final field = dep.field; 30 | 31 | if (!field.fields.isFinal) { 32 | reporter.atToken( 33 | dep.nameToken, 34 | _code, 35 | data: field.fields, 36 | ); 37 | } 38 | } 39 | 40 | for (final module in module.modules.values) { 41 | checkFinal(module); 42 | } 43 | } 44 | 45 | checkFinal(module); 46 | }); 47 | } 48 | 49 | @override 50 | List getFixes() => [FinalDepFix()]; 51 | } 52 | 53 | class FinalDepFix extends DartFix { 54 | @override 55 | void run( 56 | CustomLintResolver resolver, 57 | ChangeReporter reporter, 58 | CustomLintContext context, 59 | AnalysisError analysisError, 60 | List others, 61 | ) { 62 | final changeBuilder = reporter.createChangeBuilder( 63 | message: analysisError.correctionMessage!, 64 | priority: FixPriority.finalDep.value, 65 | ); 66 | 67 | final fields = analysisError.data as VariableDeclarationList; 68 | 69 | changeBuilder.addDartFileEdit((builder) { 70 | final keyword = fields.keyword; 71 | if (keyword != null) { 72 | builder.addSimpleReplacement( 73 | keyword.sourceRange, 74 | 'final', 75 | ); 76 | } else { 77 | builder.addSimpleInsertion(fields.lateKeyword!.end, ' final'); 78 | } 79 | 80 | final type = fields.type; 81 | if (type != null) { 82 | builder.addDeletion(type.sourceRange); 83 | } 84 | 85 | builder.format(fields.sourceRange); 86 | }); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope/lib/src/monitoring/raw_observers.dart: -------------------------------------------------------------------------------- 1 | import '../base_scope_container.dart'; 2 | 3 | part 'raw_listeners.dart'; 4 | 5 | /// Consider using [ScopeObserver], [DepObserver] and [AsyncDepObserver] instead — these are Observers with a safe read-only access. 6 | /// [RawScopeObserver], [RawDepObserver] and [RawAsyncDepObserver] are an advanced direct access for rare cases. 7 | /// So if you are not sure if this is the right choice for you then it's probably not. 8 | /// 9 | /// [RawScopeObserver] is the Observer with a direct access to [BaseScopeContainer] and [Dep]. 10 | abstract class RawScopeObserver { 11 | static RawScopeObserver? override; 12 | 13 | RawScopeObserver._(); 14 | 15 | void onScopeStartInitialize(BaseScopeContainer scope); 16 | 17 | void onScopeInitialized(BaseScopeContainer scope); 18 | 19 | void onScopeInitializeFailed( 20 | BaseScopeContainer scope, 21 | Object exception, 22 | StackTrace stackTrace, 23 | ); 24 | 25 | void onScopeStartDispose(BaseScopeContainer scope); 26 | 27 | void onScopeDisposed(BaseScopeContainer scope); 28 | 29 | void onScopeDisposeDepFailed( 30 | BaseScopeContainer scope, 31 | Dep dep, 32 | Object exception, 33 | StackTrace stackTrace, 34 | ); 35 | } 36 | 37 | /// [RawDepObserver] is the Observer with a direct access to [BaseScopeContainer], [Dep] and created instance. 38 | /// More details in [RawScopeObserver] 39 | abstract class RawDepObserver { 40 | static RawDepObserver? override; 41 | 42 | void onValueStartCreate(BaseScopeContainer scope, Dep dep); 43 | 44 | void onValueCreated(BaseScopeContainer scope, Dep dep, Object? value); 45 | 46 | void onValueCreateFailed( 47 | BaseScopeContainer scope, 48 | Dep dep, 49 | Object exception, 50 | StackTrace stackTrace, 51 | ); 52 | 53 | void onValueCleared(BaseScopeContainer scope, Dep dep, Object? value); 54 | } 55 | 56 | /// [RawAsyncDepObserver] is the Observer with a direct access to [BaseScopeContainer] and [Dep]. 57 | /// More details in [RawScopeObserver] 58 | abstract class RawAsyncDepObserver implements RawDepObserver { 59 | static RawAsyncDepObserver? override; 60 | 61 | void onDepStartInitialize(BaseScopeContainer scope, Dep dep); 62 | 63 | void onDepInitialized(BaseScopeContainer scope, Dep dep); 64 | 65 | void onDepStartDispose(BaseScopeContainer scope, Dep dep); 66 | 67 | void onDepDisposed(BaseScopeContainer scope, Dep dep); 68 | 69 | void onDepInitializeFailed( 70 | BaseScopeContainer scope, 71 | Dep dep, 72 | Object exception, 73 | StackTrace stackTrace, 74 | ); 75 | 76 | void onDepDisposeFailed( 77 | BaseScopeContainer scope, 78 | Dep dep, 79 | Object exception, 80 | StackTrace stackTrace, 81 | ); 82 | } 83 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.1.4 - unpublished 2 | * Added behaviour and dep access to improve deps customization 3 | 4 | ## 1.1.3 - unpublished 5 | * Added CustomDep/CustomAsyncDep classes for creating custom dependency types 6 | 7 | ## 1.1.2 - 2025.08.01 8 | 9 | * Fix: ScopeListners/DepListeners/AsyncDepListeners usage 10 | 11 | ## 1.1.1 - 2025.07.08 12 | 13 | * Documentation links fixed 14 | 15 | ## 1.1.0 - 2025.07.07 16 | 17 | * Public sealed scope state API added 18 | * Fix: Prohibit DataScope initialization with null data by enforcing non-nullable Data type 19 | * Documentation fixes and updates 20 | * Updated CI with new Flutter version support 21 | 22 | #### Deprecated API 23 | Listeners have been renamed into Observers due to conflicts with yx_scope_flutter package. 24 | Consider using Observers instead of Listeners. 25 | Deprecated classes and fields will be highlighted from now on. 26 | In the next major, deprecated API will be completely removed. 27 | 28 | ## 1.0.2 - 2024.12.18 29 | 30 | * Fix: corrected an inheritance of DataScopeContainer class to BaseScopeContainer instead of 31 | ScopeContainer 32 | * Updated repository link 33 | * Library topics added 34 | 35 | ## 1.0.1 - 2024.10.23 36 | 37 | * Minor fixes in formatting and links 38 | 39 | ## 1.0.0 - 2024.10.18 40 | 41 | * Remove redundant register/unregister listener methods 42 | * Ready to be open-source 43 | 44 | ## 0.1.4 - 2024.10.10 45 | 46 | * yx_scoped -> yx_scope 47 | 48 | ## 0.1.3 - 2024.09.23 49 | 50 | * AsyncDep init/dispose callback now are hidden from API 51 | * @nonVirtual nonVirtual for dep/asyncDep/rawAsyncDep methods 52 | 53 | ## 0.1.2 - 2024.08.20 54 | 55 | * Added optional `name` 56 | for `ScopeContainer`, `ChildScopeContainer`, `DataScopeContainer`, `ChildDataScopeContainer` 57 | 58 | ## 0.1.1 - 2024.05.27 59 | 60 | * Generic type of container for ScopeHolder's changed for strict link ScopedHolder's with 61 | ScopeContainer's 62 | 63 | ## 0.1.0 — 2024.05.08 64 | 65 | 1. All -Node entities has been renamed to -Container 66 | 2. Reorder parent and data args in createContainer methods 67 | 68 | ## 0.0.4 — 2024.03.09 69 | 70 | Core entities has been renamed: 71 | 72 | 1. ScopeModule -> BaseScopeNode 73 | 2. FeatureScopeModule -> ScopeModule 74 | 3. RootScopeModule -> ScopeNode 75 | 4. DataChildScopeModule -> ChildDataScopeModule 76 | 77 | And all their children has been renamed accordingly. 78 | 79 | ## 0.0.3 — 2024.03.04 80 | 81 | * New class available: FeatureScopeModule. It helps to decompose ScopeModule into a number of 82 | features. 83 | * Unnecessary build.yaml removed 84 | 85 | ## 0.0.2 — 2024.02.28 86 | 87 | * Parent scope can be defined as an abstract interface that implements BaseScope. It helps to unbind 88 | child from knowing an exact implementation of the parent and depend only on it's interface. 89 | 90 | ## 0.0.1 — 2024.02.04 91 | 92 | * Initial release 93 | -------------------------------------------------------------------------------- /yx_state/packages/yx_state/lib/src/mixin/state_manager_listener_mixin.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | import '../base/interface.dart'; 4 | import '../base/state_manager_base.dart'; 5 | import '../state_manager_observer.dart'; 6 | import '../state_manager_overrides.dart'; 7 | 8 | /// A mixin that provides default lifecycle event handling for state managers. 9 | /// 10 | /// This mixin implements the [StateManagerListener] interface and forwards 11 | /// all lifecycle events to the global [StateManagerObserver] configured in 12 | /// [StateManagerOverrides]. 13 | /// 14 | /// The mixin is typically used with [StateManagerBase] to provide standard 15 | /// event observation capabilities with minimal boilerplate. 16 | @internal 17 | mixin StateManagerListenerMixin 18 | on StateManagerBase implements StateManagerListener { 19 | /// The observer that will receive lifecycle events from this state manager. 20 | /// 21 | /// By default, this returns the global observer from [StateManagerOverrides]. 22 | StateManagerObserver get _observer => StateManagerOverrides.observer; 23 | 24 | /// Called when the state manager is created. 25 | /// 26 | /// Forwards the creation event to the observer. 27 | @mustCallSuper 28 | @protected 29 | @override 30 | void onCreate() => _observer.onCreate(this); 31 | 32 | /// Called when a handler function starts execution. 33 | /// 34 | /// Forwards the handler start event to the observer with the optional identifier. 35 | @mustCallSuper 36 | @protected 37 | @override 38 | void onStart(Object? identifier) => _observer.onHandleStart(this, identifier); 39 | 40 | /// Called when a handler function completes execution. 41 | /// 42 | /// Forwards the handler completion event to the observer with the optional identifier. 43 | @mustCallSuper 44 | @protected 45 | @override 46 | void onDone(Object? identifier) => _observer.onHandleDone(this, identifier); 47 | 48 | /// Called when the state changes. 49 | /// 50 | /// Forwards the state change event to the observer with the current and next state. 51 | @mustCallSuper 52 | @protected 53 | @override 54 | void onChange(State currentState, State nextState, Object? identifier) => 55 | _observer.onChange(this, currentState, nextState, identifier); 56 | 57 | /// Called when an error occurs within the state manager. 58 | /// 59 | /// Forwards the error event to the observer with the error and stack trace. 60 | @mustCallSuper 61 | @protected 62 | @override 63 | void onError(Object error, StackTrace stackTrace, Object? identifier) => 64 | _observer.onError(this, error, stackTrace, identifier); 65 | 66 | /// Called when the state manager is closed. 67 | /// 68 | /// Forwards the close event to the observer. 69 | @mustCallSuper 70 | @protected 71 | @override 72 | void onClose() => _observer.onClose(this); 73 | } 74 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/example/lib/data/map/map_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'map.dart'; 4 | 5 | class MapWidget extends StatefulWidget { 6 | final void Function(MapController map) onMapCreated; 7 | 8 | const MapWidget({ 9 | super.key, 10 | required this.onMapCreated, 11 | }); 12 | 13 | @override 14 | State createState() => _MapWidgetState(); 15 | } 16 | 17 | class _MapWidgetState extends State { 18 | late final Future _future; 19 | late final ScrollController _scrollController; 20 | 21 | @override 22 | void initState() { 23 | super.initState(); 24 | 25 | _scrollController = ScrollController(); 26 | 27 | // Simulate [MapWidget] loading process 28 | _future = Future.delayed( 29 | const Duration(seconds: 1), () => MapController(_scrollController)) 30 | .then((controller) { 31 | if (mounted) { 32 | widget.onMapCreated(controller); 33 | } 34 | return controller; 35 | }); 36 | 37 | _future.then((controller) => controller.addListener(_onControllerUpdate)); 38 | } 39 | 40 | @override 41 | void dispose() { 42 | _future 43 | .then((controller) => controller.removeListener(_onControllerUpdate)); 44 | _scrollController.dispose(); 45 | super.dispose(); 46 | } 47 | 48 | void _onControllerUpdate() => setState(() {}); 49 | 50 | @override 51 | Widget build(BuildContext context) => FutureBuilder( 52 | future: _future, 53 | builder: (context, snap) { 54 | if (!snap.hasData) { 55 | return const Center(child: CircularProgressIndicator()); 56 | } 57 | final controller = snap.requireData; 58 | final list = controller.items.values.toList(growable: false); 59 | return ListView.builder( 60 | controller: _scrollController, 61 | itemCount: controller.items.length, 62 | itemBuilder: (context, index) { 63 | final item = list[index]; 64 | return Theme( 65 | data: Theme.of(context).copyWith( 66 | listTileTheme: Theme.of(context).listTileTheme.copyWith( 67 | selectedTileColor: 68 | Theme.of(context).secondaryHeaderColor), 69 | ), 70 | child: SizedBox( 71 | height: MapController.itemHeight, 72 | child: ListTile( 73 | selected: item.selected, 74 | title: Text(item.name), 75 | onTap: () { 76 | controller.selectPosition( 77 | item.position, 78 | selected: !item.selected, 79 | ); 80 | }, 81 | ), 82 | ), 83 | ); 84 | }, 85 | ); 86 | }, 87 | ); 88 | } 89 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/example/lib/ui/orders/accept_order_wrapper.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:yx_scope_flutter/yx_scope_flutter.dart'; 5 | 6 | import '../../di/account/account_scope.dart'; 7 | import '../../di/online_order/online_scope.dart'; 8 | import '../../domain/order/models/order.dart'; 9 | 10 | class AcceptOrderWrapper extends StatefulWidget { 11 | final Widget child; 12 | 13 | const AcceptOrderWrapper({ 14 | required this.child, 15 | Key? key, 16 | }) : super(key: key); 17 | 18 | @override 19 | State createState() => _AcceptOrderWrapperState(); 20 | } 21 | 22 | class _AcceptOrderWrapperState extends State { 23 | StreamSubscription? _acceptOrdersSub; 24 | 25 | @override 26 | void dispose() { 27 | super.dispose(); 28 | _acceptOrdersSub?.cancel(); 29 | _acceptOrdersSub = null; 30 | } 31 | 32 | void _listener( 33 | BuildContext _, 34 | OnlineScope? onlineScope, 35 | ) { 36 | if (onlineScope != null) { 37 | final acceptOrderManger = onlineScope.acceptOrderManager; 38 | 39 | _acceptOrdersSub?.cancel(); 40 | _acceptOrdersSub = acceptOrderManger.toAcceptOrdersStream.listen((order) { 41 | if (!mounted) { 42 | return; 43 | } 44 | _handleIncomingOrder(onlineScope, order); 45 | }); 46 | return; 47 | } 48 | 49 | _acceptOrdersSub?.cancel(); 50 | _acceptOrdersSub = null; 51 | } 52 | 53 | Future _handleIncomingOrder( 54 | OnlineScope onlineScope, 55 | Order order, 56 | ) async { 57 | final acceptOrderManager = onlineScope.acceptOrderManager; 58 | final result = await showDialog( 59 | context: context, 60 | builder: (context) => AlertDialog( 61 | title: Text( 62 | 'Accept oder ${order.incomingOrder.fromAddress.name} -> ${order.incomingOrder.toAddress.name}', 63 | ), 64 | actions: [ 65 | TextButton( 66 | onPressed: () { 67 | acceptOrderManager.cancelOrder(); 68 | Navigator.of(context).pop(false); 69 | }, 70 | child: const Text('no'), 71 | ), 72 | TextButton( 73 | onPressed: () { 74 | acceptOrderManager.acceptOrder(order); 75 | Navigator.of(context).pop(true); 76 | }, 77 | child: const Text('ok'), 78 | ), 79 | ], 80 | ), 81 | ); 82 | if (result == null) { 83 | await acceptOrderManager.cancelOrder(); 84 | } 85 | } 86 | 87 | @override 88 | Widget build(BuildContext context) => 89 | ScopeBuilder.withPlaceholder( 90 | builder: (context, accountScope) => ScopeListener( 91 | holder: accountScope.onlineScopeHolder, 92 | listener: _listener, 93 | child: widget.child, 94 | ), 95 | ); 96 | } 97 | -------------------------------------------------------------------------------- /yx_state/packages/yx_state/example/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:yx_state/yx_state.dart'; 2 | 3 | void main() { 4 | // Set the observer globally 5 | StateManagerOverrides.observer = const MyCustomObserver(); 6 | 7 | // Create a new state manager 8 | final counter = CounterStateManager(0); 9 | 10 | counter.increment(); 11 | counter.decrement(); 12 | counter.someLogic(); 13 | counter.increment(); 14 | counter.incrementBy(5); 15 | 16 | // Close the state manager 17 | counter.close(); 18 | } 19 | 20 | class CounterStateManager extends StateManager { 21 | CounterStateManager(super.state); 22 | 23 | void increment() => handle((emit) async { 24 | await Future.delayed(const Duration(seconds: 1)); 25 | emit(state + 1); 26 | }, identifier: 'increment'); 27 | 28 | void incrementBy(int value) => handle((emit) async { 29 | await Future.delayed(const Duration(seconds: 1)); 30 | emit(state + value); 31 | }, identifier: {'value': value}); 32 | 33 | void decrement() => handle((emit) async { 34 | await Future.delayed(const Duration(seconds: 1)); 35 | emit(state - 1); 36 | }, identifier: 'decrement'); 37 | 38 | void someLogic() => handle((emit) async { 39 | try { 40 | if (state == 0) { 41 | throw Exception(); 42 | } 43 | 44 | emit(state); 45 | } on Object catch (error, sk) { 46 | addError(error, sk, 'someLogic'); 47 | } 48 | 49 | emit(0); 50 | }, identifier: 'someLogic'); 51 | } 52 | 53 | class MyCustomObserver extends StateManagerObserver { 54 | const MyCustomObserver(); 55 | 56 | @override 57 | void onChange( 58 | StateManagerBase stateManager, 59 | Object? currentState, 60 | Object? nextState, 61 | Object? identifier, 62 | ) { 63 | print( 64 | 'State changed from $currentState to $nextState ' 65 | 'identifier: $identifier', 66 | ); 67 | super.onChange(stateManager, currentState, nextState, identifier); 68 | } 69 | 70 | @override 71 | void onHandleStart( 72 | StateManagerBase stateManager, 73 | Object? identifier, 74 | ) { 75 | print('Handle started: $identifier'); 76 | super.onHandleStart(stateManager, identifier); 77 | } 78 | 79 | @override 80 | void onHandleDone( 81 | StateManagerBase stateManager, 82 | Object? identifier, 83 | ) { 84 | print('Handle done: $identifier'); 85 | super.onHandleDone(stateManager, identifier); 86 | } 87 | 88 | @override 89 | void onError( 90 | StateManagerBase stateManager, 91 | Object error, 92 | StackTrace stackTrace, 93 | Object? identifier, 94 | ) { 95 | print( 96 | 'Oops error: $error, stackTrace: $stackTrace ' 97 | 'identifier: $identifier', 98 | ); 99 | super.onError(stateManager, error, stackTrace, identifier); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_flutter/example/lib/ui/tabbar/tabbar_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:yx_scope_flutter/yx_scope_flutter.dart'; 3 | 4 | import '../../di/app/app_scope.dart'; 5 | import '../../router/models/app_state.dart'; 6 | import '../account/account_page.dart'; 7 | import '../map/map_page.dart'; 8 | import '../orders/accept_order_wrapper.dart'; 9 | import '../orders/orders_page.dart'; 10 | 11 | class TabbarPage extends StatefulWidget { 12 | final TabbarPageType page; 13 | 14 | const TabbarPage({super.key, required this.page}); 15 | 16 | @override 17 | State createState() => _TabbarPageState(); 18 | } 19 | 20 | class _TabbarPageState extends State { 21 | static const _orderOfPages = [ 22 | TabbarPageType.map, 23 | TabbarPageType.orders, 24 | TabbarPageType.account, 25 | ]; 26 | 27 | @override 28 | Widget build(BuildContext context) => 29 | ScopeBuilder.withPlaceholder( 30 | builder: (context, appScope) { 31 | final routerDelegate = appScope.routerDelegateDep.get; 32 | late final Widget body; 33 | 34 | switch (widget.page) { 35 | case TabbarPageType.map: 36 | body = const MapPage(); 37 | break; 38 | case TabbarPageType.orders: 39 | body = const OrdersPage(); 40 | break; 41 | case TabbarPageType.account: 42 | body = const AccountPage(); 43 | break; 44 | } 45 | return AcceptOrderWrapper( 46 | child: Scaffold( 47 | appBar: AppBar(title: Text(_labelFromType(widget.page))), 48 | body: body, 49 | bottomNavigationBar: NavigationBar( 50 | selectedIndex: widget.page.index, 51 | onDestinationSelected: (index) { 52 | final type = _orderOfPages[index]; 53 | routerDelegate.setTabBarPage(type); 54 | }, 55 | destinations: _destinations, 56 | ), 57 | ), 58 | ); 59 | }, 60 | ); 61 | 62 | List get _destinations => _orderOfPages 63 | .map( 64 | (type) => NavigationDestination( 65 | icon: _iconFromType(type), 66 | label: _labelFromType(type), 67 | ), 68 | ) 69 | .toList(); 70 | 71 | String _labelFromType(TabbarPageType type) { 72 | switch (type) { 73 | case TabbarPageType.map: 74 | return 'Map'; 75 | case TabbarPageType.orders: 76 | return 'Orders'; 77 | case TabbarPageType.account: 78 | return 'Account'; 79 | } 80 | } 81 | 82 | Icon _iconFromType(TabbarPageType type) { 83 | switch (type) { 84 | case TabbarPageType.map: 85 | return const Icon(Icons.map); 86 | case TabbarPageType.orders: 87 | return const Icon(Icons.list); 88 | case TabbarPageType.account: 89 | return const Icon(Icons.account_box_outlined); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /yx_scope/packages/yx_scope_linter/doc/manual_linter.md: -------------------------------------------------------------------------------- 1 | ### avoid_call_dep_method_inside_functions 2 | 3 | Do not invoke the `dep` and `asyncDep` methods from within functions. Only assign them 4 | to `late final` fields. 5 | 6 | ### avoid_direct_scope_child 7 | 8 | If a `Dep` within a `ScopeContainer` is responsible for a child scope, it should be a holder (a 9 | subclass of `CoreScopeHolder`), not a `ScopeContainer`. 10 | 11 | ### consider_module_suffix 12 | 13 | Do not use the `Dep` suffix for `ScopeModule` fields. Use the `Module` suffix: `entityNameModule`. 14 | 15 | ### avoid_sync_init_dispose 16 | 17 | Entities that require initialization are often initialized asynchronously. Similarly, disposal often 18 | requires asynchronous execution (e.g., `subscription.cancel()`). Using synchronous `init`/`dispose` 19 | methods can lead to asynchronous functions being called without `await`, potentially causing 20 | non-deterministic initialization or disposal order. To avoid this, even synchronous initialization 21 | should be handled in asynchronous `init`/`dispose` methods. 22 | 23 | ### avoid_passing_deps_in_constructor 24 | 25 | Do not pass `Dep` into the constructor of other entities. `Dep` should only be used within 26 | a `ScopeContainer`. 27 | 28 | ### order_initialize_queue_first 29 | 30 | Declare `initializeQueue` as the first member in `ScopeContainer`. This helps to quickly identify 31 | asynchronous dependencies and their execution order. 32 | 33 | ### wrong_scope_container_fields_order 34 | 35 | Declare fields within a `ScopeContainer` in the following order (if applicable): 36 | 37 | 1. `initializeQueue` getter (if necessary) 38 | 2. All `ScopeHolder` fields for child scopes 39 | 3. All `ScopeModule` fields for the current scope 40 | 4. All `Dep` fields 41 | 42 | The order of private/public fields is not regulated. 43 | 44 | ### avoid_cache_dep_outscope 45 | 46 | Do not assign `Dep` to fields or global variables outside the scope. 47 | 48 | ### avoid_scope_without_holder 49 | 50 | Do not instantiate a scope manually outside of a `ScopeHolder`. 51 | 52 | ### avoid_creating_child_holder_outside_parent_scope 53 | 54 | Do not instantiate a child scope outside the parent scope. 55 | 56 | ### create_container_always_protected 57 | 58 | The `createContainer` method should always be `protected`, even in subclasses. This ensures that no 59 | one calls `createContainer` directly, but instead uses the `create` method. 60 | 61 | ### container_or_dep_in_module_constructor 62 | 63 | When instantiating a `ScopeModule`, only pass a `ScopeContainer` or a `Dep` to its constructor, not 64 | instances of classes. Otherwise, there is a risk of breaking the scope's lifecycle. For example, 65 | passing `ScopeModule(someDep.get)` would cause `someDep` to be instantiated immediately upon module 66 | creation, rather than lazily when the dependency is first accessed. 67 | 68 | ### avoid_conditions_in_initialize_queue 69 | 70 | All asynchronous dependencies in a scope must be initialized. This ensures that accessing any 71 | dependency always works predictably. If a dependency is declared, it must be ready to use. Any 72 | conditions for initialization should be placed inside the init method. -------------------------------------------------------------------------------- /yx_state/packages/yx_state/lib/src/function_stream_handler/handle_task_emitter.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:meta/meta.dart'; 4 | 5 | import '../base/interface.dart'; 6 | 7 | /// Internal implementation of [Emitter] used by [HandleTaskImpl]. 8 | /// 9 | /// This class manages the state of a single emit operation and provides 10 | /// methods for completion and cancellation. 11 | @internal 12 | class HandleTaskEmitter implements Emitter { 13 | /// The function to emit the state. 14 | final void Function(State state) _emit; 15 | 16 | /// The completer for the emitter. 17 | final _completer = Completer(); 18 | 19 | /// Whether the emitter has been canceled. 20 | var _isCanceled = false; 21 | 22 | /// Whether the emitter has been completed. 23 | var _isCompleted = false; 24 | 25 | /// Whether the emitter is done. 26 | @override 27 | bool get isDone => _isCanceled || _isCompleted; 28 | 29 | /// Returns true if the emitter has been canceled. 30 | bool get isCanceled => _isCanceled; 31 | 32 | /// A future that completes when the emitter is done. 33 | Future get future => _completer.future; 34 | 35 | /// Creates a new [HandleTaskEmitter] with the provided emit function. 36 | HandleTaskEmitter(this._emit); 37 | 38 | @override 39 | void call(State state) { 40 | assert( 41 | !_isCompleted, 42 | 'The emitter has already been completed. ' 43 | 'This usually happens because of an unawaited future in your handler. ' 44 | 'Make sure to await all asynchronous operations and check emit.isDone ' 45 | 'before emitting to avoid this issue.', 46 | ); 47 | 48 | // Only emit if the emitter is not canceled 49 | if (!_isCanceled) { 50 | _emit(state); 51 | } 52 | } 53 | 54 | /// Cancels the emitter. 55 | /// 56 | /// After calling this method, the emitter will no longer accept new state 57 | /// updates and the [future] will complete normally. 58 | void cancel() { 59 | if (isDone) { 60 | return; 61 | } 62 | 63 | _isCanceled = true; 64 | if (!_completer.isCompleted) { 65 | _completer.complete(); 66 | } 67 | } 68 | 69 | /// Marks the emitter as complete. 70 | /// 71 | /// After calling this method, the emitter will no longer accept new state 72 | /// updates and the [future] will complete normally. 73 | void complete() { 74 | if (isDone) { 75 | return; 76 | } 77 | 78 | _isCompleted = true; 79 | if (!_completer.isCompleted) { 80 | _completer.complete(); 81 | } 82 | } 83 | 84 | /// Completes the emitter with an error. 85 | /// 86 | /// After calling this method, the emitter will no longer accept new state 87 | /// updates and the [future] will complete with the provided [error] and 88 | /// [stackTrace]. 89 | void completeError(Object error, StackTrace stackTrace) { 90 | if (isDone) { 91 | return; 92 | } 93 | 94 | _isCompleted = true; 95 | if (!_completer.isCompleted) { 96 | _completer.completeError(error, stackTrace); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /yx_state/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | formatter: 2 | page_width: 80 3 | trailing_commas: preserve 4 | 5 | linter: 6 | rules: 7 | - avoid_empty_else 8 | - avoid_relative_lib_imports 9 | - avoid_shadowing_type_parameters 10 | - avoid_types_as_parameter_names 11 | - await_only_futures 12 | - camel_case_extensions 13 | - camel_case_types 14 | - collection_methods_unrelated_type 15 | - curly_braces_in_flow_control_structures 16 | - dangling_library_doc_comments 17 | - depend_on_referenced_packages 18 | - empty_catches 19 | - file_names 20 | - hash_and_equals 21 | - implicit_call_tearoffs 22 | - library_annotations 23 | - no_duplicate_case_values 24 | - no_wildcard_variable_uses 25 | - non_constant_identifier_names 26 | - null_check_on_nullable_type_parameter 27 | - prefer_generic_function_type_aliases 28 | - prefer_is_empty 29 | - prefer_is_not_empty 30 | - prefer_iterable_whereType 31 | - prefer_typing_uninitialized_variables 32 | - provide_deprecation_message 33 | - secure_pubspec_urls 34 | - type_literal_in_constant_pattern 35 | - unnecessary_overrides 36 | - unrelated_type_equality_checks 37 | - use_string_in_part_of_directives 38 | - valid_regexps 39 | - void_checks 40 | - annotate_overrides 41 | - avoid_function_literals_in_foreach_calls 42 | - avoid_init_to_null 43 | - avoid_renaming_method_parameters 44 | - avoid_return_types_on_setters 45 | - avoid_returning_null_for_void 46 | - avoid_single_cascade_in_expression_statements 47 | - constant_identifier_names 48 | - control_flow_in_finally 49 | - empty_constructor_bodies 50 | - empty_statements 51 | - exhaustive_cases 52 | - implementation_imports 53 | - library_prefixes 54 | - library_private_types_in_public_api 55 | - no_leading_underscores_for_library_prefixes 56 | - no_leading_underscores_for_local_identifiers 57 | - null_closures 58 | - overridden_fields 59 | - package_names 60 | - prefer_adjacent_string_concatenation 61 | - prefer_collection_literals 62 | - prefer_conditional_assignment 63 | - prefer_contains 64 | - prefer_final_fields 65 | - prefer_for_elements_to_map_fromIterable 66 | - prefer_function_declarations_over_variables 67 | - prefer_if_null_operators 68 | - prefer_initializing_formals 69 | - prefer_inlined_adds 70 | - prefer_interpolation_to_compose_strings 71 | - prefer_is_not_operator 72 | - prefer_null_aware_operators 73 | - prefer_spread_collections 74 | - recursive_getters 75 | - slash_for_doc_comments 76 | - type_init_formals 77 | - unnecessary_brace_in_string_interps 78 | - unnecessary_const 79 | - unnecessary_constructor_name 80 | - unnecessary_getters_setters 81 | - unnecessary_late 82 | - unnecessary_new 83 | - unnecessary_null_aware_assignments 84 | - unnecessary_null_in_if_null_operators 85 | - unnecessary_nullable_for_final_variable_declarations 86 | - unnecessary_string_escapes 87 | - unnecessary_string_interpolations 88 | - unnecessary_this 89 | - unnecessary_to_list_in_spreads 90 | - use_function_type_syntax_for_parameters 91 | - use_rethrow_when_possible 92 | - use_super_parameters 93 | --------------------------------------------------------------------------------