doc(arch): add fully featured example

This commit is contained in:
Hugo Pointcheval 2022-11-08 20:09:18 -05:00
parent db67491876
commit 6602db215f
Signed by: hugo
GPG Key ID: A9E8E9615379254F
109 changed files with 4499 additions and 29 deletions

View File

@ -0,0 +1,44 @@
# 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
.packages
.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

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.
version:
revision: e99c9c7cd9f6c0b2f8ae6e3ebfd585239f5568f4
channel: stable
project_type: app
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: e99c9c7cd9f6c0b2f8ae6e3ebfd585239f5568f4
base_revision: e99c9c7cd9f6c0b2f8ae6e3ebfd585239f5568f4
- platform: ios
create_revision: e99c9c7cd9f6c0b2f8ae6e3ebfd585239f5568f4
base_revision: e99c9c7cd9f6c0b2f8ae6e3ebfd585239f5568f4
# 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 @@
# example
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,15 @@
# 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:wyatt_analysis/analysis_options.flutter.yaml
analyzer:
exclude:
- lib/**/*.g.dart
- lib/**/*.freezed.dart

View File

@ -0,0 +1,34 @@
**/dgph
*.mode1v3
*.mode2v3
*.moved-aside
*.pbxuser
*.perspectivev3
**/*sync/
.sconsign.dblite
.tags*
**/.vagrant/
**/DerivedData/
Icon?
**/Pods/
**/.symlinks/
profile
xcuserdata
**/.generated/
Flutter/App.framework
Flutter/Flutter.framework
Flutter/Flutter.podspec
Flutter/Generated.xcconfig
Flutter/ephemeral/
Flutter/app.flx
Flutter/app.zip
Flutter/flutter_assets/
Flutter/flutter_export_environment.sh
ServiceDefinitions.json
Runner/GeneratedPluginRegistrant.*
# Exceptions to above rules.
!default.mode1v3
!default.mode2v3
!default.pbxuser
!default.perspectivev3

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>App</string>
<key>CFBundleIdentifier</key>
<string>io.flutter.flutter.app</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>App</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>11.0</string>
</dict>
</plist>

View File

@ -0,0 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"

View File

@ -0,0 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"

View File

@ -0,0 +1,41 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '11.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
flutter_ios_podfile_setup
target 'Runner' do
use_frameworks!
use_modular_headers!
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
end
end

View File

@ -0,0 +1,552 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 50;
objects = {
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
9D08BF82920EBA33F23464F5 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E946B0F0AD8828F4A7866A40 /* Pods_Runner.framework */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
06DDDDC67790BA3B67292B88 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
082A1BEB8CE5B02C4AABB40D /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
0B7103FC187D9782DF68E292 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
E946B0F0AD8828F4A7866A40 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
9D08BF82920EBA33F23464F5 /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
2C514ED236D54DCA1D9DFFC4 /* Frameworks */ = {
isa = PBXGroup;
children = (
E946B0F0AD8828F4A7866A40 /* Pods_Runner.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
9740EEB31CF90195004384FC /* Generated.xcconfig */,
);
name = Flutter;
sourceTree = "<group>";
};
97C146E51CF9000F007C117D = {
isa = PBXGroup;
children = (
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
AD70810057DA8269974A316F /* Pods */,
2C514ED236D54DCA1D9DFFC4 /* Frameworks */,
);
sourceTree = "<group>";
};
97C146EF1CF9000F007C117D /* Products */ = {
isa = PBXGroup;
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
);
name = Products;
sourceTree = "<group>";
};
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
97C147021CF9000F007C117D /* Info.plist */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
);
path = Runner;
sourceTree = "<group>";
};
AD70810057DA8269974A316F /* Pods */ = {
isa = PBXGroup;
children = (
0B7103FC187D9782DF68E292 /* Pods-Runner.debug.xcconfig */,
082A1BEB8CE5B02C4AABB40D /* Pods-Runner.release.xcconfig */,
06DDDDC67790BA3B67292B88 /* Pods-Runner.profile.xcconfig */,
);
name = Pods;
path = Pods;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
97C146ED1CF9000F007C117D /* Runner */ = {
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
FBD8EC5BAF7097A0A31AD63E /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
374960F8815E0EA685AB7576 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = Runner;
productName = Runner;
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1300;
ORGANIZATIONNAME = "";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
LastSwiftMigration = 1100;
};
};
};
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 97C146E51CF9000F007C117D;
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
97C146ED1CF9000F007C117D /* Runner */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
97C146EC1CF9000F007C117D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
374960F8815E0EA685AB7576 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Thin Binary";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
FBD8EC5BAF7097A0A31AD63E /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
97C146EA1CF9000F007C117D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C146FB1CF9000F007C117D /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C147001CF9000F007C117D /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
249021D3217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Profile;
};
249021D4217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 6Z5P8GG96U;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Profile;
};
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
97C147041CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
97C147061CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 6Z5P8GG96U;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
};
97C147071CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 6Z5P8GG96U;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147031CF9000F007C117D /* Debug */,
97C147041CF9000F007C117D /* Release */,
249021D3217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147061CF9000F007C117D /* Debug */,
97C147071CF9000F007C117D /* Release */,
249021D4217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>

View File

@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1300"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Profile"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>

View File

@ -0,0 +1,13 @@
import UIKit
import Flutter
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}

View File

@ -0,0 +1,122 @@
{
"images" : [
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@3x.png",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@3x.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@3x.png",
"scale" : "3x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@3x.png",
"scale" : "3x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@1x.png",
"scale" : "1x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@1x.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@1x.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@2x.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "Icon-App-83.5x83.5@2x.png",
"scale" : "2x"
},
{
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "Icon-App-1024x1024@1x.png",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 564 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -0,0 +1,23 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "LaunchImage.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

View File

@ -0,0 +1,5 @@
# Launch Screen Assets
You can customize the launch screen with your own desired assets by replacing the image files in this directory.
You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.

View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
<viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
</imageView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<image name="LaunchImage" width="168" height="185"/>
</resources>
</document>

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
</dependencies>
<scenes>
<!--Flutter View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>

View File

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Example</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>example</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
</dict>
</plist>

View File

@ -0,0 +1 @@
#import "GeneratedPluginRegistrant.h"

View File

@ -0,0 +1,34 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:async';
import 'package:architecture_example/core/dependency_injection/get_it.dart';
import 'package:architecture_example/core/utils/app_bloc_observer.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:hive_flutter/hive_flutter.dart';
Future<void> bootstrap(FutureOr<Widget> Function() builder) async {
WidgetsFlutterBinding.ensureInitialized();
Bloc.observer = AppBlocObserver();
await GetItInitializer.run();
await Hive.initFlutter();
runApp(
await builder.call(),
);
}

View File

@ -0,0 +1,19 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
abstract class HiveBoxes {
static const String favorites = 'favorites';
}

View File

@ -0,0 +1,78 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:async';
import 'package:architecture_example/data/data_sources/local/favorite_hive_data_source.dart';
import 'package:architecture_example/data/data_sources/local/favorite_mock_data_source.dart';
import 'package:architecture_example/data/data_sources/remote/album_api_data_source_impl.dart';
import 'package:architecture_example/data/data_sources/remote/album_mock_data_source_impl.dart';
import 'package:architecture_example/data/data_sources/remote/photo_api_data_source_impl.dart';
import 'package:architecture_example/data/data_sources/remote/photo_mock_data_source_impl.dart';
import 'package:architecture_example/domain/data_sources/local/favorite_local_data_source.dart';
import 'package:architecture_example/domain/data_sources/remote/album_remote_data_source.dart';
import 'package:architecture_example/domain/data_sources/remote/photo_remote_data_source.dart';
import 'package:get_it/get_it.dart';
import 'package:wyatt_http_client/wyatt_http_client.dart';
final getIt = GetIt.I;
abstract class GetItInitializer {
static Future<void> _initGlobal() async {}
static Future<void> _initMocks() async {
getIt
..registerLazySingleton<FavoriteLocalDataSource>(
FavoriteMockDataSource.new,
)
..registerLazySingleton<PhotoRemoteDataSource>(
PhotoMockDataSourceImpl.new,
)
..registerLazySingleton<AlbumRemoteDataSource>(
AlbumMockDataSourceImpl.new,
);
}
static Future<void> _initReal() async {
getIt
..registerLazySingleton<FavoriteLocalDataSource>(
FavoriteHiveDataSource.new,
)
..registerLazySingleton<MiddlewareClient>(() {
final Pipeline pipeline = Pipeline()
.addMiddleware(
UriPrefixMiddleware(
protocol: Protocols.https,
authority: 'jsonplaceholder.typicode.com',
),
)
.addMiddleware(BodyToJsonMiddleware());
return MiddlewareClient(pipeline: pipeline);
})
..registerLazySingleton<PhotoRemoteDataSource>(
() => PhotoApiDataSourceImpl(getIt()),
)
..registerLazySingleton<AlbumRemoteDataSource>(
() => AlbumApiDataSourceImpl(getIt()),
);
}
static Future<void> run({bool enableMocks = false}) async {
await _initGlobal();
enableMocks ? await _initMocks() : await _initReal();
await getIt.allReady();
}
}

View File

@ -0,0 +1,21 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
enum FetchStatus {
initial,
success,
failure,
}

View File

@ -0,0 +1,35 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:flutter/foundation.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class AppBlocObserver extends BlocObserver {
@override
void onTransition(
Bloc<dynamic, dynamic> bloc,
Transition<dynamic, dynamic> transition,
) {
super.onTransition(bloc, transition);
debugPrint(transition.toString());
}
@override
void onError(BlocBase<dynamic> bloc, Object error, StackTrace stackTrace) {
debugPrint(error.toString());
super.onError(bloc, error, stackTrace);
}
}

View File

@ -0,0 +1,52 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:architecture_example/core/constants/hive_boxes.dart';
import 'package:architecture_example/domain/data_sources/local/favorite_local_data_source.dart';
import 'package:architecture_example/domain/entities/photo.dart';
import 'package:hive/hive.dart';
class FavoriteHiveDataSource extends FavoriteLocalDataSource {
@override
Future<void> addPhotoToFavorites(Photo photo) async {
final box = await Hive.openBox<bool>(HiveBoxes.favorites);
if (box.containsKey(photo.id)) {
throw Exception('Photo already in favorites');
}
await box.put(photo.id, true);
}
@override
Future<bool> checkIfPhotoIsInFavorites(int id) async {
final box = await Hive.openBox<bool>(HiveBoxes.favorites);
return box.containsKey(id);
}
@override
Future<void> deletePhotoFromFavorites(int id) async {
final box = await Hive.openBox<bool>(HiveBoxes.favorites);
if (!box.containsKey(id)) {
throw Exception('Unknown photo');
}
await box.delete(id);
}
@override
Future<List<int>> getAllPhotosFromFavorites() async {
final box = await Hive.openBox<bool>(HiveBoxes.favorites);
return box.keys.cast<int>().toList();
}
}

View File

@ -0,0 +1,56 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:architecture_example/data/models/photo_model.dart';
import 'package:architecture_example/domain/data_sources/local/favorite_local_data_source.dart';
import 'package:architecture_example/domain/entities/photo.dart';
class FavoriteMockDataSource extends FavoriteLocalDataSource {
final Map<int, Photo> _mock = {
2: const PhotoModel(
albumId: 1,
id: 2,
title: 'Photo 2',
url: 'https://via.placeholder.com/600/771796',
thumbnailUrl: 'https://via.placeholder.com/150/771796',
),
};
@override
Future<void> addPhotoToFavorites(Photo photo) async {
if (_mock.containsKey(photo.id)) {
throw Exception('Photo already in favorites');
}
_mock.putIfAbsent(photo.id, () => photo);
}
@override
Future<bool> checkIfPhotoIsInFavorites(int id) async => _mock.containsKey(id);
@override
Future<void> deletePhotoFromFavorites(int id) async {
if (!_mock.containsKey(id)) {
throw Exception('Unknown photo');
}
_mock.remove(id);
}
@override
Future<List<int>> getAllPhotosFromFavorites() async {
final result = _mock.keys;
return result.toList();
}
}

View File

@ -0,0 +1,53 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:convert';
import 'package:architecture_example/data/models/album_model.dart';
import 'package:architecture_example/data/models/list_album_model.dart';
import 'package:architecture_example/domain/data_sources/remote/album_remote_data_source.dart';
import 'package:architecture_example/domain/entities/album.dart';
import 'package:wyatt_http_client/wyatt_http_client.dart';
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
class AlbumApiDataSourceImpl extends AlbumRemoteDataSource {
final MiddlewareClient _client;
AlbumApiDataSourceImpl(this._client);
@override
Future<Album> getAlbum(int id) async {
final response = await _client.get(Uri.parse('/albums/$id'));
final album =
AlbumModel.fromJson(jsonDecode(response.body) as Map<String, Object?>);
return album;
}
@override
Future<List<Album>> getAllAlbums({int? start, int? limit}) async {
final startQuery = start.isNotNull ? '_start=$start' : '';
final limitQuery = limit.isNotNull ? '_limit=$limit' : '';
final delimiter1 =
(startQuery.isNotEmpty || limitQuery.isNotEmpty) ? '?' : '';
final delimiter2 =
(startQuery.isNotEmpty && limitQuery.isNotEmpty) ? '&' : '';
final url = '/albums$delimiter1$startQuery$delimiter2$limitQuery';
final response = await _client.get(Uri.parse(url));
final albums =
ListAlbumModel.fromJson({'albums': jsonDecode(response.body)});
return albums.albums;
}
}

View File

@ -0,0 +1,46 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:architecture_example/data/models/album_model.dart';
import 'package:architecture_example/domain/data_sources/remote/album_remote_data_source.dart';
import 'package:architecture_example/domain/entities/album.dart';
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
class AlbumMockDataSourceImpl extends AlbumRemoteDataSource {
final Map<int, Album> _mock = {
1: const AlbumModel(
id: 1,
userId: 1,
title: 'Album 1',
)
};
@override
Future<Album> getAlbum(int id) async {
final response = _mock[id];
if (response.isNull) {
throw Exception('Unknown album');
}
return response!;
}
@override
Future<List<Album>> getAllAlbums({int? start, int? limit}) async {
final response = _mock.values;
final nullableEnd = (limit != null) ? start ?? 0 + limit : null;
return response.toList().sublist(start ?? 0, nullableEnd);
}
}

View File

@ -0,0 +1,70 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:convert';
import 'package:architecture_example/data/models/list_photo_model.dart';
import 'package:architecture_example/data/models/photo_model.dart';
import 'package:architecture_example/domain/data_sources/remote/photo_remote_data_source.dart';
import 'package:architecture_example/domain/entities/photo.dart';
import 'package:wyatt_http_client/wyatt_http_client.dart';
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
class PhotoApiDataSourceImpl extends PhotoRemoteDataSource {
final MiddlewareClient _client;
PhotoApiDataSourceImpl(this._client);
@override
Future<Photo> getPhoto(int id) async {
final response = await _client.get(Uri.parse('/photos/$id'));
final photo =
PhotoModel.fromJson(jsonDecode(response.body) as Map<String, Object?>);
return photo;
}
@override
Future<List<Photo>> getAllPhotos({int? start, int? limit}) async {
final startQuery = start.isNotNull ? '_start=$start' : '';
final limitQuery = limit.isNotNull ? '_limit=$limit' : '';
final delimiter1 =
(startQuery.isNotEmpty || limitQuery.isNotEmpty) ? '?' : '';
final delimiter2 =
(startQuery.isNotEmpty && limitQuery.isNotEmpty) ? '&' : '';
final url = '/photos$delimiter1$startQuery$delimiter2$limitQuery';
final response = await _client.get(Uri.parse(url));
final photos =
ListPhotoModel.fromJson({'photos': jsonDecode(response.body)});
return photos.photos;
}
@override
Future<List<Photo>> getPhotosFromAlbum(
int albumId, {
int? start,
int? limit,
}) async {
final startQuery = start.isNotNull ? '_start=$start' : '';
final limitQuery = limit.isNotNull ? '_limit=$limit' : '';
final delimiter =
(startQuery.isNotEmpty && limitQuery.isNotEmpty) ? '&' : '';
final url = '/photos?albumId=$albumId&$startQuery$delimiter$limitQuery';
final response = await _client.get(Uri.parse(url));
final photos =
ListPhotoModel.fromJson({'photos': jsonDecode(response.body)});
return photos.photos;
}
}

View File

@ -0,0 +1,67 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:architecture_example/data/models/photo_model.dart';
import 'package:architecture_example/domain/data_sources/remote/photo_remote_data_source.dart';
import 'package:architecture_example/domain/entities/photo.dart';
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
class PhotoMockDataSourceImpl extends PhotoRemoteDataSource {
final Map<int, Photo> _mock = {
1: const PhotoModel(
albumId: 1,
id: 1,
title: 'Photo 1',
url: 'https://via.placeholder.com/600/92c952',
thumbnailUrl: 'https://via.placeholder.com/150/92c952',
),
2: const PhotoModel(
albumId: 1,
id: 2,
title: 'Photo 2',
url: 'https://via.placeholder.com/600/771796',
thumbnailUrl: 'https://via.placeholder.com/150/771796',
),
};
@override
Future<Photo> getPhoto(int id) async {
final response = _mock[id];
if (response.isNull) {
throw Exception('Unknown photo');
}
return response!;
}
@override
Future<List<Photo>> getAllPhotos({int? start, int? limit}) async {
final response = _mock.values;
final nullableEnd = (limit != null) ? start ?? 0 + limit : null;
return response.toList().sublist(start ?? 0, nullableEnd);
}
@override
Future<List<Photo>> getPhotosFromAlbum(
int albumId, {
int? start,
int? limit,
}) async {
// TODO(hpcl): use start and limit
final response = _mock.values
..where((element) => element.albumId == albumId);
return response.toList();
}
}

View File

@ -0,0 +1,33 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:architecture_example/domain/entities/album.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'album_model.freezed.dart';
part 'album_model.g.dart';
@freezed
class AlbumModel extends Album with _$AlbumModel {
const factory AlbumModel({
required int userId,
required int id,
required String title,
}) = _AlbumModel;
factory AlbumModel.fromJson(Map<String, Object?> json) =>
_$AlbumModelFromJson(json);
}

View File

@ -0,0 +1,173 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target
part of 'album_model.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
AlbumModel _$AlbumModelFromJson(Map<String, dynamic> json) {
return _AlbumModel.fromJson(json);
}
/// @nodoc
mixin _$AlbumModel {
int get userId => throw _privateConstructorUsedError;
int get id => throw _privateConstructorUsedError;
String get title => throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$AlbumModelCopyWith<AlbumModel> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $AlbumModelCopyWith<$Res> {
factory $AlbumModelCopyWith(
AlbumModel value, $Res Function(AlbumModel) then) =
_$AlbumModelCopyWithImpl<$Res, AlbumModel>;
@useResult
$Res call({int userId, int id, String title});
}
/// @nodoc
class _$AlbumModelCopyWithImpl<$Res, $Val extends AlbumModel>
implements $AlbumModelCopyWith<$Res> {
_$AlbumModelCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
@pragma('vm:prefer-inline')
@override
$Res call({
Object? userId = null,
Object? id = null,
Object? title = null,
}) {
return _then(_value.copyWith(
userId: null == userId
? _value.userId
: userId // ignore: cast_nullable_to_non_nullable
as int,
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int,
title: null == title
? _value.title
: title // ignore: cast_nullable_to_non_nullable
as String,
) as $Val);
}
}
/// @nodoc
abstract class _$$_AlbumModelCopyWith<$Res>
implements $AlbumModelCopyWith<$Res> {
factory _$$_AlbumModelCopyWith(
_$_AlbumModel value, $Res Function(_$_AlbumModel) then) =
__$$_AlbumModelCopyWithImpl<$Res>;
@override
@useResult
$Res call({int userId, int id, String title});
}
/// @nodoc
class __$$_AlbumModelCopyWithImpl<$Res>
extends _$AlbumModelCopyWithImpl<$Res, _$_AlbumModel>
implements _$$_AlbumModelCopyWith<$Res> {
__$$_AlbumModelCopyWithImpl(
_$_AlbumModel _value, $Res Function(_$_AlbumModel) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@override
$Res call({
Object? userId = null,
Object? id = null,
Object? title = null,
}) {
return _then(_$_AlbumModel(
userId: null == userId
? _value.userId
: userId // ignore: cast_nullable_to_non_nullable
as int,
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int,
title: null == title
? _value.title
: title // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
@JsonSerializable()
class _$_AlbumModel implements _AlbumModel {
const _$_AlbumModel(
{required this.userId, required this.id, required this.title});
factory _$_AlbumModel.fromJson(Map<String, dynamic> json) =>
_$$_AlbumModelFromJson(json);
@override
final int userId;
@override
final int id;
@override
final String title;
@override
String toString() {
return 'AlbumModel(userId: $userId, id: $id, title: $title)';
}
@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$_AlbumModelCopyWith<_$_AlbumModel> get copyWith =>
__$$_AlbumModelCopyWithImpl<_$_AlbumModel>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$_AlbumModelToJson(
this,
);
}
}
abstract class _AlbumModel implements AlbumModel {
const factory _AlbumModel(
{required final int userId,
required final int id,
required final String title}) = _$_AlbumModel;
factory _AlbumModel.fromJson(Map<String, dynamic> json) =
_$_AlbumModel.fromJson;
@override
int get userId;
@override
int get id;
@override
String get title;
@override
@JsonKey(ignore: true)
_$$_AlbumModelCopyWith<_$_AlbumModel> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@ -0,0 +1,21 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'album_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$_AlbumModel _$$_AlbumModelFromJson(Map<String, dynamic> json) =>
_$_AlbumModel(
userId: json['userId'] as int,
id: json['id'] as int,
title: json['title'] as String,
);
Map<String, dynamic> _$$_AlbumModelToJson(_$_AlbumModel instance) =>
<String, dynamic>{
'userId': instance.userId,
'id': instance.id,
'title': instance.title,
};

View File

@ -0,0 +1,32 @@
// ignore_for_file: public_member_api_docs, sort_constructors_first
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:architecture_example/data/models/album_model.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'list_album_model.freezed.dart';
part 'list_album_model.g.dart';
@freezed
class ListAlbumModel with _$ListAlbumModel {
const factory ListAlbumModel({
required List<AlbumModel> albums,
}) = _ListAlbumModel;
factory ListAlbumModel.fromJson(Map<String, Object?> json) =>
_$ListAlbumModelFromJson(json);
}

View File

@ -0,0 +1,158 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target
part of 'list_album_model.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
ListAlbumModel _$ListAlbumModelFromJson(Map<String, dynamic> json) {
return _ListAlbumModel.fromJson(json);
}
/// @nodoc
mixin _$ListAlbumModel {
List<AlbumModel> get albums => throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$ListAlbumModelCopyWith<ListAlbumModel> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $ListAlbumModelCopyWith<$Res> {
factory $ListAlbumModelCopyWith(
ListAlbumModel value, $Res Function(ListAlbumModel) then) =
_$ListAlbumModelCopyWithImpl<$Res, ListAlbumModel>;
@useResult
$Res call({List<AlbumModel> albums});
}
/// @nodoc
class _$ListAlbumModelCopyWithImpl<$Res, $Val extends ListAlbumModel>
implements $ListAlbumModelCopyWith<$Res> {
_$ListAlbumModelCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
@pragma('vm:prefer-inline')
@override
$Res call({
Object? albums = null,
}) {
return _then(_value.copyWith(
albums: null == albums
? _value.albums
: albums // ignore: cast_nullable_to_non_nullable
as List<AlbumModel>,
) as $Val);
}
}
/// @nodoc
abstract class _$$_ListAlbumModelCopyWith<$Res>
implements $ListAlbumModelCopyWith<$Res> {
factory _$$_ListAlbumModelCopyWith(
_$_ListAlbumModel value, $Res Function(_$_ListAlbumModel) then) =
__$$_ListAlbumModelCopyWithImpl<$Res>;
@override
@useResult
$Res call({List<AlbumModel> albums});
}
/// @nodoc
class __$$_ListAlbumModelCopyWithImpl<$Res>
extends _$ListAlbumModelCopyWithImpl<$Res, _$_ListAlbumModel>
implements _$$_ListAlbumModelCopyWith<$Res> {
__$$_ListAlbumModelCopyWithImpl(
_$_ListAlbumModel _value, $Res Function(_$_ListAlbumModel) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@override
$Res call({
Object? albums = null,
}) {
return _then(_$_ListAlbumModel(
albums: null == albums
? _value._albums
: albums // ignore: cast_nullable_to_non_nullable
as List<AlbumModel>,
));
}
}
/// @nodoc
@JsonSerializable()
class _$_ListAlbumModel implements _ListAlbumModel {
const _$_ListAlbumModel({required final List<AlbumModel> albums})
: _albums = albums;
factory _$_ListAlbumModel.fromJson(Map<String, dynamic> json) =>
_$$_ListAlbumModelFromJson(json);
final List<AlbumModel> _albums;
@override
List<AlbumModel> get albums {
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_albums);
}
@override
String toString() {
return 'ListAlbumModel(albums: $albums)';
}
@override
bool operator ==(dynamic other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$_ListAlbumModel &&
const DeepCollectionEquality().equals(other._albums, _albums));
}
@JsonKey(ignore: true)
@override
int get hashCode =>
Object.hash(runtimeType, const DeepCollectionEquality().hash(_albums));
@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$_ListAlbumModelCopyWith<_$_ListAlbumModel> get copyWith =>
__$$_ListAlbumModelCopyWithImpl<_$_ListAlbumModel>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$_ListAlbumModelToJson(
this,
);
}
}
abstract class _ListAlbumModel implements ListAlbumModel {
const factory _ListAlbumModel({required final List<AlbumModel> albums}) =
_$_ListAlbumModel;
factory _ListAlbumModel.fromJson(Map<String, dynamic> json) =
_$_ListAlbumModel.fromJson;
@override
List<AlbumModel> get albums;
@override
@JsonKey(ignore: true)
_$$_ListAlbumModelCopyWith<_$_ListAlbumModel> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@ -0,0 +1,19 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'list_album_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$_ListAlbumModel _$$_ListAlbumModelFromJson(Map<String, dynamic> json) =>
_$_ListAlbumModel(
albums: (json['albums'] as List<dynamic>)
.map((e) => AlbumModel.fromJson(e as Map<String, dynamic>))
.toList(),
);
Map<String, dynamic> _$$_ListAlbumModelToJson(_$_ListAlbumModel instance) =>
<String, dynamic>{
'albums': instance.albums,
};

View File

@ -0,0 +1,34 @@
// ignore_for_file: public_member_api_docs, sort_constructors_first
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:architecture_example/data/models/photo_model.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'list_photo_model.freezed.dart';
part 'list_photo_model.g.dart';
@freezed
class ListPhotoModel with _$ListPhotoModel {
const factory ListPhotoModel({
required List<PhotoModel> photos,
}) = _ListPhotoModel;
factory ListPhotoModel.fromJson(Map<String, Object?> json) =>
_$ListPhotoModelFromJson(json);
}

View File

@ -0,0 +1,158 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target
part of 'list_photo_model.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
ListPhotoModel _$ListPhotoModelFromJson(Map<String, dynamic> json) {
return _ListPhotoModel.fromJson(json);
}
/// @nodoc
mixin _$ListPhotoModel {
List<PhotoModel> get photos => throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$ListPhotoModelCopyWith<ListPhotoModel> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $ListPhotoModelCopyWith<$Res> {
factory $ListPhotoModelCopyWith(
ListPhotoModel value, $Res Function(ListPhotoModel) then) =
_$ListPhotoModelCopyWithImpl<$Res, ListPhotoModel>;
@useResult
$Res call({List<PhotoModel> photos});
}
/// @nodoc
class _$ListPhotoModelCopyWithImpl<$Res, $Val extends ListPhotoModel>
implements $ListPhotoModelCopyWith<$Res> {
_$ListPhotoModelCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
@pragma('vm:prefer-inline')
@override
$Res call({
Object? photos = null,
}) {
return _then(_value.copyWith(
photos: null == photos
? _value.photos
: photos // ignore: cast_nullable_to_non_nullable
as List<PhotoModel>,
) as $Val);
}
}
/// @nodoc
abstract class _$$_ListPhotoModelCopyWith<$Res>
implements $ListPhotoModelCopyWith<$Res> {
factory _$$_ListPhotoModelCopyWith(
_$_ListPhotoModel value, $Res Function(_$_ListPhotoModel) then) =
__$$_ListPhotoModelCopyWithImpl<$Res>;
@override
@useResult
$Res call({List<PhotoModel> photos});
}
/// @nodoc
class __$$_ListPhotoModelCopyWithImpl<$Res>
extends _$ListPhotoModelCopyWithImpl<$Res, _$_ListPhotoModel>
implements _$$_ListPhotoModelCopyWith<$Res> {
__$$_ListPhotoModelCopyWithImpl(
_$_ListPhotoModel _value, $Res Function(_$_ListPhotoModel) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@override
$Res call({
Object? photos = null,
}) {
return _then(_$_ListPhotoModel(
photos: null == photos
? _value._photos
: photos // ignore: cast_nullable_to_non_nullable
as List<PhotoModel>,
));
}
}
/// @nodoc
@JsonSerializable()
class _$_ListPhotoModel implements _ListPhotoModel {
const _$_ListPhotoModel({required final List<PhotoModel> photos})
: _photos = photos;
factory _$_ListPhotoModel.fromJson(Map<String, dynamic> json) =>
_$$_ListPhotoModelFromJson(json);
final List<PhotoModel> _photos;
@override
List<PhotoModel> get photos {
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_photos);
}
@override
String toString() {
return 'ListPhotoModel(photos: $photos)';
}
@override
bool operator ==(dynamic other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$_ListPhotoModel &&
const DeepCollectionEquality().equals(other._photos, _photos));
}
@JsonKey(ignore: true)
@override
int get hashCode =>
Object.hash(runtimeType, const DeepCollectionEquality().hash(_photos));
@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$_ListPhotoModelCopyWith<_$_ListPhotoModel> get copyWith =>
__$$_ListPhotoModelCopyWithImpl<_$_ListPhotoModel>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$_ListPhotoModelToJson(
this,
);
}
}
abstract class _ListPhotoModel implements ListPhotoModel {
const factory _ListPhotoModel({required final List<PhotoModel> photos}) =
_$_ListPhotoModel;
factory _ListPhotoModel.fromJson(Map<String, dynamic> json) =
_$_ListPhotoModel.fromJson;
@override
List<PhotoModel> get photos;
@override
@JsonKey(ignore: true)
_$$_ListPhotoModelCopyWith<_$_ListPhotoModel> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@ -0,0 +1,19 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'list_photo_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$_ListPhotoModel _$$_ListPhotoModelFromJson(Map<String, dynamic> json) =>
_$_ListPhotoModel(
photos: (json['photos'] as List<dynamic>)
.map((e) => PhotoModel.fromJson(e as Map<String, dynamic>))
.toList(),
);
Map<String, dynamic> _$$_ListPhotoModelToJson(_$_ListPhotoModel instance) =>
<String, dynamic>{
'photos': instance.photos,
};

View File

@ -0,0 +1,35 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:architecture_example/domain/entities/photo.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'photo_model.freezed.dart';
part 'photo_model.g.dart';
@freezed
class PhotoModel extends Photo with _$PhotoModel {
const factory PhotoModel({
required int albumId,
required int id,
required String title,
required String url,
required String thumbnailUrl,
}) = _PhotoModel;
factory PhotoModel.fromJson(Map<String, Object?> json) =>
_$PhotoModelFromJson(json);
}

View File

@ -0,0 +1,211 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target
part of 'photo_model.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
PhotoModel _$PhotoModelFromJson(Map<String, dynamic> json) {
return _PhotoModel.fromJson(json);
}
/// @nodoc
mixin _$PhotoModel {
int get albumId => throw _privateConstructorUsedError;
int get id => throw _privateConstructorUsedError;
String get title => throw _privateConstructorUsedError;
String get url => throw _privateConstructorUsedError;
String get thumbnailUrl => throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$PhotoModelCopyWith<PhotoModel> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $PhotoModelCopyWith<$Res> {
factory $PhotoModelCopyWith(
PhotoModel value, $Res Function(PhotoModel) then) =
_$PhotoModelCopyWithImpl<$Res, PhotoModel>;
@useResult
$Res call(
{int albumId, int id, String title, String url, String thumbnailUrl});
}
/// @nodoc
class _$PhotoModelCopyWithImpl<$Res, $Val extends PhotoModel>
implements $PhotoModelCopyWith<$Res> {
_$PhotoModelCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
@pragma('vm:prefer-inline')
@override
$Res call({
Object? albumId = null,
Object? id = null,
Object? title = null,
Object? url = null,
Object? thumbnailUrl = null,
}) {
return _then(_value.copyWith(
albumId: null == albumId
? _value.albumId
: albumId // ignore: cast_nullable_to_non_nullable
as int,
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int,
title: null == title
? _value.title
: title // ignore: cast_nullable_to_non_nullable
as String,
url: null == url
? _value.url
: url // ignore: cast_nullable_to_non_nullable
as String,
thumbnailUrl: null == thumbnailUrl
? _value.thumbnailUrl
: thumbnailUrl // ignore: cast_nullable_to_non_nullable
as String,
) as $Val);
}
}
/// @nodoc
abstract class _$$_PhotoModelCopyWith<$Res>
implements $PhotoModelCopyWith<$Res> {
factory _$$_PhotoModelCopyWith(
_$_PhotoModel value, $Res Function(_$_PhotoModel) then) =
__$$_PhotoModelCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{int albumId, int id, String title, String url, String thumbnailUrl});
}
/// @nodoc
class __$$_PhotoModelCopyWithImpl<$Res>
extends _$PhotoModelCopyWithImpl<$Res, _$_PhotoModel>
implements _$$_PhotoModelCopyWith<$Res> {
__$$_PhotoModelCopyWithImpl(
_$_PhotoModel _value, $Res Function(_$_PhotoModel) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@override
$Res call({
Object? albumId = null,
Object? id = null,
Object? title = null,
Object? url = null,
Object? thumbnailUrl = null,
}) {
return _then(_$_PhotoModel(
albumId: null == albumId
? _value.albumId
: albumId // ignore: cast_nullable_to_non_nullable
as int,
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int,
title: null == title
? _value.title
: title // ignore: cast_nullable_to_non_nullable
as String,
url: null == url
? _value.url
: url // ignore: cast_nullable_to_non_nullable
as String,
thumbnailUrl: null == thumbnailUrl
? _value.thumbnailUrl
: thumbnailUrl // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
@JsonSerializable()
class _$_PhotoModel implements _PhotoModel {
const _$_PhotoModel(
{required this.albumId,
required this.id,
required this.title,
required this.url,
required this.thumbnailUrl});
factory _$_PhotoModel.fromJson(Map<String, dynamic> json) =>
_$$_PhotoModelFromJson(json);
@override
final int albumId;
@override
final int id;
@override
final String title;
@override
final String url;
@override
final String thumbnailUrl;
@override
String toString() {
return 'PhotoModel(albumId: $albumId, id: $id, title: $title, url: $url, thumbnailUrl: $thumbnailUrl)';
}
@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$_PhotoModelCopyWith<_$_PhotoModel> get copyWith =>
__$$_PhotoModelCopyWithImpl<_$_PhotoModel>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$_PhotoModelToJson(
this,
);
}
}
abstract class _PhotoModel implements PhotoModel {
const factory _PhotoModel(
{required final int albumId,
required final int id,
required final String title,
required final String url,
required final String thumbnailUrl}) = _$_PhotoModel;
factory _PhotoModel.fromJson(Map<String, dynamic> json) =
_$_PhotoModel.fromJson;
@override
int get albumId;
@override
int get id;
@override
String get title;
@override
String get url;
@override
String get thumbnailUrl;
@override
@JsonKey(ignore: true)
_$$_PhotoModelCopyWith<_$_PhotoModel> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@ -0,0 +1,25 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'photo_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$_PhotoModel _$$_PhotoModelFromJson(Map<String, dynamic> json) =>
_$_PhotoModel(
albumId: json['albumId'] as int,
id: json['id'] as int,
title: json['title'] as String,
url: json['url'] as String,
thumbnailUrl: json['thumbnailUrl'] as String,
);
Map<String, dynamic> _$$_PhotoModelToJson(_$_PhotoModel instance) =>
<String, dynamic>{
'albumId': instance.albumId,
'id': instance.id,
'title': instance.title,
'url': instance.url,
'thumbnailUrl': instance.thumbnailUrl,
};

View File

@ -0,0 +1,118 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:architecture_example/domain/data_sources/local/favorite_local_data_source.dart';
import 'package:architecture_example/domain/data_sources/remote/album_remote_data_source.dart';
import 'package:architecture_example/domain/data_sources/remote/photo_remote_data_source.dart';
import 'package:architecture_example/domain/entities/album.dart';
import 'package:architecture_example/domain/entities/photo.dart';
import 'package:architecture_example/domain/repositories/photo_repository.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart';
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
class PhotoRepositoryImpl extends PhotoRepository {
final PhotoRemoteDataSource _photoRemoteDataSource;
final AlbumRemoteDataSource _albumRemoteDataSource;
final FavoriteLocalDataSource _favoriteLocalDataSource;
PhotoRepositoryImpl(
this._photoRemoteDataSource,
this._albumRemoteDataSource,
this._favoriteLocalDataSource,
);
@override
FutureResult<void> addPhotoToFavorites(Photo photo) => Result.tryCatchAsync(
() => _favoriteLocalDataSource.addPhotoToFavorites(photo),
(error) => ClientException('Cannot add photo to favorites.'),
);
@override
FutureResult<bool> checkIfPhotoIsInFavorites(int id) => Result.tryCatchAsync(
() => _favoriteLocalDataSource.checkIfPhotoIsInFavorites(id),
(error) =>
ClientException('Cannot check if photo `$id` is in favorites.'),
);
@override
FutureResult<void> deletePhotoFromFavorites(int id) => Result.tryCatchAsync(
() => _favoriteLocalDataSource.deletePhotoFromFavorites(id),
(error) => ClientException('Cannot delete photo `$id` from favorites.'),
);
@override
FutureResult<Album> getAlbum(int id) => Result.tryCatchAsync(
() => _albumRemoteDataSource.getAlbum(id),
(error) => ServerException('Cannot retrieve album $id.'),
);
@override
FutureResult<List<Album>> getAllAlbums({int? start, int? limit}) =>
Result.tryCatchAsync(
() => _albumRemoteDataSource.getAllAlbums(start: start, limit: limit),
(error) => ServerException('Cannot retrieve all albums.'),
);
@override
FutureResult<List<Photo>> getAllPhotos({int? start, int? limit}) async =>
Result.tryCatchAsync(
() => _photoRemoteDataSource.getAllPhotos(start: start, limit: limit),
(error) => ServerException('Cannot retrieve all photos.'),
);
@override
FutureResult<List<Photo>> getAllPhotosFromFavorites() async {
try {
final response = <Photo>[];
final favorites =
await _favoriteLocalDataSource.getAllPhotosFromFavorites();
final photos = await _photoRemoteDataSource.getAllPhotos();
for (final photo in photos) {
if (favorites.any((favId) => photo.id == favId)) {
response.add(photo);
}
}
return Ok(response);
} catch (_) {
return Err(
ClientException('Cannot retrieve all photos from favorites.'),
);
}
}
@override
FutureResult<Photo> getPhoto(int id) => Result.tryCatchAsync(
() => _photoRemoteDataSource.getPhoto(id),
(error) => ServerException('Cannot retrieve photo $id.'),
);
@override
FutureResult<List<Photo>> getPhotosFromAlbum(
int albumId, {
int? start,
int? limit,
}) async =>
Result.tryCatchAsync(
() => _photoRemoteDataSource.getPhotosFromAlbum(
albumId,
start: start,
limit: limit,
),
(error) =>
ServerException('Cannot retrieve all photos from album $albumId'),
);
}

View File

@ -0,0 +1,25 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:architecture_example/domain/entities/photo.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart';
abstract class FavoriteLocalDataSource extends BaseLocalDataSource {
Future<void> addPhotoToFavorites(Photo photo);
Future<void> deletePhotoFromFavorites(int id);
Future<List<int>> getAllPhotosFromFavorites();
Future<bool> checkIfPhotoIsInFavorites(int id);
}

View File

@ -0,0 +1,23 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:architecture_example/domain/entities/album.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart';
abstract class AlbumRemoteDataSource extends BaseRemoteDataSource {
Future<Album> getAlbum(int id);
Future<List<Album>> getAllAlbums({int? start, int? limit});
}

View File

@ -0,0 +1,24 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:architecture_example/domain/entities/photo.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart';
abstract class PhotoRemoteDataSource extends BaseRemoteDataSource {
Future<Photo> getPhoto(int id);
Future<List<Photo>> getAllPhotos({int? start, int? limit});
Future<List<Photo>> getPhotosFromAlbum(int albumId, {int? start, int? limit});
}

View File

@ -0,0 +1,44 @@
// ignore_for_file: public_member_api_docs, sort_constructors_first
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:flutter/foundation.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart';
@immutable
class Album extends Entity {
final int userId;
final int id;
final String title;
const Album({
required this.userId,
required this.id,
required this.title,
});
@override
bool operator ==(covariant Album other) {
if (identical(this, other)) {
return true;
}
return other.userId == userId && other.id == id && other.title == title;
}
@override
int get hashCode => userId.hashCode ^ id.hashCode ^ title.hashCode;
}

View File

@ -0,0 +1,57 @@
// ignore_for_file: public_member_api_docs, sort_constructors_first
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:flutter/foundation.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart';
@immutable
class Photo extends Entity {
final int albumId;
final int id;
final String title;
final String url;
final String thumbnailUrl;
const Photo({
required this.albumId,
required this.id,
required this.title,
required this.url,
required this.thumbnailUrl,
});
@override
bool operator ==(covariant Photo other) {
if (identical(this, other)) {
return true;
}
return other.albumId == albumId &&
other.id == id &&
other.title == title &&
other.url == url &&
other.thumbnailUrl == thumbnailUrl;
}
@override
int get hashCode =>
albumId.hashCode ^
id.hashCode ^
title.hashCode ^
url.hashCode ^
thumbnailUrl.hashCode;
}

View File

@ -0,0 +1,35 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:architecture_example/domain/entities/album.dart';
import 'package:architecture_example/domain/entities/photo.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart';
abstract class PhotoRepository extends BaseRepository {
FutureResult<Album> getAlbum(int id);
FutureResult<List<Album>> getAllAlbums({int? start, int? limit});
FutureResult<Photo> getPhoto(int id);
FutureResult<List<Photo>> getAllPhotos({int? start, int? limit});
FutureResult<List<Photo>> getPhotosFromAlbum(
int albumId, {
int? start,
int? limit,
});
FutureResult<void> addPhotoToFavorites(Photo photo);
FutureResult<void> deletePhotoFromFavorites(int id);
FutureResult<List<Photo>> getAllPhotosFromFavorites();
FutureResult<bool> checkIfPhotoIsInFavorites(int id);
}

View File

@ -0,0 +1,31 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:architecture_example/domain/entities/photo.dart';
import 'package:architecture_example/domain/repositories/photo_repository.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart';
class AddPhotoToFavorites extends UseCase<Photo, List<Photo>> {
final PhotoRepository _photoRepository;
AddPhotoToFavorites(this._photoRepository);
@override
FutureResult<List<Photo>> call(Photo params) async {
await _photoRepository.addPhotoToFavorites(params);
return _photoRepository.getAllPhotosFromFavorites();
}
}

View File

@ -0,0 +1,28 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:architecture_example/domain/repositories/photo_repository.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart';
class CheckIfPhotoIsInFavorites extends UseCase<int, bool> {
final PhotoRepository _photoRepository;
CheckIfPhotoIsInFavorites(this._photoRepository);
@override
FutureResult<bool> call(int params) async =>
_photoRepository.checkIfPhotoIsInFavorites(params);
}

View File

@ -0,0 +1,31 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:architecture_example/domain/entities/photo.dart';
import 'package:architecture_example/domain/repositories/photo_repository.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart';
class DisplayFavorites extends UseCase<void, List<Photo>> {
final PhotoRepository _photoRepository;
DisplayFavorites(this._photoRepository);
@override
FutureResult<List<Photo>> call(void params) {
final photos = _photoRepository.getAllPhotosFromFavorites();
return photos;
}
}

View File

@ -0,0 +1,31 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:architecture_example/domain/entities/photo.dart';
import 'package:architecture_example/domain/repositories/photo_repository.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart';
class DisplayPhoto extends UseCase<int, Photo> {
final PhotoRepository _photoRepository;
DisplayPhoto(this._photoRepository);
@override
FutureResult<Photo> call(int params) {
final photo = _photoRepository.getPhoto(params);
return photo;
}
}

View File

@ -0,0 +1,37 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:architecture_example/domain/entities/photo.dart';
import 'package:architecture_example/domain/repositories/photo_repository.dart';
import 'package:architecture_example/domain/usecases/photos/params/query_parameters.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart';
class OpenAlbum extends UseCase<QueryParameters, List<Photo>> {
final PhotoRepository _photoRepository;
OpenAlbum(this._photoRepository);
@override
FutureResult<List<Photo>> call(QueryParameters params) {
final photos = _photoRepository.getPhotosFromAlbum(
params.albumId,
start: params.start,
limit: params.limit,
);
return photos;
}
}

View File

@ -0,0 +1,23 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
class QueryParameters {
final int albumId;
final int? start;
final int? limit;
QueryParameters(this.start, this.limit, {this.albumId = -1});
}

View File

@ -0,0 +1,31 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:architecture_example/domain/entities/photo.dart';
import 'package:architecture_example/domain/repositories/photo_repository.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart';
class RemovePhotoFromFavorites extends UseCase<int, List<Photo>> {
final PhotoRepository _photoRepository;
RemovePhotoFromFavorites(this._photoRepository);
@override
FutureResult<List<Photo>> call(int params) async {
await _photoRepository.deletePhotoFromFavorites(params);
return _photoRepository.getAllPhotosFromFavorites();
}
}

View File

@ -0,0 +1,35 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:architecture_example/domain/entities/album.dart';
import 'package:architecture_example/domain/repositories/photo_repository.dart';
import 'package:architecture_example/domain/usecases/photos/params/query_parameters.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart';
class RetrieveAllAlbums extends UseCase<QueryParameters, List<Album>> {
final PhotoRepository _photoRepository;
RetrieveAllAlbums(this._photoRepository);
@override
FutureResult<List<Album>> call(QueryParameters params) {
final albums = _photoRepository.getAllAlbums(
start: params.start,
limit: params.limit,
);
return albums;
}
}

View File

@ -14,34 +14,9 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:flutter/material.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart';
import 'package:architecture_example/bootstrap.dart';
import 'package:architecture_example/presentation/features/app/app.dart';
void main() {
runApp(const MyApp());
bootstrap(() => const App());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Architecture Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
title: const Text('Architecture Example'),
),
body: Center(child: Text(wyatt())),
),
);
}
}

View File

@ -0,0 +1,25 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:architecture_example/presentation/features/albums/state_management/albums_screen.dart';
import 'package:flutter/material.dart';
class Albums extends StatelessWidget {
const Albums({super.key});
@override
Widget build(BuildContext context) => const AlbumsScreen();
}

View File

@ -0,0 +1,94 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:architecture_example/core/enums/fetch_status.dart';
import 'package:architecture_example/domain/entities/album.dart';
import 'package:architecture_example/domain/usecases/photos/params/query_parameters.dart';
import 'package:architecture_example/domain/usecases/photos/retrieve_all_albums.dart';
import 'package:bloc_concurrency/bloc_concurrency.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:stream_transform/stream_transform.dart';
part 'album_event.dart';
part 'album_state.dart';
const _albumLimit = 20;
const throttleDuration = Duration(milliseconds: 100);
EventTransformer<E> throttleDroppable<E>(Duration duration) =>
(events, mapper) => droppable<E>().call(events.throttle(duration), mapper);
class AlbumBloc extends Bloc<AlbumEvent, AlbumState> {
final RetrieveAllAlbums _retrieveAllAlbums;
AlbumBloc(this._retrieveAllAlbums) : super(const AlbumState()) {
on<AlbumFetched>(
_onAlbumFetched,
transformer: throttleDroppable(throttleDuration),
);
}
Future<void> _onAlbumFetched(
AlbumFetched event,
Emitter<AlbumState> emit,
) async {
if (state.hasReachedMax) {
return;
}
if (state.status == FetchStatus.initial) {
final albums =
await _retrieveAllAlbums.call(QueryParameters(0, _albumLimit));
return emit(
albums.fold(
(value) => state.copyWith(
status: FetchStatus.success,
albums: value,
hasReachedMax: false,
),
(error) => state.copyWith(
status: FetchStatus.failure,
albums: [],
hasReachedMax: false,
),
),
);
}
final albums = await _retrieveAllAlbums
.call(QueryParameters(state.albums.length, _albumLimit));
return emit(
albums.fold(
(value) {
if (value.isEmpty) {
return state.copyWith(
hasReachedMax: true,
);
}
return state.copyWith(
status: FetchStatus.success,
albums: List.of(state.albums)..addAll(value),
hasReachedMax: false,
);
},
(error) => state.copyWith(
status: FetchStatus.failure,
albums: List.of(state.albums),
hasReachedMax: false,
),
),
);
}
}

View File

@ -0,0 +1,26 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
part of 'album_bloc.dart';
abstract class AlbumEvent extends Equatable {
const AlbumEvent();
@override
List<Object> get props => [];
}
class AlbumFetched extends AlbumEvent {}

View File

@ -0,0 +1,43 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
part of 'album_bloc.dart';
class AlbumState extends Equatable {
const AlbumState({
this.status = FetchStatus.initial,
this.albums = const <Album>[],
this.hasReachedMax = false,
});
final FetchStatus status;
final List<Album> albums;
final bool hasReachedMax;
AlbumState copyWith({
FetchStatus? status,
List<Album>? albums,
bool? hasReachedMax,
}) =>
AlbumState(
status: status ?? this.status,
albums: albums ?? this.albums,
hasReachedMax: hasReachedMax ?? this.hasReachedMax,
);
@override
List<Object> get props => [status, albums, hasReachedMax];
}

View File

@ -0,0 +1,58 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:architecture_example/core/enums/fetch_status.dart';
import 'package:architecture_example/domain/repositories/photo_repository.dart';
import 'package:architecture_example/domain/usecases/photos/retrieve_all_albums.dart';
import 'package:architecture_example/presentation/features/albums/blocs/album/album_bloc.dart';
import 'package:architecture_example/presentation/features/albums/state_management/albums_wrapper_widget.dart';
import 'package:architecture_example/presentation/features/albums/state_management/widgets/albums_list.dart';
import 'package:flutter/material.dart';
import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart';
class AlbumsScreen extends BlocScreen<AlbumBloc, AlbumEvent, AlbumState> {
const AlbumsScreen({super.key});
@override
AlbumBloc create(BuildContext context) =>
AlbumBloc(RetrieveAllAlbums(repo<PhotoRepository>(context)));
@override
AlbumBloc init(BuildContext context, AlbumBloc bloc) =>
bloc..add(AlbumFetched());
@override
Widget onWrap(BuildContext context, Widget child) => AlbumsWrapperWidget(
child: child,
);
@override
Widget onBuild(BuildContext context, AlbumState state) {
switch (state.status) {
case FetchStatus.initial:
return const Center(child: CircularProgressIndicator());
case FetchStatus.failure:
return const Center(
child: Text('failed to fetch albums'),
);
case FetchStatus.success:
return AlbumsList(
albums: state.albums,
hasReachedMax: state.hasReachedMax,
);
}
}
}

View File

@ -0,0 +1,31 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:flutter/material.dart';
class AlbumsWrapperWidget extends StatelessWidget {
const AlbumsWrapperWidget({required this.child, super.key});
final Widget child;
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(
title: const Text('Albums'),
),
body: child,
);
}

View File

@ -0,0 +1,77 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:architecture_example/domain/entities/album.dart';
import 'package:architecture_example/presentation/features/albums/blocs/album/album_bloc.dart';
import 'package:architecture_example/presentation/features/albums/state_management/widgets/albums_list_item.dart';
import 'package:architecture_example/presentation/shared/widgets/bottom_loader.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class AlbumsList extends StatefulWidget {
const AlbumsList(
{required this.albums, required this.hasReachedMax, super.key,});
final List<Album> albums;
final bool hasReachedMax;
@override
State<AlbumsList> createState() => _AlbumsListState();
}
class _AlbumsListState extends State<AlbumsList> {
final _scrollController = ScrollController();
@override
void initState() {
super.initState();
_scrollController.addListener(_onScroll);
}
@override
void dispose() {
_scrollController
..removeListener(_onScroll)
..dispose();
super.dispose();
}
void _onScroll() {
if (_isBottom) {
context.read<AlbumBloc>().add(AlbumFetched());
}
}
bool get _isBottom {
if (!_scrollController.hasClients) {
return false;
}
final maxScroll = _scrollController.position.maxScrollExtent;
final currentScroll = _scrollController.offset;
return currentScroll >= (maxScroll * 0.9);
}
@override
Widget build(BuildContext context) => ListView.builder(
itemBuilder: (context, index) => index >= widget.albums.length
? const BottomLoader()
: AlbumsListItem(album: widget.albums[index]),
itemCount: widget.hasReachedMax
? widget.albums.length
: widget.albums.length + 1,
controller: _scrollController,
);
}

View File

@ -0,0 +1,37 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:architecture_example/domain/entities/album.dart';
import 'package:architecture_example/presentation/features/photos/photos.dart';
import 'package:flutter/material.dart';
class AlbumsListItem extends StatelessWidget {
const AlbumsListItem({required this.album, super.key});
final Album album;
@override
Widget build(BuildContext context) => ListTile(
leading: Text('${album.id}'),
title: Text(album.title),
dense: true,
onTap: () => Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (context) => Photos(albumId: album.id),
),
),
);
}

View File

@ -0,0 +1,47 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:architecture_example/core/dependency_injection/get_it.dart';
import 'package:architecture_example/data/repositories/photo_repository_impl.dart';
import 'package:architecture_example/domain/data_sources/local/favorite_local_data_source.dart';
import 'package:architecture_example/domain/data_sources/remote/album_remote_data_source.dart';
import 'package:architecture_example/domain/data_sources/remote/photo_remote_data_source.dart';
import 'package:architecture_example/domain/repositories/photo_repository.dart';
import 'package:architecture_example/presentation/features/albums/albums.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class App extends StatelessWidget {
const App({super.key});
@override
Widget build(BuildContext context) => MultiRepositoryProvider(
providers: [
RepositoryProvider<PhotoRepository>(
create: (_) => PhotoRepositoryImpl(
getIt<PhotoRemoteDataSource>(),
getIt<AlbumRemoteDataSource>(),
getIt<FavoriteLocalDataSource>(),
),
),
],
child: const MaterialApp(
title: 'Demo',
debugShowCheckedModeBanner: false,
home: Albums(),
),
);
}

View File

@ -0,0 +1,79 @@
// ignore_for_file: public_member_api_docs, sort_constructors_first
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:async';
import 'package:architecture_example/domain/entities/photo.dart';
import 'package:architecture_example/domain/usecases/photos/add_photo_to_favorites.dart';
import 'package:architecture_example/domain/usecases/photos/check_if_photo_is_in_favorites.dart';
import 'package:architecture_example/domain/usecases/photos/display_photo.dart';
import 'package:architecture_example/domain/usecases/photos/remove_photo_from_favorites.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
part 'photo_details_state.dart';
class PhotoDetailsCubit extends Cubit<PhotoDetailsState> {
final DisplayPhoto _displayPhoto;
final CheckIfPhotoIsInFavorites _checkIfPhotoIsInFavorites;
final AddPhotoToFavorites _addPhotoToFavorites;
final RemovePhotoFromFavorites _removePhotoFromFavorites;
PhotoDetailsCubit(
this._displayPhoto,
this._checkIfPhotoIsInFavorites,
this._addPhotoToFavorites,
this._removePhotoFromFavorites,
) : super(PhotoDetailsInitial());
FutureOr<void> load(int photoId) async {
final photo = await _displayPhoto.call(photoId);
final isFavorite = await _checkIfPhotoIsInFavorites.call(photoId);
emit(
photo.fold(
(value) => PhotoDetailsSuccess(
value,
isFavorite: isFavorite.fold((value) => value, (error) => false),
),
(error) => PhotoDetailsFailure(error.toString()),
),
);
}
FutureOr<void> addToFavorites() async {
if (state is! PhotoDetailsSuccess) {
return;
}
final actualState = state as PhotoDetailsSuccess;
final response = await _addPhotoToFavorites.call(actualState.photo);
if (response.isOk) {
emit(PhotoDetailsSuccess(actualState.photo, isFavorite: true));
}
}
FutureOr<void> removeFromFavorites() async {
if (state is! PhotoDetailsSuccess) {
return;
}
final actualState = state as PhotoDetailsSuccess;
final response = await _removePhotoFromFavorites.call(actualState.photo.id);
if (response.isOk) {
emit(PhotoDetailsSuccess(actualState.photo, isFavorite: false));
}
}
}

View File

@ -0,0 +1,45 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
part of 'photo_details_cubit.dart';
abstract class PhotoDetailsState extends Equatable {
const PhotoDetailsState();
@override
List<Object> get props => [];
}
class PhotoDetailsInitial extends PhotoDetailsState {}
class PhotoDetailsSuccess extends PhotoDetailsState {
final Photo photo;
final bool isFavorite;
const PhotoDetailsSuccess(this.photo, {required this.isFavorite});
@override
List<Object> get props => [photo, isFavorite];
}
class PhotoDetailsFailure extends PhotoDetailsState {
final String error;
const PhotoDetailsFailure(this.error);
@override
List<Object> get props => [error];
}

View File

@ -0,0 +1,27 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:architecture_example/presentation/features/photo_details/state_management/photo_details_screen.dart';
import 'package:flutter/material.dart';
class PhotoDetails extends StatelessWidget {
const PhotoDetails({required this.photoId, super.key});
final int photoId;
@override
Widget build(BuildContext context) => PhotoDetailsScreen(photoId: photoId);
}

View File

@ -0,0 +1,119 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:architecture_example/domain/repositories/photo_repository.dart';
import 'package:architecture_example/domain/usecases/photos/add_photo_to_favorites.dart';
import 'package:architecture_example/domain/usecases/photos/check_if_photo_is_in_favorites.dart';
import 'package:architecture_example/domain/usecases/photos/display_photo.dart';
import 'package:architecture_example/domain/usecases/photos/remove_photo_from_favorites.dart';
import 'package:architecture_example/presentation/features/photo_details/blocs/photo_details/photo_details_cubit.dart';
import 'package:architecture_example/presentation/features/photo_details/state_management/photo_details_wrapper_widget.dart';
import 'package:flutter/material.dart';
import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart';
class PhotoDetailsScreen
extends CubitScreen<PhotoDetailsCubit, PhotoDetailsState> {
const PhotoDetailsScreen({required this.photoId, super.key});
final int photoId;
@override
PhotoDetailsCubit create(BuildContext context) => PhotoDetailsCubit(
DisplayPhoto(repo<PhotoRepository>(context)),
CheckIfPhotoIsInFavorites(repo<PhotoRepository>(context)),
AddPhotoToFavorites(repo<PhotoRepository>(context)),
RemovePhotoFromFavorites(repo<PhotoRepository>(context)),
);
@override
PhotoDetailsCubit init(BuildContext context, PhotoDetailsCubit bloc) =>
bloc..load(photoId);
@override
Widget onWrap(BuildContext context, Widget child) =>
PhotoDetailsWrapperWidget(child: child);
@override
Widget onBuild(BuildContext context, PhotoDetailsState state) {
if (state is PhotoDetailsFailure) {
return const Center(
child: Text('failed to fetch photo details'),
);
}
if (state is PhotoDetailsSuccess) {
return CustomScrollView(
slivers: [
SliverAppBar(
expandedHeight: 400,
stretch: true,
flexibleSpace: FlexibleSpaceBar(
background: Stack(
children: [
Positioned.fill(
child: Image.network(
state.photo.thumbnailUrl,
fit: BoxFit.cover,
),
),
Positioned.fill(
child: Image.network(
state.photo.url,
fit: BoxFit.cover,
),
),
],
),
),
),
SliverList(
delegate: SliverChildListDelegate([
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
child: Text(
state.photo.title,
overflow: TextOverflow.ellipsis,
style: const TextStyle(fontWeight: FontWeight.bold),
),
),
IconButton(
onPressed: () {
state.isFavorite
? bloc(context).removeFromFavorites()
: bloc(context).addToFavorites();
},
icon: Icon(
state.isFavorite
? Icons.favorite
: Icons.favorite_outline,
),
),
],
),
),
]),
),
],
);
}
return const Center(
child: CircularProgressIndicator(),
);
}
}

View File

@ -0,0 +1,28 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:flutter/material.dart';
class PhotoDetailsWrapperWidget extends StatelessWidget {
const PhotoDetailsWrapperWidget({required this.child, super.key});
final Widget child;
@override
Widget build(BuildContext context) => Scaffold(
body: child,
);
}

View File

@ -0,0 +1,40 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:async';
import 'package:architecture_example/domain/usecases/photos/check_if_photo_is_in_favorites.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
part 'favorite_checker_state.dart';
class FavoriteCheckerCubit extends Cubit<FavoriteCheckerState> {
final CheckIfPhotoIsInFavorites _checkIfPhotoIsInFavorites;
FavoriteCheckerCubit(this._checkIfPhotoIsInFavorites)
: super(FavoriteCheckerInitial());
FutureOr<void> checkIfPhotoIsInFavorites(int photoId) async {
final response = await _checkIfPhotoIsInFavorites.call(photoId);
emit(
response.fold(
(value) => FavoriteCheckerSuccess(photoId, isFavorite: value),
(error) => FavoriteCheckerFailure(error.toString()),
),
);
}
}

View File

@ -0,0 +1,29 @@
part of 'favorite_checker_cubit.dart';
abstract class FavoriteCheckerState extends Equatable {
const FavoriteCheckerState();
@override
List<Object> get props => [];
}
class FavoriteCheckerInitial extends FavoriteCheckerState {}
class FavoriteCheckerSuccess extends FavoriteCheckerState {
final int photoId;
final bool isFavorite;
const FavoriteCheckerSuccess(this.photoId, {required this.isFavorite});
@override
List<Object> get props => [photoId, isFavorite];
}
class FavoriteCheckerFailure extends FavoriteCheckerState {
final String error;
const FavoriteCheckerFailure(this.error);
@override
List<Object> get props => [error];
}

View File

@ -0,0 +1,104 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:architecture_example/core/enums/fetch_status.dart';
import 'package:architecture_example/domain/entities/photo.dart';
import 'package:architecture_example/domain/usecases/photos/open_album.dart';
import 'package:architecture_example/domain/usecases/photos/params/query_parameters.dart';
import 'package:bloc_concurrency/bloc_concurrency.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:stream_transform/stream_transform.dart';
part 'photo_event.dart';
part 'photo_state.dart';
const _photoLimit = 40;
const throttleDuration = Duration(milliseconds: 100);
EventTransformer<E> throttleDroppable<E>(Duration duration) =>
(events, mapper) => droppable<E>().call(events.throttle(duration), mapper);
class PhotoBloc extends Bloc<PhotoEvent, PhotoState> {
final OpenAlbum _openAlbum;
PhotoBloc(this._openAlbum) : super(const PhotoState()) {
on<PhotoFetched>(
_onPhotoFetched,
transformer: throttleDroppable(throttleDuration),
);
}
Future<void> _onPhotoFetched(
PhotoFetched event,
Emitter<PhotoState> emit,
) async {
if (state.hasReachedMax) {
return;
}
if (state.status == FetchStatus.initial) {
final photos = await _openAlbum.call(
QueryParameters(
0,
_photoLimit,
albumId: event.albumId,
),
);
return emit(
photos.fold(
(value) => state.copyWith(
status: FetchStatus.success,
photos: value,
hasReachedMax: false,
),
(error) => state.copyWith(
status: FetchStatus.failure,
photos: [],
hasReachedMax: false,
),
),
);
}
final photos = await _openAlbum.call(
QueryParameters(
state.photos.length,
_photoLimit,
albumId: event.albumId,
),
);
return emit(
photos.fold(
(value) {
if (value.isEmpty) {
return state.copyWith(
hasReachedMax: true,
);
}
return state.copyWith(
status: FetchStatus.success,
photos: List.of(state.photos)..addAll(value),
hasReachedMax: false,
);
},
(error) => state.copyWith(
status: FetchStatus.failure,
photos: List.of(state.photos),
hasReachedMax: false,
),
),
);
}
}

View File

@ -0,0 +1,30 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
part of 'photo_bloc.dart';
abstract class PhotoEvent extends Equatable {
const PhotoEvent();
@override
List<Object> get props => [];
}
class PhotoFetched extends PhotoEvent {
final int albumId;
const PhotoFetched(this.albumId);
}

Some files were not shown because too many files have changed in this diff Show More