diff --git a/navigation_and_routing/.gitignore b/navigation_and_routing/.gitignore index 9d532b18a..0fa6b675c 100644 --- a/navigation_and_routing/.gitignore +++ b/navigation_and_routing/.gitignore @@ -39,3 +39,8 @@ app.*.symbols # Obfuscation related app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/navigation_and_routing/.metadata b/navigation_and_routing/.metadata index f0274b3eb..3733535c8 100644 --- a/navigation_and_routing/.metadata +++ b/navigation_and_routing/.metadata @@ -4,7 +4,7 @@ # This file should be version controlled and should not be manually edited. version: - revision: 1aafb3a8b9b0c36241c5f5b34ee914770f015818 + revision: 02c026b03cd31dd3f867e5faeb7e104cce174c5f channel: stable project_type: app diff --git a/navigation_and_routing/README.md b/navigation_and_routing/README.md index 96e616453..2839f0e14 100644 --- a/navigation_and_routing/README.md +++ b/navigation_and_routing/README.md @@ -1,47 +1,5 @@ -# Navigation and Routing sample code +# bookstore -Sample code for [Learning Flutter's New Navigation and Routing System][article], -an article explaining the Router widget and its associated classes. +This sample shows how to set up a Router using a custom RouterDelegate and +RouteInformationParser. -## Samples - -**Navigator samples** - -* [navigator/anonymous_routes.dart](lib/navigator/anonymous_routes.dart) - - Shows how to use a Navigator to push and pop anonymous routes (e.g. - MaterialPageRoute) -* [navigator/named_routes.dart](lib/navigator/named_routes.dart) - Shows - how to use define named routes via the `routes` parameter on MaterialApp, and - navigate using Navigator.pushNamed -* [navigator/on_generate_route.dart](lib/navigator/on_generate_route.dart) - - Shows how to handle arbitrary named routes using the `onGenerateRoute` - callback defined in the `MaterialApp` constructor. - -**Router samples** - -* [router/pages.dart](lib/router/pages.dart) - Shows how to define a list of - [Page] objects on Navigator declaratively. -* [router/router.dart](lib/router/router.dart) - Full sample that shows a custom - RouteInformationParser and RouterDelegate parsing named routes and - declaratively building the stack of pages for the Navigator. - -**Advanced** - -* [router_advanced/nested_router.dart](lib/router_advanced/nested_router.dart) - - Shows two [RouterDelegate], one nested within the other. A - [BottomNavigationBar] can be used to select the route of the outer - RouterDelegate, and additional routes can be pushed onto the inner - RouterDelegate / Navigator. -* [router_advanced/transition_delegate.dart](lib/router_advanced/transition_delegate.dart) - Shows how a custom TransitionDelegate can be used to customize when - transition animations are shown. - -## Running - -Each file in this project is an entrypoint. To run, specify the filename of -the sample: - -```bash -flutter run lib/router/router.dart -``` -[article]: https://medium.com/flutter/learning-flutters-new-navigation-and-routing-system-7c9068155ade diff --git a/navigation_and_routing/analysis_options.yaml b/navigation_and_routing/analysis_options.yaml new file mode 100644 index 000000000..c87f668e0 --- /dev/null +++ b/navigation_and_routing/analysis_options.yaml @@ -0,0 +1,19 @@ +include: package:flutter_lints/flutter.yaml + +analyzer: + strong-mode: + implicit-casts: false + implicit-dynamic: false + +linter: + rules: + avoid_types_on_closure_parameters: true + avoid_void_async: true + cancel_subscriptions: true + close_sinks: true + directives_ordering: true + package_api_docs: true + package_prefixed_library_names: true + test_types_in_equals: true + throw_in_finally: true + unnecessary_statements: true \ No newline at end of file diff --git a/navigation_and_routing/android/app/build.gradle b/navigation_and_routing/android/app/build.gradle index 43ab00829..5737e344a 100644 --- a/navigation_and_routing/android/app/build.gradle +++ b/navigation_and_routing/android/app/build.gradle @@ -26,21 +26,17 @@ apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 29 + compileSdkVersion 30 sourceSets { main.java.srcDirs += 'src/main/kotlin' } - lintOptions { - disable 'InvalidPackage' - } - defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "com.example.navigation_and_routing" + applicationId "dev.flutter.bookstore" minSdkVersion 16 - targetSdkVersion 29 + targetSdkVersion 30 versionCode flutterVersionCode.toInteger() versionName flutterVersionName } diff --git a/navigation_and_routing/android/app/src/debug/AndroidManifest.xml b/navigation_and_routing/android/app/src/debug/AndroidManifest.xml index c50a172da..d1fa938eb 100644 --- a/navigation_and_routing/android/app/src/debug/AndroidManifest.xml +++ b/navigation_and_routing/android/app/src/debug/AndroidManifest.xml @@ -1,5 +1,5 @@ + package="dev.flutter.bookstore"> diff --git a/navigation_and_routing/android/app/src/main/AndroidManifest.xml b/navigation_and_routing/android/app/src/main/AndroidManifest.xml index aa1a09d24..35f4431b5 100644 --- a/navigation_and_routing/android/app/src/main/AndroidManifest.xml +++ b/navigation_and_routing/android/app/src/main/AndroidManifest.xml @@ -1,13 +1,7 @@ - - + + + + + + + + + + + diff --git a/navigation_and_routing/android/app/src/main/kotlin/com/example/navigation_and_routing/MainActivity.kt b/navigation_and_routing/android/app/src/main/kotlin/dev/flutter/bookstore/MainActivity.kt similarity index 69% rename from navigation_and_routing/android/app/src/main/kotlin/com/example/navigation_and_routing/MainActivity.kt rename to navigation_and_routing/android/app/src/main/kotlin/dev/flutter/bookstore/MainActivity.kt index d5a2b6c80..67f0c8b04 100644 --- a/navigation_and_routing/android/app/src/main/kotlin/com/example/navigation_and_routing/MainActivity.kt +++ b/navigation_and_routing/android/app/src/main/kotlin/dev/flutter/bookstore/MainActivity.kt @@ -1,4 +1,4 @@ -package com.example.navigation_and_routing +package dev.flutter.bookstore import io.flutter.embedding.android.FlutterActivity diff --git a/navigation_and_routing/android/app/src/main/res/drawable-v21/launch_background.xml b/navigation_and_routing/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 000000000..f74085f3f --- /dev/null +++ b/navigation_and_routing/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/navigation_and_routing/android/app/src/main/res/values-night/styles.xml b/navigation_and_routing/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 000000000..449a9f930 --- /dev/null +++ b/navigation_and_routing/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/navigation_and_routing/android/app/src/main/res/values/styles.xml b/navigation_and_routing/android/app/src/main/res/values/styles.xml index 1f83a33fd..d74aa35c2 100644 --- a/navigation_and_routing/android/app/src/main/res/values/styles.xml +++ b/navigation_and_routing/android/app/src/main/res/values/styles.xml @@ -1,7 +1,7 @@ - - diff --git a/navigation_and_routing/android/app/src/profile/AndroidManifest.xml b/navigation_and_routing/android/app/src/profile/AndroidManifest.xml index c50a172da..d1fa938eb 100644 --- a/navigation_and_routing/android/app/src/profile/AndroidManifest.xml +++ b/navigation_and_routing/android/app/src/profile/AndroidManifest.xml @@ -1,5 +1,5 @@ + package="dev.flutter.bookstore"> diff --git a/navigation_and_routing/android/build.gradle b/navigation_and_routing/android/build.gradle index 3100ad2d5..9b6ed06eb 100644 --- a/navigation_and_routing/android/build.gradle +++ b/navigation_and_routing/android/build.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.5.0' + classpath 'com.android.tools.build:gradle:4.1.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } @@ -21,8 +21,6 @@ allprojects { rootProject.buildDir = '../build' subprojects { project.buildDir = "${rootProject.buildDir}/${project.name}" -} -subprojects { project.evaluationDependsOn(':app') } diff --git a/navigation_and_routing/android/gradle/wrapper/gradle-wrapper.properties b/navigation_and_routing/android/gradle/wrapper/gradle-wrapper.properties index 296b146b7..bc6a58afd 100644 --- a/navigation_and_routing/android/gradle/wrapper/gradle-wrapper.properties +++ b/navigation_and_routing/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip diff --git a/navigation_and_routing/ios/.gitignore b/navigation_and_routing/ios/.gitignore index e96ef602b..151026b91 100644 --- a/navigation_and_routing/ios/.gitignore +++ b/navigation_and_routing/ios/.gitignore @@ -18,6 +18,7 @@ Flutter/App.framework Flutter/Flutter.framework Flutter/Flutter.podspec Flutter/Generated.xcconfig +Flutter/ephemeral/ Flutter/app.flx Flutter/app.zip Flutter/flutter_assets/ diff --git a/navigation_and_routing/ios/Flutter/AppFrameworkInfo.plist b/navigation_and_routing/ios/Flutter/AppFrameworkInfo.plist index 6b4c0f78a..9367d483e 100644 --- a/navigation_and_routing/ios/Flutter/AppFrameworkInfo.plist +++ b/navigation_and_routing/ios/Flutter/AppFrameworkInfo.plist @@ -3,7 +3,7 @@ CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) + en CFBundleExecutable App CFBundleIdentifier diff --git a/navigation_and_routing/ios/Flutter/Debug.xcconfig b/navigation_and_routing/ios/Flutter/Debug.xcconfig index 592ceee85..ec97fc6f3 100644 --- a/navigation_and_routing/ios/Flutter/Debug.xcconfig +++ b/navigation_and_routing/ios/Flutter/Debug.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "Generated.xcconfig" diff --git a/navigation_and_routing/ios/Flutter/Release.xcconfig b/navigation_and_routing/ios/Flutter/Release.xcconfig index 592ceee85..c4855bfe2 100644 --- a/navigation_and_routing/ios/Flutter/Release.xcconfig +++ b/navigation_and_routing/ios/Flutter/Release.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "Generated.xcconfig" diff --git a/navigation_and_routing/ios/Podfile b/navigation_and_routing/ios/Podfile new file mode 100644 index 000000000..1e8c3c90a --- /dev/null +++ b/navigation_and_routing/ios/Podfile @@ -0,0 +1,41 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '9.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 diff --git a/navigation_and_routing/ios/Podfile.lock b/navigation_and_routing/ios/Podfile.lock new file mode 100644 index 000000000..d736005ec --- /dev/null +++ b/navigation_and_routing/ios/Podfile.lock @@ -0,0 +1,22 @@ +PODS: + - Flutter (1.0.0) + - url_launcher (0.0.1): + - Flutter + +DEPENDENCIES: + - Flutter (from `Flutter`) + - url_launcher (from `.symlinks/plugins/url_launcher/ios`) + +EXTERNAL SOURCES: + Flutter: + :path: Flutter + url_launcher: + :path: ".symlinks/plugins/url_launcher/ios" + +SPEC CHECKSUMS: + Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c + url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef + +PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c + +COCOAPODS: 1.10.1 diff --git a/navigation_and_routing/ios/Runner.xcodeproj/project.pbxproj b/navigation_and_routing/ios/Runner.xcodeproj/project.pbxproj index f4b40b249..b6a4283d0 100644 --- a/navigation_and_routing/ios/Runner.xcodeproj/project.pbxproj +++ b/navigation_and_routing/ios/Runner.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 163107DCA98CF5408A42DD8A /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0AE3A8CEB94AABB517354DA1 /* Pods_Runner.framework */; }; 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 */; }; @@ -29,12 +30,16 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 0AE3A8CEB94AABB517354DA1 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 49D7EF814BF5A0D3E798ADAE /* 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 = ""; }; + 71C70971C603166B5757B8AC /* 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 = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 9682F7751FBA2903F6CCD179 /* 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 = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -49,12 +54,24 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 163107DCA98CF5408A42DD8A /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 7B81C16A03A3AB694F08AD07 /* Pods */ = { + isa = PBXGroup; + children = ( + 71C70971C603166B5757B8AC /* Pods-Runner.debug.xcconfig */, + 9682F7751FBA2903F6CCD179 /* Pods-Runner.release.xcconfig */, + 49D7EF814BF5A0D3E798ADAE /* Pods-Runner.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( @@ -72,6 +89,8 @@ 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, + 7B81C16A03A3AB694F08AD07 /* Pods */, + DCFFB20C3B2B4F4DCC6CF244 /* Frameworks */, ); sourceTree = ""; }; @@ -98,6 +117,14 @@ path = Runner; sourceTree = ""; }; + DCFFB20C3B2B4F4DCC6CF244 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 0AE3A8CEB94AABB517354DA1 /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -105,12 +132,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( + E223DFBBAC6A945FFD0847A4 /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + 766B88730AAF26F1ADAF797C /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -183,6 +212,23 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; + 766B88730AAF26F1ADAF797C /* [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; + }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -197,6 +243,28 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; + E223DFBBAC6A945FFD0847A4 /* [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 */ @@ -289,17 +357,9 @@ CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.navigationAndRouting; + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.bookstore; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; @@ -421,17 +481,9 @@ CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.navigationAndRouting; + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.bookstore; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -448,17 +500,9 @@ CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.navigationAndRouting; + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.bookstore; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; diff --git a/navigation_and_routing/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/navigation_and_routing/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata index 1d526a16e..919434a62 100644 --- a/navigation_and_routing/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/navigation_and_routing/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -2,6 +2,6 @@ + location = "self:"> diff --git a/navigation_and_routing/ios/Runner.xcworkspace/contents.xcworkspacedata b/navigation_and_routing/ios/Runner.xcworkspace/contents.xcworkspacedata index 1d526a16e..21a3cc14c 100644 --- a/navigation_and_routing/ios/Runner.xcworkspace/contents.xcworkspacedata +++ b/navigation_and_routing/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -4,4 +4,7 @@ + + diff --git a/navigation_and_routing/ios/Runner/Info.plist b/navigation_and_routing/ios/Runner/Info.plist index 7f840e028..6096b4391 100644 --- a/navigation_and_routing/ios/Runner/Info.plist +++ b/navigation_and_routing/ios/Runner/Info.plist @@ -11,7 +11,7 @@ CFBundleInfoDictionaryVersion 6.0 CFBundleName - navigation_and_routing + bookstore CFBundlePackageType APPL CFBundleShortVersionString @@ -41,5 +41,20 @@ UIViewControllerBasedStatusBarAppearance + FlutterDeepLinkingEnabled + + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLName + flutterbooksample.com + CFBundleURLSchemes + + customscheme + + + diff --git a/navigation_and_routing/lib/main.dart b/navigation_and_routing/lib/main.dart new file mode 100644 index 000000000..9b68df0ea --- /dev/null +++ b/navigation_and_routing/lib/main.dart @@ -0,0 +1,15 @@ +// Copyright 2021, the Flutter project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:flutter/material.dart'; +import 'package:url_strategy/url_strategy.dart'; + +import 'src/app.dart'; + +void main() { + // Use package:url_strategy until this pull request is released: + // https://github.com/flutter/flutter/pull/77103 + setPathUrlStrategy(); + runApp(const Bookstore()); +} diff --git a/navigation_and_routing/lib/src/app.dart b/navigation_and_routing/lib/src/app.dart new file mode 100644 index 000000000..9c50ec4b0 --- /dev/null +++ b/navigation_and_routing/lib/src/app.dart @@ -0,0 +1,101 @@ +// Copyright 2021, the Flutter project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:flutter/material.dart'; + +import 'auth.dart'; +import 'data.dart'; +import 'routing.dart'; +import 'screens/navigator.dart'; +import 'widgets/library_scope.dart'; + +class Bookstore extends StatefulWidget { + const Bookstore({Key? key}) : super(key: key); + + @override + _BookstoreState createState() => _BookstoreState(); +} + +class _BookstoreState extends State { + final auth = BookstoreAuth(); + late final BookstoreRouteGuard guard; + late final RouteState routeState; + late final SimpleRouterDelegate routerDelegate; + late final TemplateRouteParser routeParser; + final GlobalKey navigatorKey = GlobalKey(); + + final library = Library() + ..addBook('Left Hand of Darkness', 'Ursula K. Le Guin', true, true) + ..addBook('Too Like the Lightning', 'Ada Palmer', false, true) + ..addBook('Kindred', 'Octavia E. Butler', true, false) + ..addBook('The Lathe of Heaven', 'Ursula K. Le Guin', false, false); + + @override + void initState() { + guard = BookstoreRouteGuard(auth: auth); + + /// Configure the parser with all of the app's allowed path templates. + routeParser = TemplateRouteParser( + [ + '/signin', + '/authors', + '/settings', + '/books/new', + '/books/all', + '/books/popular', + '/book/:bookId', + '/author/:authorId', + ], + guard: guard, + initialRoute: '/signin', + ); + + routeState = RouteState(routeParser); + + routerDelegate = SimpleRouterDelegate( + routeState: routeState, + navigatorKey: navigatorKey, + builder: (context) => BookstoreNavigator( + navigatorKey: navigatorKey, + auth: auth, + ), + ); + + // Listen for when the user logs out and display the signin screen. + auth.addListener(_handleAuthStateChanged); + + super.initState(); + } + + @override + Widget build(BuildContext context) { + return RouteStateScope( + notifier: routeState, + child: BookstoreAuthScope( + notifier: auth, + child: LibraryScope( + library: library, + child: MaterialApp.router( + routerDelegate: routerDelegate, + routeInformationParser: routeParser, + ), + ), + ), + ); + } + + Future _handleAuthStateChanged() async { + if (!auth.signedIn) { + routeState.go('/signin'); + } + } + + @override + void dispose() { + auth.removeListener(_handleAuthStateChanged); + routeState.dispose(); + routerDelegate.dispose(); + super.dispose(); + } +} diff --git a/navigation_and_routing/lib/src/auth.dart b/navigation_and_routing/lib/src/auth.dart new file mode 100644 index 000000000..07d46c741 --- /dev/null +++ b/navigation_and_routing/lib/src/auth.dart @@ -0,0 +1,2 @@ +export 'auth/auth.dart'; +export 'auth/auth_guard.dart'; diff --git a/navigation_and_routing/lib/src/auth/auth.dart b/navigation_and_routing/lib/src/auth/auth.dart new file mode 100644 index 000000000..7247c8fdb --- /dev/null +++ b/navigation_and_routing/lib/src/auth/auth.dart @@ -0,0 +1,53 @@ +// Copyright 2021, the Flutter project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; + +/// A mock authentication service +class BookstoreAuth extends ChangeNotifier { + bool _signedIn = false; + + bool get signedIn { + return _signedIn; + } + + Future signOut() async { + await Future.delayed(const Duration(milliseconds: 200)); + // Sign out. + _signedIn = false; + notifyListeners(); + } + + Future signIn(String username, String password) async { + await Future.delayed(const Duration(milliseconds: 200)); + + // Sign in. Allow any password. + _signedIn = true; + notifyListeners(); + return _signedIn; + } + + @override + bool operator ==(Object other) { + return other is BookstoreAuth && other._signedIn == _signedIn; + } + + @override + int get hashCode => _signedIn.hashCode; +} + +class BookstoreAuthScope extends InheritedNotifier { + const BookstoreAuthScope({ + required BookstoreAuth notifier, + required Widget child, + Key? key, + }) : super(key: key, notifier: notifier, child: child); + + static BookstoreAuth? of(BuildContext context) { + return context + .dependOnInheritedWidgetOfExactType() + ?.notifier; + } +} diff --git a/navigation_and_routing/lib/src/auth/auth_guard.dart b/navigation_and_routing/lib/src/auth/auth_guard.dart new file mode 100644 index 000000000..aa28b9ca2 --- /dev/null +++ b/navigation_and_routing/lib/src/auth/auth_guard.dart @@ -0,0 +1,32 @@ +// Copyright 2021, the Flutter project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import '../routing.dart'; +import 'auth.dart'; + +/// An implementation of [RouteGuard] that redirects to /signIn +class BookstoreRouteGuard implements RouteGuard { + BookstoreAuth auth; + + BookstoreRouteGuard({ + required this.auth, + }); + + /// Redirect to /signin if the user isn't signed in. + @override + Future redirect(ParsedRoute from) async { + final signedIn = auth.signedIn; + final signInRoute = ParsedRoute('/signin', '/signin', {}, {}); + + // Go to /signin if the user is not signed in + if (!signedIn && from != signInRoute) { + return signInRoute; + } + // Go to /books if the user is signed in and tries to go to /signin. + else if (signedIn && from == signInRoute) { + return ParsedRoute('/books/popular', '/books/popular', {}, {}); + } + return from; + } +} diff --git a/navigation_and_routing/lib/src/data.dart b/navigation_and_routing/lib/src/data.dart new file mode 100644 index 000000000..eb3ce57d0 --- /dev/null +++ b/navigation_and_routing/lib/src/data.dart @@ -0,0 +1,3 @@ +export 'data/author.dart'; +export 'data/book.dart'; +export 'data/library.dart'; diff --git a/navigation_and_routing/lib/src/data/author.dart b/navigation_and_routing/lib/src/data/author.dart new file mode 100644 index 000000000..9a1e5e2fd --- /dev/null +++ b/navigation_and_routing/lib/src/data/author.dart @@ -0,0 +1,13 @@ +// Copyright 2021, the Flutter project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'book.dart'; + +class Author { + final int id; + final String name; + final List books; + + Author(this.id, this.name, this.books); +} diff --git a/navigation_and_routing/lib/src/data/book.dart b/navigation_and_routing/lib/src/data/book.dart new file mode 100644 index 000000000..a0940ad92 --- /dev/null +++ b/navigation_and_routing/lib/src/data/book.dart @@ -0,0 +1,15 @@ +// Copyright 2021, the Flutter project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'author.dart'; + +class Book { + final int id; + final String title; + late final Author author; + final bool isPopular; + final bool isNew; + + Book(this.id, this.title, this.isPopular, this.isNew); +} diff --git a/navigation_and_routing/lib/src/data/library.dart b/navigation_and_routing/lib/src/data/library.dart new file mode 100644 index 000000000..bbded022e --- /dev/null +++ b/navigation_and_routing/lib/src/data/library.dart @@ -0,0 +1,41 @@ +// Copyright 2021, the Flutter project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:collection/collection.dart'; + +import 'author.dart'; +import 'book.dart'; + +class Library { + final List allBooks = []; + final List allAuthors = []; + + void addBook(String title, String authorName, bool isPopular, bool isNew) { + var author = + allAuthors.firstWhereOrNull((author) => author.name == authorName); + var book = Book(allBooks.length, title, isPopular, isNew); + + if (author == null) { + author = Author(allAuthors.length, authorName, [book]); + allAuthors.add(author); + } else { + author.books.add(book); + } + + book.author = author; + allBooks.add(book); + } + + List get popularBooks { + return [ + ...allBooks.where((book) => book.isPopular), + ]; + } + + List get newBooks { + return [ + ...allBooks.where((book) => book.isNew), + ]; + } +} diff --git a/navigation_and_routing/lib/src/routing.dart b/navigation_and_routing/lib/src/routing.dart new file mode 100644 index 000000000..5bb948d7a --- /dev/null +++ b/navigation_and_routing/lib/src/routing.dart @@ -0,0 +1,4 @@ +export 'routing/delegate.dart'; +export 'routing/parsed_route.dart'; +export 'routing/parser.dart'; +export 'routing/route_state.dart'; diff --git a/navigation_and_routing/lib/src/routing/delegate.dart b/navigation_and_routing/lib/src/routing/delegate.dart new file mode 100644 index 000000000..63be2aa2e --- /dev/null +++ b/navigation_and_routing/lib/src/routing/delegate.dart @@ -0,0 +1,52 @@ +// Copyright 2021, the Flutter project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; + +import 'parsed_route.dart'; +import 'route_state.dart'; + +class SimpleRouterDelegate extends RouterDelegate + with ChangeNotifier, PopNavigatorRouterDelegateMixin { + final RouteState routeState; + final WidgetBuilder builder; + + @override + final GlobalKey navigatorKey; + + SimpleRouterDelegate({ + required this.routeState, + required this.builder, + required this.navigatorKey, + // ignore: prefer_initializing_formals + }) { + routeState.addListener(notifyListeners); + } + + @override + Widget build(BuildContext context) { + return builder(context); + } + + @override + Future setNewRoutePath(ParsedRoute configuration) async { + routeState.route = configuration; + return SynchronousFuture(null); + } + + @override + ParsedRoute get currentConfiguration { + return routeState.route; + } + + @override + void dispose() { + routeState.removeListener(notifyListeners); + routeState.dispose(); + super.dispose(); + } +} diff --git a/navigation_and_routing/lib/src/routing/parsed_route.dart b/navigation_and_routing/lib/src/routing/parsed_route.dart new file mode 100644 index 000000000..41a5f64c2 --- /dev/null +++ b/navigation_and_routing/lib/src/routing/parsed_route.dart @@ -0,0 +1,45 @@ +// Copyright 2021, the Flutter project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:collection/collection.dart'; +import 'package:quiver/core.dart'; + +import 'parser.dart'; + +/// A route path that has been parsed by [TemplateRouteParser]. +class ParsedRoute { + final String path; + final String pathTemplate; + final Map parameters; + final Map queryParameters; + + static const _mapEquality = MapEquality(); + + ParsedRoute( + this.path, this.pathTemplate, this.parameters, this.queryParameters); + + @override + bool operator ==(Object other) { + return other is ParsedRoute && + other.pathTemplate == pathTemplate && + other.path == path && + _mapEquality.equals(parameters, other.parameters) && + _mapEquality.equals(queryParameters, other.queryParameters); + } + + @override + int get hashCode => hash4( + path, + pathTemplate, + _mapEquality.hash(parameters), + _mapEquality.hash(queryParameters), + ); + + @override + String toString() => ''; +} diff --git a/navigation_and_routing/lib/src/routing/parser.dart b/navigation_and_routing/lib/src/routing/parser.dart new file mode 100644 index 000000000..1904e024a --- /dev/null +++ b/navigation_and_routing/lib/src/routing/parser.dart @@ -0,0 +1,68 @@ +// Copyright 2021, the Flutter project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:flutter/widgets.dart'; +import 'package:path_to_regexp/path_to_regexp.dart'; + +import 'parsed_route.dart'; + +abstract class RouteGuard { + Future redirect(T from); +} + +/// Parses the URI path into a [ParsedRoute]. +class TemplateRouteParser extends RouteInformationParser { + final List _pathTemplates = []; + RouteGuard? guard; + final ParsedRoute initialRoute; + + TemplateRouteParser(List pathTemplates, + {String? initialRoute = '/', this.guard}) + : initialRoute = + ParsedRoute(initialRoute ?? '/', initialRoute ?? '/', {}, {}) { + for (var template in pathTemplates) { + _addRoute(template); + } + } + + void _addRoute(String pathTemplate) { + _pathTemplates.add(pathTemplate); + } + + @override + Future parseRouteInformation( + RouteInformation routeInformation) async { + return await _parse(routeInformation); + } + + Future _parse(RouteInformation routeInformation) async { + final path = routeInformation.location!; + final queryParams = Uri.parse(path).queryParameters; + var parsedRoute = initialRoute; + + for (var pathTemplate in _pathTemplates) { + final parameters = []; + var pathRegExp = pathToRegExp(pathTemplate, parameters: parameters); + if (pathRegExp.hasMatch(path)) { + final match = pathRegExp.matchAsPrefix(path); + if (match == null) continue; + final params = extract(parameters, match); + parsedRoute = ParsedRoute(path, pathTemplate, params, queryParams); + } + } + + // Redirect if a guard is present + var guard = this.guard; + if (guard != null) { + return guard.redirect(parsedRoute); + } + + return parsedRoute; + } + + @override + RouteInformation restoreRouteInformation(ParsedRoute configuration) { + return RouteInformation(location: configuration.path); + } +} diff --git a/navigation_and_routing/lib/src/routing/route_state.dart b/navigation_and_routing/lib/src/routing/route_state.dart new file mode 100644 index 000000000..5ee7e176f --- /dev/null +++ b/navigation_and_routing/lib/src/routing/route_state.dart @@ -0,0 +1,50 @@ +// Copyright 2021, the Flutter project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; + +import 'parsed_route.dart'; +import 'parser.dart'; + +/// The current route state. To change the current route, call obtain the state using +/// `RouteState.of(context)` and call `go()`: +/// +/// ``` +/// RouteState.of(context).go('/book/2'); +/// ``` +class RouteState extends ChangeNotifier { + TemplateRouteParser parser; + ParsedRoute _route; + + RouteState(this.parser) + : _route = parser.initialRoute; + + ParsedRoute get route => _route; + + set route(ParsedRoute route) { + _route = route; + notifyListeners(); + } + + Future go(String route) async { + this.route = + await parser.parseRouteInformation(RouteInformation(location: route)); + } +} + +/// Provides the current [RouteState] to descendent widgets in the tree. +class RouteStateScope extends InheritedNotifier { + const RouteStateScope({ + required RouteState notifier, + required Widget child, + Key? key, + }) : super(key: key, notifier: notifier, child: child); + + static RouteState? of(BuildContext context) { + return context + .dependOnInheritedWidgetOfExactType() + ?.notifier; + } +} diff --git a/navigation_and_routing/lib/src/screens/author_details.dart b/navigation_and_routing/lib/src/screens/author_details.dart new file mode 100644 index 000000000..5bcf48568 --- /dev/null +++ b/navigation_and_routing/lib/src/screens/author_details.dart @@ -0,0 +1,49 @@ +// Copyright 2021, the Flutter project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:flutter/material.dart'; + +import '../data.dart'; +import '../widgets/book_list.dart'; +import 'book_details.dart'; + +class AuthorDetailsScreen extends StatelessWidget { + final Author author; + + const AuthorDetailsScreen({ + Key? key, + required this.author, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(author.name), + ), + body: Center( + child: Column( + children: [ + Expanded( + child: BookList( + books: author.books, + onTap: (book) { + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) { + return BookDetailsScreen( + book: book, + ); + }, + ), + ); + }, + ), + ), + ], + ), + ), + ); + } +} diff --git a/navigation_and_routing/lib/src/screens/authors.dart b/navigation_and_routing/lib/src/screens/authors.dart new file mode 100644 index 000000000..9a76237dc --- /dev/null +++ b/navigation_and_routing/lib/src/screens/authors.dart @@ -0,0 +1,30 @@ +// Copyright 2021, the Flutter project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:flutter/material.dart'; + +import '../routing.dart'; +import '../widgets/author_list.dart'; +import '../widgets/library_scope.dart'; + +class AuthorsScreen extends StatelessWidget { + final String title = "Authors"; + + const AuthorsScreen({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(title), + ), + body: AuthorList( + authors: LibraryScope.of(context).allAuthors, + onTap: (author) { + RouteStateScope.of(context)!.go('/author/${author.id}'); + }, + ), + ); + } +} diff --git a/navigation_and_routing/lib/src/screens/book_details.dart b/navigation_and_routing/lib/src/screens/book_details.dart new file mode 100644 index 000000000..96e71e015 --- /dev/null +++ b/navigation_and_routing/lib/src/screens/book_details.dart @@ -0,0 +1,75 @@ +// Copyright 2021, the Flutter project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:flutter/material.dart'; +import 'package:url_launcher/link.dart'; + +import '../data.dart'; +import 'author_details.dart'; + +class BookDetailsScreen extends StatelessWidget { + final Book? book; + + const BookDetailsScreen({ + Key? key, + this.book, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + if (book == null) { + return const Scaffold( + body: Center( + child: Text('No book with found.'), + ), + ); + } + return Scaffold( + appBar: AppBar( + title: Text(book!.title), + ), + body: Center( + child: Column( + children: [ + Text( + book!.title, + style: Theme + .of(context) + .textTheme + .headline4, + ), + Text( + book!.author.name, + style: Theme + .of(context) + .textTheme + .subtitle1, + ), + TextButton( + child: const Text('View author (Push)'), + onPressed: () { + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) { + return AuthorDetailsScreen(author: book!.author); + }, + ), + ); + }, + ), + Link( + uri: Uri.parse('/author/${book!.author.id}'), + builder: (context, followLink) { + return TextButton( + child: const Text('View author (Link)'), + onPressed: followLink, + ); + }, + ), + ], + ), + ), + ); + } +} diff --git a/navigation_and_routing/lib/src/screens/books.dart b/navigation_and_routing/lib/src/screens/books.dart new file mode 100644 index 000000000..d4239fb24 --- /dev/null +++ b/navigation_and_routing/lib/src/screens/books.dart @@ -0,0 +1,130 @@ +// Copyright 2021, the Flutter project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:flutter/material.dart'; + +import '../data.dart'; +import '../routing.dart'; +import '../widgets/book_list.dart'; +import '../widgets/library_scope.dart'; + +class BooksScreen extends StatefulWidget { + final ParsedRoute currentRoute; + + const BooksScreen({ + Key? key, + required this.currentRoute, + }) : super(key: key); + + @override + _BooksScreenState createState() => _BooksScreenState(); +} + +class _BooksScreenState extends State + with SingleTickerProviderStateMixin { + late TabController _tabController; + + @override + void initState() { + super.initState(); + _tabController = TabController(length: 3, vsync: this) + ..addListener(_handleTabIndexChanged); + } + + @override + void dispose() { + _tabController.removeListener(_handleTabIndexChanged); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final library = LibraryScope.of(context); + return Scaffold( + appBar: AppBar( + title: const Text('Books'), + bottom: TabBar( + controller: _tabController, + tabs: const [ + Tab( + text: 'Popular', + icon: Icon(Icons.people), + ), + Tab( + text: 'New', + icon: Icon(Icons.new_releases), + ), + Tab( + text: 'All', + icon: Icon(Icons.list), + ), + ], + ), + ), + body: TabBarView( + controller: _tabController, + children: [ + BookList( + books: library.popularBooks, + onTap: _handleBookTapped, + ), + BookList( + books: library.newBooks, + onTap: _handleBookTapped, + ), + BookList( + books: library.allBooks, + onTap: _handleBookTapped, + ), + ], + ), + ); + } + + String get title { + switch (_tabController.index) { + case 1: + return 'New'; + case 2: + return 'All'; + case 0: + default: + return 'Popular'; + } + } + + RouteState get routeState => RouteStateScope.of(context)!; + + void _handleBookTapped(Book book) { + routeState.go('/book/${book.id}'); + } + + void _handleTabIndexChanged() { + switch (_tabController.index) { + case 1: + routeState.go('/books/new'); + break; + case 2: + routeState.go('/books/all'); + break; + case 0: + default: + routeState.go('/books/popular'); + break; + } + } + + @override + void didUpdateWidget(BooksScreen oldWidget) { + var newPath = routeState.route.pathTemplate; + if (newPath.startsWith('/books/popular')) { + _tabController.index = 0; + } else if (newPath.startsWith('/books/new')) { + _tabController.index = 1; + } else if (newPath == '/books/all') { + _tabController.index = 2; + } + super.didUpdateWidget(oldWidget); + } +} diff --git a/navigation_and_routing/lib/src/screens/navigator.dart b/navigation_and_routing/lib/src/screens/navigator.dart new file mode 100644 index 000000000..d02b99f6f --- /dev/null +++ b/navigation_and_routing/lib/src/screens/navigator.dart @@ -0,0 +1,111 @@ +import 'package:collection/collection.dart'; +import 'package:flutter/material.dart'; + +import '../auth.dart'; +import '../data.dart'; +import '../routing.dart'; +import '../screens/sign_in.dart'; +import '../widgets/fade_transition_page.dart'; +import '../widgets/library_scope.dart'; +import 'author_details.dart'; +import 'book_details.dart'; +import 'scaffold.dart'; + +/// Builds the top-level navigator for the app. The pages to display are based +/// on the [routeState] that was parsed by the TemplateRouteParser. +class BookstoreNavigator extends StatefulWidget { + final GlobalKey navigatorKey; + final BookstoreAuth auth; + + const BookstoreNavigator({ + required this.auth, + required this.navigatorKey, + Key? key, + }) : super(key: key); + + @override + _BookstoreNavigatorState createState() => _BookstoreNavigatorState(); +} + +class _BookstoreNavigatorState extends State { + final scaffoldKey = const ValueKey('App scaffold'); + final bookDetailsKey = const ValueKey('Book details screen'); + final authorDetailsKey = const ValueKey('Author details screen'); + + @override + Widget build(BuildContext context) { + final routeState = RouteStateScope.of(context)!; + final pathTemplate = routeState.route.pathTemplate; + final library = LibraryScope.of(context); + + Book? book; + if (pathTemplate == '/book/:bookId') { + book = library.allBooks.firstWhereOrNull( + (b) => b.id.toString() == routeState.route.parameters['bookId']); + } + + Author? author; + if (pathTemplate == '/author/:authorId') { + author = library.allAuthors.firstWhereOrNull( + (b) => b.id.toString() == routeState.route.parameters['authorId']); + } + + return Navigator( + key: widget.navigatorKey, + onPopPage: (route, dynamic result) { + // When a page that is stacked on top of the scaffold is popped, display + // the /books or /authors tab in BookstoreScaffold. + if (route.settings is Page && + (route.settings as Page).key == bookDetailsKey) { + routeState.go('/books/popular'); + } + + if (route.settings is Page && + (route.settings as Page).key == authorDetailsKey) { + routeState.go('/authors'); + } + + return route.didPop(result); + }, + pages: [ + if (routeState.route.pathTemplate == '/signin') + // Display the sign in screen. + FadeTransitionPage( + key: const ValueKey('Sign in'), + child: SignInScreen( + onSignIn: (credentials) async { + var signedIn = await widget.auth + .signIn(credentials.username, credentials.password); + if (signedIn) { + routeState.go('/books/popular'); + } + }, + ), + ) + else ...[ + // Display the app + FadeTransitionPage( + key: scaffoldKey, + child: const BookstoreScaffold(), + ), + // Add an additional page to the stack if the user is viewing a book + // or an author + if (book != null) + MaterialPage( + key: bookDetailsKey, + child: BookDetailsScreen( + book: book, + ), + ) + else if (author != null) + MaterialPage( + key: authorDetailsKey, + child: AuthorDetailsScreen( + author: author, + ), + ), + ], + ], + ); + } +} diff --git a/navigation_and_routing/lib/src/screens/scaffold.dart b/navigation_and_routing/lib/src/screens/scaffold.dart new file mode 100644 index 000000000..5da034527 --- /dev/null +++ b/navigation_and_routing/lib/src/screens/scaffold.dart @@ -0,0 +1,54 @@ +// Copyright 2021, the Flutter project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:adaptive_navigation/adaptive_navigation.dart'; +import 'package:flutter/material.dart'; + +import '../routing.dart'; +import 'scaffold_body.dart'; + +class BookstoreScaffold extends StatelessWidget { + const BookstoreScaffold({ + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + final routeState = RouteStateScope.of(context)!; + final selectedIndex = _getSelectedIndex(routeState.route.pathTemplate); + + return Scaffold( + body: AdaptiveNavigationScaffold( + selectedIndex: selectedIndex, + body: const BookstoreScaffoldBody(), + onDestinationSelected: (idx) { + if (idx == 0) routeState.go('/books/popular'); + if (idx == 1) routeState.go('/authors'); + if (idx == 2) routeState.go('/settings'); + }, + destinations: const [ + AdaptiveScaffoldDestination( + title: 'Books', + icon: Icons.book, + ), + AdaptiveScaffoldDestination( + title: 'Authors', + icon: Icons.person, + ), + AdaptiveScaffoldDestination( + title: 'Settings', + icon: Icons.settings, + ), + ], + ), + ); + } + + int _getSelectedIndex(String pathTemplate) { + if (pathTemplate.startsWith('/books')) return 0; + if (pathTemplate == '/authors') return 1; + if (pathTemplate == '/settings') return 2; + return 0; + } +} diff --git a/navigation_and_routing/lib/src/screens/scaffold_body.dart b/navigation_and_routing/lib/src/screens/scaffold_body.dart new file mode 100644 index 000000000..c30081a98 --- /dev/null +++ b/navigation_and_routing/lib/src/screens/scaffold_body.dart @@ -0,0 +1,62 @@ +// Copyright 2021, the Flutter project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:flutter/material.dart'; + +import '../routing.dart'; +import '../screens/settings.dart'; +import '../widgets/fade_transition_page.dart'; +import 'authors.dart'; +import 'books.dart'; + +/// Displays the contents of the body of [BookstoreScaffold] +class BookstoreScaffoldBody extends StatelessWidget { + static GlobalKey navigatorKey = GlobalKey(); + + const BookstoreScaffoldBody({ + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + var currentRoute = RouteStateScope.of(context)!.route; + + // A nested Router isn't necessary because the back button behavior doesn't + // need to be customized. + return Navigator( + key: navigatorKey, + onPopPage: (route, dynamic result) => route.didPop(result), + pages: [ + if (currentRoute.pathTemplate.startsWith('/authors')) + const FadeTransitionPage( + key: ValueKey('authors'), + child: AuthorsScreen(), + ) + else if (currentRoute.pathTemplate.startsWith('/settings')) + const FadeTransitionPage( + key: ValueKey('settings'), + child: SettingsScreen(), + ) + else if (currentRoute.pathTemplate.startsWith('/books') || + currentRoute.pathTemplate == '/') + FadeTransitionPage( + key: const ValueKey('books'), + child: BooksScreen(currentRoute: currentRoute), + ) + + // Avoid building a Navigator with an empty `pages` list when the + // RouteState is set to an unexpected path, such as /signin. + // + // Since RouteStateScope is an InheritedNotifier, any change to the + // route will result in a call to this build method, even though this + // widget isn't built when those routes are active. + else + FadeTransitionPage( + key: const ValueKey('empty'), + child: Container(), + ), + ], + ); + } +} diff --git a/navigation_and_routing/lib/src/screens/settings.dart b/navigation_and_routing/lib/src/screens/settings.dart new file mode 100644 index 000000000..eafb8c3e2 --- /dev/null +++ b/navigation_and_routing/lib/src/screens/settings.dart @@ -0,0 +1,103 @@ +// Copyright 2021, the Flutter project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:flutter/material.dart'; +import 'package:url_launcher/link.dart'; + +import '../auth/auth.dart'; + +class SettingsScreen extends StatefulWidget { + const SettingsScreen({Key? key}) : super(key: key); + + @override + _SettingsScreenState createState() => _SettingsScreenState(); +} + +class _SettingsScreenState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + body: SafeArea( + child: SingleChildScrollView( + child: Align( + alignment: Alignment.topCenter, + child: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 400), + child: const Card( + child: Padding( + padding: EdgeInsets.symmetric(vertical: 18, horizontal: 12), + child: SettingsContent(), + ), + ), + ), + ), + ), + ), + ); + } +} + +class SettingsContent extends StatelessWidget { + const SettingsContent({ + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + ...[ + Text( + 'Settings', + style: Theme.of(context).textTheme.headline4, + ), + ElevatedButton( + onPressed: () { + BookstoreAuthScope.of(context)!.signOut(); + }, + child: const Text('Sign out'), + ), + Link( + uri: Uri.parse('/book/0'), + builder: (context, followLink) { + return TextButton( + child: const Text('Go directly to /book/0'), + onPressed: followLink, + ); + }, + ), + Link( + uri: Uri.parse('/author/0'), + builder: (context, followLink) { + return TextButton( + child: const Text('Go directly to /author/0'), + onPressed: followLink, + ); + }, + ), + ].map((w) => Padding(padding: const EdgeInsets.all(8), child: w)), + TextButton( + onPressed: () => showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text('Alert!'), + content: const Text('The alert description goes here.'), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context, 'Cancel'), + child: const Text('Cancel'), + ), + TextButton( + onPressed: () => Navigator.pop(context, 'OK'), + child: const Text('OK'), + ), + ], + ), + ), + child: const Text('Show Dialog'), + ) + ], + ); + } +} diff --git a/navigation_and_routing/lib/src/screens/sign_in.dart b/navigation_and_routing/lib/src/screens/sign_in.dart new file mode 100644 index 000000000..1d2926560 --- /dev/null +++ b/navigation_and_routing/lib/src/screens/sign_in.dart @@ -0,0 +1,76 @@ +// Copyright 2021, the Flutter project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +class Credentials { + final String username; + final String password; + Credentials(this.username, this.password); +} + +class SignInScreen extends StatefulWidget { + final ValueChanged onSignIn; + + const SignInScreen({ + required this.onSignIn, + Key? key, + }) : super(key: key); + + @override + _SignInScreenState createState() => _SignInScreenState(); +} + +class _SignInScreenState extends State { + String username = ''; + String password = ''; + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Center( + child: Card( + child: Container( + constraints: BoxConstraints.loose(const Size(600, 600)), + padding: const EdgeInsets.all(8), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + Text('Sign in', style: Theme.of(context).textTheme.headline4), + TextField( + decoration: const InputDecoration(labelText: 'Username'), + onChanged: (v) { + setState(() { + username = v; + }); + }, + ), + TextField( + decoration: const InputDecoration(labelText: 'Password'), + obscureText: true, + onChanged: (v) { + setState(() { + password = v; + }); + }, + ), + Padding( + padding: const EdgeInsets.all(16), + child: TextButton( + onPressed: () async { + widget.onSignIn(Credentials(username, password)); + }, + child: const Text('Sign in'), + ), + ), + ], + ), + ), + ), + ), + ); + } +} diff --git a/navigation_and_routing/lib/src/widgets/author_list.dart b/navigation_and_routing/lib/src/widgets/author_list.dart new file mode 100644 index 000000000..bb5321e0b --- /dev/null +++ b/navigation_and_routing/lib/src/widgets/author_list.dart @@ -0,0 +1,36 @@ +// Copyright 2021, the Flutter project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:flutter/material.dart'; + +import '../data.dart'; + +class AuthorList extends StatelessWidget { + final List authors; + final ValueChanged? onTap; + + const AuthorList({ + required this.authors, + this.onTap, + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return ListView.builder( + itemCount: authors.length, + itemBuilder: (context, index) { + return ListTile( + title: Text( + authors[index].name, + ), + subtitle: Text( + '${authors[index].books.length} books', + ), + onTap: onTap != null ? () => onTap!(authors[index]) : null, + ); + }, + ); + } +} diff --git a/navigation_and_routing/lib/src/widgets/book_list.dart b/navigation_and_routing/lib/src/widgets/book_list.dart new file mode 100644 index 000000000..50cfc8805 --- /dev/null +++ b/navigation_and_routing/lib/src/widgets/book_list.dart @@ -0,0 +1,36 @@ +// Copyright 2021, the Flutter project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:flutter/material.dart'; + +import '../data.dart'; + +class BookList extends StatelessWidget { + final List books; + final ValueChanged? onTap; + + const BookList({ + required this.books, + this.onTap, + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return ListView.builder( + itemCount: books.length, + itemBuilder: (context, index) { + return ListTile( + title: Text( + books[index].title, + ), + subtitle: Text( + books[index].author.name, + ), + onTap: onTap != null ? () => onTap!(books[index]) : null, + ); + }, + ); + } +} diff --git a/navigation_and_routing/lib/src/widgets/fade_transition_page.dart b/navigation_and_routing/lib/src/widgets/fade_transition_page.dart new file mode 100644 index 000000000..2d64a4e1c --- /dev/null +++ b/navigation_and_routing/lib/src/widgets/fade_transition_page.dart @@ -0,0 +1,52 @@ +// Copyright 2021, the Flutter project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:flutter/material.dart'; + +class FadeTransitionPage extends Page { + final Widget child; + + const FadeTransitionPage({LocalKey? key, required this.child}) + : super(key: key); + + @override + Route createRoute(BuildContext context) { + return PageBasedFadeTransitionRoute(this); + } +} + +class PageBasedFadeTransitionRoute extends PageRoute { + PageBasedFadeTransitionRoute(Page page) + : super( + settings: page, + ); + + @override + Color? get barrierColor => null; + + @override + String? get barrierLabel => null; + + @override + Duration get transitionDuration => const Duration(milliseconds: 300); + + @override + bool get maintainState => true; + + @override + Widget buildPage(BuildContext context, Animation animation, + Animation secondaryAnimation) { + var curveTween = CurveTween(curve: Curves.easeIn); + return FadeTransition( + opacity: animation.drive(curveTween), + child: (settings as FadeTransitionPage).child, + ); + } + + @override + Widget buildTransitions(BuildContext context, Animation animation, + Animation secondaryAnimation, Widget child) { + return child; + } +} diff --git a/navigation_and_routing/lib/src/widgets/library_scope.dart b/navigation_and_routing/lib/src/widgets/library_scope.dart new file mode 100644 index 000000000..4841dcdff --- /dev/null +++ b/navigation_and_routing/lib/src/widgets/library_scope.dart @@ -0,0 +1,25 @@ +// Copyright 2021, the Flutter project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:flutter/widgets.dart'; + +import '../data.dart'; + +class LibraryScope extends InheritedWidget { + final Library library; + + const LibraryScope({ + Key? key, + required this.library, + required Widget child, + }) : super(key: key, child: child); + + @override + bool updateShouldNotify(LibraryScope oldWidget) => + library != oldWidget.library; + + static Library of(BuildContext context) { + return context.dependOnInheritedWidgetOfExactType()!.library; + } +} diff --git a/navigation_and_routing/macos/.gitignore b/navigation_and_routing/macos/.gitignore new file mode 100644 index 000000000..d2fd37723 --- /dev/null +++ b/navigation_and_routing/macos/.gitignore @@ -0,0 +1,6 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/xcuserdata/ diff --git a/navigation_and_routing/macos/Flutter/Flutter-Debug.xcconfig b/navigation_and_routing/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 000000000..4b81f9b2d --- /dev/null +++ b/navigation_and_routing/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/navigation_and_routing/macos/Flutter/Flutter-Release.xcconfig b/navigation_and_routing/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 000000000..5caa9d157 --- /dev/null +++ b/navigation_and_routing/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/navigation_and_routing/macos/Flutter/GeneratedPluginRegistrant.swift b/navigation_and_routing/macos/Flutter/GeneratedPluginRegistrant.swift new file mode 100644 index 000000000..8236f5728 --- /dev/null +++ b/navigation_and_routing/macos/Flutter/GeneratedPluginRegistrant.swift @@ -0,0 +1,12 @@ +// +// Generated file. Do not edit. +// + +import FlutterMacOS +import Foundation + +import url_launcher_macos + +func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) +} diff --git a/navigation_and_routing/macos/Podfile b/navigation_and_routing/macos/Podfile new file mode 100644 index 000000000..dade8dfad --- /dev/null +++ b/navigation_and_routing/macos/Podfile @@ -0,0 +1,40 @@ +platform :osx, '10.11' + +# 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', 'ephemeral', '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 Flutter-Generated.xcconfig, then run \"flutter pub get\"" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_macos_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_macos_build_settings(target) + end +end diff --git a/navigation_and_routing/macos/Runner.xcodeproj/project.pbxproj b/navigation_and_routing/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 000000000..ea2586580 --- /dev/null +++ b/navigation_and_routing/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,572 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 51; + objects = { + +/* Begin PBXAggregateTarget section */ + 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; + buildPhases = ( + 33CC111E2044C6BF0003C045 /* ShellScript */, + ); + dependencies = ( + ); + name = "Flutter Assemble"; + productName = FLX; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* bookstore.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "bookstore.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; + 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; + 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; + 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; + 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* bookstore.app */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* bookstore.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 0930; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; + }; + 33CC111E2044C6BF0003C045 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + Flutter/ephemeral/FlutterInputs.xcfilelist, + ); + inputPaths = ( + Flutter/ephemeral/tripwire, + ); + outputFileListPaths = ( + Flutter/ephemeral/FlutterOutputs.xcfilelist, + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + 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_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + 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_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Profile; + }; + 338D0CEA231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Profile; + }; + 338D0CEB231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + 33CC10F92044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + 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_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + 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_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + 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_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 33CC10FA2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + 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_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + 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_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 33CC10FC2044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 33CC10FD2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 33CC111D2044C6BA0003C045 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + 338D0CE9231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + 338D0CEA231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + 338D0CEB231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/navigation_and_routing/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/navigation_and_routing/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/navigation_and_routing/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/navigation_and_routing/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/navigation_and_routing/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 000000000..ae5d18f5a --- /dev/null +++ b/navigation_and_routing/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/navigation_and_routing/macos/Runner.xcworkspace/contents.xcworkspacedata b/navigation_and_routing/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..1d526a16e --- /dev/null +++ b/navigation_and_routing/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/navigation_and_routing/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/navigation_and_routing/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/navigation_and_routing/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/navigation_and_routing/macos/Runner/AppDelegate.swift b/navigation_and_routing/macos/Runner/AppDelegate.swift new file mode 100644 index 000000000..d53ef6437 --- /dev/null +++ b/navigation_and_routing/macos/Runner/AppDelegate.swift @@ -0,0 +1,9 @@ +import Cocoa +import FlutterMacOS + +@NSApplicationMain +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } +} diff --git a/navigation_and_routing/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/navigation_and_routing/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..a2ec33f19 --- /dev/null +++ b/navigation_and_routing/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_16.png", + "scale" : "1x" + }, + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "1x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_64.png", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_128.png", + "scale" : "1x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "1x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "1x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_1024.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/navigation_and_routing/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/navigation_and_routing/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100644 index 000000000..3c4935a7c Binary files /dev/null and b/navigation_and_routing/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png differ diff --git a/navigation_and_routing/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/navigation_and_routing/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 000000000..ed4cc1642 Binary files /dev/null and b/navigation_and_routing/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png differ diff --git a/navigation_and_routing/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/navigation_and_routing/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png new file mode 100644 index 000000000..483be6138 Binary files /dev/null and b/navigation_and_routing/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png differ diff --git a/navigation_and_routing/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/navigation_and_routing/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png new file mode 100644 index 000000000..bcbf36df2 Binary files /dev/null and b/navigation_and_routing/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png differ diff --git a/navigation_and_routing/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/navigation_and_routing/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png new file mode 100644 index 000000000..9c0a65286 Binary files /dev/null and b/navigation_and_routing/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png differ diff --git a/navigation_and_routing/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/navigation_and_routing/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png new file mode 100644 index 000000000..e71a72613 Binary files /dev/null and b/navigation_and_routing/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png differ diff --git a/navigation_and_routing/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/navigation_and_routing/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png new file mode 100644 index 000000000..8a31fe2dd Binary files /dev/null and b/navigation_and_routing/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png differ diff --git a/navigation_and_routing/macos/Runner/Base.lproj/MainMenu.xib b/navigation_and_routing/macos/Runner/Base.lproj/MainMenu.xib new file mode 100644 index 000000000..537341abf --- /dev/null +++ b/navigation_and_routing/macos/Runner/Base.lproj/MainMenu.xib @@ -0,0 +1,339 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/navigation_and_routing/macos/Runner/Configs/AppInfo.xcconfig b/navigation_and_routing/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 000000000..385713a56 --- /dev/null +++ b/navigation_and_routing/macos/Runner/Configs/AppInfo.xcconfig @@ -0,0 +1,14 @@ +// Application-level settings for the Runner target. +// +// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the +// future. If not, the values below would default to using the project name when this becomes a +// 'flutter create' template. + +// The application's name. By default this is also the title of the Flutter window. +PRODUCT_NAME = bookstore + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.bookstore + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2021 dev.flutter. All rights reserved. diff --git a/navigation_and_routing/macos/Runner/Configs/Debug.xcconfig b/navigation_and_routing/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 000000000..36b0fd946 --- /dev/null +++ b/navigation_and_routing/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/navigation_and_routing/macos/Runner/Configs/Release.xcconfig b/navigation_and_routing/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 000000000..dff4f4956 --- /dev/null +++ b/navigation_and_routing/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/navigation_and_routing/macos/Runner/Configs/Warnings.xcconfig b/navigation_and_routing/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 000000000..42bcbf478 --- /dev/null +++ b/navigation_and_routing/macos/Runner/Configs/Warnings.xcconfig @@ -0,0 +1,13 @@ +WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings +GCC_WARN_UNDECLARED_SELECTOR = YES +CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES +CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE +CLANG_WARN__DUPLICATE_METHOD_MATCH = YES +CLANG_WARN_PRAGMA_PACK = YES +CLANG_WARN_STRICT_PROTOTYPES = YES +CLANG_WARN_COMMA = YES +GCC_WARN_STRICT_SELECTOR_MATCH = YES +CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES +CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES +GCC_WARN_SHADOW = YES +CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/navigation_and_routing/macos/Runner/DebugProfile.entitlements b/navigation_and_routing/macos/Runner/DebugProfile.entitlements new file mode 100644 index 000000000..dddb8a30c --- /dev/null +++ b/navigation_and_routing/macos/Runner/DebugProfile.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.server + + + diff --git a/navigation_and_routing/macos/Runner/Info.plist b/navigation_and_routing/macos/Runner/Info.plist new file mode 100644 index 000000000..4789daa6a --- /dev/null +++ b/navigation_and_routing/macos/Runner/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + $(PRODUCT_COPYRIGHT) + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/navigation_and_routing/macos/Runner/MainFlutterWindow.swift b/navigation_and_routing/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 000000000..2722837ec --- /dev/null +++ b/navigation_and_routing/macos/Runner/MainFlutterWindow.swift @@ -0,0 +1,15 @@ +import Cocoa +import FlutterMacOS + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController.init() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } +} diff --git a/navigation_and_routing/macos/Runner/Release.entitlements b/navigation_and_routing/macos/Runner/Release.entitlements new file mode 100644 index 000000000..852fa1a47 --- /dev/null +++ b/navigation_and_routing/macos/Runner/Release.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.app-sandbox + + + diff --git a/navigation_and_routing/lib/navigator/anonymous_routes.dart b/navigation_and_routing/old_snippets/navigator/anonymous_routes.dart similarity index 76% rename from navigation_and_routing/lib/navigator/anonymous_routes.dart rename to navigation_and_routing/old_snippets/navigator/anonymous_routes.dart index 139d7b06a..bfd00cfa7 100644 --- a/navigation_and_routing/lib/navigator/anonymous_routes.dart +++ b/navigation_and_routing/old_snippets/navigator/anonymous_routes.dart @@ -9,31 +9,35 @@ library anonymous_routes; import 'package:flutter/material.dart'; void main() { - runApp(Nav2App()); + runApp(const Nav2App()); } class Nav2App extends StatelessWidget { + const Nav2App({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { - return MaterialApp( + return const MaterialApp( home: HomeScreen(), ); } } class HomeScreen extends StatelessWidget { + const HomeScreen({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(), body: Center( child: TextButton( - child: Text('View Details'), + child: const Text('View Details'), onPressed: () { - Navigator.push( + Navigator.push( context, MaterialPageRoute(builder: (context) { - return DetailScreen(); + return const DetailScreen(); }), ); }, @@ -44,13 +48,15 @@ class HomeScreen extends StatelessWidget { } class DetailScreen extends StatelessWidget { + const DetailScreen({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(), body: Center( child: TextButton( - child: Text('Pop!'), + child: const Text('Pop!'), onPressed: () { Navigator.pop(context); }, diff --git a/navigation_and_routing/lib/navigator/named_routes.dart b/navigation_and_routing/old_snippets/navigator/named_routes.dart similarity index 77% rename from navigation_and_routing/lib/navigator/named_routes.dart rename to navigation_and_routing/old_snippets/navigator/named_routes.dart index 3271b3a6a..58ebc2b4e 100644 --- a/navigation_and_routing/lib/navigator/named_routes.dart +++ b/navigation_and_routing/old_snippets/navigator/named_routes.dart @@ -9,29 +9,33 @@ library named_routes; import 'package:flutter/material.dart'; void main() { - runApp(Nav2App()); + runApp(const Nav2App()); } class Nav2App extends StatelessWidget { + const Nav2App({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( routes: { - '/': (context) => HomeScreen(), - '/details': (context) => DetailScreen(), + '/': (context) => const HomeScreen(), + '/details': (context) => const DetailScreen(), }, ); } } class HomeScreen extends StatelessWidget { + const HomeScreen({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(), body: Center( child: TextButton( - child: Text('View Details'), + child: const Text('View Details'), onPressed: () { Navigator.pushNamed( context, @@ -45,13 +49,15 @@ class HomeScreen extends StatelessWidget { } class DetailScreen extends StatelessWidget { + const DetailScreen({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(), body: Center( child: TextButton( - child: Text('Pop!'), + child: const Text('Pop!'), onPressed: () { Navigator.pop(context); }, diff --git a/navigation_and_routing/lib/navigator/on_generate_route.dart b/navigation_and_routing/old_snippets/navigator/on_generate_route.dart similarity index 72% rename from navigation_and_routing/lib/navigator/on_generate_route.dart rename to navigation_and_routing/old_snippets/navigator/on_generate_route.dart index e9fa918b9..467a59ae0 100644 --- a/navigation_and_routing/lib/navigator/on_generate_route.dart +++ b/navigation_and_routing/old_snippets/navigator/on_generate_route.dart @@ -9,41 +9,48 @@ library on_generate_route; import 'package:flutter/material.dart'; void main() { - runApp(Nav2App()); + runApp(const Nav2App()); } class Nav2App extends StatelessWidget { + const Nav2App({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( onGenerateRoute: (settings) { // Handle '/' if (settings.name == '/') { - return MaterialPageRoute(builder: (context) => HomeScreen()); + return MaterialPageRoute( + builder: (context) => const HomeScreen()); } // Handle '/details/:id' - var uri = Uri.parse(settings.name); + var uri = Uri.parse(settings.name ?? ''); if (uri.pathSegments.length == 2 && uri.pathSegments.first == 'details') { var id = uri.pathSegments[1]; - return MaterialPageRoute(builder: (context) => DetailScreen(id: id)); + return MaterialPageRoute( + builder: (context) => DetailScreen(id: id)); } - return MaterialPageRoute(builder: (context) => UnknownScreen()); + return MaterialPageRoute( + builder: (context) => const UnknownScreen()); }, ); } } class HomeScreen extends StatelessWidget { + const HomeScreen({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(), body: Center( child: TextButton( - child: Text('View Details'), + child: const Text('View Details'), onPressed: () { Navigator.pushNamed( context, @@ -59,9 +66,10 @@ class HomeScreen extends StatelessWidget { class DetailScreen extends StatelessWidget { final String id; - DetailScreen({ - this.id, - }); + const DetailScreen({ + required this.id, + Key? key, + }) : super(key: key); @override Widget build(BuildContext context) { @@ -73,7 +81,7 @@ class DetailScreen extends StatelessWidget { children: [ Text('Viewing details for item $id'), TextButton( - child: Text('Pop!'), + child: const Text('Pop!'), onPressed: () { Navigator.pop(context); }, @@ -86,11 +94,13 @@ class DetailScreen extends StatelessWidget { } class UnknownScreen extends StatelessWidget { + const UnknownScreen({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(), - body: Center( + body: const Center( child: Text('404!'), ), ); diff --git a/navigation_and_routing/lib/router/pages.dart b/navigation_and_routing/old_snippets/router/pages.dart similarity index 78% rename from navigation_and_routing/lib/router/pages.dart rename to navigation_and_routing/old_snippets/router/pages.dart index 2d77b9b3a..fae931907 100644 --- a/navigation_and_routing/lib/router/pages.dart +++ b/navigation_and_routing/old_snippets/router/pages.dart @@ -8,7 +8,7 @@ library nav2_pages; import 'package:flutter/material.dart'; void main() { - runApp(BooksApp()); + runApp(const BooksApp()); } class Book { @@ -19,12 +19,14 @@ class Book { } class BooksApp extends StatefulWidget { + const BooksApp({Key? key}) : super(key: key); + @override State createState() => _BooksAppState(); } class _BooksAppState extends State { - Book _selectedBook; + Book? _selectedBook; final List books = [ Book('Left Hand of Darkness', 'Ursula K. Le Guin'), @@ -38,8 +40,8 @@ class _BooksAppState extends State { title: 'Books App', home: Navigator( pages: [ - MaterialPage( - key: ValueKey('BooksListPage'), + MaterialPage( + key: const ValueKey('BooksListPage'), child: BooksListScreen( books: books, onTapped: _handleBookTapped, @@ -47,7 +49,7 @@ class _BooksAppState extends State { ), if (_selectedBook != null) BookDetailsPage(book: _selectedBook) ], - onPopPage: (route, result) { + onPopPage: (route, dynamic result) { if (!route.didPop(result)) { return false; } @@ -70,17 +72,18 @@ class _BooksAppState extends State { } } -class BookDetailsPage extends Page { - final Book book; +class BookDetailsPage extends Page { + final Book? book; BookDetailsPage({ this.book, }) : super(key: ValueKey(book)); + @override Route createRoute(BuildContext context) { - return MaterialPageRoute( + return MaterialPageRoute( settings: this, - builder: (BuildContext context) { + builder: (context) { return BookDetailsScreen(book: book); }, ); @@ -91,10 +94,11 @@ class BooksListScreen extends StatelessWidget { final List books; final ValueChanged onTapped; - BooksListScreen({ - @required this.books, - @required this.onTapped, - }); + const BooksListScreen({ + required this.books, + required this.onTapped, + Key? key, + }) : super(key: key); @override Widget build(BuildContext context) { @@ -115,11 +119,12 @@ class BooksListScreen extends StatelessWidget { } class BookDetailsScreen extends StatelessWidget { - final Book book; + final Book? book; - BookDetailsScreen({ + const BookDetailsScreen({ @required this.book, - }); + Key? key, + }) : super(key: key); @override Widget build(BuildContext context) { @@ -131,8 +136,8 @@ class BookDetailsScreen extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ if (book != null) ...[ - Text(book.title, style: Theme.of(context).textTheme.headline6), - Text(book.author, style: Theme.of(context).textTheme.subtitle1), + Text(book!.title, style: Theme.of(context).textTheme.headline6), + Text(book!.author, style: Theme.of(context).textTheme.subtitle1), ], ], ), diff --git a/navigation_and_routing/lib/router/router.dart b/navigation_and_routing/old_snippets/router/router.dart similarity index 71% rename from navigation_and_routing/lib/router/router.dart rename to navigation_and_routing/old_snippets/router/router.dart index ca1384c72..bdceb73fe 100644 --- a/navigation_and_routing/lib/router/router.dart +++ b/navigation_and_routing/old_snippets/router/router.dart @@ -9,7 +9,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; void main() { - runApp(BooksApp()); + runApp(const BooksApp()); } class Book { @@ -20,13 +20,15 @@ class Book { } class BooksApp extends StatefulWidget { + const BooksApp({Key? key}) : super(key: key); + @override State createState() => _BooksAppState(); } class _BooksAppState extends State { - BookRouterDelegate _routerDelegate = BookRouterDelegate(); - BookRouteInformationParser _routeInformationParser = + final BookRouterDelegate _routerDelegate = BookRouterDelegate(); + final BookRouteInformationParser _routeInformationParser = BookRouteInformationParser(); @override @@ -44,9 +46,9 @@ class BookRouteInformationParser extends RouteInformationParser { Future parseRouteInformation( RouteInformation routeInformation, ) { - final uri = Uri.parse(routeInformation.location); + final uri = Uri.parse(routeInformation.location ?? ''); // Handle '/' - if (uri.pathSegments.length == 0) { + if (uri.pathSegments.isEmpty) { return SynchronousFuture(BookRoutePath.home()); } @@ -64,15 +66,15 @@ class BookRouteInformationParser extends RouteInformationParser { } @override - RouteInformation restoreRouteInformation(BookRoutePath path) { - if (path.isUnknown) { - return RouteInformation(location: '/404'); + RouteInformation? restoreRouteInformation(BookRoutePath configuration) { + if (configuration.isUnknown) { + return const RouteInformation(location: '/404'); } - if (path.isHomePage) { - return RouteInformation(location: '/'); + if (configuration.isHomePage) { + return const RouteInformation(location: '/'); } - if (path.isDetailsPage) { - return RouteInformation(location: '/book/${path.id}'); + if (configuration.isDetailsPage) { + return RouteInformation(location: '/book/${configuration.id}'); } return null; } @@ -80,9 +82,10 @@ class BookRouteInformationParser extends RouteInformationParser { class BookRouterDelegate extends RouterDelegate with ChangeNotifier, PopNavigatorRouterDelegateMixin { + @override final GlobalKey navigatorKey; - Book _selectedBook; + Book? _selectedBook; bool show404 = false; final List books = [ @@ -93,13 +96,14 @@ class BookRouterDelegate extends RouterDelegate BookRouterDelegate() : navigatorKey = GlobalKey(); + @override BookRoutePath get currentConfiguration { if (show404) { return BookRoutePath.unknown(); } return _selectedBook == null ? BookRoutePath.home() - : BookRoutePath.details(books.indexOf(_selectedBook)); + : BookRoutePath.details(books.indexOf(_selectedBook!)); } @override @@ -107,19 +111,22 @@ class BookRouterDelegate extends RouterDelegate return Navigator( key: navigatorKey, pages: [ - MaterialPage( - key: ValueKey('BooksListPage'), + MaterialPage( + key: const ValueKey('BooksListPage'), child: BooksListScreen( books: books, onTapped: _handleBookTapped, ), ), if (show404) - MaterialPage(key: ValueKey('UnknownPage'), child: UnknownScreen()) + const MaterialPage( + key: ValueKey('UnknownPage'), + child: UnknownScreen(), + ) else if (_selectedBook != null) - BookDetailsPage(book: _selectedBook) + BookDetailsPage(book: _selectedBook!) ], - onPopPage: (route, result) { + onPopPage: (route, dynamic result) { if (!route.didPop(result)) { return false; } @@ -135,20 +142,20 @@ class BookRouterDelegate extends RouterDelegate } @override - Future setNewRoutePath(BookRoutePath path) { - if (path.isUnknown) { + Future setNewRoutePath(BookRoutePath configuration) { + if (configuration.isUnknown) { _selectedBook = null; show404 = true; return SynchronousFuture(null); } - if (path.isDetailsPage) { - if (path.id < 0 || path.id > books.length - 1) { + if (configuration.isDetailsPage) { + if (configuration.id! < 0 || configuration.id! > books.length - 1) { show404 = true; return SynchronousFuture(null); } - _selectedBook = books[path.id]; + _selectedBook = books[configuration.id!]; } else { _selectedBook = null; } @@ -163,17 +170,18 @@ class BookRouterDelegate extends RouterDelegate } } -class BookDetailsPage extends Page { +class BookDetailsPage extends Page { final Book book; BookDetailsPage({ - this.book, + required this.book, }) : super(key: ValueKey(book)); + @override Route createRoute(BuildContext context) { - return MaterialPageRoute( + return MaterialPageRoute( settings: this, - builder: (BuildContext context) { + builder: (context) { return BookDetailsScreen(book: book); }, ); @@ -181,7 +189,7 @@ class BookDetailsPage extends Page { } class BookRoutePath { - final int id; + final int? id; final bool isUnknown; BookRoutePath.home() @@ -203,10 +211,11 @@ class BooksListScreen extends StatelessWidget { final List books; final ValueChanged onTapped; - BooksListScreen({ - @required this.books, - @required this.onTapped, - }); + const BooksListScreen({ + required this.books, + required this.onTapped, + Key? key, + }) : super(key: key); @override Widget build(BuildContext context) { @@ -229,9 +238,10 @@ class BooksListScreen extends StatelessWidget { class BookDetailsScreen extends StatelessWidget { final Book book; - BookDetailsScreen({ - @required this.book, - }); + const BookDetailsScreen({ + required this.book, + Key? key, + }) : super(key: key); @override Widget build(BuildContext context) { @@ -242,10 +252,8 @@ class BookDetailsScreen extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (book != null) ...[ - Text(book.title, style: Theme.of(context).textTheme.headline6), - Text(book.author, style: Theme.of(context).textTheme.subtitle1), - ], + Text(book.title, style: Theme.of(context).textTheme.headline6), + Text(book.author, style: Theme.of(context).textTheme.subtitle1), ], ), ), @@ -254,11 +262,13 @@ class BookDetailsScreen extends StatelessWidget { } class UnknownScreen extends StatelessWidget { + const UnknownScreen({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(), - body: Center( + body: const Center( child: Text('404!'), ), ); diff --git a/navigation_and_routing/lib/router_advanced/nested_router.dart b/navigation_and_routing/old_snippets/router_advanced/nested_router.dart similarity index 80% rename from navigation_and_routing/lib/router_advanced/nested_router.dart rename to navigation_and_routing/old_snippets/router_advanced/nested_router.dart index 67169c90f..117b3f45a 100644 --- a/navigation_and_routing/lib/router_advanced/nested_router.dart +++ b/navigation_and_routing/old_snippets/router_advanced/nested_router.dart @@ -10,7 +10,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; void main() { - runApp(NestedRouterDemo()); + runApp(const NestedRouterDemo()); } class Book { @@ -21,13 +21,15 @@ class Book { } class NestedRouterDemo extends StatefulWidget { + const NestedRouterDemo({Key? key}) : super(key: key); + @override _NestedRouterDemoState createState() => _NestedRouterDemoState(); } class _NestedRouterDemoState extends State { - BookRouterDelegate _routerDelegate = BookRouterDelegate(); - BookRouteInformationParser _routeInformationParser = + final BookRouterDelegate _routerDelegate = BookRouterDelegate(); + final BookRouteInformationParser _routeInformationParser = BookRouteInformationParser(); @override @@ -43,7 +45,7 @@ class _NestedRouterDemoState extends State { class BooksAppState extends ChangeNotifier { int _selectedIndex; - Book _selectedBook; + Book? _selectedBook; final List books = [ Book('Left Hand of Darkness', 'Ursula K. Le Guin'), @@ -66,16 +68,17 @@ class BooksAppState extends ChangeNotifier { notifyListeners(); } - Book get selectedBook => _selectedBook; + Book? get selectedBook => _selectedBook; - set selectedBook(Book book) { + set selectedBook(Book? book) { _selectedBook = book; notifyListeners(); } int getSelectedBookById() { + if (_selectedBook == null) return 0; if (!books.contains(_selectedBook)) return 0; - return books.indexOf(_selectedBook); + return books.indexOf(_selectedBook!); } void setSelectedBookById(int id) { @@ -93,14 +96,14 @@ class BookRouteInformationParser extends RouteInformationParser { Future parseRouteInformation( RouteInformation routeInformation, ) { - final uri = Uri.parse(routeInformation.location); + final uri = Uri.parse(routeInformation.location ?? ''); if (uri.pathSegments.isNotEmpty && uri.pathSegments.first == 'settings') { return SynchronousFuture(BooksSettingsPath()); } else { if (uri.pathSegments.length >= 2 && uri.pathSegments[0] == 'book') { return SynchronousFuture( - BooksDetailsPath(int.tryParse(uri.pathSegments[1])), + BooksDetailsPath(int.tryParse(uri.pathSegments[1])!), ); } return SynchronousFuture(BooksListPath()); @@ -108,12 +111,12 @@ class BookRouteInformationParser extends RouteInformationParser { } @override - RouteInformation restoreRouteInformation(BookRoutePath configuration) { + RouteInformation? restoreRouteInformation(BookRoutePath configuration) { if (configuration is BooksListPath) { - return RouteInformation(location: '/home'); + return const RouteInformation(location: '/home'); } if (configuration is BooksSettingsPath) { - return RouteInformation(location: '/settings'); + return const RouteInformation(location: '/settings'); } if (configuration is BooksDetailsPath) { return RouteInformation(location: '/book/${configuration.id}'); @@ -124,6 +127,7 @@ class BookRouteInformationParser extends RouteInformationParser { class BookRouterDelegate extends RouterDelegate with ChangeNotifier, PopNavigatorRouterDelegateMixin { + @override final GlobalKey navigatorKey; BooksAppState appState = BooksAppState(); @@ -132,6 +136,7 @@ class BookRouterDelegate extends RouterDelegate appState.addListener(notifyListeners); } + @override BookRoutePath get currentConfiguration { if (appState.selectedIndex == 1) { return BooksSettingsPath(); @@ -149,11 +154,11 @@ class BookRouterDelegate extends RouterDelegate return Navigator( key: navigatorKey, pages: [ - MaterialPage( + MaterialPage( child: AppShell(appState: appState), ), ], - onPopPage: (route, result) { + onPopPage: (route, dynamic result) { if (!route.didPop(result)) { return false; } @@ -168,14 +173,14 @@ class BookRouterDelegate extends RouterDelegate } @override - Future setNewRoutePath(BookRoutePath path) { - if (path is BooksListPath) { + Future setNewRoutePath(BookRoutePath configuration) { + if (configuration is BooksListPath) { appState.selectedIndex = 0; appState.selectedBook = null; - } else if (path is BooksSettingsPath) { + } else if (configuration is BooksSettingsPath) { appState.selectedIndex = 1; - } else if (path is BooksDetailsPath) { - appState.setSelectedBookById(path.id); + } else if (configuration is BooksDetailsPath) { + appState.setSelectedBookById(configuration.id); } return SynchronousFuture(null); } @@ -198,18 +203,20 @@ class BooksDetailsPath extends BookRoutePath { class AppShell extends StatefulWidget { final BooksAppState appState; - AppShell({ - @required this.appState, - }); + const AppShell({ + required this.appState, + Key? key, + }) : super(key: key); @override _AppShellState createState() => _AppShellState(); } class _AppShellState extends State { - InnerRouterDelegate _routerDelegate; - ChildBackButtonDispatcher _backButtonDispatcher; + late final InnerRouterDelegate _routerDelegate; + late final ChildBackButtonDispatcher _backButtonDispatcher; + @override void initState() { super.initState(); _routerDelegate = InnerRouterDelegate(widget.appState); @@ -226,7 +233,7 @@ class _AppShellState extends State { super.didChangeDependencies(); // Defer back button dispatching to the child router _backButtonDispatcher = Router.of(context) - .backButtonDispatcher + .backButtonDispatcher! .createChildBackButtonDispatcher(); } @@ -245,7 +252,7 @@ class _AppShellState extends State { backButtonDispatcher: _backButtonDispatcher, ), bottomNavigationBar: BottomNavigationBar( - items: [ + items: const [ BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'), BottomNavigationBarItem( icon: Icon(Icons.settings), label: 'Settings'), @@ -261,9 +268,12 @@ class _AppShellState extends State { class InnerRouterDelegate extends RouterDelegate with ChangeNotifier, PopNavigatorRouterDelegateMixin { + @override final GlobalKey navigatorKey = GlobalKey(); + BooksAppState get appState => _appState; BooksAppState _appState; + set appState(BooksAppState value) { if (value == _appState) { return; @@ -285,20 +295,20 @@ class InnerRouterDelegate extends RouterDelegate books: appState.books, onTapped: _handleBookTapped, ), - key: ValueKey('BooksListPage'), + key: const ValueKey('BooksListPage'), ), if (appState.selectedBook != null) - MaterialPage( + MaterialPage( key: ValueKey(appState.selectedBook), - child: BookDetailsScreen(book: appState.selectedBook), + child: BookDetailsScreen(book: appState.selectedBook!), ), ] else - FadeAnimationPage( + const FadeAnimationPage( child: SettingsScreen(), key: ValueKey('SettingsPage'), ), ], - onPopPage: (route, result) { + onPopPage: (route, dynamic result) { appState.selectedBook = null; notifyListeners(); return route.didPop(result); @@ -307,7 +317,7 @@ class InnerRouterDelegate extends RouterDelegate } @override - Future setNewRoutePath(BookRoutePath path) { + Future setNewRoutePath(BookRoutePath configuration) { // This is not required for inner router delegate because it does not // parse route assert(false); @@ -320,13 +330,15 @@ class InnerRouterDelegate extends RouterDelegate } } -class FadeAnimationPage extends Page { +class FadeAnimationPage extends Page { final Widget child; - FadeAnimationPage({Key key, this.child}) : super(key: key); + const FadeAnimationPage({LocalKey? key, required this.child}) + : super(key: key); + @override Route createRoute(BuildContext context) { - return PageRouteBuilder( + return PageRouteBuilder( settings: this, pageBuilder: (context, animation, animation2) { var curveTween = CurveTween(curve: Curves.easeIn); @@ -344,10 +356,11 @@ class BooksListScreen extends StatelessWidget { final List books; final ValueChanged onTapped; - BooksListScreen({ - @required this.books, - @required this.onTapped, - }); + const BooksListScreen({ + required this.books, + required this.onTapped, + Key? key, + }) : super(key: key); @override Widget build(BuildContext context) { @@ -369,9 +382,10 @@ class BooksListScreen extends StatelessWidget { class BookDetailsScreen extends StatelessWidget { final Book book; - BookDetailsScreen({ - @required this.book, - }); + const BookDetailsScreen({ + required this.book, + Key? key, + }) : super(key: key); @override Widget build(BuildContext context) { @@ -385,9 +399,9 @@ class BookDetailsScreen extends StatelessWidget { onPressed: () { Navigator.of(context).pop(); }, - child: Text('Back'), + child: const Text('Back'), ), - if (book != null) ...[ + ...[ Text(book.title, style: Theme.of(context).textTheme.headline6), Text(book.author, style: Theme.of(context).textTheme.subtitle1), ], @@ -399,9 +413,11 @@ class BookDetailsScreen extends StatelessWidget { } class SettingsScreen extends StatelessWidget { + const SettingsScreen({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { - return Scaffold( + return const Scaffold( body: Center( child: Text('Settings screen'), ), diff --git a/navigation_and_routing/lib/router_advanced/transition_delegate.dart b/navigation_and_routing/old_snippets/router_advanced/transition_delegate.dart similarity index 75% rename from navigation_and_routing/lib/router_advanced/transition_delegate.dart rename to navigation_and_routing/old_snippets/router_advanced/transition_delegate.dart index 94f190c4a..98a43cb38 100644 --- a/navigation_and_routing/lib/router_advanced/transition_delegate.dart +++ b/navigation_and_routing/old_snippets/router_advanced/transition_delegate.dart @@ -10,7 +10,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; void main() { - runApp(BooksApp()); + runApp(const BooksApp()); } class Book { @@ -21,13 +21,15 @@ class Book { } class BooksApp extends StatefulWidget { + const BooksApp({Key? key}) : super(key: key); + @override State createState() => _BooksAppState(); } class _BooksAppState extends State { - BookRouterDelegate _routerDelegate = BookRouterDelegate(); - BookRouteInformationParser _routeInformationParser = + final BookRouterDelegate _routerDelegate = BookRouterDelegate(); + final BookRouteInformationParser _routeInformationParser = BookRouteInformationParser(); @override @@ -45,23 +47,23 @@ class BookRouteInformationParser extends RouteInformationParser { Future parseRouteInformation( RouteInformation routeInformation, ) { - final uri = Uri.parse(routeInformation.location); + final uri = Uri.parse(routeInformation.location ?? ''); if (uri.pathSegments.length >= 2) { var remaining = uri.pathSegments[1]; - return SynchronousFuture(BookRoutePath.details(int.tryParse(remaining))); + return SynchronousFuture(BookRoutePath.details(int.tryParse(remaining)!)); } else { return SynchronousFuture(BookRoutePath.home()); } } @override - RouteInformation restoreRouteInformation(BookRoutePath path) { - if (path.isHomePage) { - return RouteInformation(location: '/'); + RouteInformation? restoreRouteInformation(BookRoutePath configuration) { + if (configuration.isHomePage) { + return const RouteInformation(location: '/'); } - if (path.isDetailsPage) { - return RouteInformation(location: '/book/${path.id}'); + if (configuration.isDetailsPage) { + return RouteInformation(location: '/book/${configuration.id}'); } return null; } @@ -69,9 +71,10 @@ class BookRouteInformationParser extends RouteInformationParser { class BookRouterDelegate extends RouterDelegate with ChangeNotifier, PopNavigatorRouterDelegateMixin { + @override final GlobalKey navigatorKey; - Book _selectedBook; + Book? _selectedBook; final List books = [ Book('Left Hand of Darkness', 'Ursula K. Le Guin'), @@ -81,9 +84,10 @@ class BookRouterDelegate extends RouterDelegate BookRouterDelegate() : navigatorKey = GlobalKey(); + @override BookRoutePath get currentConfiguration => _selectedBook == null ? BookRoutePath.home() - : BookRoutePath.details(books.indexOf(_selectedBook)); + : BookRoutePath.details(books.indexOf(_selectedBook!)); @override Widget build(BuildContext context) { @@ -91,16 +95,16 @@ class BookRouterDelegate extends RouterDelegate key: navigatorKey, transitionDelegate: NoAnimationTransitionDelegate(), pages: [ - MaterialPage( - key: ValueKey('BooksListPage'), + MaterialPage( + key: const ValueKey('BooksListPage'), child: BooksListScreen( books: books, onTapped: _handleBookTapped, ), ), - if (_selectedBook != null) BookDetailsPage(book: _selectedBook) + if (_selectedBook != null) BookDetailsPage(book: _selectedBook!) ], - onPopPage: (route, result) { + onPopPage: (route, dynamic result) { if (!route.didPop(result)) { return false; } @@ -115,9 +119,9 @@ class BookRouterDelegate extends RouterDelegate } @override - Future setNewRoutePath(BookRoutePath path) { - if (path.isDetailsPage) { - _selectedBook = books[path.id]; + Future setNewRoutePath(BookRoutePath configuration) { + if (configuration.isDetailsPage) { + _selectedBook = books[configuration.id!]; } return SynchronousFuture(null); } @@ -128,17 +132,18 @@ class BookRouterDelegate extends RouterDelegate } } -class BookDetailsPage extends Page { +class BookDetailsPage extends Page { final Book book; BookDetailsPage({ - this.book, + required this.book, }) : super(key: ValueKey(book)); + @override Route createRoute(BuildContext context) { - return MaterialPageRoute( + return MaterialPageRoute( settings: this, - builder: (BuildContext context) { + builder: (context) { return BookDetailsScreen(book: book); }, ); @@ -146,7 +151,7 @@ class BookDetailsPage extends Page { } class BookRoutePath { - final int id; + final int? id; BookRoutePath.home() : id = null; @@ -161,10 +166,11 @@ class BooksListScreen extends StatelessWidget { final List books; final ValueChanged onTapped; - BooksListScreen({ - @required this.books, - @required this.onTapped, - }); + const BooksListScreen({ + required this.books, + required this.onTapped, + Key? key, + }) : super(key: key); @override Widget build(BuildContext context) { @@ -187,9 +193,10 @@ class BooksListScreen extends StatelessWidget { class BookDetailsScreen extends StatelessWidget { final Book book; - BookDetailsScreen({ - @required this.book, - }); + const BookDetailsScreen({ + required this.book, + Key? key, + }) : super(key: key); @override Widget build(BuildContext context) { @@ -200,10 +207,8 @@ class BookDetailsScreen extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (book != null) ...[ - Text(book.title, style: Theme.of(context).textTheme.headline6), - Text(book.author, style: Theme.of(context).textTheme.subtitle1), - ], + Text(book.title, style: Theme.of(context).textTheme.headline6), + Text(book.author, style: Theme.of(context).textTheme.subtitle1), ], ), ), @@ -214,10 +219,10 @@ class BookDetailsScreen extends StatelessWidget { class NoAnimationTransitionDelegate extends TransitionDelegate { @override Iterable resolve({ - List newPageRouteHistory, - Map + required List newPageRouteHistory, + required Map locationToExitingPageRoute, - Map> + required Map> pageRouteToPagelessRoutes, }) { final results = []; diff --git a/navigation_and_routing/pubspec.lock b/navigation_and_routing/pubspec.lock index 7ffced4ee..7296d7a5a 100644 --- a/navigation_and_routing/pubspec.lock +++ b/navigation_and_routing/pubspec.lock @@ -1,6 +1,41 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + url: "https://pub.dartlang.org" + source: hosted + version: "22.0.0" + adaptive_breakpoints: + dependency: transitive + description: + name: adaptive_breakpoints + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.4" + adaptive_navigation: + dependency: "direct main" + description: + name: adaptive_navigation + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.3" + analyzer: + dependency: transitive + description: + name: analyzer + url: "https://pub.dartlang.org" + source: hosted + version: "1.7.1" + args: + dependency: transitive + description: + name: args + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" async: dependency: transitive description: @@ -29,6 +64,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.2.0" + cli_util: + dependency: transitive + description: + name: cli_util + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.0" clock: dependency: transitive description: @@ -37,12 +79,33 @@ packages: source: hosted version: "1.1.0" collection: - dependency: transitive + dependency: "direct main" description: name: collection url: "https://pub.dartlang.org" source: hosted version: "1.15.0" + convert: + dependency: transitive + description: + name: convert + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" + coverage: + dependency: transitive + description: + name: coverage + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.3" + crypto: + dependency: transitive + description: + name: crypto + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" cupertino_icons: dependency: "direct main" description: @@ -57,16 +120,84 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.2.0" + file: + dependency: transitive + description: + name: file + url: "https://pub.dartlang.org" + source: hosted + version: "6.1.1" flutter: dependency: "direct main" description: flutter source: sdk version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.3" flutter_test: dependency: "direct dev" description: flutter source: sdk version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + glob: + dependency: transitive + description: + name: glob + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" + http_parser: + dependency: transitive + description: + name: http_parser + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.0" + io: + dependency: transitive + description: + name: io + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + js: + dependency: transitive + description: + name: js + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.3" + lints: + dependency: transitive + description: + name: lints + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + logging: + dependency: transitive + description: + name: logging + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" matcher: dependency: transitive description: @@ -81,6 +212,27 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.3.0" + mime: + dependency: transitive + description: + name: mime + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + node_preamble: + dependency: transitive + description: + name: node_preamble + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + package_config: + dependency: transitive + description: + name: package_config + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" path: dependency: transitive description: @@ -88,11 +240,95 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.8.0" + path_to_regexp: + dependency: "direct main" + description: + name: path_to_regexp + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.0" + pedantic: + dependency: transitive + description: + name: pedantic + url: "https://pub.dartlang.org" + source: hosted + version: "1.11.0" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + pool: + dependency: transitive + description: + name: pool + url: "https://pub.dartlang.org" + source: hosted + version: "1.5.0" + pub_semver: + dependency: transitive + description: + name: pub_semver + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + quiver: + dependency: "direct main" + description: + name: quiver + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" + shelf: + dependency: transitive + description: + name: shelf + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.4" + shelf_packages_handler: + dependency: transitive + description: + name: shelf_packages_handler + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" + shelf_static: + dependency: transitive + description: + name: shelf_static + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" sky_engine: dependency: transitive description: flutter source: sdk version: "0.0.99" + source_map_stack_trace: + dependency: transitive + description: + name: source_map_stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + source_maps: + dependency: transitive + description: + name: source_maps + url: "https://pub.dartlang.org" + source: hosted + version: "0.10.10" source_span: dependency: transitive description: @@ -128,6 +364,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.2.0" + test: + dependency: "direct dev" + description: + name: test + url: "https://pub.dartlang.org" + source: hosted + version: "1.16.8" test_api: dependency: transitive description: @@ -135,6 +378,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.3.0" + test_core: + dependency: transitive + description: + name: test_core + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.19" typed_data: dependency: transitive description: @@ -142,6 +392,55 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.3.0" + url_launcher: + dependency: "direct main" + description: + name: url_launcher + url: "https://pub.dartlang.org" + source: hosted + version: "6.0.6" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + url_launcher_macos: + dependency: transitive + description: + name: url_launcher_macos + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.3" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + url_strategy: + dependency: "direct main" + description: + name: url_strategy + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.0" vector_math: dependency: transitive description: @@ -149,5 +448,41 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.0" + vm_service: + dependency: transitive + description: + name: vm_service + url: "https://pub.dartlang.org" + source: hosted + version: "6.2.0" + watcher: + dependency: transitive + description: + name: watcher + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + webkit_inspection_protocol: + dependency: transitive + description: + name: webkit_inspection_protocol + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + yaml: + dependency: transitive + description: + name: yaml + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.0" sdks: dart: ">=2.12.0 <3.0.0" + flutter: ">=2.0.0" diff --git a/navigation_and_routing/pubspec.yaml b/navigation_and_routing/pubspec.yaml index 58ebefa8e..bcedeb647 100644 --- a/navigation_and_routing/pubspec.yaml +++ b/navigation_and_routing/pubspec.yaml @@ -1,20 +1,23 @@ -name: navigation_and_routing -description: Navigation and routing samples -publish_to: 'none' - +name: bookstore +description: Navigation and routing sample app +publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 - environment: - sdk: ">=2.10.0 <3.0.0" - + sdk: ">=2.12.0 <3.0.0" dependencies: flutter: sdk: flutter - cupertino_icons: ^1.0.0 - + cupertino_icons: ^1.0.2 + quiver: ^3.0.0 + path_to_regexp: ^0.4.0 + adaptive_navigation: ^0.0.3 + collection: ^1.15.0 + url_launcher: ^6.0.0 + url_strategy: ^0.2.0 dev_dependencies: + test: ^1.16.0 flutter_test: sdk: flutter - + flutter_lints: ^1.0.0 flutter: uses-material-design: true diff --git a/navigation_and_routing/test/library_test.dart b/navigation_and_routing/test/library_test.dart new file mode 100644 index 000000000..bdebca088 --- /dev/null +++ b/navigation_and_routing/test/library_test.dart @@ -0,0 +1,22 @@ +// Copyright 2021, the Flutter project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:bookstore/src/data/library.dart'; +import 'package:test/test.dart'; + +void main() { + group('Library', () { + test('addBook', () { + final library = Library(); + library.addBook('Left Hand of Darkness', 'Ursula K. Le Guin', true, true); + library.addBook('Too Like the Lightning', 'Ada Palmer', false, true); + library.addBook('Kindred', 'Octavia E. Butler', true, false); + library.addBook('The Lathe of Heaven', 'Ursula K. Le Guin', false, false); + expect(library.allAuthors.length, 3); + expect(library.allAuthors.first.books.length, 2); + expect(library.allBooks.length, 4); + expect(library.allBooks.first.author.name, startsWith('Ursula')); + }); + }); +} diff --git a/navigation_and_routing/test/widget_test.dart b/navigation_and_routing/test/widget_test.dart new file mode 100644 index 000000000..47a8fcac4 --- /dev/null +++ b/navigation_and_routing/test/widget_test.dart @@ -0,0 +1,9 @@ +// Copyright 2021, the Flutter project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:flutter_test/flutter_test.dart'; + +void main() { + testWidgets('empty test', (tester) async {}); +} diff --git a/navigation_and_routing/web/favicon.png b/navigation_and_routing/web/favicon.png new file mode 100644 index 000000000..8aaa46ac1 Binary files /dev/null and b/navigation_and_routing/web/favicon.png differ diff --git a/navigation_and_routing/web/icons/Icon-192.png b/navigation_and_routing/web/icons/Icon-192.png new file mode 100644 index 000000000..b749bfef0 Binary files /dev/null and b/navigation_and_routing/web/icons/Icon-192.png differ diff --git a/navigation_and_routing/web/icons/Icon-512.png b/navigation_and_routing/web/icons/Icon-512.png new file mode 100644 index 000000000..88cfd48df Binary files /dev/null and b/navigation_and_routing/web/icons/Icon-512.png differ diff --git a/navigation_and_routing/web/index.html b/navigation_and_routing/web/index.html new file mode 100644 index 000000000..cc580a6ed --- /dev/null +++ b/navigation_and_routing/web/index.html @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + bookstore + + + + + + + diff --git a/navigation_and_routing/web/manifest.json b/navigation_and_routing/web/manifest.json new file mode 100644 index 000000000..4b988813e --- /dev/null +++ b/navigation_and_routing/web/manifest.json @@ -0,0 +1,23 @@ +{ + "name": "bookstore", + "short_name": "bookstore", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "Navigation and routing sample app", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + } + ] +} diff --git a/web/_tool/.copyright b/web/_tool/.copyright new file mode 100644 index 000000000..3ee5e79c6 --- /dev/null +++ b/web/_tool/.copyright @@ -0,0 +1,3 @@ +// Copyright {{ year }} The Flutter team. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file