├── .gitignore ├── .metadata ├── .run ├── Color Helper.run.xml └── Theme Switcher.run.xml ├── README.md ├── analysis_options.yaml ├── android ├── .gitignore ├── app │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── flutter_tutorials │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable-v21 │ │ │ └── launch_background.xml │ │ │ ├── drawable │ │ │ └── launch_background.xml │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── values-night │ │ │ └── styles.xml │ │ │ └── values │ │ │ └── styles.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── assets └── images │ └── avatar.png ├── ios ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Podfile ├── Podfile.lock ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings └── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-App-1024x1024@1x.png │ │ ├── Icon-App-20x20@1x.png │ │ ├── Icon-App-20x20@2x.png │ │ ├── Icon-App-20x20@3x.png │ │ ├── Icon-App-29x29@1x.png │ │ ├── Icon-App-29x29@2x.png │ │ ├── Icon-App-29x29@3x.png │ │ ├── Icon-App-40x40@1x.png │ │ ├── Icon-App-40x40@2x.png │ │ ├── Icon-App-40x40@3x.png │ │ ├── Icon-App-60x60@2x.png │ │ ├── Icon-App-60x60@3x.png │ │ ├── Icon-App-76x76@1x.png │ │ ├── Icon-App-76x76@2x.png │ │ └── Icon-App-83.5x83.5@2x.png │ └── LaunchImage.imageset │ │ ├── Contents.json │ │ ├── LaunchImage.png │ │ ├── LaunchImage@2x.png │ │ ├── LaunchImage@3x.png │ │ └── README.md │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ └── Runner-Bridging-Header.h ├── lib ├── animated-cross-fade │ ├── pages │ │ └── animated_cross_fade_example.dart │ └── widgets │ │ ├── avatar_image.dart │ │ └── cover_image.dart ├── avatar-uploader-tutorial │ ├── data │ │ ├── services │ │ │ ├── media │ │ │ │ ├── media_service.dart │ │ │ │ └── media_service_interface.dart │ │ │ ├── permission │ │ │ │ ├── permission_handler_permission_service.dart │ │ │ │ └── permission_service.dart │ │ │ └── service_locator.dart │ │ └── utils.dart │ └── ui │ │ ├── file_upload_service_home_page.dart │ │ └── widgets │ │ ├── avatar_container.dart │ │ ├── avatar_uploader.dart │ │ └── image_picker_action_sheet.dart ├── common │ └── ui │ │ └── widgets │ │ ├── app_alert_dialog.dart │ │ └── bordered_container.dart ├── data │ ├── app_data.dart │ └── models │ │ ├── scroll_physics_data.dart │ │ └── tutorial.dart ├── main.dart ├── mains │ ├── main_color_helper.dart │ └── main_theme_switcher.dart ├── refresh-indicator-thread │ └── refresh_indicator_thread_home.dart ├── reorderable-list-view │ └── pages │ │ └── reorderable_list_view_page.dart ├── scroll-physics-thread │ ├── always_scrollable_scroll_physics_example.dart │ ├── bouncing_scroll_physics_example.dart │ ├── clamping_scroll_physics_example.dart │ ├── fixed_extent_scroll_physics_example.dart │ ├── never_scrollable_scroll_physics_example.dart │ └── scroll_physics_thread_home.dart └── ui │ ├── home │ ├── pages │ │ └── home_page.dart │ └── widgets │ │ ├── list_item.dart │ │ └── tutorial_list_item.dart │ └── ui-helper │ └── navigation_helper.dart ├── pubspec.yaml └── test └── widget_test.dart /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Android Studio will place build artifacts here 44 | /android/app/debug 45 | /android/app/profile 46 | /android/app/release 47 | 48 | pubspec.lock -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 18116933e77adc82f80866c928266a5b4f1ed645 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /.run/Color Helper.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.run/Theme Switcher.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flutter Tutorials 2 | 3 | This application contains source code of [tutorials I publish on Medium](https://medium.com/@roaa_k) as well as threads [I share on Twitter](https://twitter.com/roaakdm). Updated frequently with more tutorials as they're published. 4 | 5 | ## Tutorials List 6 | 7 | 1. [Flutter Image Uploader With App Permissions and Compression Using GetIt Services](https://medium.com/@roaa_k/flutter-image-uploader-with-app-permissions-and-compression-using-getit-services-59ffea13f913) 8 | 9 | Allow image upload in your Flutter app with one line of code in your widget while handling everything from camera & 10 | gallery permissions to image compression under the hood with GetIt and the service locator pattern. 11 | 12 | 2. [ToDo List With RefreshIndicator](https://twitter.com/roaakdm/status/1472181621759655939) 13 | 14 | Fetch a ToDo list from jsonplaceholder and use RefreshIndicator widget to refetch data 15 | 16 | 3. [ScrollPhysics Types With Examples](https://twitter.com/roaakdm/status/1473375063600779272) 17 | 18 | See all types of ScrollPhysics with example usages and code 19 | 20 | 4. [ReorderableListView Widget](https://twitter.com/roaakdm/status/1475957270064283652) 21 | 22 | Easily allow your users to reorder a list using the ReorderableListView widget 23 | 24 | 4. [AnimatedCrossFade Widget](https://twitter.com/roaakdm/status/1486306936274440192) 25 | 26 | Use the AnimatedCrossFade widget to apply smooth fade animation when switching between two widgets for a better user experience, instead of abruptly doing so 27 | 28 | 29 | 30 | https://user-images.githubusercontent.com/50345358/151159630-cfbaae2f-41d0-4577-a2fa-d380a69c3c12.mp4 31 | 32 | 33 | ## Minimum Requirements 34 | Dart Version 2.15 35 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at 17 | # https://dart-lang.github.io/linter/lints/index.html. 18 | # 19 | # Instead of disabling a lint rule for the entire project in the 20 | # section below, it can also be suppressed for a single line of code 21 | # or a specific dart file by using the `// ignore: name_of_lint` and 22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 23 | # producing the lint. 24 | rules: 25 | avoid_print: false # Uncomment to disable the `avoid_print` rule 26 | prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 27 | avoid_function_literals_in_foreach_calls: false 28 | use_key_in_widget_constructors: false 29 | 30 | # Additional information about this file can be found at 31 | # https://dart.dev/guides/language/analysis-options 32 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion 30 30 | 31 | compileOptions { 32 | sourceCompatibility JavaVersion.VERSION_1_8 33 | targetCompatibility JavaVersion.VERSION_1_8 34 | } 35 | 36 | kotlinOptions { 37 | jvmTarget = '1.8' 38 | } 39 | 40 | sourceSets { 41 | main.java.srcDirs += 'src/main/kotlin' 42 | } 43 | 44 | defaultConfig { 45 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 46 | applicationId "com.example.flutter_tutorials" 47 | minSdkVersion 16 48 | targetSdkVersion 30 49 | versionCode flutterVersionCode.toInteger() 50 | versionName flutterVersionName 51 | } 52 | 53 | buildTypes { 54 | release { 55 | // TODO: Add your own signing config for the release build. 56 | // Signing with the debug keys for now, so `flutter run --release` works. 57 | signingConfig signingConfigs.debug 58 | } 59 | } 60 | } 61 | 62 | flutter { 63 | source '../..' 64 | } 65 | 66 | dependencies { 67 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 68 | } 69 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 13 | 17 | 21 | 26 | 30 | 31 | 32 | 33 | 34 | 35 | 37 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/flutter_tutorials/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.flutter_tutorials 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roaa94/flutter-tutorials/616230fe440587aa83228b9422f22a784c6a6fb1/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roaa94/flutter-tutorials/616230fe440587aa83228b9422f22a784c6a6fb1/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roaa94/flutter-tutorials/616230fe440587aa83228b9422f22a784c6a6fb1/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roaa94/flutter-tutorials/616230fe440587aa83228b9422f22a784c6a6fb1/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roaa94/flutter-tutorials/616230fe440587aa83228b9422f22a784c6a6fb1/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.3.50' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:4.1.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | mavenCentral() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | project.evaluationDependsOn(':app') 25 | } 26 | 27 | task clean(type: Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip 7 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | -------------------------------------------------------------------------------- /assets/images/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roaa94/flutter-tutorials/616230fe440587aa83228b9422f22a784c6a6fb1/assets/images/avatar.png -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 9.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | end 36 | 37 | post_install do |installer| 38 | installer.pods_project.targets.each do |target| 39 | flutter_additional_ios_build_settings(target) 40 | target.build_configurations.each do |config| 41 | config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [ 42 | '$(inherited)', 43 | 44 | ## dart: PermissionGroup.calendar 45 | # 'PERMISSION_EVENTS=0', 46 | 47 | ## dart: PermissionGroup.reminders 48 | # 'PERMISSION_REMINDERS=0', 49 | 50 | ## dart: PermissionGroup.contacts 51 | # 'PERMISSION_CONTACTS=0', 52 | 53 | ## dart: PermissionGroup.camera 54 | 'PERMISSION_CAMERA=1', 55 | 56 | ## dart: PermissionGroup.microphone 57 | # 'PERMISSION_MICROPHONE=1', 58 | 59 | ## dart: PermissionGroup.speech 60 | # 'PERMISSION_SPEECH_RECOGNIZER=0', 61 | 62 | ## dart: PermissionGroup.photos 63 | 'PERMISSION_PHOTOS=1', 64 | 65 | ## dart: [PermissionGroup.location, PermissionGroup.locationAlways, PermissionGroup.locationWhenInUse] 66 | # 'PERMISSION_LOCATION=0', 67 | 68 | ## dart: PermissionGroup.notification 69 | # 'PERMISSION_NOTIFICATIONS=0', 70 | 71 | ## dart: PermissionGroup.mediaLibrary 72 | 'PERMISSION_MEDIA_LIBRARY=1', 73 | 74 | ## dart: PermissionGroup.sensors 75 | # 'PERMISSION_SENSORS=0' 76 | 77 | # 'AUDIO_SESSION_MICROPHONE=0', 78 | ] 79 | end 80 | end 81 | end 82 | -------------------------------------------------------------------------------- /ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Flutter (1.0.0) 3 | - flutter_image_compress (0.0.1): 4 | - Flutter 5 | - Mantle 6 | - SDWebImage 7 | - SDWebImageWebPCoder 8 | - image_picker (0.0.1): 9 | - Flutter 10 | - libwebp (1.2.1): 11 | - libwebp/demux (= 1.2.1) 12 | - libwebp/mux (= 1.2.1) 13 | - libwebp/webp (= 1.2.1) 14 | - libwebp/demux (1.2.1): 15 | - libwebp/webp 16 | - libwebp/mux (1.2.1): 17 | - libwebp/demux 18 | - libwebp/webp (1.2.1) 19 | - Mantle (2.1.6): 20 | - Mantle/extobjc (= 2.1.6) 21 | - Mantle/extobjc (2.1.6) 22 | - path_provider_ios (0.0.1): 23 | - Flutter 24 | - "permission_handler (5.1.0+2)": 25 | - Flutter 26 | - SDWebImage (5.12.1): 27 | - SDWebImage/Core (= 5.12.1) 28 | - SDWebImage/Core (5.12.1) 29 | - SDWebImageWebPCoder (0.8.4): 30 | - libwebp (~> 1.0) 31 | - SDWebImage/Core (~> 5.10) 32 | 33 | DEPENDENCIES: 34 | - Flutter (from `Flutter`) 35 | - flutter_image_compress (from `.symlinks/plugins/flutter_image_compress/ios`) 36 | - image_picker (from `.symlinks/plugins/image_picker/ios`) 37 | - path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`) 38 | - permission_handler (from `.symlinks/plugins/permission_handler/ios`) 39 | 40 | SPEC REPOS: 41 | trunk: 42 | - libwebp 43 | - Mantle 44 | - SDWebImage 45 | - SDWebImageWebPCoder 46 | 47 | EXTERNAL SOURCES: 48 | Flutter: 49 | :path: Flutter 50 | flutter_image_compress: 51 | :path: ".symlinks/plugins/flutter_image_compress/ios" 52 | image_picker: 53 | :path: ".symlinks/plugins/image_picker/ios" 54 | path_provider_ios: 55 | :path: ".symlinks/plugins/path_provider_ios/ios" 56 | permission_handler: 57 | :path: ".symlinks/plugins/permission_handler/ios" 58 | 59 | SPEC CHECKSUMS: 60 | Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a 61 | flutter_image_compress: fd2b476345226e1a10ea352fa306af95704642c1 62 | image_picker: 9aa50e1d8cdacdbed739e925b7eea16d014367e6 63 | libwebp: 98a37e597e40bfdb4c911fc98f2c53d0b12d05fc 64 | Mantle: 4c0ed6ce47c96eccc4dc3bb071deb3def0e2c3be 65 | path_provider_ios: 7d7ce634493af4477d156294792024ec3485acd5 66 | permission_handler: ccb20a9fad0ee9b1314a52b70b76b473c5f8dab0 67 | SDWebImage: 4dc3e42d9ec0c1028b960a33ac6b637bb432207b 68 | SDWebImageWebPCoder: f93010f3f6c031e2f8fb3081ca4ee6966c539815 69 | 70 | PODFILE CHECKSUM: dbd6872e034c796d9958efebce52e444008215ed 71 | 72 | COCOAPODS: 1.11.2 73 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 51; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 12 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 13 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 14 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 15 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 16 | CF37247928B03CD8E4AB59F1 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DB5C116EBC9528CB1A185402 /* Pods_Runner.framework */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXCopyFilesBuildPhase section */ 20 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 21 | isa = PBXCopyFilesBuildPhase; 22 | buildActionMask = 2147483647; 23 | dstPath = ""; 24 | dstSubfolderSpec = 10; 25 | files = ( 26 | ); 27 | name = "Embed Frameworks"; 28 | runOnlyForDeploymentPostprocessing = 0; 29 | }; 30 | /* End PBXCopyFilesBuildPhase section */ 31 | 32 | /* Begin PBXFileReference section */ 33 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 34 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 35 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 36 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 37 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 38 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 39 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 40 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 41 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 42 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 43 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 44 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 45 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 46 | 97EAA63A2A5070653279E994 /* 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 = ""; }; 47 | DB5C116EBC9528CB1A185402 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 48 | F64255C655D7D9AAD9F02892 /* 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 = ""; }; 49 | FDF203C3A55C69D9678CB2CD /* 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 = ""; }; 50 | /* End PBXFileReference section */ 51 | 52 | /* Begin PBXFrameworksBuildPhase section */ 53 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 54 | isa = PBXFrameworksBuildPhase; 55 | buildActionMask = 2147483647; 56 | files = ( 57 | CF37247928B03CD8E4AB59F1 /* Pods_Runner.framework in Frameworks */, 58 | ); 59 | runOnlyForDeploymentPostprocessing = 0; 60 | }; 61 | /* End PBXFrameworksBuildPhase section */ 62 | 63 | /* Begin PBXGroup section */ 64 | 21209F2822F69352173258E1 /* Frameworks */ = { 65 | isa = PBXGroup; 66 | children = ( 67 | DB5C116EBC9528CB1A185402 /* Pods_Runner.framework */, 68 | ); 69 | name = Frameworks; 70 | sourceTree = ""; 71 | }; 72 | 4359DDA015A41EE8BA122A3F /* Pods */ = { 73 | isa = PBXGroup; 74 | children = ( 75 | 97EAA63A2A5070653279E994 /* Pods-Runner.debug.xcconfig */, 76 | F64255C655D7D9AAD9F02892 /* Pods-Runner.release.xcconfig */, 77 | FDF203C3A55C69D9678CB2CD /* Pods-Runner.profile.xcconfig */, 78 | ); 79 | path = Pods; 80 | sourceTree = ""; 81 | }; 82 | 9740EEB11CF90186004384FC /* Flutter */ = { 83 | isa = PBXGroup; 84 | children = ( 85 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 86 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 87 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 88 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 89 | ); 90 | name = Flutter; 91 | sourceTree = ""; 92 | }; 93 | 97C146E51CF9000F007C117D = { 94 | isa = PBXGroup; 95 | children = ( 96 | 9740EEB11CF90186004384FC /* Flutter */, 97 | 97C146F01CF9000F007C117D /* Runner */, 98 | 97C146EF1CF9000F007C117D /* Products */, 99 | 4359DDA015A41EE8BA122A3F /* Pods */, 100 | 21209F2822F69352173258E1 /* Frameworks */, 101 | ); 102 | sourceTree = ""; 103 | }; 104 | 97C146EF1CF9000F007C117D /* Products */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | 97C146EE1CF9000F007C117D /* Runner.app */, 108 | ); 109 | name = Products; 110 | sourceTree = ""; 111 | }; 112 | 97C146F01CF9000F007C117D /* Runner */ = { 113 | isa = PBXGroup; 114 | children = ( 115 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 116 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 117 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 118 | 97C147021CF9000F007C117D /* Info.plist */, 119 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 120 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 121 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 122 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 123 | ); 124 | path = Runner; 125 | sourceTree = ""; 126 | }; 127 | /* End PBXGroup section */ 128 | 129 | /* Begin PBXNativeTarget section */ 130 | 97C146ED1CF9000F007C117D /* Runner */ = { 131 | isa = PBXNativeTarget; 132 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 133 | buildPhases = ( 134 | 3CF3494BE76857C8DA391D73 /* [CP] Check Pods Manifest.lock */, 135 | 9740EEB61CF901F6004384FC /* Run Script */, 136 | 97C146EA1CF9000F007C117D /* Sources */, 137 | 97C146EB1CF9000F007C117D /* Frameworks */, 138 | 97C146EC1CF9000F007C117D /* Resources */, 139 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 140 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 141 | 61E009B73D797D502629BCBE /* [CP] Embed Pods Frameworks */, 142 | ); 143 | buildRules = ( 144 | ); 145 | dependencies = ( 146 | ); 147 | name = Runner; 148 | productName = Runner; 149 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 150 | productType = "com.apple.product-type.application"; 151 | }; 152 | /* End PBXNativeTarget section */ 153 | 154 | /* Begin PBXProject section */ 155 | 97C146E61CF9000F007C117D /* Project object */ = { 156 | isa = PBXProject; 157 | attributes = { 158 | LastUpgradeCheck = 1300; 159 | ORGANIZATIONNAME = ""; 160 | TargetAttributes = { 161 | 97C146ED1CF9000F007C117D = { 162 | CreatedOnToolsVersion = 7.3.1; 163 | LastSwiftMigration = 1100; 164 | }; 165 | }; 166 | }; 167 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 168 | compatibilityVersion = "Xcode 9.3"; 169 | developmentRegion = en; 170 | hasScannedForEncodings = 0; 171 | knownRegions = ( 172 | en, 173 | Base, 174 | ); 175 | mainGroup = 97C146E51CF9000F007C117D; 176 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 177 | projectDirPath = ""; 178 | projectRoot = ""; 179 | targets = ( 180 | 97C146ED1CF9000F007C117D /* Runner */, 181 | ); 182 | }; 183 | /* End PBXProject section */ 184 | 185 | /* Begin PBXResourcesBuildPhase section */ 186 | 97C146EC1CF9000F007C117D /* Resources */ = { 187 | isa = PBXResourcesBuildPhase; 188 | buildActionMask = 2147483647; 189 | files = ( 190 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 191 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 192 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 193 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 194 | ); 195 | runOnlyForDeploymentPostprocessing = 0; 196 | }; 197 | /* End PBXResourcesBuildPhase section */ 198 | 199 | /* Begin PBXShellScriptBuildPhase section */ 200 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 201 | isa = PBXShellScriptBuildPhase; 202 | buildActionMask = 2147483647; 203 | files = ( 204 | ); 205 | inputPaths = ( 206 | ); 207 | name = "Thin Binary"; 208 | outputPaths = ( 209 | ); 210 | runOnlyForDeploymentPostprocessing = 0; 211 | shellPath = /bin/sh; 212 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 213 | }; 214 | 3CF3494BE76857C8DA391D73 /* [CP] Check Pods Manifest.lock */ = { 215 | isa = PBXShellScriptBuildPhase; 216 | buildActionMask = 2147483647; 217 | files = ( 218 | ); 219 | inputFileListPaths = ( 220 | ); 221 | inputPaths = ( 222 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 223 | "${PODS_ROOT}/Manifest.lock", 224 | ); 225 | name = "[CP] Check Pods Manifest.lock"; 226 | outputFileListPaths = ( 227 | ); 228 | outputPaths = ( 229 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", 230 | ); 231 | runOnlyForDeploymentPostprocessing = 0; 232 | shellPath = /bin/sh; 233 | 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"; 234 | showEnvVarsInLog = 0; 235 | }; 236 | 61E009B73D797D502629BCBE /* [CP] Embed Pods Frameworks */ = { 237 | isa = PBXShellScriptBuildPhase; 238 | buildActionMask = 2147483647; 239 | files = ( 240 | ); 241 | inputFileListPaths = ( 242 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", 243 | ); 244 | name = "[CP] Embed Pods Frameworks"; 245 | outputFileListPaths = ( 246 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", 247 | ); 248 | runOnlyForDeploymentPostprocessing = 0; 249 | shellPath = /bin/sh; 250 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; 251 | showEnvVarsInLog = 0; 252 | }; 253 | 9740EEB61CF901F6004384FC /* Run Script */ = { 254 | isa = PBXShellScriptBuildPhase; 255 | buildActionMask = 2147483647; 256 | files = ( 257 | ); 258 | inputPaths = ( 259 | ); 260 | name = "Run Script"; 261 | outputPaths = ( 262 | ); 263 | runOnlyForDeploymentPostprocessing = 0; 264 | shellPath = /bin/sh; 265 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 266 | }; 267 | /* End PBXShellScriptBuildPhase section */ 268 | 269 | /* Begin PBXSourcesBuildPhase section */ 270 | 97C146EA1CF9000F007C117D /* Sources */ = { 271 | isa = PBXSourcesBuildPhase; 272 | buildActionMask = 2147483647; 273 | files = ( 274 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 275 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 276 | ); 277 | runOnlyForDeploymentPostprocessing = 0; 278 | }; 279 | /* End PBXSourcesBuildPhase section */ 280 | 281 | /* Begin PBXVariantGroup section */ 282 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 283 | isa = PBXVariantGroup; 284 | children = ( 285 | 97C146FB1CF9000F007C117D /* Base */, 286 | ); 287 | name = Main.storyboard; 288 | sourceTree = ""; 289 | }; 290 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 291 | isa = PBXVariantGroup; 292 | children = ( 293 | 97C147001CF9000F007C117D /* Base */, 294 | ); 295 | name = LaunchScreen.storyboard; 296 | sourceTree = ""; 297 | }; 298 | /* End PBXVariantGroup section */ 299 | 300 | /* Begin XCBuildConfiguration section */ 301 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 302 | isa = XCBuildConfiguration; 303 | buildSettings = { 304 | ALWAYS_SEARCH_USER_PATHS = NO; 305 | CLANG_ANALYZER_NONNULL = YES; 306 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 307 | CLANG_CXX_LIBRARY = "libc++"; 308 | CLANG_ENABLE_MODULES = YES; 309 | CLANG_ENABLE_OBJC_ARC = YES; 310 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 311 | CLANG_WARN_BOOL_CONVERSION = YES; 312 | CLANG_WARN_COMMA = YES; 313 | CLANG_WARN_CONSTANT_CONVERSION = YES; 314 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 315 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 316 | CLANG_WARN_EMPTY_BODY = YES; 317 | CLANG_WARN_ENUM_CONVERSION = YES; 318 | CLANG_WARN_INFINITE_RECURSION = YES; 319 | CLANG_WARN_INT_CONVERSION = YES; 320 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 321 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 322 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 323 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 324 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 325 | CLANG_WARN_STRICT_PROTOTYPES = YES; 326 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 327 | CLANG_WARN_UNREACHABLE_CODE = YES; 328 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 329 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 330 | COPY_PHASE_STRIP = NO; 331 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 332 | ENABLE_NS_ASSERTIONS = NO; 333 | ENABLE_STRICT_OBJC_MSGSEND = YES; 334 | GCC_C_LANGUAGE_STANDARD = gnu99; 335 | GCC_NO_COMMON_BLOCKS = YES; 336 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 337 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 338 | GCC_WARN_UNDECLARED_SELECTOR = YES; 339 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 340 | GCC_WARN_UNUSED_FUNCTION = YES; 341 | GCC_WARN_UNUSED_VARIABLE = YES; 342 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 343 | MTL_ENABLE_DEBUG_INFO = NO; 344 | SDKROOT = iphoneos; 345 | SUPPORTED_PLATFORMS = iphoneos; 346 | TARGETED_DEVICE_FAMILY = "1,2"; 347 | VALIDATE_PRODUCT = YES; 348 | }; 349 | name = Profile; 350 | }; 351 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 352 | isa = XCBuildConfiguration; 353 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 354 | buildSettings = { 355 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 356 | CLANG_ENABLE_MODULES = YES; 357 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 358 | DEVELOPMENT_TEAM = X9FZA29898; 359 | ENABLE_BITCODE = NO; 360 | INFOPLIST_FILE = Runner/Info.plist; 361 | LD_RUNPATH_SEARCH_PATHS = ( 362 | "$(inherited)", 363 | "@executable_path/Frameworks", 364 | ); 365 | PRODUCT_BUNDLE_IDENTIFIER = com.multicaret.flutter.tutorials; 366 | PRODUCT_NAME = "$(TARGET_NAME)"; 367 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 368 | SWIFT_VERSION = 5.0; 369 | VERSIONING_SYSTEM = "apple-generic"; 370 | }; 371 | name = Profile; 372 | }; 373 | 97C147031CF9000F007C117D /* Debug */ = { 374 | isa = XCBuildConfiguration; 375 | buildSettings = { 376 | ALWAYS_SEARCH_USER_PATHS = NO; 377 | CLANG_ANALYZER_NONNULL = YES; 378 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 379 | CLANG_CXX_LIBRARY = "libc++"; 380 | CLANG_ENABLE_MODULES = YES; 381 | CLANG_ENABLE_OBJC_ARC = YES; 382 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 383 | CLANG_WARN_BOOL_CONVERSION = YES; 384 | CLANG_WARN_COMMA = YES; 385 | CLANG_WARN_CONSTANT_CONVERSION = YES; 386 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 387 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 388 | CLANG_WARN_EMPTY_BODY = YES; 389 | CLANG_WARN_ENUM_CONVERSION = YES; 390 | CLANG_WARN_INFINITE_RECURSION = YES; 391 | CLANG_WARN_INT_CONVERSION = YES; 392 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 393 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 394 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 395 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 396 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 397 | CLANG_WARN_STRICT_PROTOTYPES = YES; 398 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 399 | CLANG_WARN_UNREACHABLE_CODE = YES; 400 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 401 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 402 | COPY_PHASE_STRIP = NO; 403 | DEBUG_INFORMATION_FORMAT = dwarf; 404 | ENABLE_STRICT_OBJC_MSGSEND = YES; 405 | ENABLE_TESTABILITY = YES; 406 | GCC_C_LANGUAGE_STANDARD = gnu99; 407 | GCC_DYNAMIC_NO_PIC = NO; 408 | GCC_NO_COMMON_BLOCKS = YES; 409 | GCC_OPTIMIZATION_LEVEL = 0; 410 | GCC_PREPROCESSOR_DEFINITIONS = ( 411 | "DEBUG=1", 412 | "$(inherited)", 413 | ); 414 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 415 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 416 | GCC_WARN_UNDECLARED_SELECTOR = YES; 417 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 418 | GCC_WARN_UNUSED_FUNCTION = YES; 419 | GCC_WARN_UNUSED_VARIABLE = YES; 420 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 421 | MTL_ENABLE_DEBUG_INFO = YES; 422 | ONLY_ACTIVE_ARCH = YES; 423 | SDKROOT = iphoneos; 424 | TARGETED_DEVICE_FAMILY = "1,2"; 425 | }; 426 | name = Debug; 427 | }; 428 | 97C147041CF9000F007C117D /* Release */ = { 429 | isa = XCBuildConfiguration; 430 | buildSettings = { 431 | ALWAYS_SEARCH_USER_PATHS = NO; 432 | CLANG_ANALYZER_NONNULL = YES; 433 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 434 | CLANG_CXX_LIBRARY = "libc++"; 435 | CLANG_ENABLE_MODULES = YES; 436 | CLANG_ENABLE_OBJC_ARC = YES; 437 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 438 | CLANG_WARN_BOOL_CONVERSION = YES; 439 | CLANG_WARN_COMMA = YES; 440 | CLANG_WARN_CONSTANT_CONVERSION = YES; 441 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 442 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 443 | CLANG_WARN_EMPTY_BODY = YES; 444 | CLANG_WARN_ENUM_CONVERSION = YES; 445 | CLANG_WARN_INFINITE_RECURSION = YES; 446 | CLANG_WARN_INT_CONVERSION = YES; 447 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 448 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 449 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 450 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 451 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 452 | CLANG_WARN_STRICT_PROTOTYPES = YES; 453 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 454 | CLANG_WARN_UNREACHABLE_CODE = YES; 455 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 456 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 457 | COPY_PHASE_STRIP = NO; 458 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 459 | ENABLE_NS_ASSERTIONS = NO; 460 | ENABLE_STRICT_OBJC_MSGSEND = YES; 461 | GCC_C_LANGUAGE_STANDARD = gnu99; 462 | GCC_NO_COMMON_BLOCKS = YES; 463 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 464 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 465 | GCC_WARN_UNDECLARED_SELECTOR = YES; 466 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 467 | GCC_WARN_UNUSED_FUNCTION = YES; 468 | GCC_WARN_UNUSED_VARIABLE = YES; 469 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 470 | MTL_ENABLE_DEBUG_INFO = NO; 471 | SDKROOT = iphoneos; 472 | SUPPORTED_PLATFORMS = iphoneos; 473 | SWIFT_COMPILATION_MODE = wholemodule; 474 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 475 | TARGETED_DEVICE_FAMILY = "1,2"; 476 | VALIDATE_PRODUCT = YES; 477 | }; 478 | name = Release; 479 | }; 480 | 97C147061CF9000F007C117D /* Debug */ = { 481 | isa = XCBuildConfiguration; 482 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 483 | buildSettings = { 484 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 485 | CLANG_ENABLE_MODULES = YES; 486 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 487 | DEVELOPMENT_TEAM = X9FZA29898; 488 | ENABLE_BITCODE = NO; 489 | INFOPLIST_FILE = Runner/Info.plist; 490 | LD_RUNPATH_SEARCH_PATHS = ( 491 | "$(inherited)", 492 | "@executable_path/Frameworks", 493 | ); 494 | PRODUCT_BUNDLE_IDENTIFIER = com.multicaret.flutter.tutorials; 495 | PRODUCT_NAME = "$(TARGET_NAME)"; 496 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 497 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 498 | SWIFT_VERSION = 5.0; 499 | VERSIONING_SYSTEM = "apple-generic"; 500 | }; 501 | name = Debug; 502 | }; 503 | 97C147071CF9000F007C117D /* Release */ = { 504 | isa = XCBuildConfiguration; 505 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 506 | buildSettings = { 507 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 508 | CLANG_ENABLE_MODULES = YES; 509 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 510 | DEVELOPMENT_TEAM = X9FZA29898; 511 | ENABLE_BITCODE = NO; 512 | INFOPLIST_FILE = Runner/Info.plist; 513 | LD_RUNPATH_SEARCH_PATHS = ( 514 | "$(inherited)", 515 | "@executable_path/Frameworks", 516 | ); 517 | PRODUCT_BUNDLE_IDENTIFIER = com.multicaret.flutter.tutorials; 518 | PRODUCT_NAME = "$(TARGET_NAME)"; 519 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 520 | SWIFT_VERSION = 5.0; 521 | VERSIONING_SYSTEM = "apple-generic"; 522 | }; 523 | name = Release; 524 | }; 525 | /* End XCBuildConfiguration section */ 526 | 527 | /* Begin XCConfigurationList section */ 528 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 529 | isa = XCConfigurationList; 530 | buildConfigurations = ( 531 | 97C147031CF9000F007C117D /* Debug */, 532 | 97C147041CF9000F007C117D /* Release */, 533 | 249021D3217E4FDB00AE95B9 /* Profile */, 534 | ); 535 | defaultConfigurationIsVisible = 0; 536 | defaultConfigurationName = Release; 537 | }; 538 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 539 | isa = XCConfigurationList; 540 | buildConfigurations = ( 541 | 97C147061CF9000F007C117D /* Debug */, 542 | 97C147071CF9000F007C117D /* Release */, 543 | 249021D4217E4FDB00AE95B9 /* Profile */, 544 | ); 545 | defaultConfigurationIsVisible = 0; 546 | defaultConfigurationName = Release; 547 | }; 548 | /* End XCConfigurationList section */ 549 | }; 550 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 551 | } 552 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 52 | 54 | 60 | 61 | 62 | 63 | 69 | 71 | 77 | 78 | 79 | 80 | 82 | 83 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roaa94/flutter-tutorials/616230fe440587aa83228b9422f22a784c6a6fb1/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roaa94/flutter-tutorials/616230fe440587aa83228b9422f22a784c6a6fb1/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roaa94/flutter-tutorials/616230fe440587aa83228b9422f22a784c6a6fb1/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roaa94/flutter-tutorials/616230fe440587aa83228b9422f22a784c6a6fb1/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roaa94/flutter-tutorials/616230fe440587aa83228b9422f22a784c6a6fb1/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roaa94/flutter-tutorials/616230fe440587aa83228b9422f22a784c6a6fb1/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roaa94/flutter-tutorials/616230fe440587aa83228b9422f22a784c6a6fb1/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roaa94/flutter-tutorials/616230fe440587aa83228b9422f22a784c6a6fb1/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roaa94/flutter-tutorials/616230fe440587aa83228b9422f22a784c6a6fb1/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roaa94/flutter-tutorials/616230fe440587aa83228b9422f22a784c6a6fb1/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roaa94/flutter-tutorials/616230fe440587aa83228b9422f22a784c6a6fb1/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roaa94/flutter-tutorials/616230fe440587aa83228b9422f22a784c6a6fb1/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roaa94/flutter-tutorials/616230fe440587aa83228b9422f22a784c6a6fb1/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roaa94/flutter-tutorials/616230fe440587aa83228b9422f22a784c6a6fb1/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roaa94/flutter-tutorials/616230fe440587aa83228b9422f22a784c6a6fb1/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roaa94/flutter-tutorials/616230fe440587aa83228b9422f22a784c6a6fb1/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roaa94/flutter-tutorials/616230fe440587aa83228b9422f22a784c6a6fb1/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Roaa94/flutter-tutorials/616230fe440587aa83228b9422f22a784c6a6fb1/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | Flutter Tutorials 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | NSCameraUsageDescription 46 | You need to allow camera permission to use this feature 47 | 48 | NSPhotoLibraryUsageDescription 49 | You need to allow photos permission to use this feature 50 | 51 | 52 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /lib/animated-cross-fade/pages/animated_cross_fade_example.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_tutorials/animated-cross-fade/widgets/avatar_image.dart'; 3 | import 'package:flutter_tutorials/animated-cross-fade/widgets/cover_image.dart'; 4 | 5 | class AnimatedCrossFadeExample extends StatefulWidget { 6 | static const routeName = 'animated-cross-fade-example'; 7 | 8 | @override 9 | State createState() => _AnimatedCrossFadeExampleState(); 10 | } 11 | 12 | class _AnimatedCrossFadeExampleState extends State { 13 | final ValueNotifier avatarIsSelectedNotifier = ValueNotifier(true); 14 | 15 | final List buttons = [ 16 | 'Profile Picture', 17 | 'Cover', 18 | ]; 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | return Scaffold( 23 | appBar: AppBar( 24 | backgroundColor: Colors.white, 25 | iconTheme: const IconThemeData(color: Colors.black), 26 | elevation: 0, 27 | title: const Text( 28 | 'Profile', 29 | style: TextStyle( 30 | color: Colors.black, 31 | fontSize: 18, 32 | ), 33 | ), 34 | ), 35 | body: SingleChildScrollView( 36 | padding: const EdgeInsets.all(17), 37 | child: Column( 38 | crossAxisAlignment: CrossAxisAlignment.start, 39 | children: [ 40 | Wrap( 41 | spacing: 10, 42 | children: List.generate( 43 | buttons.length, 44 | (index) => InkWell( 45 | borderRadius: BorderRadius.circular(20), 46 | onTap: () => avatarIsSelectedNotifier.value = index == 0, 47 | child: Container( 48 | padding: const EdgeInsets.all(12), 49 | decoration: BoxDecoration( 50 | color: Colors.grey.withOpacity(0.3), 51 | borderRadius: BorderRadius.circular(20), 52 | ), 53 | child: Text(buttons[index]), 54 | ), 55 | ), 56 | ), 57 | ), 58 | const SizedBox(height: 20), 59 | ValueListenableBuilder( 60 | valueListenable: avatarIsSelectedNotifier, 61 | builder: (c, bool avatarIsSelected, _) => AnimatedCrossFade( 62 | firstChild: AvatarImage(), 63 | secondChild: CoverImage(), 64 | sizeCurve: Curves.easeInOut, 65 | crossFadeState: avatarIsSelected ? CrossFadeState.showFirst : CrossFadeState.showSecond, 66 | duration: const Duration(milliseconds: 400), 67 | ), 68 | ), 69 | const SizedBox(height: 20), 70 | Text('Bio', style: TextStyle(color: Colors.grey.withOpacity(0.7))), 71 | const SizedBox(height: 10), 72 | Column( 73 | children: List.generate( 74 | 10, 75 | (index) => Container( 76 | height: 15, 77 | margin: const EdgeInsets.only(bottom: 5), 78 | decoration: BoxDecoration( 79 | color: Colors.grey.withOpacity(0.3), 80 | borderRadius: BorderRadius.circular(5), 81 | ), 82 | ), 83 | ), 84 | ) 85 | ], 86 | ), 87 | ), 88 | ); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /lib/animated-cross-fade/widgets/avatar_image.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class AvatarImage extends StatelessWidget { 4 | @override 5 | Widget build(BuildContext context) { 6 | return Container( 7 | decoration: BoxDecoration( 8 | borderRadius: BorderRadius.circular(15), 9 | border: Border.all(color: Colors.grey, width: 2), 10 | ), 11 | child: ClipRRect( 12 | borderRadius: BorderRadius.circular(15), 13 | child: Image.asset( 14 | 'assets/images/avatar.png', 15 | fit: BoxFit.cover, 16 | height: 400, 17 | width: double.infinity, 18 | alignment: Alignment.topCenter, 19 | ), 20 | ), 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/animated-cross-fade/widgets/cover_image.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class CoverImage extends StatelessWidget { 4 | @override 5 | Widget build(BuildContext context) { 6 | return ClipRRect( 7 | borderRadius: BorderRadius.circular(15), 8 | child: Image.network( 9 | 'https://cdn.pixabay.com/photo/2016/03/26/13/09/workspace-1280538_1280.jpg', 10 | fit: BoxFit.cover, 11 | height: 200, 12 | width: MediaQuery.of(context).size.width, 13 | ), 14 | ); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lib/avatar-uploader-tutorial/data/services/media/media_service.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'dart:math'; 3 | 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_image_compress/flutter_image_compress.dart'; 6 | import 'package:flutter_tutorials/avatar-uploader-tutorial/data/services/media/media_service_interface.dart'; 7 | import 'package:flutter_tutorials/avatar-uploader-tutorial/data/services/permission/permission_service.dart'; 8 | import 'package:flutter_tutorials/avatar-uploader-tutorial/data/services/service_locator.dart'; 9 | import 'package:image_picker/image_picker.dart'; 10 | import 'package:path_provider/path_provider.dart' as path_provider; 11 | 12 | class MediaService implements MediaServiceInterface { 13 | @override 14 | PermissionService get permissionService => getIt(); 15 | 16 | Future _handleImageUploadPermissions(BuildContext context, AppImageSource? _imageSource) async { 17 | if (_imageSource == null) { 18 | return false; 19 | } 20 | if (_imageSource == AppImageSource.camera) { 21 | return await permissionService.handleCameraPermission(context); 22 | } else if (_imageSource == AppImageSource.gallery) { 23 | return await permissionService.handlePhotosPermission(context); 24 | } else { 25 | return false; 26 | } 27 | } 28 | 29 | @override 30 | Future uploadImage( 31 | BuildContext context, 32 | AppImageSource appImageSource, { 33 | bool shouldCompress = true, 34 | }) async { 35 | // Handle permissions according to image source, 36 | bool canProceed = await _handleImageUploadPermissions(context, appImageSource); 37 | 38 | if (canProceed) { 39 | File? processedPickedImageFile; 40 | 41 | // Convert our own AppImageSource into a format readable by the used package 42 | // In this case it's an ImageSource enum 43 | ImageSource? _imageSource = ImageSource.values.byName(appImageSource.name); 44 | 45 | final imagePicker = ImagePicker(); 46 | final rawPickedImageFile = await imagePicker.pickImage(source: _imageSource, imageQuality: 50); 47 | 48 | if (rawPickedImageFile != null) { 49 | //to convert from XFile type provided by the package to dart:io's File type 50 | processedPickedImageFile = File(rawPickedImageFile.path); 51 | if (shouldCompress) { 52 | processedPickedImageFile = await compressFile(processedPickedImageFile); 53 | } 54 | } 55 | return processedPickedImageFile; 56 | } 57 | } 58 | 59 | @override 60 | Future compressFile(File file, {int quality = 30}) async { 61 | final dir = await path_provider.getTemporaryDirectory(); 62 | final targetPath = dir.absolute.path + '/${Random().nextInt(1000)}-temp.jpg'; 63 | 64 | return await FlutterImageCompress.compressAndGetFile( 65 | file.absolute.path, 66 | targetPath, 67 | quality: quality, 68 | ); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /lib/avatar-uploader-tutorial/data/services/media/media_service_interface.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter/cupertino.dart'; 4 | import 'package:flutter_tutorials/avatar-uploader-tutorial/data/services/permission/permission_service.dart'; 5 | 6 | // Created our own enum to prevent an abstract interface 7 | // from having a dependency on an external package 8 | enum AppImageSource { 9 | camera, 10 | gallery, 11 | } 12 | 13 | abstract class MediaServiceInterface { 14 | PermissionService get permissionService; 15 | 16 | Future uploadImage( 17 | BuildContext context, 18 | AppImageSource appImageSource, { 19 | bool shouldCompress = true, 20 | }); 21 | 22 | Future compressFile(File file, {int quality = 30}); 23 | } 24 | -------------------------------------------------------------------------------- /lib/avatar-uploader-tutorial/data/services/permission/permission_handler_permission_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_tutorials/common/ui/widgets/app_alert_dialog.dart'; 3 | import 'package:flutter_tutorials/avatar-uploader-tutorial/data/services/permission/permission_service.dart'; 4 | import 'package:permission_handler/permission_handler.dart'; 5 | 6 | class PermissionHandlerPermissionService implements PermissionService { 7 | @override 8 | Future requestCameraPermission() async { 9 | return await Permission.camera.request(); 10 | } 11 | 12 | @override 13 | Future requestPhotosPermission() async { 14 | return await Permission.photos.request(); 15 | } 16 | 17 | @override 18 | Future handleCameraPermission(BuildContext context) async { 19 | PermissionStatus cameraPermissionStatus = await requestCameraPermission(); 20 | 21 | if (cameraPermissionStatus != PermissionStatus.granted) { 22 | print('😰 😰 😰 😰 😰 😰 Permission to camera was not granted! 😰 😰 😰 😰 😰 😰'); 23 | await showDialog( 24 | context: context, 25 | builder: (_context) => AppAlertDialog( 26 | onConfirm: () => openAppSettings(), 27 | title: 'Camera Permission', 28 | subtitle: 'Camera permission should Be granted to use this feature, would you like to go to app settings to give camera permission?', 29 | ), 30 | ); 31 | return false; 32 | } 33 | return true; 34 | } 35 | 36 | @override 37 | Future handlePhotosPermission(BuildContext context) async { 38 | PermissionStatus photosPermissionStatus = await requestPhotosPermission(); 39 | 40 | if (photosPermissionStatus != PermissionStatus.granted) { 41 | print('😰 😰 😰 😰 😰 😰 Permission to photos not granted! 😰 😰 😰 😰 😰 😰'); 42 | await showDialog( 43 | context: context, 44 | builder: (_context) => AppAlertDialog( 45 | onConfirm: () => openAppSettings(), 46 | title: 'Photos Permission', 47 | subtitle: 'Photos permission should Be granted to use this feature, would you like to go to app settings to give photos permission?', 48 | ), 49 | ); 50 | return false; 51 | } 52 | return true; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /lib/avatar-uploader-tutorial/data/services/permission/permission_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | abstract class PermissionService { 4 | Future requestPhotosPermission(); 5 | 6 | Future handlePhotosPermission(BuildContext context); 7 | 8 | Future requestCameraPermission(); 9 | 10 | Future handleCameraPermission(BuildContext context); 11 | } 12 | -------------------------------------------------------------------------------- /lib/avatar-uploader-tutorial/data/services/service_locator.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_tutorials/avatar-uploader-tutorial/data/services/media/media_service.dart'; 2 | import 'package:flutter_tutorials/avatar-uploader-tutorial/data/services/media/media_service_interface.dart'; 3 | import 'package:flutter_tutorials/avatar-uploader-tutorial/data/services/permission/permission_handler_permission_service.dart'; 4 | import 'package:flutter_tutorials/avatar-uploader-tutorial/data/services/permission/permission_service.dart'; 5 | import 'package:get_it/get_it.dart'; 6 | 7 | final getIt = GetIt.instance; 8 | 9 | setupServiceLocator() { 10 | // Permission service is used in FileUploaderService 11 | // so it must be located first 12 | getIt.registerSingleton(PermissionHandlerPermissionService()); 13 | 14 | getIt.registerSingleton(MediaService()); 15 | } -------------------------------------------------------------------------------- /lib/avatar-uploader-tutorial/data/utils.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | double getMBFileSize(File file) { 4 | return file.lengthSync() / 125000; 5 | } 6 | -------------------------------------------------------------------------------- /lib/avatar-uploader-tutorial/ui/file_upload_service_home_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_tutorials/avatar-uploader-tutorial/ui/widgets/avatar_uploader.dart'; 3 | 4 | class FileUploadServiceHomePage extends StatelessWidget { 5 | static const String routeName = 'file-upload-service-home-page'; 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | return Scaffold( 10 | extendBodyBehindAppBar: true, 11 | appBar: AppBar( 12 | title: const Text('Avatar Uploader'), 13 | ), 14 | body: Center( 15 | child: AvatarUploader(), 16 | ), 17 | ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/avatar-uploader-tutorial/ui/widgets/avatar_container.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | class AvatarContainer extends StatelessWidget { 6 | final Function onTap; 7 | final File? imageFile; 8 | final bool isLoading; 9 | 10 | const AvatarContainer({ 11 | required this.onTap, 12 | required this.imageFile, 13 | required this.isLoading, 14 | }); 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return GestureDetector( 19 | onTap: isLoading ? null : () => onTap(), 20 | child: Container( 21 | width: 200, 22 | height: 200, 23 | decoration: BoxDecoration( 24 | shape: BoxShape.circle, 25 | color: Colors.blueGrey.withOpacity(0.5), 26 | border: Border.all(width: 3, color: Colors.teal), 27 | image: imageFile == null 28 | ? null 29 | : DecorationImage( 30 | image: FileImage(imageFile!), 31 | fit: BoxFit.cover, 32 | ), 33 | ), 34 | child: ClipRRect( 35 | borderRadius: BorderRadius.circular(100), 36 | child: Column( 37 | mainAxisAlignment: MainAxisAlignment.end, 38 | children: [ 39 | const SizedBox(height: 70), 40 | if (isLoading) const CircularProgressIndicator(), 41 | const SizedBox(height: 30), 42 | Container( 43 | height: 50, 44 | width: 200, 45 | color: Colors.black.withOpacity(0.5), 46 | padding: const EdgeInsets.only(bottom: 12), 47 | child: Center( 48 | child: Text( 49 | imageFile == null ? 'Upload Image' : 'Change Image', 50 | style: const TextStyle(color: Colors.white, fontSize: 16), 51 | ), 52 | ), 53 | ), 54 | ], 55 | ), 56 | ), 57 | ), 58 | ); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /lib/avatar-uploader-tutorial/ui/widgets/avatar_uploader.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter/cupertino.dart'; 4 | import 'package:flutter_tutorials/avatar-uploader-tutorial/data/services/media/media_service_interface.dart'; 5 | import 'package:flutter_tutorials/avatar-uploader-tutorial/data/services/service_locator.dart'; 6 | import 'package:flutter_tutorials/avatar-uploader-tutorial/ui/widgets/avatar_container.dart'; 7 | import 'package:flutter_tutorials/avatar-uploader-tutorial/ui/widgets/image_picker_action_sheet.dart'; 8 | 9 | class AvatarUploader extends StatefulWidget { 10 | @override 11 | _AvatarUploaderState createState() => _AvatarUploaderState(); 12 | } 13 | 14 | class _AvatarUploaderState extends State { 15 | final MediaServiceInterface _mediaService = getIt(); 16 | 17 | File? imageFile; 18 | bool _isLoadingGettingImage = false; 19 | 20 | Future _pickImageSource() async { 21 | AppImageSource? _appImageSource = await showCupertinoModalPopup( 22 | context: context, 23 | builder: (BuildContext context) => ImagePickerActionSheet(), 24 | ); 25 | if (_appImageSource != null) { 26 | _getImage(_appImageSource); 27 | } 28 | } 29 | 30 | Future _getImage(AppImageSource _appImageSource) async { 31 | setState(() => _isLoadingGettingImage = true); 32 | final _pickedImageFile = await _mediaService.uploadImage(context, _appImageSource); 33 | setState(() => _isLoadingGettingImage = false); 34 | 35 | if (_pickedImageFile != null) { 36 | setState(() => imageFile = _pickedImageFile); 37 | } 38 | } 39 | 40 | @override 41 | Widget build(BuildContext context) { 42 | return AvatarContainer( 43 | isLoading: _isLoadingGettingImage, 44 | onTap: _pickImageSource, 45 | imageFile: imageFile, 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/avatar-uploader-tutorial/ui/widgets/image_picker_action_sheet.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter_tutorials/avatar-uploader-tutorial/data/services/media/media_service_interface.dart'; 3 | 4 | class ImagePickerActionSheet extends StatelessWidget { 5 | @override 6 | Widget build(BuildContext context) { 7 | return CupertinoActionSheet( 8 | actions: [ 9 | CupertinoActionSheetAction( 10 | child: const Text('Take Photo'), 11 | onPressed: () => Navigator.of(context).pop(AppImageSource.camera), 12 | ), 13 | CupertinoActionSheetAction( 14 | child: const Text('Upload From Gallery'), 15 | onPressed: () => Navigator.of(context).pop(AppImageSource.gallery), 16 | ), 17 | ], 18 | cancelButton: CupertinoActionSheetAction( 19 | child: const Text('Cancel'), 20 | onPressed: () => Navigator.of(context).pop(), 21 | ), 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/common/ui/widgets/app_alert_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class AppAlertDialog extends StatelessWidget { 4 | final Function onConfirm; 5 | final String title; 6 | final String subtitle; 7 | 8 | const AppAlertDialog({ 9 | required this.onConfirm, 10 | required this.title, 11 | required this.subtitle, 12 | }); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return AlertDialog( 17 | title: Text(title), 18 | content: Text(subtitle), 19 | actions: [ 20 | ElevatedButton( 21 | onPressed: () => onConfirm(), 22 | child: const Text('Confirm'), 23 | ), 24 | ElevatedButton( 25 | onPressed: () => Navigator.of(context).pop(), 26 | child: const Text('Cancel'), 27 | ), 28 | ], 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/common/ui/widgets/bordered_container.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class BorderedContainer extends StatelessWidget { 4 | final Widget child; 5 | final Color? color; 6 | 7 | const BorderedContainer({ 8 | required this.child, 9 | this.color, 10 | }); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return Container( 15 | // width: double.infinity, 16 | padding: const EdgeInsets.symmetric(horizontal: 17, vertical: 20), 17 | decoration: BoxDecoration( 18 | border: Border(bottom: BorderSide(color: Theme.of(context).dividerColor)), 19 | color: color, 20 | ), 21 | child: child, 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/data/app_data.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_tutorials/animated-cross-fade/pages/animated_cross_fade_example.dart'; 2 | import 'package:flutter_tutorials/avatar-uploader-tutorial/ui/file_upload_service_home_page.dart'; 3 | import 'package:flutter_tutorials/data/models/scroll_physics_data.dart'; 4 | import 'package:flutter_tutorials/data/models/tutorial.dart'; 5 | import 'package:flutter_tutorials/refresh-indicator-thread/refresh_indicator_thread_home.dart'; 6 | import 'package:flutter_tutorials/reorderable-list-view/pages/reorderable_list_view_page.dart'; 7 | import 'package:flutter_tutorials/scroll-physics-thread/always_scrollable_scroll_physics_example.dart'; 8 | import 'package:flutter_tutorials/scroll-physics-thread/bouncing_scroll_physics_example.dart'; 9 | import 'package:flutter_tutorials/scroll-physics-thread/clamping_scroll_physics_example.dart'; 10 | import 'package:flutter_tutorials/scroll-physics-thread/fixed_extent_scroll_physics_example.dart'; 11 | import 'package:flutter_tutorials/scroll-physics-thread/never_scrollable_scroll_physics_example.dart'; 12 | import 'package:flutter_tutorials/scroll-physics-thread/scroll_physics_thread_home.dart'; 13 | 14 | class AppData { 15 | static List appTutorials = [ 16 | Tutorial( 17 | title: 'Image Uploader With App Permissions and Compression Using GetIt Services', 18 | description: 19 | 'Allow image upload in your Flutter app with one line of code in your widget while handling everything from camera & gallery permissions to image compression under the hood with GetIt and the service locator pattern.', 20 | tutorialPageRoute: FileUploadServiceHomePage.routeName, 21 | tutorialLink: 'https://medium.com/@roaa_k/flutter-image-uploader-with-app-permissions-and-compression-using-getit-services-59ffea13f913', 22 | ), 23 | Tutorial( 24 | title: 'ToDo List With RefreshIndicator', 25 | description: 'Fetch a ToDo list from jsonplaceholder and use RefreshIndicator widget to refetch data', 26 | tutorialPageRoute: RefreshIndicatorThreadHome.routeName, 27 | tutorialLink: 'https://twitter.com/roaakdm/status/1472181621759655939'), 28 | Tutorial( 29 | title: 'ScrollPhysics Types With Examples', 30 | description: 'See all types of ScrollPhysics with example usages and code', 31 | tutorialPageRoute: ScrollPhysicsThreadHome.routeName, 32 | ), 33 | Tutorial( 34 | title: 'ReorderableListView', 35 | description: 'Easily allow your users to reorder a list using the ReorderableListView widget', 36 | tutorialPageRoute: ReorderableListViewPage.routeName, 37 | ), 38 | Tutorial( 39 | title: 'AnimatedCrossFade widget', 40 | description: 'Use the AnimatedCrossFade widget to apply smooth fade animation when switching between two widgets for a better user experience, instead of abruptly doing so', 41 | tutorialPageRoute: AnimatedCrossFadeExample.routeName, 42 | ), 43 | ]; 44 | 45 | static List scrollPhysicsTypes = [ 46 | ScrollPhysicsData( 47 | title: 'NeverScrollableScrollPhysics', 48 | pageRoute: NeverScrollableScrollPhysicsExample.routeName, 49 | ), 50 | ScrollPhysicsData( 51 | title: 'AlwaysScrollableScrollPhysics', 52 | pageRoute: AlwaysScrollableScrollPhysicsExample.routeName, 53 | ), 54 | ScrollPhysicsData( 55 | title: 'BouncingScrollPhysics', 56 | pageRoute: BouncingScrollPhysicsExample.routeName, 57 | ), 58 | ScrollPhysicsData( 59 | title: 'ClampingScrollPhysics', 60 | pageRoute: ClampingScrollPhysicsExample.routeName, 61 | ), 62 | ScrollPhysicsData( 63 | title: 'FixedExtentScrollPhysics', 64 | pageRoute: FixedExtentScrollPhysicsExample.routeName, 65 | ), 66 | ]; 67 | } 68 | -------------------------------------------------------------------------------- /lib/data/models/scroll_physics_data.dart: -------------------------------------------------------------------------------- 1 | class ScrollPhysicsData { 2 | String title; 3 | String pageRoute; 4 | 5 | ScrollPhysicsData({ 6 | required this.title, 7 | required this.pageRoute, 8 | }); 9 | } 10 | -------------------------------------------------------------------------------- /lib/data/models/tutorial.dart: -------------------------------------------------------------------------------- 1 | class Tutorial { 2 | String title; 3 | String description; 4 | String? tutorialLink; 5 | String? tutorialPageRoute; 6 | 7 | Tutorial({ 8 | required this.title, 9 | required this.description, 10 | this.tutorialLink, 11 | this.tutorialPageRoute, 12 | }); 13 | } 14 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_tutorials/animated-cross-fade/pages/animated_cross_fade_example.dart'; 3 | import 'package:flutter_tutorials/avatar-uploader-tutorial/data/services/service_locator.dart'; 4 | import 'package:flutter_tutorials/avatar-uploader-tutorial/ui/file_upload_service_home_page.dart'; 5 | import 'package:flutter_tutorials/refresh-indicator-thread/refresh_indicator_thread_home.dart'; 6 | import 'package:flutter_tutorials/reorderable-list-view/pages/reorderable_list_view_page.dart'; 7 | import 'package:flutter_tutorials/scroll-physics-thread/always_scrollable_scroll_physics_example.dart'; 8 | import 'package:flutter_tutorials/scroll-physics-thread/bouncing_scroll_physics_example.dart'; 9 | import 'package:flutter_tutorials/scroll-physics-thread/clamping_scroll_physics_example.dart'; 10 | import 'package:flutter_tutorials/scroll-physics-thread/fixed_extent_scroll_physics_example.dart'; 11 | import 'package:flutter_tutorials/scroll-physics-thread/never_scrollable_scroll_physics_example.dart'; 12 | import 'package:flutter_tutorials/scroll-physics-thread/scroll_physics_thread_home.dart'; 13 | import 'package:flutter_tutorials/ui/home/pages/home_page.dart'; 14 | 15 | void main() { 16 | setupServiceLocator(); 17 | runApp(const MyApp()); 18 | } 19 | 20 | class MyApp extends StatelessWidget { 21 | const MyApp({Key? key}) : super(key: key); 22 | 23 | @override 24 | Widget build(BuildContext context) { 25 | return MaterialApp( 26 | debugShowCheckedModeBanner: false, 27 | title: 'Flutter Demo', 28 | theme: ThemeData( 29 | primaryColor: Colors.teal, 30 | primarySwatch: Colors.teal, 31 | elevatedButtonTheme: ElevatedButtonThemeData( 32 | style: ElevatedButton.styleFrom( 33 | shape: RoundedRectangleBorder( 34 | borderRadius: BorderRadius.circular(10), 35 | ), 36 | ), 37 | ), 38 | ), 39 | routes: { 40 | FileUploadServiceHomePage.routeName: (context) => FileUploadServiceHomePage(), 41 | RefreshIndicatorThreadHome.routeName: (context) => RefreshIndicatorThreadHome(), 42 | ScrollPhysicsThreadHome.routeName: (context) => ScrollPhysicsThreadHome(), 43 | NeverScrollableScrollPhysicsExample.routeName: (context) => NeverScrollableScrollPhysicsExample(), 44 | BouncingScrollPhysicsExample.routeName: (context) => BouncingScrollPhysicsExample(), 45 | ClampingScrollPhysicsExample.routeName: (context) => ClampingScrollPhysicsExample(), 46 | FixedExtentScrollPhysicsExample.routeName: (context) => FixedExtentScrollPhysicsExample(), 47 | AlwaysScrollableScrollPhysicsExample.routeName: (context) => AlwaysScrollableScrollPhysicsExample(), 48 | ReorderableListViewPage.routeName: (context) => ReorderableListViewPage(), 49 | AnimatedCrossFadeExample.routeName: (context) => AnimatedCrossFadeExample(), 50 | }, 51 | home: HomePage(), 52 | ); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /lib/mains/main_color_helper.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | void main() { 4 | runApp(App()); 5 | } 6 | 7 | class App extends StatelessWidget { 8 | @override 9 | Widget build(BuildContext context) { 10 | return MaterialApp( 11 | debugShowCheckedModeBanner: false, 12 | home: HomePage(), 13 | ); 14 | } 15 | } 16 | 17 | class HomePage extends StatelessWidget { 18 | @override 19 | Widget build(BuildContext context) { 20 | return Scaffold( 21 | appBar: AppBar( 22 | title: const Text('Material Color Shades'), 23 | ), 24 | body: Center( 25 | child: Column( 26 | children: [ 27 | PrimaryColorOptions(), 28 | MaterialColorShades(), 29 | PrimaryColorOptions(), 30 | ], 31 | ), 32 | ), 33 | ); 34 | } 35 | } 36 | 37 | class PrimaryColorOptions extends StatelessWidget { 38 | @override 39 | Widget build(BuildContext context) { 40 | return Container( 41 | decoration: BoxDecoration( 42 | border: Border.all(color: Colors.blueGrey, width: 2), 43 | ), 44 | child: Row( 45 | children: List.generate( 46 | AppColors.primaryColors.length, 47 | (i) => Expanded( 48 | child: Container( 49 | height: 50, 50 | color: AppColors.primaryColors[i], 51 | ), 52 | ), 53 | ), 54 | ), 55 | ); 56 | } 57 | } 58 | 59 | class MaterialColorShades extends StatelessWidget { 60 | @override 61 | Widget build(BuildContext context) { 62 | return SizedBox( 63 | height: 500, 64 | child: Row( 65 | children: List.generate( 66 | AppColors.materialColors.length, 67 | (i) { 68 | return Expanded( 69 | child: Column( 70 | children: _buildShadesContainers(AppColors.materialColors[i]), 71 | ), 72 | ); 73 | }, 74 | ), 75 | ), 76 | ); 77 | } 78 | 79 | List _buildShadesContainers(MaterialColor materialColor) { 80 | List _shadesContainers = []; 81 | AppColors.getMaterialColorShades(materialColor).forEach( 82 | (materialColorShadeIndex, materialColorShade) { 83 | _shadesContainers.add(Expanded( 84 | child: Container( 85 | width: double.infinity, 86 | decoration: BoxDecoration( 87 | color: materialColorShade, 88 | border: materialColorShadeIndex == 500 89 | ? const Border( 90 | top: BorderSide(width: 2, color: Colors.blueGrey), 91 | bottom: BorderSide(width: 2, color: Colors.blueGrey), 92 | ) 93 | : null, 94 | ), 95 | child: Column( 96 | mainAxisAlignment: MainAxisAlignment.center, 97 | children: [ 98 | if (materialColorShadeIndex == 500) const Text('MAIN'), 99 | Text('$materialColorShadeIndex'), 100 | ], 101 | ), 102 | ), 103 | )); 104 | }, 105 | ); 106 | return _shadesContainers; 107 | } 108 | } 109 | 110 | class AppColors { 111 | static List hexColors = [ 112 | 'd23156', 113 | '16b9fd', 114 | '13d0c1', 115 | 'e5672f', 116 | 'b73d99', 117 | ]; 118 | 119 | static List get primaryColors { 120 | List _primaryColors = []; 121 | 122 | for (String hexColor in hexColors) { 123 | Color _color = Color(int.parse('0xff$hexColor')); 124 | _primaryColors.add(_color); 125 | } 126 | 127 | return _primaryColors; 128 | } 129 | 130 | static Color getShade(Color color, {bool darker = false, double value = .1}) { 131 | assert(value >= 0 && value <= 1); 132 | 133 | final hsl = HSLColor.fromColor(color); 134 | final hslDark = hsl.withLightness((darker ? (hsl.lightness - value) : (hsl.lightness + value)).clamp(0.0, 1.0)); 135 | 136 | return hslDark.toColor(); 137 | } 138 | 139 | static MaterialColor getMaterialColorFromColor(Color color) { 140 | Map _colorShades = { 141 | 50: getShade(color, value: 0.5), 142 | 100: getShade(color, value: 0.4), 143 | 200: getShade(color, value: 0.3), 144 | 300: getShade(color, value: 0.2), 145 | 400: getShade(color, value: 0.1), 146 | 500: color, 147 | 600: getShade(color, value: 0.1, darker: true), 148 | 700: getShade(color, value: 0.15, darker: true), 149 | 800: getShade(color, value: 0.2, darker: true), 150 | 900: getShade(color, value: 0.25, darker: true), 151 | }; 152 | return MaterialColor(color.value, _colorShades); 153 | } 154 | 155 | static List get materialColors { 156 | List _materialColors = []; 157 | for (Color primaryColor in primaryColors) { 158 | _materialColors.add(getMaterialColorFromColor(primaryColor)); 159 | } 160 | return _materialColors; 161 | } 162 | 163 | static Map getMaterialColorShades(MaterialColor materialColor) { 164 | return { 165 | 50: materialColor.shade50, 166 | 100: materialColor.shade100, 167 | 200: materialColor.shade200, 168 | 300: materialColor.shade300, 169 | 400: materialColor.shade400, 170 | 500: materialColor.shade500, 171 | 600: materialColor.shade600, 172 | 700: materialColor.shade700, 173 | 800: materialColor.shade800, 174 | 900: materialColor.shade900, 175 | }; 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /lib/mains/main_theme_switcher.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:provider/provider.dart'; 3 | 4 | void main() { 5 | runApp(App()); 6 | } 7 | 8 | const double _containerWidth = 450.0; 9 | 10 | class App extends StatelessWidget { 11 | @override 12 | Widget build(BuildContext context) { 13 | return MultiProvider( 14 | providers: [ 15 | ChangeNotifierProvider( 16 | create: (_) => ThemeProvider(), 17 | ), 18 | ], 19 | child: Consumer( 20 | child: HomePage(), 21 | builder: (c, themeProvider, child) { 22 | return MaterialApp( 23 | debugShowCheckedModeBanner: false, 24 | themeMode: themeProvider.selectedThemeMode, 25 | theme: ThemeData( 26 | brightness: Brightness.light, 27 | primarySwatch: AppColors.getMaterialColorFromColor(themeProvider.selectedPrimaryColor), 28 | primaryColor: themeProvider.selectedPrimaryColor, 29 | ), 30 | darkTheme: ThemeData( 31 | brightness: Brightness.dark, 32 | primarySwatch: AppColors.getMaterialColorFromColor(themeProvider.selectedPrimaryColor), 33 | primaryColor: themeProvider.selectedPrimaryColor, 34 | ), 35 | home: child, 36 | ); 37 | }, 38 | ), 39 | ); 40 | } 41 | } 42 | 43 | class HomePage extends StatelessWidget { 44 | @override 45 | Widget build(BuildContext context) { 46 | return Scaffold( 47 | appBar: AppBar( 48 | title: const Text('Theme & Primary Color Switcher'), 49 | ), 50 | body: Center( 51 | child: Container( 52 | padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 17), 53 | width: _containerWidth, 54 | child: Column( 55 | crossAxisAlignment: CrossAxisAlignment.center, 56 | mainAxisAlignment: MainAxisAlignment.center, 57 | children: [ 58 | const Padding( 59 | padding: EdgeInsets.symmetric(vertical: 10), 60 | child: Text('Theme'), 61 | ), 62 | ThemeSwitcher(), 63 | const Padding( 64 | padding: EdgeInsets.symmetric(vertical: 10), 65 | child: Text('Primary Color'), 66 | ), 67 | PrimaryColorSwitcher(), 68 | ], 69 | ), 70 | ), 71 | ), 72 | ); 73 | } 74 | } 75 | 76 | class ThemeProvider with ChangeNotifier { 77 | ThemeMode selectedThemeMode = appThemes[0].mode; 78 | 79 | setSelectedThemeMode(ThemeMode _themeMode) { 80 | selectedThemeMode = _themeMode; 81 | notifyListeners(); 82 | } 83 | 84 | Color selectedPrimaryColor = AppColors.primaryColors[0]; 85 | 86 | setSelectedPrimaryColor(Color _color) { 87 | selectedPrimaryColor = _color; 88 | notifyListeners(); 89 | } 90 | } 91 | 92 | class ThemeSwitcher extends StatelessWidget { 93 | @override 94 | Widget build(BuildContext context) { 95 | return Consumer( 96 | builder: (c, themeProvider, _) => SizedBox( 97 | height: (_containerWidth - (17 * 2) - (10 * 2)) / 3, 98 | child: GridView.count( 99 | physics: const NeverScrollableScrollPhysics(), 100 | crossAxisSpacing: 10, 101 | crossAxisCount: appThemes.length, 102 | children: List.generate( 103 | appThemes.length, 104 | (i) { 105 | bool _isSelectedTheme = appThemes[i].mode == themeProvider.selectedThemeMode; 106 | return GestureDetector( 107 | onTap: _isSelectedTheme ? null : () => themeProvider.setSelectedThemeMode(appThemes[i].mode), 108 | child: AnimatedContainer( 109 | height: 100, 110 | duration: const Duration(milliseconds: 200), 111 | decoration: BoxDecoration( 112 | color: _isSelectedTheme ? Theme.of(context).primaryColor : Colors.transparent, 113 | borderRadius: BorderRadius.circular(10), 114 | border: Border.all(width: 2, color: Theme.of(context).primaryColor), 115 | ), 116 | child: Center( 117 | child: Container( 118 | padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 7), 119 | margin: const EdgeInsets.symmetric(horizontal: 10), 120 | decoration: BoxDecoration( 121 | borderRadius: BorderRadius.circular(8), 122 | color: Theme.of(context).cardColor.withOpacity(0.5), 123 | ), 124 | child: Row( 125 | mainAxisAlignment: MainAxisAlignment.spaceAround, 126 | children: [ 127 | Icon(appThemes[i].icon), 128 | Text( 129 | appThemes[i].title, 130 | style: Theme.of(context).textTheme.subtitle2, 131 | ), 132 | ], 133 | ), 134 | ), 135 | ), 136 | ), 137 | ); 138 | }, 139 | ), 140 | ), 141 | ), 142 | ); 143 | } 144 | } 145 | 146 | class PrimaryColorSwitcher extends StatelessWidget { 147 | @override 148 | Widget build(BuildContext context) { 149 | return Consumer( 150 | builder: (c, themeProvider, _) => SizedBox( 151 | height: (_containerWidth - (17 * 2) - (10 * 2)) / 3, 152 | child: GridView.count( 153 | crossAxisCount: AppColors.primaryColors.length, 154 | physics: const NeverScrollableScrollPhysics(), 155 | crossAxisSpacing: 10, 156 | children: List.generate( 157 | AppColors.primaryColors.length, 158 | (i) { 159 | bool _isSelectedColor = AppColors.primaryColors[i] == themeProvider.selectedPrimaryColor; 160 | return GestureDetector( 161 | onTap: _isSelectedColor ? null : () => themeProvider.setSelectedPrimaryColor(AppColors.primaryColors[i]), 162 | child: Container( 163 | height: 50, 164 | decoration: BoxDecoration( 165 | color: AppColors.primaryColors[i], 166 | borderRadius: BorderRadius.circular(10), 167 | ), 168 | child: AnimatedOpacity( 169 | duration: const Duration(milliseconds: 200), 170 | opacity: _isSelectedColor ? 1 : 0, 171 | child: Center( 172 | child: ClipRRect( 173 | borderRadius: BorderRadius.circular(8), 174 | child: Container( 175 | padding: const EdgeInsets.all(5), 176 | decoration: BoxDecoration( 177 | borderRadius: BorderRadius.circular(3), 178 | color: Theme.of(context).cardColor.withOpacity(0.5), 179 | ), 180 | child: const Icon( 181 | Icons.check, 182 | size: 20, 183 | ), 184 | ), 185 | ), 186 | ), 187 | ), 188 | ), 189 | ); 190 | }, 191 | ), 192 | ), 193 | ), 194 | ); 195 | } 196 | } 197 | 198 | class AppColors { 199 | static List primaryColors = const [ 200 | Color(0xffd23156), 201 | Color(0xff16b9fd), 202 | Color(0xff13d0c1), 203 | Color(0xffe5672f), 204 | Color(0xffb73d99), 205 | ]; 206 | 207 | static Color getShade(Color color, {bool darker = false, double value = .1}) { 208 | assert(value >= 0 && value <= 1); 209 | 210 | final hsl = HSLColor.fromColor(color); 211 | final hslDark = hsl.withLightness((darker ? (hsl.lightness - value) : (hsl.lightness + value)).clamp(0.0, 1.0)); 212 | 213 | return hslDark.toColor(); 214 | } 215 | 216 | static MaterialColor getMaterialColorFromColor(Color color) { 217 | Map _colorShades = { 218 | 50: getShade(color, value: 0.5), 219 | 100: getShade(color, value: 0.4), 220 | 200: getShade(color, value: 0.3), 221 | 300: getShade(color, value: 0.2), 222 | 400: getShade(color, value: 0.1), 223 | 500: color, //Primary value 224 | 600: getShade(color, value: 0.1, darker: true), 225 | 700: getShade(color, value: 0.15, darker: true), 226 | 800: getShade(color, value: 0.2, darker: true), 227 | 900: getShade(color, value: 0.25, darker: true), 228 | }; 229 | return MaterialColor(color.value, _colorShades); 230 | } 231 | } 232 | 233 | class AppTheme { 234 | ThemeMode mode; 235 | String title; 236 | IconData icon; 237 | 238 | AppTheme({ 239 | required this.mode, 240 | required this.title, 241 | required this.icon, 242 | }); 243 | } 244 | 245 | List appThemes = [ 246 | AppTheme( 247 | mode: ThemeMode.light, 248 | title: 'Light', 249 | icon: Icons.brightness_5_rounded, 250 | ), 251 | AppTheme( 252 | mode: ThemeMode.dark, 253 | title: 'Dark', 254 | icon: Icons.brightness_2_rounded, 255 | ), 256 | AppTheme( 257 | mode: ThemeMode.system, 258 | title: 'Auto', 259 | icon: Icons.brightness_4_rounded, 260 | ), 261 | ]; 262 | -------------------------------------------------------------------------------- /lib/refresh-indicator-thread/refresh_indicator_thread_home.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:dio/dio.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | class RefreshIndicatorThreadHome extends StatefulWidget { 7 | static const String routeName = 'refresh-indicator-thread-home'; 8 | 9 | @override 10 | _RefreshIndicatorThreadHomeState createState() => _RefreshIndicatorThreadHomeState(); 11 | } 12 | 13 | class _RefreshIndicatorThreadHomeState extends State { 14 | StreamController> streamController = StreamController>(); 15 | 16 | Future> _getToDos() async { 17 | const endpoint = 'https://jsonplaceholder.typicode.com/todos?userId=2'; 18 | print('Fetching from: $endpoint'); 19 | var response = await Dio().get(endpoint); 20 | return response.data; 21 | } 22 | 23 | @override 24 | void initState() { 25 | _getToDos().then((todos) => streamController.add(todos)); 26 | super.initState(); 27 | } 28 | 29 | @override 30 | Widget build(BuildContext context) { 31 | print('Rebuilt page!'); 32 | return Scaffold( 33 | appBar: AppBar( 34 | title: const Text('Refresh Indicator Example'), 35 | ), 36 | body: RefreshIndicator( 37 | onRefresh: () async => streamController.add(await _getToDos()), 38 | child: StreamBuilder>( 39 | stream: streamController.stream, 40 | builder: (BuildContext context, AsyncSnapshot> snapshot) { 41 | if (snapshot.hasError) { 42 | return const Center(child: Text('An Error Occurred')); 43 | } else { 44 | switch (snapshot.connectionState) { 45 | case ConnectionState.waiting: 46 | return const Center(child: CircularProgressIndicator()); 47 | case ConnectionState.active: 48 | case ConnectionState.done: 49 | return SingleChildScrollView( 50 | clipBehavior: Clip.none, 51 | physics: const AlwaysScrollableScrollPhysics(), 52 | child: Column( 53 | crossAxisAlignment: CrossAxisAlignment.start, 54 | children: [ 55 | Padding( 56 | padding: const EdgeInsets.symmetric(horizontal: 17, vertical: 20), 57 | child: Text( 58 | 'Dummy Paragraph', 59 | style: Theme.of(context).textTheme.headline5, 60 | ), 61 | ), 62 | const Padding( 63 | padding: EdgeInsets.symmetric(horizontal: 17), 64 | child: Text( 65 | 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam ut justo feugiat, sodales mauris eget, tristique metus. Aliquam mi tortor, fermentum nec viverra et, semper eget risus. Phasellus tincidunt suscipit erat a ullamcorper. In a mattis lectus. Mauris interdum condimentum ullamcorper. Sed eget porta risus, quis pulvinar lorem. Etiam porttitor aliquam diam, tristique mollis nisi ullamcorper eget.'), 66 | ), 67 | Padding( 68 | padding: const EdgeInsets.symmetric(horizontal: 17, vertical: 20), 69 | child: Text( 70 | 'Checklist', 71 | style: Theme.of(context).textTheme.headline5, 72 | ), 73 | ), 74 | ...List.generate( 75 | 3, 76 | (i) => Container( 77 | width: double.infinity, 78 | padding: const EdgeInsets.symmetric(horizontal: 17, vertical: 10), 79 | decoration: BoxDecoration( 80 | border: Border(bottom: BorderSide(width: 1, color: Theme.of(context).dividerColor)), 81 | ), 82 | child: Row( 83 | children: [ 84 | const Icon(Icons.check), 85 | const SizedBox(width: 5), 86 | Expanded(child: Text(snapshot.data![i]['title'])), 87 | ], 88 | ), 89 | ), 90 | ), 91 | ], 92 | ), 93 | ); 94 | case ConnectionState.none: 95 | default: 96 | return const Center(child: Text('Nothing!')); 97 | } 98 | } 99 | }, 100 | ), 101 | ), 102 | ); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /lib/reorderable-list-view/pages/reorderable_list_view_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_tutorials/ui/home/widgets/list_item.dart'; 3 | 4 | class ReorderableListViewPage extends StatefulWidget { 5 | static const String routeName = 'reorderable-list-view-page'; 6 | 7 | @override 8 | _ReorderableListViewPageState createState() => _ReorderableListViewPageState(); 9 | } 10 | 11 | class _ReorderableListViewPageState extends State { 12 | final List _list = [ 13 | 'List Item 1', 14 | 'List Item 2', 15 | 'List Item 3', 16 | 'List Item 4', 17 | ]; 18 | 19 | void reorderList(int oldIndex, int newIndex) { 20 | if (newIndex > oldIndex) { 21 | newIndex -= 1; 22 | } 23 | setState(() { 24 | final String _item = _list.removeAt(oldIndex); 25 | _list.insert(newIndex, _item); 26 | }); 27 | } 28 | 29 | @override 30 | Widget build(BuildContext context) { 31 | return Scaffold( 32 | appBar: AppBar( 33 | title: const Text('ReorderableListView Example'), 34 | ), 35 | body: ReorderableListView( 36 | padding: const EdgeInsets.symmetric(vertical: 20), 37 | onReorder: reorderList, 38 | children: List.generate( 39 | _list.length, 40 | (index) => ListItem(_list[index], ValueKey(index)), 41 | ), 42 | ), 43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/scroll-physics-thread/always_scrollable_scroll_physics_example.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_tutorials/common/ui/widgets/bordered_container.dart'; 3 | 4 | class AlwaysScrollableScrollPhysicsExample extends StatelessWidget { 5 | static const String routeName = 'always-scrollable-scroll-physics-example'; 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | return Scaffold( 10 | appBar: AppBar( 11 | title: Text( 12 | 'AlwaysScrollableScrollPhysics Example', 13 | style: Theme.of(context).textTheme.subtitle1!.copyWith(color: Colors.white, fontSize: 14), 14 | ), 15 | ), 16 | body: SingleChildScrollView( 17 | padding: const EdgeInsets.symmetric(vertical: 20), 18 | physics: const AlwaysScrollableScrollPhysics(), 19 | clipBehavior: Clip.none, 20 | child: Column( 21 | children: [ 22 | const Padding( 23 | padding: EdgeInsets.symmetric(horizontal: 17), 24 | child: Text( 25 | 'Adding AlwaysScrollableScrollPhysics to the parent SingleChildScrollView in this page forces scrolling even when the content of the scrollable widget’s content doesn’t exceed the height of the screen', 26 | ), 27 | ), 28 | const Padding( 29 | padding: EdgeInsets.symmetric(horizontal: 17, vertical: 15), 30 | child: Text( 31 | 'Note: It is important that you set shrinkWrap to true in the ListView.builder() widget or else you will get the scary "Vertical viewport was given unbounded height." Error', 32 | ), 33 | ), 34 | ListView.builder( 35 | shrinkWrap: true, 36 | physics: const NeverScrollableScrollPhysics(), 37 | itemCount: 5, 38 | itemBuilder: (c, i) => BorderedContainer( 39 | child: Text( 40 | 'List Item ${i + 1}', 41 | style: Theme.of(context).textTheme.headline6, 42 | ), 43 | ), 44 | ), 45 | ], 46 | ), 47 | ), 48 | ); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/scroll-physics-thread/bouncing_scroll_physics_example.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_tutorials/common/ui/widgets/bordered_container.dart'; 3 | 4 | class BouncingScrollPhysicsExample extends StatelessWidget { 5 | static const String routeName = 'bouncing-scroll-physics-example'; 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | return Scaffold( 10 | appBar: AppBar( 11 | title: Text( 12 | 'BouncingScrollPhysics Example', 13 | style: Theme.of(context).textTheme.subtitle1!.copyWith(color: Colors.white), 14 | ), 15 | ), 16 | body: ListView.builder( 17 | physics: const BouncingScrollPhysics(), 18 | itemCount: 20, 19 | itemBuilder: (c, i) => BorderedContainer( 20 | child: Text( 21 | 'List Item ${i + 1}', 22 | style: Theme.of(context).textTheme.headline6, 23 | ), 24 | ), 25 | ), 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/scroll-physics-thread/clamping_scroll_physics_example.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_tutorials/common/ui/widgets/bordered_container.dart'; 3 | 4 | class ClampingScrollPhysicsExample extends StatelessWidget { 5 | static const String routeName = 'clamping-scroll-physics-example'; 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | return Scaffold( 10 | appBar: AppBar( 11 | title: Text( 12 | 'ClampingScrollPhysics Example', 13 | style: Theme.of(context).textTheme.subtitle1!.copyWith(color: Colors.white), 14 | ), 15 | ), 16 | body: ListView.builder( 17 | physics: const ClampingScrollPhysics(), 18 | itemCount: 20, 19 | itemBuilder: (c, i) => BorderedContainer( 20 | child: Text( 21 | 'List Item ${i + 1}', 22 | style: Theme.of(context).textTheme.headline6, 23 | ), 24 | ), 25 | ), 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/scroll-physics-thread/fixed_extent_scroll_physics_example.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_tutorials/common/ui/widgets/bordered_container.dart'; 3 | 4 | class FixedExtentScrollPhysicsExample extends StatelessWidget { 5 | static const String routeName = 'fixed-extent-scroll-physics-example'; 6 | final FixedExtentScrollController fixedExtentScrollController = FixedExtentScrollController(); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return Scaffold( 11 | appBar: AppBar( 12 | title: Text( 13 | 'FixedExtentScrollPhysics Example', 14 | style: Theme.of(context).textTheme.subtitle1!.copyWith(color: Colors.white), 15 | ), 16 | ), 17 | body: ListWheelScrollView( 18 | controller: fixedExtentScrollController, 19 | physics: const FixedExtentScrollPhysics(), 20 | itemExtent: MediaQuery.of(context).size.height, 21 | overAndUnderCenterOpacity: 0, 22 | children: List.generate( 23 | 20, 24 | (i) => BorderedContainer( 25 | color: Colors.teal.withOpacity(0.3), 26 | child: Center( 27 | child: Text( 28 | 'List Item ${i + 1}', 29 | style: Theme.of(context).textTheme.headline4, 30 | ), 31 | ), 32 | ), 33 | ), 34 | ), 35 | ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/scroll-physics-thread/never_scrollable_scroll_physics_example.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_tutorials/common/ui/widgets/bordered_container.dart'; 3 | 4 | class NeverScrollableScrollPhysicsExample extends StatelessWidget { 5 | static const String routeName = 'never-scrollable-scroll-physics-example'; 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | return Scaffold( 10 | appBar: AppBar( 11 | title: Text( 12 | 'NeverScrollableScrollPhysics Example', 13 | style: Theme.of(context).textTheme.subtitle1!.copyWith(color: Colors.white), 14 | ), 15 | ), 16 | body: SingleChildScrollView( 17 | padding: const EdgeInsets.symmetric(vertical: 20), 18 | child: Column( 19 | children: [ 20 | InkWell( 21 | onTap: () {}, 22 | child: Column( 23 | children: const [ 24 | Padding( 25 | padding: EdgeInsets.symmetric(horizontal: 17), 26 | child: Text( 27 | 'The list below is a created using a ListView.builder() widget and has its physics set to NeverScrollableScrollPhysics() because otherwise you will not be able to scroll it unless you tab inside it ans scroll, so the whole screen does not scroll as a whole, it scrolls by section, which is not a good user experience', 28 | ), 29 | ), 30 | Padding( 31 | padding: EdgeInsets.symmetric(horizontal: 17, vertical: 15), 32 | child: Text( 33 | 'Note: It is important that you set shrinkWrap to true in the ListView.builder() widget or else you will get the scary "Vertical viewport was given unbounded height." Error', 34 | ), 35 | ), 36 | ], 37 | ), 38 | ), 39 | ListView.builder( 40 | shrinkWrap: true, 41 | physics: const NeverScrollableScrollPhysics(), 42 | itemCount: 20, 43 | itemBuilder: (c, i) => InkWell( 44 | onTap: () {}, 45 | child: BorderedContainer( 46 | child: Text( 47 | 'List Item ${i + 1}', 48 | style: Theme.of(context).textTheme.headline6, 49 | ), 50 | ), 51 | ), 52 | ), 53 | ], 54 | ), 55 | ), 56 | ); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lib/scroll-physics-thread/scroll_physics_thread_home.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_tutorials/data/app_data.dart'; 3 | 4 | class ScrollPhysicsThreadHome extends StatelessWidget { 5 | static const String routeName = 'scroll-physics-thread-home'; 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | return Scaffold( 10 | appBar: AppBar( 11 | title: Text( 12 | 'ScrollPhysics Types w/ Examples', 13 | style: Theme.of(context).textTheme.subtitle1!.copyWith(color: Colors.white), 14 | ), 15 | ), 16 | body: ListView.builder( 17 | itemCount: AppData.scrollPhysicsTypes.length, 18 | itemBuilder: (c, i) => InkWell( 19 | onTap: () => Navigator.of(context).pushNamed(AppData.scrollPhysicsTypes[i].pageRoute), 20 | child: Container( 21 | padding: const EdgeInsets.symmetric(horizontal: 17, vertical: 20), 22 | decoration: BoxDecoration( 23 | border: Border( 24 | bottom: BorderSide(color: Theme.of(context).dividerColor), 25 | ), 26 | ), 27 | child: Text( 28 | AppData.scrollPhysicsTypes[i].title, 29 | style: Theme.of(context).textTheme.headline6, 30 | ), 31 | ), 32 | ), 33 | ), 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/ui/home/pages/home_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_tutorials/data/app_data.dart'; 3 | import 'package:flutter_tutorials/ui/home/widgets/tutorial_list_item.dart'; 4 | 5 | class HomePage extends StatelessWidget { 6 | @override 7 | Widget build(BuildContext context) { 8 | return Scaffold( 9 | appBar: AppBar( 10 | title: const Text('Flutter Tutorials'), 11 | ), 12 | body: SingleChildScrollView( 13 | child: Column( 14 | children: List.generate( 15 | AppData.appTutorials.length, 16 | (i) => TutorialListItem(AppData.appTutorials[i]), 17 | ), 18 | ), 19 | ), 20 | ); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/ui/home/widgets/list_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class ListItem extends StatelessWidget { 4 | final String text; 5 | 6 | const ListItem(this.text, Key? key) : super(key: key); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return Container( 11 | margin: const EdgeInsets.symmetric(horizontal: 17, vertical: 5), 12 | padding: const EdgeInsets.all(20), 13 | decoration: BoxDecoration( 14 | color: Colors.teal.withOpacity(0.2), 15 | borderRadius: BorderRadius.circular(10), 16 | ), 17 | child: Text( 18 | text, 19 | style: Theme.of(context).textTheme.headline6, 20 | ), 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/ui/home/widgets/tutorial_list_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_tutorials/data/models/tutorial.dart'; 3 | 4 | class TutorialListItem extends StatelessWidget { 5 | final Tutorial tutorial; 6 | 7 | const TutorialListItem(this.tutorial); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return InkWell( 12 | onTap: tutorial.tutorialPageRoute == null ? null : () => Navigator.of(context).pushNamed(tutorial.tutorialPageRoute!), 13 | child: Container( 14 | padding: const EdgeInsets.symmetric(horizontal: 17, vertical: 15), 15 | decoration: BoxDecoration( 16 | border: Border( 17 | bottom: BorderSide(width: 1, color: Colors.black.withOpacity(0.2)), 18 | ), 19 | ), 20 | child: Row( 21 | children: [ 22 | Expanded( 23 | child: Column( 24 | crossAxisAlignment: CrossAxisAlignment.start, 25 | children: [ 26 | Text(tutorial.title, style: Theme.of(context).textTheme.headline6), 27 | const SizedBox(height: 10), 28 | Text(tutorial.description), 29 | ], 30 | ), 31 | ), 32 | const SizedBox(width: 10), 33 | const Icon(Icons.arrow_forward), 34 | ], 35 | ), 36 | ), 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/ui/ui-helper/navigation_helper.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | void pushMaterialPageRoute(BuildContext context, Widget page, {bool rootNavigator = true}) { 5 | Navigator.of(context, rootNavigator: rootNavigator).push( 6 | MaterialPageRoute( 7 | builder: (context) => page, 8 | ), 9 | ); 10 | } 11 | 12 | Future pushCupertinoPage( 13 | BuildContext context, 14 | Widget page, { 15 | bool rootNavigator = true, 16 | bool fullscreenDialog = false, 17 | ValueChanged? onPop, 18 | }) async { 19 | final response = await Navigator.of(context, rootNavigator: rootNavigator).push( 20 | CupertinoPageRoute( 21 | builder: (BuildContext context) => page, 22 | fullscreenDialog: fullscreenDialog, 23 | ), 24 | ); 25 | if (onPop != null) { 26 | onPop(response); 27 | } 28 | } 29 | 30 | void pushAndReplaceCupertinoPage(BuildContext context, Widget page, {bool rootNavigator = false}) { 31 | Navigator.of(context, rootNavigator: rootNavigator).pushReplacement( 32 | CupertinoPageRoute( 33 | builder: (BuildContext context) => page, 34 | ), 35 | ); 36 | } 37 | 38 | void pushAndRemoveUntilCupertinoPage(BuildContext context, Widget page, {bool rootNavigator = true}) { 39 | Navigator.of(context, rootNavigator: rootNavigator).pushAndRemoveUntil( 40 | CupertinoPageRoute( 41 | builder: (BuildContext context) => page, 42 | ), 43 | (Route route) => false, 44 | ); 45 | } 46 | 47 | void pushAndRemoveUntilMaterialPage(BuildContext context, Widget page, {bool rootNavigator = true}) { 48 | Navigator.of(context, rootNavigator: rootNavigator).pushAndRemoveUntil( 49 | MaterialPageRoute(builder: (BuildContext context) => page), 50 | (Route route) => false, 51 | ); 52 | } 53 | 54 | void pushFadeInPage( 55 | BuildContext context, 56 | Widget page, { 57 | bool rootNavigator = true, 58 | Function? onPop, 59 | }) { 60 | Navigator.of(context, rootNavigator: rootNavigator) 61 | .push( 62 | PageRouteBuilder( 63 | transitionDuration: const Duration(milliseconds: 300), 64 | pageBuilder: (BuildContext context, Animation animation, Animation secondaryAnimation) { 65 | return page; 66 | }, 67 | transitionsBuilder: (BuildContext context, Animation animation, Animation secondaryAnimation, Widget child) { 68 | return Align( 69 | child: FadeTransition( 70 | opacity: animation, 71 | child: child, 72 | ), 73 | ); 74 | }, 75 | ), 76 | ) 77 | .then((_) { 78 | if (onPop != null) { 79 | onPop(); 80 | } 81 | }); 82 | } 83 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_tutorials 2 | description: A new Flutter project. 3 | 4 | # The following line prevents the package from being accidentally published to 5 | # pub.dev using `flutter pub publish`. This is preferred for private packages. 6 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 7 | 8 | # The following defines the version and build number for your application. 9 | # A version number is three numbers separated by dots, like 1.2.43 10 | # followed by an optional build number separated by a +. 11 | # Both the version and the builder number may be overridden in flutter 12 | # build by specifying --build-name and --build-number, respectively. 13 | # In Android, build-name is used as versionName while build-number used as versionCode. 14 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 15 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. 16 | # Read more about iOS versioning at 17 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 18 | version: 1.0.0+1 19 | 20 | environment: 21 | sdk: ">=2.12.0 <3.0.0" 22 | 23 | # Dependencies specify other packages that your package needs in order to work. 24 | # To automatically upgrade your package dependencies to the latest versions 25 | # consider running `flutter pub upgrade --major-versions`. Alternatively, 26 | # dependencies can be manually updated by changing the version numbers below to 27 | # the latest version available on pub.dev. To see which dependencies have newer 28 | # versions available, run `flutter pub outdated`. 29 | dependencies: 30 | flutter: 31 | sdk: flutter 32 | 33 | 34 | # The following adds the Cupertino Icons font to your application. 35 | # Use with the CupertinoIcons class for iOS style icons. 36 | cupertino_icons: ^1.0.2 37 | 38 | dev_dependencies: 39 | flutter_test: 40 | sdk: flutter 41 | 42 | # The "flutter_lints" package below contains a set of recommended lints to 43 | # encourage good coding practices. The lint set provided by the package is 44 | # activated in the `analysis_options.yaml` file located at the root of your 45 | # package. See that file for information about deactivating specific lint 46 | # rules and activating additional ones. 47 | flutter_lints: ^1.0.4 48 | get_it: ^7.2.0 49 | flutter_image_compress: ^1.1.0 50 | image_picker: ^0.8.4+4 51 | permission_handler: ^8.3.0 52 | path_provider: ^2.0.7 53 | provider: ^6.0.1 54 | dio: ^4.0.4 55 | 56 | # For information on the generic Dart part of this file, see the 57 | # following page: https://dart.dev/tools/pub/pubspec 58 | 59 | # The following section is specific to Flutter. 60 | flutter: 61 | 62 | # The following line ensures that the Material Icons font is 63 | # included with your application, so that you can use the icons in 64 | # the material Icons class. 65 | uses-material-design: true 66 | 67 | # To add assets to your application, add an assets section, like this: 68 | assets: 69 | - assets/images/ 70 | # - images/a_dot_ham.jpeg 71 | 72 | # An image asset can refer to one or more resolution-specific "variants", see 73 | # https://flutter.dev/assets-and-images/#resolution-aware. 74 | 75 | # For details regarding adding assets from package dependencies, see 76 | # https://flutter.dev/assets-and-images/#from-packages 77 | 78 | # To add custom fonts to your application, add a fonts section here, 79 | # in this "flutter" section. Each entry in this list should have a 80 | # "family" key with the font family name, and a "fonts" key with a 81 | # list giving the asset and other descriptors for the font. For 82 | # example: 83 | # fonts: 84 | # - family: Schyler 85 | # fonts: 86 | # - asset: fonts/Schyler-Regular.ttf 87 | # - asset: fonts/Schyler-Italic.ttf 88 | # style: italic 89 | # - family: Trajan Pro 90 | # fonts: 91 | # - asset: fonts/TrajanPro.ttf 92 | # - asset: fonts/TrajanPro_Bold.ttf 93 | # weight: 700 94 | # 95 | # For details regarding fonts from package dependencies, 96 | # see https://flutter.dev/custom-fonts/#from-packages 97 | -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility that Flutter provides. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | import 'package:flutter_tutorials/main.dart'; 12 | 13 | void main() { 14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 15 | // Build our app and trigger a frame. 16 | await tester.pumpWidget(const MyApp()); 17 | 18 | // Verify that our counter starts at 0. 19 | expect(find.text('0'), findsOneWidget); 20 | expect(find.text('1'), findsNothing); 21 | 22 | // Tap the '+' icon and trigger a frame. 23 | await tester.tap(find.byIcon(Icons.add)); 24 | await tester.pump(); 25 | 26 | // Verify that our counter has incremented. 27 | expect(find.text('0'), findsNothing); 28 | expect(find.text('1'), findsOneWidget); 29 | }); 30 | } 31 | --------------------------------------------------------------------------------