Compare commits

...

8 Commits

Author SHA1 Message Date
Sergey Penkovsky
009808975e chore(release): publish packages
- cherrypick@2.1.0-dev.1
 - cherrypick_flutter@1.1.1-dev.1
2025-05-16 13:28:35 +03:00
Sergey Penkovsky
e3eb0eef06 implement example 2025-05-16 13:26:12 +03:00
Sergey Penkovsky
32bda69476 doc: update README 2025-05-16 13:26:12 +03:00
Sergey Penkovsky
d24d6e3f2b chore(release): publish packages
- cherrypick@2.1.0-dev.0
 - cherrypick_flutter@1.1.1-dev.0
2025-05-16 13:26:06 +03:00
Sergey Penkovsky
1f08231402 doc: update README and example 2025-05-16 13:19:43 +03:00
Sergey Penkovsky
09de8bca98 implement test 2025-05-16 13:19:43 +03:00
Sergey Penkovsky
b3660eee93 fix warnings 2025-05-16 13:19:42 +03:00
Sergey Penkovsky
62c8b2247b feat: Add async dependency resolution and enhance example
- Implemented async provider methods `toProvideAsync` and `toProvideAsyncWithParams` in `Binding` class, allowing asynchronous initialization with dynamic parameters.
- Added typedefs `AsyncProvider<T>` and `AsyncProviderWithParams<T>` for better type clarity with async operations.
- Introduced async resolution methods `resolveAsync` and `tryResolveAsync` in `Scope` for resolving asynchronous dependencies.
- Updated example in `main.dart` to demonstrate async dependency resolution capabilities.
  - Modified `FeatureModule` to utilize async providers for `DataRepository` and `DataBloc`.
  - Replaced synchronous resolution with `resolveAsync` where applicable.
  - Handled potential errors in dependency resolution with try-catch.
- Removed unnecessary whitespace for cleaner code formatting.
2025-05-16 13:16:14 +03:00
32 changed files with 843 additions and 109 deletions

View File

@@ -3,6 +3,36 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## 2025-05-16
### Changes
---
Packages with breaking changes:
- There are no breaking changes in this release.
Packages with other changes:
- [`cherrypick` - `v2.1.0-dev.1`](#cherrypick---v210-dev1)
- [`cherrypick_flutter` - `v1.1.1-dev.1`](#cherrypick_flutter---v111-dev1)
---
#### `cherrypick` - `v2.1.0-dev.1`
- **FIX**: fix warnings.
- **FIX**: fix warnings.
- **FIX**: support passing params when resolving dependency recursively in parent scope.
- **FEAT**: Add async dependency resolution and enhance example.
- **FEAT**: Add async dependency resolution and enhance example.
#### `cherrypick_flutter` - `v1.1.1-dev.1`
- **FIX**: fix warnings.
## 2025-05-16
### Changes

View File

@@ -19,3 +19,6 @@ doc/api/
*.js_
*.js.deps
*.js.map
# FVM Version Cache
.fvm/

View File

@@ -1,6 +1,18 @@
## 2.1.0-dev.1
- **FIX**: fix warnings.
- **FIX**: fix warnings.
- **FIX**: support passing params when resolving dependency recursively in parent scope.
- **FEAT**: Add async dependency resolution and enhance example.
- **FEAT**: Add async dependency resolution and enhance example.
## 2.0.2
- **FIX**: support passing params when resolving dependency recursively in parent scope.
## 2.1.0-dev.0
- **FEAT**: Add async dependency resolution and enhance example.
## 2.0.1
- **FIX**: fix warning.

View File

@@ -1,31 +1,43 @@
# CherryPick Flutter
`cherrypick_flutter` is a powerful Flutter library for managing and accessing dependencies within your application through a root scope context using `CherryPickProvider`. It offers simplified dependency injection, making your application more modular and test-friendly.
`cherrypick_flutter` is a robust Flutter library designed for managing and accessing dependencies using a scope context provided by `CherryPickProvider`. It enhances your application's modularity and testability by simplifying dependency injection.
## Quick Start
### Main Components in Dependency Injection (DI)
### Core Components of Dependency Injection (DI)
#### Binding
A Binding is a custom instance configurator, essential for setting up dependencies. It offers the following key methods:
A Binding is a custom instance configurator crucial for setting up dependencies. It offers the following key methods:
- `toInstance()`: Directly provides an initialized instance.
- `toProvide()`: Accepts a provider function or constructor for lazy initialization.
- `withName()`: Assigns a name to an instance, allowing for retrieval by name.
- `singleton()`: Marks the instance as a singleton, ensuring only one instance exists in the scope.
- `toProvide()`: Accepts a provider function for lazy initialization.
- `toProvideAsync()`: Accepts an asynchronous provider for lazy initialization.
- `toProvideWithParams()`: Accepts a provider function requiring dynamic parameters.
- `toProvideAsyncWithParams()`: Accepts an asynchronous provider requiring dynamic parameters.
- `withName()`: Assigns a name for instance retrieval by name.
- `singleton()`: Marks the instance as a singleton, ensuring only one instance exists within the scope.
##### Example:
```dart
// Direct instance initialization with toInstance()
// Direct instance initialization using toInstance()
Binding<String>().toInstance("hello world");
// Or use a provider for lazy initialization
// Lazy initialization via provider
Binding<String>().toProvide(() => "hello world");
// Named instance
Binding<String>().withName("my_string").toInstance("hello world");
// Asynchronous lazy initialization
Binding<String>().toProvideAsync(() async => "hello async world");
/ Asynchronous lazy initialization with dynamic parameters
Binding<String>().toProvideAsyncWithParams((params) async => "hello $params");
// Initialization with dynamic parameters
Binding<String>().toProvideWithParams((params) => "hello $params");
// Named instance for resolution
Binding<String>().toProvide(() => "hello world").withName("my_string").toInstance("hello world");
// Singleton instance
Binding<String>().toProvide(() => "hello world").singleton();
@@ -33,7 +45,7 @@ Binding<String>().toProvide(() => "hello world").singleton();
#### Module
A Module encapsulates bindings, allowing you to organize dependencies logically. To create a custom module, implement the `void builder(Scope currentScope)` method.
A Module encapsulates bindings, logically organizing dependencies. Implement the `void builder(Scope currentScope)` method to create a custom module.
##### Example:
@@ -48,13 +60,13 @@ class AppModule extends Module {
#### Scope
A Scope is the container that manages your dependency tree, holding modules and instances. Use the scope to access dependencies with the `resolve<T>()` method.
A Scope manages your dependency tree, holding modules and instances. Use the scope to access dependencies with `resolve<T>()` or `resolveAsync<T>()` for asynchronous operations.
##### Example:
```dart
// Open the main scope
final rootScope = Cherrypick.openRootScope();
final rootScope = CherryPick.openRootScope();
// Install custom modules
rootScope.installModules([AppModule()]);
@@ -62,13 +74,16 @@ rootScope.installModules([AppModule()]);
// Resolve an instance
final str = rootScope.resolve<String>();
// Asynchronously resolve an instance
final asyncStr = await rootScope.resolveAsync<String>();
// Close the main scope
Cherrypick.closeRootScope();
CherryPick.closeRootScope();
```
## Example Application
The following example demonstrates module setup, scope management, and dependency resolution.
The following example demonstrates module setup, scope management, and dependency resolution (both synchronous and asynchronous).
```dart
import 'dart:async';
@@ -84,26 +99,30 @@ class AppModule extends Module {
}
class FeatureModule extends Module {
bool isMock;
final bool isMock;
FeatureModule({required this.isMock});
@override
void builder(Scope currentScope) {
// Using toProvideAsync for async initialization
bind<DataRepository>()
.withName("networkRepo")
.toProvide(
() => NetworkDataRepository(
currentScope.resolve<ApiClient>(
named: isMock ? "apiClientMock" : "apiClientImpl",
),
),
)
.toProvideAsync(() async {
final client = await Future.delayed(
Duration(milliseconds: 100),
() => currentScope.resolve<ApiClient>(
named: isMock ? "apiClientMock" : "apiClientImpl"));
return NetworkDataRepository(client);
})
.singleton();
bind<DataBloc>().toProvide(
() => DataBloc(
currentScope.resolve<DataRepository>(named: "networkRepo"),
),
// Asynchronous initialization of DataBloc
bind<DataBloc>().toProvideAsync(
() async {
final repo = await currentScope.resolveAsync<DataRepository>(named: "networkRepo");
return DataBloc(repo);
},
);
}
}
@@ -117,7 +136,8 @@ void main() async {
.openSubScope("featureScope")
.installModules([FeatureModule(isMock: true)]);
final dataBloc = subScope.resolve<DataBloc>();
// Asynchronous instance resolution
final dataBloc = await subScope.resolveAsync<DataBloc>();
dataBloc.data.listen((d) => print('Received data: $d'),
onError: (e) => print('Error: $e'), onDone: () => print('DONE'));
@@ -128,7 +148,7 @@ class DataBloc {
final DataRepository _dataRepository;
Stream<String> get data => _dataController.stream;
StreamController<String> _dataController = new StreamController.broadcast();
StreamController<String> _dataController = StreamController.broadcast();
DataBloc(this._dataRepository);
@@ -185,6 +205,8 @@ class ApiClientImpl implements ApiClient {
- [x] Main Scope and Sub Scopes
- [x] Named Instance Initialization
- [x] Asynchronous Dependency Resolution
- [x] Dynamic Parameter Support for Providers
## Contributing

View File

@@ -1,39 +1,38 @@
import 'dart:async';
import 'package:cherrypick/cherrypick.dart';
import 'package:meta/meta.dart';
import 'package:cherrypick/cherrypick.dart';
class AppModule extends Module {
@override
void builder(Scope currentScope) {
bind<ApiClient>().withName('apiClientMock').toInstance(ApiClientMock());
bind<ApiClient>().withName('apiClientImpl').toInstance(ApiClientImpl());
bind<ApiClient>().withName("apiClientMock").toInstance(ApiClientMock());
bind<ApiClient>().withName("apiClientImpl").toInstance(ApiClientImpl());
}
}
class FeatureModule extends Module {
bool isMock;
final bool isMock;
FeatureModule({required this.isMock});
@override
void builder(Scope currentScope) {
bind<DataRepository>()
.withName('networkRepo')
.toProvide(
() => NetworkDataRepository(
currentScope.resolve<ApiClient>(
named: isMock ? 'apiClientMock' : 'apiClientImpl',
),
),
)
.singleton();
// Using toProvideAsync for async initialization
bind<DataRepository>().withName("networkRepo").toProvideAsync(() async {
final client = await Future.delayed(
Duration(milliseconds: 100),
() => currentScope.resolve<ApiClient>(
named: isMock ? "apiClientMock" : "apiClientImpl"));
return NetworkDataRepository(client);
}).singleton();
bind<DataBloc>().toProvideWithParams(
(param) => DataBloc(
currentScope.resolve<DataRepository>(named: 'networkRepo'),
param,
),
// Asynchronous initialization of DataBloc
bind<DataBloc>().toProvideAsync(
() async {
final repo = await currentScope.resolveAsync<DataRepository>(
named: "networkRepo");
return DataBloc(repo);
},
);
}
}
@@ -44,10 +43,11 @@ void main() async {
]);
final subScope = scope
.openSubScope('featureScope')
.openSubScope("featureScope")
.installModules([FeatureModule(isMock: true)]);
final dataBloc = subScope.resolve<DataBloc>(params: 'PARAMETER');
// Asynchronous instance resolution
final dataBloc = await subScope.resolveAsync<DataBloc>();
dataBloc.data.listen((d) => print('Received data: $d'),
onError: (e) => print('Error: $e'), onDone: () => print('DONE'));
@@ -60,13 +60,11 @@ class DataBloc {
Stream<String> get data => _dataController.stream;
final StreamController<String> _dataController = StreamController.broadcast();
final String param;
DataBloc(this._dataRepository, this.param);
DataBloc(this._dataRepository);
Future<void> fetchData() async {
try {
_dataController.sink.add(await _dataRepository.getData(param));
_dataController.sink.add(await _dataRepository.getData());
} catch (e) {
_dataController.sink.addError(e);
}
@@ -78,7 +76,7 @@ class DataBloc {
}
abstract class DataRepository {
Future<String> getData(String param);
Future<String> getData();
}
class NetworkDataRepository implements DataRepository {
@@ -88,42 +86,26 @@ class NetworkDataRepository implements DataRepository {
NetworkDataRepository(this._apiClient);
@override
Future<String> getData(String param) async => await _apiClient.sendRequest(
url: 'www.google.com',
token: _token,
requestBody: {'type': 'data'},
param: param);
Future<String> getData() async => await _apiClient.sendRequest(
url: 'www.google.com', token: _token, requestBody: {'type': 'data'});
}
abstract class ApiClient {
Future sendRequest({
@required String url,
String token,
Map requestBody,
String param,
});
Future sendRequest({@required String url, String token, Map requestBody});
}
class ApiClientMock implements ApiClient {
@override
Future sendRequest({
@required String? url,
String? token,
Map? requestBody,
String? param,
}) async {
return 'Local Data $param';
Future sendRequest(
{@required String? url, String? token, Map? requestBody}) async {
return 'Local Data';
}
}
class ApiClientImpl implements ApiClient {
@override
Future sendRequest({
@required String? url,
String? token,
Map? requestBody,
String? param,
}) async {
return 'Network data $param';
Future sendRequest(
{@required String? url, String? token, Map? requestBody}) async {
return 'Network data';
}
}

View File

@@ -1,4 +1,4 @@
library cherrypick;
library;
//
// Copyright 2021 Sergey Penkovsky (sergey.penkovsky@gmail.com)

View File

@@ -15,6 +15,10 @@ enum Mode { simple, instance, providerInstance, providerInstanceWithParams }
typedef ProviderWithParams<T> = T Function(dynamic params);
typedef AsyncProvider<T> = Future<T> Function();
typedef AsyncProviderWithParams<T> = Future<T> Function(dynamic params);
/// RU: Класс Binding<T> настраивает параметры экземпляра.
/// ENG: The Binding<T> class configures the settings for the instance.
///
@@ -24,6 +28,9 @@ class Binding<T> {
late String _name;
T? _instance;
T? Function()? _provider;
AsyncProvider<T>? asyncProvider;
AsyncProviderWithParams<T>? asyncProviderWithParams;
ProviderWithParams<T>? _providerWithParams;
late bool _isSingleton = false;
late bool _isNamed = false;
@@ -94,6 +101,16 @@ class Binding<T> {
return this;
}
/// RU: Инициализация экземляпяра  через провайдер [value].
/// ENG: Initialization instance via provider [value].
///
/// return [Binding]
Binding<T> toProvideAsync(AsyncProvider<T> provider) {
_mode = Mode.providerInstance;
asyncProvider = provider;
return this;
}
/// RU: Инициализация экземляпяра  через провайдер [value] c динамическим параметром.
/// ENG: Initialization instance via provider [value] with a dynamic param.
///
@@ -104,6 +121,16 @@ class Binding<T> {
return this;
}
/// RU: Инициализация экземляра через асинхронный провайдер [value] с динамическим параметром.
/// ENG: Initializes the instance via async provider [value] with a dynamic param.
///
/// return [Binding]
Binding<T> toProvideAsyncWithParams(AsyncProviderWithParams<T> provider) {
_mode = Mode.providerInstanceWithParams;
asyncProviderWithParams = provider;
return this;
}
/// RU: Инициализация экземляпяра  как сингелтон [value].
/// ENG: Initialization instance as a singelton [value].
///

View File

@@ -132,4 +132,45 @@ class Scope {
// 2 Поиск зависимостей в родительском скоупе
return _parentScope?.tryResolve(named: named, params: params);
}
/// RU: Асинхронно возвращает найденную зависимость, определенную параметром типа [T].
/// Выдает [StateError], если зависимость не может быть разрешена.
/// Если хотите получить [null], если зависимость не может быть найдена, используйте [tryResolveAsync].
/// return - возвращает объект типа [T] or [StateError]
///
/// ENG: Asynchronously returns the found dependency specified by the type parameter [T].
/// Throws [StateError] if the dependency cannot be resolved.
/// If you want to get [null] if the dependency cannot be found, use [tryResolveAsync] instead.
/// return - returns an object of type [T] or [StateError]
///
Future<T> resolveAsync<T>({String? named, dynamic params}) async {
var resolved = await tryResolveAsync<T>(named: named, params: params);
if (resolved != null) {
return resolved;
} else {
throw StateError(
'Can\'t resolve async dependency `$T`. Maybe you forget register it?');
}
}
Future<T?> tryResolveAsync<T>({String? named, dynamic params}) async {
if (_modulesList.isNotEmpty) {
for (var module in _modulesList) {
for (var binding in module.bindingSet) {
if (binding.key == T &&
((!binding.isNamed && named == null) ||
(binding.isNamed && named == binding.name))) {
if (binding.asyncProvider != null) {
return await binding.asyncProvider?.call();
}
if (binding.asyncProviderWithParams != null) {
return await binding.asyncProviderWithParams!(params);
}
}
}
}
}
return _parentScope?.tryResolveAsync(named: named, params: params);
}
}

View File

@@ -1,6 +1,6 @@
name: cherrypick
description: Cherrypick is a small dependency injection (DI) library for dart/flutter projects.
version: 2.0.2
version: 2.1.0-dev.1
homepage: https://pese-git.github.io/cherrypick-site/
documentation: https://github.com/pese-git/cherrypick/wiki
repository: https://github.com/pese-git/cherrypick
@@ -13,9 +13,8 @@ dependencies:
meta: ^1.3.0
dev_dependencies:
#pedantic: ^1.11.0
test: ^1.17.2
lints: ^5.0.0
test: ^1.25.15
mockito: ^5.0.6
lints: ^2.1.0
melos: ^6.3.2

View File

@@ -188,6 +188,25 @@ void main() {
});
});
group('Check Async provider.', () {
test('Binding resolves value asynchronously', () async {
final expectedValue = 5;
final binding = Binding<int>().toProvideAsync(() async => expectedValue);
final result = await binding.asyncProvider?.call();
expect(result, expectedValue);
});
test('Binding resolves value asynchronously with params', () async {
final expectedValue = 5;
final binding = Binding<int>().toProvideAsyncWithParams(
(param) async => expectedValue + (param as int));
final result = await binding.asyncProviderWithParams?.call(3);
expect(result, expectedValue + 3);
});
});
group('Check singleton provide.', () {
group('Without name.', () {
test('Binding resolves null', () {

View File

@@ -1,4 +1,8 @@
## 1.1.1
## 1.1.1-dev.1
- **FIX**: fix warnings.
## 1.1.1-dev.0
- Update a dependency to the latest release.

View File

@@ -1,6 +1,6 @@
# CherryPick Flutter
`cherrypick_flutter` is a Flutter library that allows access to the root scope through the context using `CherryPickProvider`. This package is designed to provide a simple and convenient way to interact with the root scope within the widget tree.
`cherrypick_flutter` offers a Flutter integration to access and manage dependency injection scopes using the `CherryPickProvider`. This setup facilitates accessing the root scope directly from the widget tree, providing a straightforward mechanism for dependences management within Flutter applications.
## Installation
@@ -11,13 +11,13 @@ dependencies:
cherrypick_flutter: ^1.0.0
```
Then run `flutter pub get` to install the package.
Run `flutter pub get` to install the package dependencies.
## Usage
### Importing the Package
To start using `cherrypick_flutter`, import it into your Dart code:
To begin using `cherrypick_flutter`, import it within your Dart file:
```dart
import 'package:cherrypick_flutter/cherrypick_flutter.dart';
@@ -25,7 +25,7 @@ import 'package:cherrypick_flutter/cherrypick_flutter.dart';
### Providing State with `CherryPickProvider`
Use `CherryPickProvider` to wrap the part of the widget tree that requires access to the provided state.
Use `CherryPickProvider` to encase the widget tree section that requires access to the root or specific subscopes:
```dart
import 'package:flutter/material.dart';
@@ -34,35 +34,40 @@ import 'package:cherrypick_flutter/cherrypick_flutter.dart';
void main() {
runApp(
CherryPickProvider(
rootScope: yourRootScopeInstance,
child: MyApp(),
),
);
}
```
Note: The current implementation of `CherryPickProvider` does not directly pass a `rootScope`. Instead, it utilizes its methods to open root and sub-scopes internally.
### Accessing State
To access the state provided by `CherryPickProvider`, use the `of` method. This is typically done in the `build` method of your widgets.
Access the state provided by `CherryPickProvider` within widget `build` methods using the `of` method:
```dart
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final rootScope = CherryPickProvider.of(context).rootScope;
final CherryPickProvider cherryPick = CherryPickProvider.of(context);
final rootScope = cherryPick.openRootScope();
return Text('Current state: ${rootScope.someStateValue}');
// Use the rootScope or open a subScope as needed
final subScope = cherryPick.openSubScope(scopeName: "exampleScope");
return Text('Scope accessed!');
}
}
```
### Updating State
The `CherryPickProvider` will automatically update its dependents when its state changes. Ensure to override the `updateShouldNotify` method to specify when notifications should occur, as shown in the provided implementation.
The `CherryPickProvider` setup internally manages state updates. Ensure the `updateShouldNotify` method accurately reflects when the dependents should receive updates. In the provided implementation, it currently does not notify updates automatically.
## Example
Here is a simple example showing how to implement and use the `CherryPickProvider`.
Here is an example illustrating how to implement and utilize `CherryPickProvider` within a Flutter application:
```dart
class MyApp extends StatelessWidget {
@@ -70,7 +75,7 @@ class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
final rootScope = CherryPickProvider.of(context).rootScope;
final rootScope = CherryPickProvider.of(context).openRootScope();
return MaterialApp.router(
routerDelegate: rootScope.resolve<AppRouter>().delegate(),
@@ -81,10 +86,12 @@ class MyApp extends StatelessWidget {
}
```
In this example, `CherryPickProvider` accesses and resolves dependencies using root scope and potentially sub-scopes configured by the application.
## Contributing
We welcome contributions from the community. Please open issues and pull requests if you have ideas for improvements.
Contributions to improve this library are welcome. Feel free to open issues and submit pull requests on the repository.
## License
This project is licensed under the Apache License 2.0.
This project is licensed under the Apache License 2.0. A copy of the license can be obtained at [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0).

View File

@@ -1,3 +1,5 @@
library;
///
/// Copyright 2021 Sergey Penkovsky (sergey.penkovsky@gmail.com)
/// Licensed under the Apache License, Version 2.0 (the "License");
@@ -10,6 +12,5 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
library cherrypick_flutter;
export 'src/cherrypick_provider.dart';

View File

@@ -1,6 +1,6 @@
name: cherrypick_flutter
description: "Flutter library that allows access to the root scope through the context using `CherryPickProvider`."
version: 1.1.1
version: 1.1.1-dev.1
homepage: https://pese-git.github.io/cherrypick-site/
documentation: https://github.com/pese-git/cherrypick/wiki
repository: https://github.com/pese-git/cherrypick
@@ -13,12 +13,14 @@ environment:
dependencies:
flutter:
sdk: flutter
cherrypick: ^2.0.2
cherrypick: ^2.1.0-dev.1
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^4.0.0
flutter_lints: ^5.0.0
test: ^1.25.7
melos: ^6.3.2
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec

47
examples/client_app/.gitignore vendored Normal file
View File

@@ -0,0 +1,47 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.pub-cache/
.pub/
/build/
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release
pubspec_overrides.yaml

View File

@@ -0,0 +1,30 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: "4cf269e36de2573851eaef3c763994f8f9be494d"
channel: "stable"
project_type: app
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: 4cf269e36de2573851eaef3c763994f8f9be494d
base_revision: 4cf269e36de2573851eaef3c763994f8f9be494d
- platform: web
create_revision: 4cf269e36de2573851eaef3c763994f8f9be494d
base_revision: 4cf269e36de2573851eaef3c763994f8f9be494d
# User provided section
# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'

View File

@@ -0,0 +1,16 @@
# client_app
A new Flutter project.
## Getting Started
This project is a starting point for a Flutter application.
A few resources to get you started if this is your first Flutter project:
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
For help getting started with Flutter development, view the
[online documentation](https://docs.flutter.dev/), which offers tutorials,
samples, guidance on mobile development, and a full API reference.

View File

@@ -0,0 +1,28 @@
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.
# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml
linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at https://dart.dev/lints.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options

View File

@@ -0,0 +1,41 @@
import 'package:cherrypick/cherrypick.dart';
import 'package:flutter/material.dart';
import 'package:cherrypick_flutter/cherrypick_flutter.dart';
import 'my_home_page.dart';
import 'use_case.dart';
void main() {
// Здесь происходит инициализация рутового скоупа и привязка зависимостей
CherryPick.openRootScope().installModules([
// Создаем модуль, который будет предоставлять UseCase
ModuleWithUseCase(),
]);
runApp(
const CherryPickProvider(
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Example App',
theme: ThemeData(primarySwatch: Colors.blue),
home: MyHomePage(),
);
}
}
// Модуль для настройки зависимостей
class ModuleWithUseCase extends Module {
@override
void builder(Scope currentScope) {
// Привязка UseCase как singleton
bind<UseCase>().toInstance(UseCase());
}
}

View File

@@ -0,0 +1,21 @@
import 'package:flutter/material.dart';
import 'package:cherrypick_flutter/cherrypick_flutter.dart';
import 'use_case.dart';
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Разрешение зависимости UseCase из рутового скоупа
final UseCase useCase =
CherryPickProvider.of(context).openRootScope().resolve<UseCase>();
return Scaffold(
appBar: AppBar(
title: Text('Example App'),
),
body: Center(
child: Text(useCase.fetchData()),
),
);
}
}

View File

@@ -0,0 +1,3 @@
class UseCase {
String fetchData() => "Data fetched by UseCase";
}

View File

@@ -0,0 +1,229 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
async:
dependency: transitive
description:
name: async
sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
url: "https://pub.dev"
source: hosted
version: "2.11.0"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
characters:
dependency: transitive
description:
name: characters
sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
url: "https://pub.dev"
source: hosted
version: "1.3.0"
cherrypick:
dependency: "direct main"
description:
name: cherrypick
sha256: "802427b777bc80e39216ec7070e452edd1a01eca84f824dfb57cc5e055797c4c"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
cherrypick_flutter:
dependency: "direct main"
description:
name: cherrypick_flutter
sha256: "6fa2ee4ea06a8f9edfaab43e665199b378add34c52ca081f55ed82cded090257"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
clock:
dependency: transitive
description:
name: clock
sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
url: "https://pub.dev"
source: hosted
version: "1.1.1"
collection:
dependency: transitive
description:
name: collection
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
url: "https://pub.dev"
source: hosted
version: "1.18.0"
cupertino_icons:
dependency: "direct main"
description:
name: cupertino_icons
sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6
url: "https://pub.dev"
source: hosted
version: "1.0.8"
fake_async:
dependency: transitive
description:
name: fake_async
sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
url: "https://pub.dev"
source: hosted
version: "1.3.1"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_lints:
dependency: "direct dev"
description:
name: flutter_lints
sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c"
url: "https://pub.dev"
source: hosted
version: "4.0.0"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
leak_tracker:
dependency: transitive
description:
name: leak_tracker
sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05"
url: "https://pub.dev"
source: hosted
version: "10.0.5"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806"
url: "https://pub.dev"
source: hosted
version: "3.0.5"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
url: "https://pub.dev"
source: hosted
version: "3.0.1"
lints:
dependency: transitive
description:
name: lints
sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235"
url: "https://pub.dev"
source: hosted
version: "4.0.0"
matcher:
dependency: transitive
description:
name: matcher
sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
url: "https://pub.dev"
source: hosted
version: "0.12.16+1"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
url: "https://pub.dev"
source: hosted
version: "0.11.1"
meta:
dependency: transitive
description:
name: meta
sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7
url: "https://pub.dev"
source: hosted
version: "1.15.0"
path:
dependency: transitive
description:
name: path
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
url: "https://pub.dev"
source: hosted
version: "1.9.0"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.99"
source_span:
dependency: transitive
description:
name: source_span
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
url: "https://pub.dev"
source: hosted
version: "1.10.0"
stack_trace:
dependency: transitive
description:
name: stack_trace
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
url: "https://pub.dev"
source: hosted
version: "1.11.1"
stream_channel:
dependency: transitive
description:
name: stream_channel
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
url: "https://pub.dev"
source: hosted
version: "2.1.2"
string_scanner:
dependency: transitive
description:
name: string_scanner
sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
url: "https://pub.dev"
source: hosted
version: "1.2.0"
term_glyph:
dependency: transitive
description:
name: term_glyph
sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
url: "https://pub.dev"
source: hosted
version: "1.2.1"
test_api:
dependency: transitive
description:
name: test_api
sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb"
url: "https://pub.dev"
source: hosted
version: "0.7.2"
vector_math:
dependency: transitive
description:
name: vector_math
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
url: "https://pub.dev"
source: hosted
version: "2.1.4"
vm_service:
dependency: transitive
description:
name: vm_service
sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d"
url: "https://pub.dev"
source: hosted
version: "14.2.5"
sdks:
dart: ">=3.5.2 <4.0.0"
flutter: ">=3.18.0-18.0.pre.54"

View File

@@ -0,0 +1,65 @@
name: client_app
description: "A new Flutter project."
publish_to: 'none'
version: 1.0.0+1
environment:
sdk: ^3.5.2
dependencies:
flutter:
sdk: flutter
cherrypick: any
cherrypick_flutter: any
cupertino_icons: ^1.0.8
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^4.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter packages.
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
# To add assets to your application, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/to/resolution-aware-images
# For details regarding adding assets from package dependencies, see
# https://flutter.dev/to/asset-from-package
# To add custom fonts to your application, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts from package dependencies,
# see https://flutter.dev/to/font-from-package

View File

@@ -0,0 +1,30 @@
// This is a basic Flutter widget test.
//
// To perform an interaction with a widget in your test, use the WidgetTester
// utility in the flutter_test package. For example, you can send tap and scroll
// gestures. You can also use WidgetTester to find child widgets in the widget
// tree, read text, and verify that the values of widget properties are correct.
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:client_app/main.dart';
void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(const MyApp());
// Verify that our counter starts at 0.
expect(find.text('0'), findsOneWidget);
expect(find.text('1'), findsNothing);
// Tap the '+' icon and trigger a frame.
await tester.tap(find.byIcon(Icons.add));
await tester.pump();
// Verify that our counter has incremented.
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsOneWidget);
});
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 917 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@@ -0,0 +1,38 @@
<!DOCTYPE html>
<html>
<head>
<!--
If you are serving your web app in a path other than the root, change the
href value below to reflect the base path you are serving from.
The path provided below has to start and end with a slash "/" in order for
it to work correctly.
For more details:
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
This is a placeholder for base href that will be replaced by the value of
the `--base-href` argument provided to `flutter build`.
-->
<base href="$FLUTTER_BASE_HREF">
<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="A new Flutter project.">
<!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="client_app">
<link rel="apple-touch-icon" href="icons/Icon-192.png">
<!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png"/>
<title>client_app</title>
<link rel="manifest" href="manifest.json">
</head>
<body>
<script src="flutter_bootstrap.js" async></script>
</body>
</html>

View File

@@ -0,0 +1,35 @@
{
"name": "client_app",
"short_name": "client_app",
"start_url": ".",
"display": "standalone",
"background_color": "#0175C2",
"theme_color": "#0175C2",
"description": "A new Flutter project.",
"orientation": "portrait-primary",
"prefer_related_applications": false,
"icons": [
{
"src": "icons/Icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "icons/Icon-512.png",
"sizes": "512x512",
"type": "image/png"
},
{
"src": "icons/Icon-maskable-192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "icons/Icon-maskable-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
]
}

View File

@@ -8,8 +8,10 @@ packages:
scripts:
analyze:
run: |
flutter analyze
exec: dart analyze
format:
run: |
flutter format
exec: dart format
test:
exec: flutter test