├── .gitignore ├── .metadata ├── README.md ├── analysis_options.yaml ├── android ├── .gitignore ├── app │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── brocodev │ │ │ │ └── flutter_samples │ │ │ │ └── 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 └── img │ ├── samples │ ├── broco.png │ ├── smart_home.gif │ └── vice_app.gif │ └── vice │ ├── vice-logo.png │ ├── vice1.png │ ├── vice10.png │ ├── vice11.png │ ├── vice12.png │ ├── vice13.png │ ├── vice2.png │ ├── vice3.png │ ├── vice4.png │ ├── vice5.png │ ├── vice6.png │ ├── vice7.png │ ├── vice8.png │ └── vice9.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 ├── core │ ├── app │ │ └── app.dart │ ├── routes │ │ └── routes.dart │ └── shared │ │ ├── domain │ │ └── entities │ │ │ └── sample.dart │ │ └── presentation │ │ ├── screens │ │ └── samples_screen.dart │ │ └── widgets │ │ └── sample_card.dart ├── main.dart └── samples │ ├── smart_home │ ├── core │ │ ├── app │ │ │ └── app.dart │ │ ├── core.dart │ │ ├── shared │ │ │ ├── domain │ │ │ │ ├── domain.dart │ │ │ │ └── entities │ │ │ │ │ ├── entities.dart │ │ │ │ │ ├── music_info.dart │ │ │ │ │ ├── smart_device.dart │ │ │ │ │ └── smart_room.dart │ │ │ ├── presentation │ │ │ │ ├── presentation.dart │ │ │ │ └── widgets │ │ │ │ │ ├── blue_light_dot.dart │ │ │ │ │ ├── parallax_image_card.dart │ │ │ │ │ ├── room_card.dart │ │ │ │ │ ├── sh_app_bar.dart │ │ │ │ │ ├── sh_card.dart │ │ │ │ │ ├── sh_divider.dart │ │ │ │ │ ├── sh_switch.dart │ │ │ │ │ ├── shimmer_arrows.dart │ │ │ │ │ └── widgets.dart │ │ │ └── shared.dart │ │ └── theme │ │ │ ├── sh_colors.dart │ │ │ ├── sh_icons.dart │ │ │ ├── sh_theme.dart │ │ │ └── theme.dart │ └── features │ │ ├── home │ │ └── presentation │ │ │ ├── screens │ │ │ └── home_screen.dart │ │ │ └── widgets │ │ │ ├── background_room_card.dart │ │ │ ├── lighted_wall_background.dart │ │ │ ├── page_view_indicators.dart │ │ │ ├── smart_rooms_page_view.dart │ │ │ └── widgets.dart │ │ └── smart_room │ │ └── presentation │ │ ├── screens │ │ └── room_detail_screen.dart │ │ └── widgets │ │ ├── air_conditioner_controls_card.dart │ │ ├── animated_counter.dart │ │ ├── light_intensity_slider_card.dart │ │ ├── lights_and_timer_switchers.dart │ │ ├── music_switchers.dart │ │ ├── room_details_page_view.dart │ │ └── widgets.dart │ └── vice_app │ ├── core │ ├── app │ │ └── vice_app.dart │ ├── constants │ │ ├── constants.dart │ │ └── vice_ui_consts.dart │ ├── core.dart │ ├── shared │ │ ├── domain │ │ │ ├── domain.dart │ │ │ └── entities │ │ │ │ ├── entities.dart │ │ │ │ └── magazine.dart │ │ ├── presentation │ │ │ ├── presentation.dart │ │ │ └── widgets │ │ │ │ ├── custom_tween_animation.dart │ │ │ │ ├── magazine_cover_image.dart │ │ │ │ ├── menu_button.dart │ │ │ │ └── widgets.dart │ │ └── shared.dart │ └── theme │ │ ├── theme.dart │ │ ├── vice_colors.dart │ │ ├── vice_icons.dart │ │ └── vice_theme.dart │ └── features │ ├── home │ └── presentation │ │ ├── screens │ │ └── home_screen.dart │ │ └── widgets │ │ ├── all_editions_list_view.dart │ │ ├── draggable_widget.dart │ │ ├── infinite_draggable_slider.dart │ │ └── widgets.dart │ └── magazines_details │ └── presentation │ ├── screens │ ├── back_to_home_screen.dart │ └── magazines_details_screen.dart │ └── widgets │ ├── content_magazines_page_view.dart │ ├── heart_and_save_button.dart │ ├── magazines_3d_page_view.dart │ ├── rectangle_page_view_indicators.dart │ ├── sticky_sliver_app_bar.dart │ └── widgets.dart ├── pubspec.lock └── pubspec.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | **/doc/api/ 26 | **/ios/Flutter/.last_build_id 27 | .dart_tool/ 28 | .flutter-plugins 29 | .flutter-plugins-dependencies 30 | .packages 31 | .pub-cache/ 32 | .pub/ 33 | /build/ 34 | 35 | # Symbolication related 36 | app.*.symbols 37 | 38 | # Obfuscation related 39 | app.*.map.json 40 | 41 | # Android Studio will place build artifacts here 42 | /android/app/debug 43 | /android/app/profile 44 | /android/app/release 45 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled. 5 | 6 | version: 7 | revision: 135454af32477f815a7525073027a3ff9eff1bfd 8 | channel: stable 9 | 10 | project_type: app 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: 135454af32477f815a7525073027a3ff9eff1bfd 17 | base_revision: 135454af32477f815a7525073027a3ff9eff1bfd 18 | - platform: android 19 | create_revision: 135454af32477f815a7525073027a3ff9eff1bfd 20 | base_revision: 135454af32477f815a7525073027a3ff9eff1bfd 21 | - platform: ios 22 | create_revision: 135454af32477f815a7525073027a3ff9eff1bfd 23 | base_revision: 135454af32477f815a7525073027a3ff9eff1bfd 24 | 25 | # User provided section 26 | 27 | # List of Local paths (relative to this file) that should be 28 | # ignored by the migrate tool. 29 | # 30 | # Files that are not part of the templates will be ignored by default. 31 | unmanaged_files: 32 | - 'lib/main.dart' 33 | - 'ios/Runner.xcodeproj/project.pbxproj' 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # flutter_samples 2 | 3 | This is a project created to develop sample apps and widgets developed with flutter. 4 | 5 | ## 2 - Vice App 6 | | DraggableSlider, Transform Animations, 3DPageView & Custom Route Transitions | 7 | |----------------------| 8 | || 9 | 10 | ### [Design Credits](https://www.instagram.com/p/CG49SrfABgV/?hl=es) 11 | 12 | ## 1 - Smart Home App 13 | | Parallax Effect, Implicit Animations & Custom Hero Transition | 14 | |----------------------| 15 | || 16 | 17 | ### [Design Credits](https://www.instagram.com/p/B-SrHZTiWMr/?hl=es) 18 | 19 | # My Social Media 20 | 21 | #### [Youtube](https://www.youtube.com/channel/UCe2G2ZkcHG9TliZ03L14U7g) [Facebook](https://www.facebook.com/brocodev) [Twitter](https://twitter.com/brocodev1) 22 | 23 | ## Getting Started 24 | 25 | This project is a starting point for a Flutter application. 26 | 27 | A few resources to get you started if this is your first Flutter project: 28 | 29 | - [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) 30 | - [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) 31 | 32 | For help getting started with Flutter development, view the 33 | [online documentation](https://docs.flutter.dev/), which offers tutorials, 34 | samples, guidance on mobile development, and a full API reference. 35 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:very_good_analysis/analysis_options.yaml 2 | linter: 3 | rules: 4 | public_member_api_docs: false 5 | -------------------------------------------------------------------------------- /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 flutter.compileSdkVersion 30 | ndkVersion flutter.ndkVersion 31 | 32 | compileOptions { 33 | sourceCompatibility JavaVersion.VERSION_1_8 34 | targetCompatibility JavaVersion.VERSION_1_8 35 | } 36 | 37 | kotlinOptions { 38 | jvmTarget = '1.8' 39 | } 40 | 41 | sourceSets { 42 | main.java.srcDirs += 'src/main/kotlin' 43 | } 44 | 45 | defaultConfig { 46 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 47 | applicationId "com.brocodev.flutter_samples" 48 | // You can update the following values to match your application needs. 49 | // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration. 50 | minSdkVersion flutter.minSdkVersion 51 | targetSdkVersion flutter.targetSdkVersion 52 | versionCode flutterVersionCode.toInteger() 53 | versionName flutterVersionName 54 | } 55 | 56 | buildTypes { 57 | release { 58 | // TODO: Add your own signing config for the release build. 59 | // Signing with the debug keys for now, so `flutter run --release` works. 60 | signingConfig signingConfigs.debug 61 | } 62 | } 63 | } 64 | 65 | flutter { 66 | source '../..' 67 | } 68 | 69 | dependencies { 70 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 71 | } 72 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 7 | 15 | 19 | 23 | 24 | 25 | 26 | 27 | 28 | 30 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/brocodev/flutter_samples/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.brocodev.flutter_samples 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/brocodev/flutter_samples/4419265295acba9c410ef6f27abe48f0dc63a9dd/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brocodev/flutter_samples/4419265295acba9c410ef6f27abe48f0dc63a9dd/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brocodev/flutter_samples/4419265295acba9c410ef6f27abe48f0dc63a9dd/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brocodev/flutter_samples/4419265295acba9c410ef6f27abe48f0dc63a9dd/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brocodev/flutter_samples/4419265295acba9c410ef6f27abe48f0dc63a9dd/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 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.6.10' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:7.1.3' 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 | } 25 | subprojects { 26 | project.evaluationDependsOn(':app') 27 | } 28 | 29 | tasks.register("clean", Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip 6 | -------------------------------------------------------------------------------- /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/img/samples/broco.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brocodev/flutter_samples/4419265295acba9c410ef6f27abe48f0dc63a9dd/assets/img/samples/broco.png -------------------------------------------------------------------------------- /assets/img/samples/smart_home.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brocodev/flutter_samples/4419265295acba9c410ef6f27abe48f0dc63a9dd/assets/img/samples/smart_home.gif -------------------------------------------------------------------------------- /assets/img/samples/vice_app.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brocodev/flutter_samples/4419265295acba9c410ef6f27abe48f0dc63a9dd/assets/img/samples/vice_app.gif -------------------------------------------------------------------------------- /assets/img/vice/vice-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brocodev/flutter_samples/4419265295acba9c410ef6f27abe48f0dc63a9dd/assets/img/vice/vice-logo.png -------------------------------------------------------------------------------- /assets/img/vice/vice1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brocodev/flutter_samples/4419265295acba9c410ef6f27abe48f0dc63a9dd/assets/img/vice/vice1.png -------------------------------------------------------------------------------- /assets/img/vice/vice10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brocodev/flutter_samples/4419265295acba9c410ef6f27abe48f0dc63a9dd/assets/img/vice/vice10.png -------------------------------------------------------------------------------- /assets/img/vice/vice11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brocodev/flutter_samples/4419265295acba9c410ef6f27abe48f0dc63a9dd/assets/img/vice/vice11.png -------------------------------------------------------------------------------- /assets/img/vice/vice12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brocodev/flutter_samples/4419265295acba9c410ef6f27abe48f0dc63a9dd/assets/img/vice/vice12.png -------------------------------------------------------------------------------- /assets/img/vice/vice13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brocodev/flutter_samples/4419265295acba9c410ef6f27abe48f0dc63a9dd/assets/img/vice/vice13.png -------------------------------------------------------------------------------- /assets/img/vice/vice2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brocodev/flutter_samples/4419265295acba9c410ef6f27abe48f0dc63a9dd/assets/img/vice/vice2.png -------------------------------------------------------------------------------- /assets/img/vice/vice3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brocodev/flutter_samples/4419265295acba9c410ef6f27abe48f0dc63a9dd/assets/img/vice/vice3.png -------------------------------------------------------------------------------- /assets/img/vice/vice4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brocodev/flutter_samples/4419265295acba9c410ef6f27abe48f0dc63a9dd/assets/img/vice/vice4.png -------------------------------------------------------------------------------- /assets/img/vice/vice5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brocodev/flutter_samples/4419265295acba9c410ef6f27abe48f0dc63a9dd/assets/img/vice/vice5.png -------------------------------------------------------------------------------- /assets/img/vice/vice6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brocodev/flutter_samples/4419265295acba9c410ef6f27abe48f0dc63a9dd/assets/img/vice/vice6.png -------------------------------------------------------------------------------- /assets/img/vice/vice7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brocodev/flutter_samples/4419265295acba9c410ef6f27abe48f0dc63a9dd/assets/img/vice/vice7.png -------------------------------------------------------------------------------- /assets/img/vice/vice8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brocodev/flutter_samples/4419265295acba9c410ef6f27abe48f0dc63a9dd/assets/img/vice/vice8.png -------------------------------------------------------------------------------- /assets/img/vice/vice9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brocodev/flutter_samples/4419265295acba9c410ef6f27abe48f0dc63a9dd/assets/img/vice/vice9.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 | 11.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, '11.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 | end 41 | end 42 | -------------------------------------------------------------------------------- /ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Flutter (1.0.0) 3 | - FMDB (2.7.5): 4 | - FMDB/standard (= 2.7.5) 5 | - FMDB/standard (2.7.5) 6 | - path_provider_foundation (0.0.1): 7 | - Flutter 8 | - FlutterMacOS 9 | - sqflite (0.0.2): 10 | - Flutter 11 | - FMDB (>= 2.7.5) 12 | 13 | DEPENDENCIES: 14 | - Flutter (from `Flutter`) 15 | - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) 16 | - sqflite (from `.symlinks/plugins/sqflite/ios`) 17 | 18 | SPEC REPOS: 19 | trunk: 20 | - FMDB 21 | 22 | EXTERNAL SOURCES: 23 | Flutter: 24 | :path: Flutter 25 | path_provider_foundation: 26 | :path: ".symlinks/plugins/path_provider_foundation/darwin" 27 | sqflite: 28 | :path: ".symlinks/plugins/sqflite/ios" 29 | 30 | SPEC CHECKSUMS: 31 | Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 32 | FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a 33 | path_provider_foundation: 37748e03f12783f9de2cb2c4eadfaa25fe6d4852 34 | sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 35 | 36 | PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3 37 | 38 | COCOAPODS: 1.12.1 39 | -------------------------------------------------------------------------------- /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/brocodev/flutter_samples/4419265295acba9c410ef6f27abe48f0dc63a9dd/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/brocodev/flutter_samples/4419265295acba9c410ef6f27abe48f0dc63a9dd/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/brocodev/flutter_samples/4419265295acba9c410ef6f27abe48f0dc63a9dd/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/brocodev/flutter_samples/4419265295acba9c410ef6f27abe48f0dc63a9dd/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/brocodev/flutter_samples/4419265295acba9c410ef6f27abe48f0dc63a9dd/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/brocodev/flutter_samples/4419265295acba9c410ef6f27abe48f0dc63a9dd/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/brocodev/flutter_samples/4419265295acba9c410ef6f27abe48f0dc63a9dd/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/brocodev/flutter_samples/4419265295acba9c410ef6f27abe48f0dc63a9dd/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/brocodev/flutter_samples/4419265295acba9c410ef6f27abe48f0dc63a9dd/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/brocodev/flutter_samples/4419265295acba9c410ef6f27abe48f0dc63a9dd/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/brocodev/flutter_samples/4419265295acba9c410ef6f27abe48f0dc63a9dd/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/brocodev/flutter_samples/4419265295acba9c410ef6f27abe48f0dc63a9dd/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/brocodev/flutter_samples/4419265295acba9c410ef6f27abe48f0dc63a9dd/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/brocodev/flutter_samples/4419265295acba9c410ef6f27abe48f0dc63a9dd/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/brocodev/flutter_samples/4419265295acba9c410ef6f27abe48f0dc63a9dd/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/brocodev/flutter_samples/4419265295acba9c410ef6f27abe48f0dc63a9dd/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brocodev/flutter_samples/4419265295acba9c410ef6f27abe48f0dc63a9dd/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brocodev/flutter_samples/4419265295acba9c410ef6f27abe48f0dc63a9dd/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 | CFBundleDisplayName 8 | Flutter Samples 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | flutter_samples 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(FLUTTER_BUILD_NUMBER) 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | UIViewControllerBasedStatusBarAppearance 45 | 46 | CADisableMinimumFrameDurationOnPhone 47 | 48 | UIApplicationSupportsIndirectInputEvents 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /lib/core/app/app.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_samples/core/routes/routes.dart'; 3 | import 'package:google_fonts/google_fonts.dart'; 4 | 5 | class SamplesApp extends StatelessWidget { 6 | const SamplesApp({super.key}); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return MaterialApp( 11 | debugShowCheckedModeBanner: false, 12 | title: 'Broco Samples App', 13 | theme: ThemeData( 14 | scaffoldBackgroundColor: Colors.grey.shade50, 15 | appBarTheme: AppBarTheme( 16 | elevation: 0, 17 | titleTextStyle: GoogleFonts.poppins( 18 | fontWeight: FontWeight.w600, 19 | fontSize: 20, 20 | color: Colors.grey.shade800, 21 | ), 22 | backgroundColor: Colors.grey.shade50, 23 | ), 24 | colorScheme: ColorScheme.fromSwatch( 25 | backgroundColor: Colors.grey.shade50, 26 | ), 27 | textTheme: GoogleFonts.poppinsTextTheme(), 28 | primarySwatch: Colors.green, 29 | ), 30 | initialRoute: '/', 31 | routes: AppRoutes.routes, 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/core/routes/routes.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter_samples/core/shared/domain/entities/sample.dart'; 3 | import 'package:flutter_samples/core/shared/presentation/screens/samples_screen.dart'; 4 | import 'package:flutter_samples/samples/smart_home/core/app/app.dart'; 5 | import 'package:flutter_samples/samples/vice_app/core/app/vice_app.dart'; 6 | 7 | class AppRoutes { 8 | static Map routes = { 9 | '/': (context) => const SamplesScreen(), 10 | Sample.smartHome.route: (context) => const SmartHomeApp(), 11 | Sample.viceApp.route: (context) => const ViceApp() 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /lib/core/shared/domain/entities/sample.dart: -------------------------------------------------------------------------------- 1 | enum Sample { 2 | viceApp._( 3 | title: 'Vice App', 4 | description: 'Draggable Slider + 3D PageView + Slivers', 5 | designer: 'giulio_cuscito', 6 | pathImage: 'assets/img/samples/vice_app.gif', 7 | route: 'vice_app', 8 | heightCard: 250, 9 | ), 10 | smartHome._( 11 | title: 'Smart Home', 12 | description: 'Parallax Effect + Animations + Custom Hero ', 13 | designer: 'giulio_cuscito', 14 | pathImage: 'assets/img/samples/smart_home.gif', 15 | route: 'smart_home', 16 | heightCard: 250, 17 | ); 18 | 19 | const Sample._({ 20 | required this.title, 21 | required this.description, 22 | required this.designer, 23 | required this.pathImage, 24 | required this.route, 25 | this.heightCard = 220, 26 | }); 27 | 28 | final String title; 29 | final String description; 30 | final String designer; 31 | final String pathImage; 32 | final String route; 33 | 34 | final double heightCard; 35 | } 36 | -------------------------------------------------------------------------------- /lib/core/shared/presentation/screens/samples_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_samples/core/shared/domain/entities/sample.dart'; 3 | import 'package:flutter_samples/core/shared/presentation/widgets/sample_card.dart'; 4 | 5 | class SamplesScreen extends StatelessWidget { 6 | const SamplesScreen({super.key}); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return Scaffold( 11 | appBar: AppBar( 12 | leading: const Padding( 13 | padding: EdgeInsets.only(left: 20), 14 | child: Center( 15 | child: CircleAvatar( 16 | backgroundImage: AssetImage('assets/img/samples/broco.png'), 17 | ), 18 | ), 19 | ), 20 | actions: const [ 21 | Padding( 22 | padding: EdgeInsets.only(right: 20), 23 | child: FlutterLogo(size: 25), 24 | ) 25 | ], 26 | title: const Text('Flutter Samples'), 27 | ), 28 | body: ListView.builder( 29 | physics: const BouncingScrollPhysics(), 30 | padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 20), 31 | itemCount: Sample.values.length, 32 | itemBuilder: (_, index) { 33 | final sample = Sample.values[index]; 34 | return SampleCard( 35 | sample: sample, 36 | index: Sample.values.length - index, 37 | ); 38 | }, 39 | ), 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/core/shared/presentation/widgets/sample_card.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_samples/core/shared/domain/entities/sample.dart'; 3 | import 'package:google_fonts/google_fonts.dart'; 4 | 5 | class SampleCard extends StatelessWidget { 6 | const SampleCard({ 7 | required this.sample, 8 | required this.index, 9 | super.key, 10 | }); 11 | 12 | final Sample sample; 13 | final int index; 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return Container( 18 | height: sample.heightCard, 19 | clipBehavior: Clip.antiAlias, 20 | margin: const EdgeInsets.only(bottom: 20), 21 | decoration: BoxDecoration( 22 | color: Colors.white, 23 | borderRadius: BorderRadius.circular(20), 24 | boxShadow: const [ 25 | BoxShadow( 26 | color: Color(0x06000000), 27 | blurRadius: 20, 28 | offset: Offset(-10, 10), 29 | ), 30 | ], 31 | ), 32 | child: Stack( 33 | children: [ 34 | Positioned.fill( 35 | child: Image.asset( 36 | sample.pathImage, 37 | fit: BoxFit.cover, 38 | ), 39 | ), 40 | const Positioned.fill( 41 | child: DecoratedBox( 42 | decoration: BoxDecoration(color: Colors.black26), 43 | ), 44 | ), 45 | Positioned( 46 | bottom: 12, 47 | top: 12, 48 | right: 12, 49 | left: 12, 50 | child: Column( 51 | children: [ 52 | Row( 53 | children: [ 54 | Opacity( 55 | opacity: .6, 56 | child: DecoratedBox( 57 | decoration: BoxDecoration( 58 | color: Colors.white, 59 | shape: BoxShape.circle, 60 | border: Border.all(width: 4), 61 | ), 62 | child: Padding( 63 | padding: const EdgeInsets.all(14), 64 | child: Text( 65 | '$index', 66 | style: GoogleFonts.poppins( 67 | fontWeight: FontWeight.bold, 68 | fontSize: 20, 69 | color: Colors.black, 70 | ), 71 | ), 72 | ), 73 | ), 74 | ), 75 | const SizedBox(width: 10), 76 | Flexible( 77 | child: Column( 78 | crossAxisAlignment: CrossAxisAlignment.start, 79 | children: [ 80 | Text( 81 | sample.title, 82 | maxLines: 1, 83 | style: GoogleFonts.poppins( 84 | color: Colors.white, 85 | fontWeight: FontWeight.bold, 86 | fontSize: 18, 87 | ), 88 | ), 89 | FittedBox( 90 | fit: BoxFit.scaleDown, 91 | child: Text( 92 | sample.description, 93 | maxLines: 1, 94 | style: GoogleFonts.poppins( 95 | color: Colors.white, 96 | fontSize: 14, 97 | ), 98 | ), 99 | ), 100 | ], 101 | ), 102 | ) 103 | ], 104 | ), 105 | const Spacer(), 106 | Row( 107 | crossAxisAlignment: CrossAxisAlignment.end, 108 | children: [ 109 | Expanded( 110 | flex: 5, 111 | child: Column( 112 | crossAxisAlignment: CrossAxisAlignment.start, 113 | children: [ 114 | Text( 115 | 'Designer', 116 | style: GoogleFonts.poppins( 117 | color: Colors.white, 118 | ), 119 | ), 120 | Text( 121 | sample.designer, 122 | style: GoogleFonts.poppins( 123 | fontWeight: FontWeight.w600, 124 | color: Colors.white, 125 | fontSize: 16, 126 | ), 127 | ), 128 | ], 129 | ), 130 | ), 131 | Expanded( 132 | flex: 4, 133 | child: TextButton( 134 | onPressed: () => 135 | Navigator.pushNamed(context, sample.route), 136 | style: TextButton.styleFrom( 137 | padding: const EdgeInsets.symmetric( 138 | horizontal: 15, 139 | vertical: 10, 140 | ), 141 | shape: RoundedRectangleBorder( 142 | borderRadius: BorderRadius.circular(10), 143 | side: const BorderSide(color: Colors.white), 144 | ), 145 | backgroundColor: Colors.black.withOpacity(.5), 146 | ), 147 | child: FittedBox( 148 | child: Text( 149 | 'Open sample', 150 | maxLines: 1, 151 | softWrap: false, 152 | style: GoogleFonts.poppins( 153 | color: Colors.white, 154 | fontWeight: FontWeight.w500, 155 | ), 156 | ), 157 | ), 158 | ), 159 | ) 160 | ], 161 | ) 162 | ], 163 | ), 164 | ) 165 | ], 166 | ), 167 | ); 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'package:flutter_samples/core/app/app.dart'; 4 | 5 | void main() { 6 | SystemChrome.setSystemUIOverlayStyle( 7 | const SystemUiOverlayStyle(statusBarColor: Colors.transparent), 8 | ); 9 | runApp(const SamplesApp()); 10 | } 11 | -------------------------------------------------------------------------------- /lib/samples/smart_home/core/app/app.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'package:flutter_samples/samples/smart_home/core/theme/sh_theme.dart'; 4 | import 'package:flutter_samples/samples/smart_home/features/home/presentation/screens/home_screen.dart'; 5 | import 'package:ui_common/ui_common.dart'; 6 | 7 | class SmartHomeApp extends StatelessWidget { 8 | const SmartHomeApp({super.key}); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | SystemChrome.setSystemUIOverlayStyle( 13 | SystemUiOverlayStyle.light.copyWith(statusBarColor: Colors.transparent), 14 | ); 15 | return ScreenUtilInit( 16 | builder: (_, child) => MaterialApp( 17 | title: 'Smart Home App', 18 | debugShowCheckedModeBanner: false, 19 | theme: SHTheme.dark, 20 | home: child, 21 | ), 22 | child: const HomeScreen(), 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/samples/smart_home/core/core.dart: -------------------------------------------------------------------------------- 1 | export 'shared/shared.dart'; 2 | export 'theme/theme.dart'; 3 | -------------------------------------------------------------------------------- /lib/samples/smart_home/core/shared/domain/domain.dart: -------------------------------------------------------------------------------- 1 | export 'entities/entities.dart'; 2 | -------------------------------------------------------------------------------- /lib/samples/smart_home/core/shared/domain/entities/entities.dart: -------------------------------------------------------------------------------- 1 | export 'music_info.dart'; 2 | export 'smart_device.dart'; 3 | export 'smart_room.dart'; 4 | -------------------------------------------------------------------------------- /lib/samples/smart_home/core/shared/domain/entities/music_info.dart: -------------------------------------------------------------------------------- 1 | class MusicInfo { 2 | MusicInfo({required this.isOn, required this.currentSong}); 3 | 4 | final bool isOn; 5 | final Song currentSong; 6 | } 7 | 8 | class Song { 9 | const Song(this.title, this.artist, this.thumbUrl); 10 | 11 | final String title; 12 | final String artist; 13 | final String thumbUrl; 14 | 15 | static const Song defaultSong = Song( 16 | 'I wanna be your slave', 17 | 'MANESKIN', 18 | 'https://i.ibb.co/bQ65QkD/manesking.jpg', 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /lib/samples/smart_home/core/shared/domain/entities/smart_device.dart: -------------------------------------------------------------------------------- 1 | class SmartDevice { 2 | SmartDevice({required this.isOn, required this.value}); 3 | 4 | final bool isOn; 5 | final int value; 6 | } 7 | -------------------------------------------------------------------------------- /lib/samples/smart_home/core/shared/domain/entities/smart_room.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_samples/samples/smart_home/core/shared/domain/entities/music_info.dart'; 2 | import 'package:flutter_samples/samples/smart_home/core/shared/domain/entities/smart_device.dart'; 3 | 4 | class SmartRoom { 5 | SmartRoom({ 6 | required this.id, 7 | required this.name, 8 | required this.imageUrl, 9 | required this.temperature, 10 | required this.airHumidity, 11 | required this.lights, 12 | required this.airCondition, 13 | required this.timer, 14 | required this.musicInfo, 15 | }); 16 | 17 | final String id; 18 | final String name; 19 | final String imageUrl; 20 | final double temperature; 21 | final double airHumidity; 22 | final SmartDevice lights; 23 | final SmartDevice airCondition; 24 | final SmartDevice timer; 25 | final MusicInfo musicInfo; 26 | 27 | SmartRoom copyWith({ 28 | String? id, 29 | String? name, 30 | String? imageUrl, 31 | double? temperature, 32 | double? airHumidity, 33 | SmartDevice? lights, 34 | SmartDevice? airCondition, 35 | SmartDevice? timer, 36 | MusicInfo? musicInfo, 37 | }) => 38 | SmartRoom( 39 | id: id ?? this.id, 40 | name: name ?? this.name, 41 | imageUrl: imageUrl ?? this.imageUrl, 42 | temperature: temperature ?? this.temperature, 43 | airHumidity: airHumidity ?? this.airHumidity, 44 | lights: lights ?? this.lights, 45 | airCondition: airCondition ?? this.airCondition, 46 | musicInfo: musicInfo ?? this.musicInfo, 47 | timer: timer ?? this.timer, 48 | ); 49 | 50 | static List fakeValues = [ 51 | _room, 52 | _room.copyWith(id: '2', name: 'DINING ROOM', imageUrl: _imagesUrls[2]), 53 | _room.copyWith(id: '3', name: 'KITCHEN', imageUrl: _imagesUrls[3]), 54 | _room.copyWith(id: '4', name: 'BEDROOM', imageUrl: _imagesUrls[4]), 55 | _room.copyWith(id: '5', name: 'BATHROOM', imageUrl: _imagesUrls[1]), 56 | ]; 57 | } 58 | 59 | final _room = SmartRoom( 60 | id: '1', 61 | name: 'LIVING ROOM', 62 | imageUrl: _imagesUrls[0], 63 | temperature: 12, 64 | airHumidity: 23, 65 | lights: SmartDevice(isOn: true, value: 20), 66 | timer: SmartDevice(isOn: false, value: 20), 67 | airCondition: SmartDevice(isOn: false, value: 10), 68 | musicInfo: MusicInfo( 69 | isOn: false, 70 | currentSong: Song.defaultSong, 71 | ), 72 | ); 73 | 74 | const _imagesUrls = [ 75 | 'https://images.unsplash.com/photo-1600121848594-d8644e57abab?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1740&q=80', 76 | 'https://images.unsplash.com/photo-1564540583246-934409427776?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1706&q=80', 77 | 'https://images.unsplash.com/photo-1616486886892-ff366aa67ba4?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1160&q=80', 78 | 'https://images.unsplash.com/photo-1588854337221-4cf9fa96059c?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1740&q=80', 79 | 'https://m.media-amazon.com/images/I/715hLONUQOL._AC_SL1500_.jpg' 80 | ]; 81 | -------------------------------------------------------------------------------- /lib/samples/smart_home/core/shared/presentation/presentation.dart: -------------------------------------------------------------------------------- 1 | export 'widgets/widgets.dart'; 2 | -------------------------------------------------------------------------------- /lib/samples/smart_home/core/shared/presentation/widgets/blue_light_dot.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:ui_common/ui_common.dart'; 3 | 4 | class BlueLightDot extends StatelessWidget { 5 | const BlueLightDot({ 6 | super.key, 7 | }); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return SizedBox.square( 12 | dimension: 8.sp, 13 | child: const DecoratedBox( 14 | decoration: BoxDecoration( 15 | color: Colors.cyan, 16 | shape: BoxShape.circle, 17 | boxShadow: [ 18 | BoxShadow( 19 | color: Colors.cyan, 20 | blurRadius: 10, 21 | ) 22 | ], 23 | ), 24 | ), 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/samples/smart_home/core/shared/presentation/widgets/parallax_image_card.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:cached_network_image/cached_network_image.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_samples/samples/smart_home/core/theme/sh_colors.dart'; 6 | import 'package:ui_common/ui_common.dart'; 7 | 8 | class ParallaxImageCard extends StatelessWidget { 9 | const ParallaxImageCard({ 10 | required this.imageUrl, 11 | this.parallaxValue = 0, 12 | super.key, 13 | }); 14 | 15 | final String imageUrl; 16 | final double parallaxValue; 17 | 18 | // ----------------------------------------------- 19 | // Decoration for image and parallax effect 20 | // ----------------------------------------------- 21 | BoxDecoration get _parallaxUrlImageDecoration => BoxDecoration( 22 | borderRadius: 12.borderRadiusA, 23 | color: SHColors.hintColor, 24 | boxShadow: const [ 25 | BoxShadow( 26 | color: Colors.black26, 27 | blurRadius: 12, 28 | offset: Offset(-7, 7), 29 | ), 30 | ], 31 | image: DecorationImage( 32 | image: CachedNetworkImageProvider(imageUrl), 33 | fit: BoxFit.cover, 34 | colorFilter: 35 | const ColorFilter.mode(Colors.black26, BlendMode.colorBurn), 36 | alignment: Alignment(lerpDouble(.5, -.5, parallaxValue)!, 0), 37 | ), 38 | ); 39 | 40 | // ----------------------------------------------- 41 | // Radial vignette effect decoration 42 | // ----------------------------------------------- 43 | BoxDecoration get _vignetteDecoration => BoxDecoration( 44 | borderRadius: 12.borderRadiusA, 45 | gradient: const RadialGradient( 46 | radius: 2, 47 | colors: [Colors.transparent, Colors.black], 48 | ), 49 | ); 50 | 51 | @override 52 | Widget build(BuildContext context) { 53 | return Stack( 54 | fit: StackFit.expand, 55 | children: [ 56 | DecoratedBox(decoration: _parallaxUrlImageDecoration), 57 | DecoratedBox(decoration: _vignetteDecoration), 58 | ], 59 | ); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /lib/samples/smart_home/core/shared/presentation/widgets/room_card.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_samples/samples/smart_home/core/core.dart'; 5 | import 'package:flutter_samples/samples/smart_home/features/home/presentation/widgets/background_room_card.dart'; 6 | import 'package:flutter_samples/samples/smart_home/features/smart_room/presentation/screens/room_detail_screen.dart'; 7 | import 'package:ui_common/ui_common.dart'; 8 | 9 | class RoomCard extends StatelessWidget { 10 | const RoomCard({ 11 | required this.percent, 12 | required this.room, 13 | required this.expand, 14 | required this.onSwipeUp, 15 | required this.onSwipeDown, 16 | required this.onTap, 17 | super.key, 18 | }); 19 | 20 | final double percent; 21 | final SmartRoom room; 22 | final VoidCallback onSwipeUp; 23 | final VoidCallback onSwipeDown; 24 | final VoidCallback onTap; 25 | final bool expand; 26 | 27 | @override 28 | Widget build(BuildContext context) { 29 | return TweenAnimationBuilder( 30 | duration: kThemeAnimationDuration, 31 | curve: Curves.fastOutSlowIn, 32 | tween: Tween(begin: 0, end: expand ? 1 : 0), 33 | builder: (_, value, __) => Stack( 34 | children: [ 35 | // ----------------------------------------------- 36 | // Background information card 37 | // ----------------------------------------------- 38 | Transform.scale( 39 | scale: lerpDouble(.85, 1.2, value), 40 | child: Padding( 41 | padding: EdgeInsets.only(bottom: 160.h), 42 | child: BackgroundRoomCard(room: room, translation: value), 43 | ), 44 | ), 45 | // ----------------------------------------------- 46 | // Room image card with parallax effect 47 | // ----------------------------------------------- 48 | Padding( 49 | padding: 160.edgeInsetsB, 50 | child: Transform( 51 | transform: Matrix4.translationValues(0, -80.h * value, 0), 52 | child: GestureDetector( 53 | onTap: onTap, 54 | onVerticalDragUpdate: (details) { 55 | if (details.primaryDelta! < -10) onSwipeUp(); 56 | if (details.primaryDelta! > 10) onSwipeDown(); 57 | }, 58 | child: Hero( 59 | tag: room.id, 60 | // ----------------------------------------------- 61 | // Custom hero widget 62 | // ----------------------------------------------- 63 | flightShuttleBuilder: (_, animation, __, ___, ____) { 64 | return AnimatedBuilder( 65 | animation: animation, 66 | builder: (context, _) => Material( 67 | type: MaterialType.transparency, 68 | child: RoomDetailItems( 69 | animation: animation, 70 | topPadding: context.mediaQuery.padding.top, 71 | room: room, 72 | ), 73 | ), 74 | ); 75 | }, 76 | child: Stack( 77 | fit: StackFit.expand, 78 | children: [ 79 | ParallaxImageCard( 80 | imageUrl: room.imageUrl, 81 | parallaxValue: percent, 82 | ), 83 | VerticalRoomTitle(room: room), 84 | const CameraIconButton(), 85 | const AnimatedUpwardArrows() 86 | ], 87 | ), 88 | ), 89 | ), 90 | ), 91 | ), 92 | ], 93 | ), 94 | ); 95 | } 96 | } 97 | 98 | class AnimatedUpwardArrows extends StatelessWidget { 99 | const AnimatedUpwardArrows({ 100 | super.key, 101 | }); 102 | 103 | @override 104 | Widget build(BuildContext context) { 105 | return Align( 106 | alignment: Alignment.bottomCenter, 107 | child: Column( 108 | mainAxisAlignment: MainAxisAlignment.end, 109 | children: [ 110 | const ShimmerArrows(), 111 | height24, 112 | Container( 113 | margin: 16.edgeInsetsB, 114 | height: 4.h, 115 | width: 0.35.sw, 116 | decoration: BoxDecoration( 117 | color: SHColors.textColor, 118 | borderRadius: 8.borderRadiusA, 119 | ), 120 | ), 121 | ], 122 | ), 123 | ); 124 | } 125 | } 126 | 127 | class CameraIconButton extends StatelessWidget { 128 | const CameraIconButton({ 129 | super.key, 130 | }); 131 | 132 | @override 133 | Widget build(BuildContext context) { 134 | return Material( 135 | type: MaterialType.transparency, 136 | child: Align( 137 | alignment: Alignment.topRight, 138 | child: IconButton( 139 | onPressed: () {}, 140 | icon: const Icon( 141 | SHIcons.camera, 142 | color: SHColors.textColor, 143 | ), 144 | ), 145 | ), 146 | ); 147 | } 148 | } 149 | 150 | class VerticalRoomTitle extends StatelessWidget { 151 | const VerticalRoomTitle({ 152 | required this.room, 153 | super.key, 154 | }); 155 | 156 | final SmartRoom room; 157 | 158 | @override 159 | Widget build(BuildContext context) { 160 | // final dx = 50 * animationValue; 161 | // final opacity = 1 - animationValue; 162 | return Align( 163 | alignment: Alignment.centerLeft, 164 | child: RotatedBox( 165 | quarterTurns: -1, 166 | child: FittedBox( 167 | child: Padding( 168 | padding: EdgeInsets.only(left: 40.h, right: 20.h, top: 12.w), 169 | child: Text( 170 | room.name.replaceAll(' ', ''), 171 | maxLines: 1, 172 | style: context.displayLarge.copyWith(color: SHColors.textColor), 173 | ), 174 | ), 175 | ), 176 | ), 177 | ); 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /lib/samples/smart_home/core/shared/presentation/widgets/sh_app_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_samples/samples/smart_home/core/theme/sh_icons.dart'; 3 | import 'package:ui_common/ui_common.dart'; 4 | 5 | class SHAppBar extends StatelessWidget implements PreferredSizeWidget { 6 | const SHAppBar({ 7 | super.key, 8 | }); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return AppBar( 13 | leading: Hero( 14 | tag: 'app-bar-icon-1', 15 | child: Material( 16 | type: MaterialType.transparency, 17 | child: IconButton( 18 | onPressed: () {}, 19 | icon: const Icon(SHIcons.menu), 20 | ), 21 | ), 22 | ), 23 | actions: [ 24 | Hero( 25 | tag: 'app-bar-icon-2', 26 | child: Material( 27 | type: MaterialType.transparency, 28 | child: IconButton( 29 | onPressed: () {}, 30 | icon: const Icon(SHIcons.search), 31 | ), 32 | ), 33 | ), 34 | width12, 35 | ], 36 | ); 37 | } 38 | 39 | @override 40 | Size get preferredSize => const Size.fromHeight(kToolbarHeight); 41 | } 42 | -------------------------------------------------------------------------------- /lib/samples/smart_home/core/shared/presentation/widgets/sh_card.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_samples/samples/smart_home/core/shared/presentation/widgets/sh_divider.dart'; 3 | import 'package:flutter_samples/samples/smart_home/core/theme/sh_colors.dart'; 4 | import 'package:ui_common/ui_common.dart'; 5 | 6 | class SHCard extends StatelessWidget { 7 | const SHCard({ 8 | super.key, 9 | this.height, 10 | this.children = const [], 11 | this.childrenPadding = EdgeInsets.zero, 12 | }); 13 | 14 | final double? height; 15 | final List children; 16 | final EdgeInsets childrenPadding; 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return SizedBox( 21 | height: height, 22 | child: DecoratedBox( 23 | decoration: BoxDecoration( 24 | gradient: const LinearGradient( 25 | begin: Alignment.topCenter, 26 | end: Alignment.bottomCenter, 27 | colors: SHColors.cardColors, 28 | ), 29 | boxShadow: const [ 30 | BoxShadow( 31 | color: Colors.black38, 32 | blurRadius: 20, 33 | offset: Offset(-10, 10), 34 | ), 35 | ], 36 | borderRadius: 12.borderRadiusA, 37 | ), 38 | child: Column( 39 | mainAxisSize: MainAxisSize.min, 40 | children: [ 41 | for (int index = 0; index < children.length; index++) ...[ 42 | Padding( 43 | padding: childrenPadding, 44 | child: children[index], 45 | ), 46 | if (index < children.length - 1) const SHDivider(), 47 | ], 48 | ], 49 | ), 50 | ), 51 | ); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/samples/smart_home/core/shared/presentation/widgets/sh_divider.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_samples/samples/smart_home/core/theme/sh_colors.dart'; 3 | import 'package:ui_common/ui_common.dart'; 4 | 5 | class SHDivider extends StatelessWidget { 6 | const SHDivider({ 7 | super.key, 8 | }); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return DecoratedBox( 13 | decoration: const BoxDecoration( 14 | color: SHColors.backgroundColor, 15 | boxShadow: [ 16 | BoxShadow( 17 | blurRadius: 1, 18 | color: Colors.white54, 19 | offset: Offset(0, 1), 20 | ), 21 | ], 22 | ), 23 | child: SizedBox(height: 1.r, width: double.infinity), 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/samples/smart_home/core/shared/presentation/widgets/sh_switch.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_samples/samples/smart_home/core/theme/sh_colors.dart'; 4 | import 'package:ui_common/ui_common.dart'; 5 | 6 | class SHSwitcher extends StatelessWidget { 7 | const SHSwitcher({ 8 | required this.value, 9 | required this.onChanged, 10 | this.icon, 11 | super.key, 12 | }); 13 | 14 | final bool value; 15 | final Icon? icon; 16 | final ValueChanged onChanged; 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return FittedBox( 21 | fit: BoxFit.scaleDown, 22 | child: Row( 23 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 24 | children: [ 25 | if (icon != null) ...[ 26 | IconTheme( 27 | data: IconThemeData( 28 | color: value ? SHColors.selectedColor : Colors.white38, 29 | ), 30 | child: icon!, 31 | ), 32 | width8, 33 | ], 34 | CupertinoSwitch( 35 | trackColor: SHColors.trackColor, 36 | activeColor: SHColors.trackColor, 37 | thumbColor: value ? null : Colors.white60, 38 | value: value, 39 | onChanged: onChanged, 40 | ), 41 | width8, 42 | Text( 43 | value ? 'ON' : 'OFF', 44 | style: TextStyle( 45 | fontSize: 16.sp, 46 | fontWeight: FontWeight.w900, 47 | color: value ? SHColors.selectedColor : Colors.white38, 48 | ), 49 | ) 50 | ], 51 | ), 52 | ); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /lib/samples/smart_home/core/shared/presentation/widgets/shimmer_arrows.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_samples/samples/smart_home/core/theme/sh_icons.dart'; 3 | 4 | class ShimmerArrows extends StatefulWidget { 5 | const ShimmerArrows({super.key}); 6 | 7 | @override 8 | State createState() => _ShimmerArrowsState(); 9 | } 10 | 11 | class _ShimmerArrowsState extends State 12 | with TickerProviderStateMixin { 13 | late final AnimationController _controller; 14 | 15 | @override 16 | void initState() { 17 | _controller = AnimationController.unbounded(vsync: this) 18 | ..repeat(min: -0.5, max: 1.5, period: const Duration(milliseconds: 1000)); 19 | super.initState(); 20 | } 21 | 22 | @override 23 | void dispose() { 24 | _controller.dispose(); 25 | super.dispose(); 26 | } 27 | 28 | Gradient get gradient => LinearGradient( 29 | begin: Alignment.topCenter, 30 | end: Alignment.bottomCenter, 31 | colors: const [Colors.white10, Colors.white, Colors.white10], 32 | stops: const [0.0, 0.3, 1], 33 | transform: _SlideGradientTransform(_controller.value), 34 | ); 35 | 36 | @override 37 | Widget build(BuildContext context) { 38 | return AnimatedBuilder( 39 | animation: _controller, 40 | builder: (_, child) => ShaderMask( 41 | shaderCallback: (bounds) => gradient.createShader(bounds), 42 | child: child, 43 | ), 44 | child: const Column( 45 | children: [ 46 | Align(heightFactor: .4, child: Icon(SHIcons.arrowUp)), 47 | Align(heightFactor: .4, child: Icon(SHIcons.arrowUp)), 48 | Align(heightFactor: .4, child: Icon(SHIcons.arrowUp)), 49 | ], 50 | ), 51 | ); 52 | } 53 | } 54 | 55 | class _SlideGradientTransform extends GradientTransform { 56 | const _SlideGradientTransform(this.percent); 57 | 58 | final double percent; 59 | 60 | @override 61 | Matrix4? transform(Rect bounds, {TextDirection? textDirection}) => 62 | Matrix4.translationValues(0, -(bounds.height * percent), 0); 63 | } 64 | -------------------------------------------------------------------------------- /lib/samples/smart_home/core/shared/presentation/widgets/widgets.dart: -------------------------------------------------------------------------------- 1 | export 'blue_light_dot.dart'; 2 | export 'parallax_image_card.dart'; 3 | export 'sh_app_bar.dart'; 4 | export 'sh_card.dart'; 5 | export 'sh_divider.dart'; 6 | export 'sh_switch.dart'; 7 | export 'shimmer_arrows.dart'; 8 | -------------------------------------------------------------------------------- /lib/samples/smart_home/core/shared/shared.dart: -------------------------------------------------------------------------------- 1 | export 'domain/domain.dart'; 2 | export 'presentation/presentation.dart'; 3 | -------------------------------------------------------------------------------- /lib/samples/smart_home/core/theme/sh_colors.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | abstract class SHColors { 4 | static const Color textColor = Color(0xFFD0D7E1); 5 | static const Color hintColor = Color(0xFF717578); 6 | static const Color backgroundColor = Color(0xff343941); 7 | static const Color cardColor = Color(0xff4D565F); 8 | static const Color trackColor = Color(0xff2C3037); 9 | static const Color selectedColor = Color(0xffE3D0B2); 10 | 11 | static const List cardColors = [ 12 | Color(0xff60656D), 13 | Color(0xff4D565F), 14 | Color(0xff464D57), 15 | ]; 16 | static const List dimmedLightColors = [ 17 | Color(0xff505863), 18 | Color(0xff424a53), 19 | Color(0xff343941), 20 | ]; 21 | } 22 | -------------------------------------------------------------------------------- /lib/samples/smart_home/core/theme/sh_icons.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:fluttericon/font_awesome5_icons.dart'; 4 | 5 | abstract class SHIcons { 6 | static const IconData search = CupertinoIcons.search; 7 | static const IconData menu = CupertinoIcons.line_horizontal_3; 8 | static const IconData lock = CupertinoIcons.lock_fill; 9 | static const IconData home = CupertinoIcons.home; 10 | static const IconData settings = CupertinoIcons.settings; 11 | static const IconData camera = CupertinoIcons.camera_fill; 12 | static const IconData arrowUp = Icons.keyboard_arrow_up; 13 | static const IconData fan = FontAwesome5.fan; 14 | static const IconData lightBulbOutline = Icons.lightbulb_outline; 15 | static const IconData lightBulb = Icons.lightbulb; 16 | static const IconData music = Icons.music_note; 17 | static const IconData thermostat = Icons.device_thermostat; 18 | static const IconData waterDrop = Icons.water_drop_outlined; 19 | static const IconData timer = Icons.timer_outlined; 20 | static const IconData timerOff = Icons.timer_off_outlined; 21 | static const IconData lightMin = Icons.light_mode_outlined; 22 | static const IconData lightMax = Icons.light_mode; 23 | static const IconData snowFlake = CupertinoIcons.snow; 24 | static const IconData wind = CupertinoIcons.wind; 25 | static const IconData air = FontAwesome5.air_freshener; 26 | } 27 | -------------------------------------------------------------------------------- /lib/samples/smart_home/core/theme/sh_theme.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_samples/samples/smart_home/core/theme/sh_colors.dart'; 3 | import 'package:google_fonts/google_fonts.dart'; 4 | import 'package:ui_common/ui_common.dart'; 5 | 6 | abstract class SHTheme { 7 | static ThemeData dark = ThemeData( 8 | textTheme: TextTheme( 9 | bodyLarge: GoogleFonts.gruppo( 10 | fontSize: 16.sp, 11 | fontWeight: FontWeight.w800, 12 | color: SHColors.textColor, 13 | ), 14 | bodyMedium: GoogleFonts.gruppo( 15 | fontSize: 14.sp, 16 | fontWeight: FontWeight.w800, 17 | color: SHColors.textColor, 18 | ), 19 | bodySmall: GoogleFonts.montserrat( 20 | fontSize: 10.sp, 21 | fontWeight: FontWeight.w400, 22 | color: SHColors.textColor, 23 | ), 24 | displayLarge: GoogleFonts.buda( 25 | fontSize: 70.sp, 26 | color: SHColors.textColor, 27 | ), 28 | displayMedium: GoogleFonts.buda( 29 | fontSize: 50.sp, 30 | color: SHColors.textColor, 31 | ), 32 | displaySmall: GoogleFonts.buda( 33 | fontSize: 40.sp, 34 | color: SHColors.textColor, 35 | ), 36 | ), 37 | appBarTheme: const AppBarTheme( 38 | elevation: 0, 39 | backgroundColor: Colors.transparent, 40 | ), 41 | iconTheme: const IconThemeData(color: SHColors.textColor), 42 | textButtonTheme: TextButtonThemeData( 43 | style: TextButton.styleFrom( 44 | foregroundColor: Colors.white, 45 | shape: const StadiumBorder(), 46 | textStyle: GoogleFonts.montserrat( 47 | fontSize: 12.sp, 48 | fontWeight: FontWeight.w800, 49 | ), 50 | ), 51 | ), 52 | sliderTheme: SliderThemeData( 53 | activeTrackColor: SHColors.selectedColor, 54 | inactiveTrackColor: SHColors.trackColor, 55 | thumbColor: SHColors.selectedColor, 56 | trackHeight: 2.sp, 57 | ), 58 | bottomNavigationBarTheme: BottomNavigationBarThemeData( 59 | backgroundColor: Colors.transparent, 60 | elevation: 0, 61 | selectedIconTheme: IconThemeData(size: 40.sp), 62 | unselectedIconTheme: IconThemeData(size: 40.sp), 63 | selectedItemColor: SHColors.hintColor, 64 | unselectedItemColor: SHColors.hintColor, 65 | selectedLabelStyle: GoogleFonts.montserrat( 66 | fontSize: 12.sp, 67 | fontWeight: FontWeight.w700, 68 | ), 69 | unselectedLabelStyle: GoogleFonts.montserrat( 70 | fontSize: 12.sp, 71 | fontWeight: FontWeight.w700, 72 | ), 73 | ), 74 | scaffoldBackgroundColor: SHColors.backgroundColor, 75 | ); 76 | } 77 | -------------------------------------------------------------------------------- /lib/samples/smart_home/core/theme/theme.dart: -------------------------------------------------------------------------------- 1 | export 'sh_colors.dart'; 2 | export 'sh_icons.dart'; 3 | export 'sh_theme.dart'; 4 | -------------------------------------------------------------------------------- /lib/samples/smart_home/features/home/presentation/screens/home_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_samples/samples/smart_home/core/core.dart'; 3 | import 'package:flutter_samples/samples/smart_home/features/home/presentation/widgets/widgets.dart'; 4 | import 'package:ui_common/ui_common.dart'; 5 | 6 | class HomeScreen extends StatefulWidget { 7 | const HomeScreen({super.key}); 8 | 9 | @override 10 | State createState() => _HomeScreenState(); 11 | } 12 | 13 | class _HomeScreenState extends State { 14 | final PageController controller = PageController(viewportFraction: .8); 15 | final ValueNotifier pageNotifier = ValueNotifier(0); 16 | final ValueNotifier roomSelectorNotifier = ValueNotifier(-1); 17 | 18 | @override 19 | void initState() { 20 | controller.addListener(pageListener); 21 | super.initState(); 22 | } 23 | 24 | @override 25 | void dispose() { 26 | controller 27 | ..removeListener(pageListener) 28 | ..dispose(); 29 | super.dispose(); 30 | } 31 | 32 | void pageListener() { 33 | pageNotifier.value = controller.page ?? 0; 34 | } 35 | 36 | @override 37 | Widget build(BuildContext context) { 38 | return LightedBackground( 39 | child: Scaffold( 40 | backgroundColor: Colors.transparent, 41 | appBar: const SHAppBar(), 42 | body: SafeArea( 43 | child: Column( 44 | children: [ 45 | height24, 46 | Text('SELECT A ROOM', style: context.bodyLarge), 47 | height32, 48 | Expanded( 49 | child: Stack( 50 | fit: StackFit.expand, 51 | children: [ 52 | SmartRoomsPageView( 53 | pageNotifier: pageNotifier, 54 | roomSelectorNotifier: roomSelectorNotifier, 55 | controller: controller, 56 | ), 57 | Positioned.fill( 58 | top: null, 59 | child: Column( 60 | children: [ 61 | _PageIndicators( 62 | roomSelectorNotifier: roomSelectorNotifier, 63 | pageNotifier: pageNotifier, 64 | ), 65 | _BottomNavigationBar( 66 | roomSelectorNotifier: roomSelectorNotifier, 67 | ), 68 | ], 69 | ), 70 | ), 71 | ], 72 | ), 73 | ), 74 | ], 75 | ), 76 | ), 77 | ), 78 | ); 79 | } 80 | } 81 | 82 | class _PageIndicators extends StatelessWidget { 83 | const _PageIndicators({ 84 | required this.roomSelectorNotifier, 85 | required this.pageNotifier, 86 | }); 87 | 88 | final ValueNotifier roomSelectorNotifier; 89 | final ValueNotifier pageNotifier; 90 | 91 | @override 92 | Widget build(BuildContext context) { 93 | return ValueListenableBuilder( 94 | valueListenable: roomSelectorNotifier, 95 | builder: (_, value, child) => AnimatedOpacity( 96 | opacity: value != -1 ? 0 : 1, 97 | duration: value != -1 98 | ? const Duration(milliseconds: 1) 99 | : const Duration(milliseconds: 400), 100 | child: child, 101 | ), 102 | child: ValueListenableBuilder( 103 | valueListenable: pageNotifier, 104 | builder: (_, value, __) => Center( 105 | child: PageViewIndicators( 106 | length: SmartRoom.fakeValues.length, 107 | pageIndex: value, 108 | ), 109 | ), 110 | ), 111 | ); 112 | } 113 | } 114 | 115 | class _BottomNavigationBar extends StatelessWidget { 116 | const _BottomNavigationBar({ 117 | required this.roomSelectorNotifier, 118 | }); 119 | 120 | final ValueNotifier roomSelectorNotifier; 121 | 122 | @override 123 | Widget build(BuildContext context) { 124 | return Padding( 125 | padding: 20.edgeInsetsA, 126 | child: ValueListenableBuilder( 127 | valueListenable: roomSelectorNotifier, 128 | builder: (_, value, child) => AnimatedOpacity( 129 | duration: kThemeAnimationDuration, 130 | opacity: value != -1 ? 0 : 1, 131 | child: AnimatedContainer( 132 | duration: kThemeAnimationDuration, 133 | transform: 134 | Matrix4.translationValues(0, value != -1 ? -30.0 : 0.0, 0), 135 | child: child, 136 | ), 137 | ), 138 | child: BottomNavigationBar( 139 | items: [ 140 | BottomNavigationBarItem( 141 | icon: Padding( 142 | padding: 8.edgeInsetsA, 143 | child: const Icon(SHIcons.lock), 144 | ), 145 | label: 'UNLOCK', 146 | ), 147 | BottomNavigationBarItem( 148 | icon: Padding( 149 | padding: 8.edgeInsetsA, 150 | child: const Icon(SHIcons.home), 151 | ), 152 | label: 'MAIN', 153 | ), 154 | BottomNavigationBarItem( 155 | icon: Padding( 156 | padding: 8.edgeInsetsA, 157 | child: const Icon(SHIcons.settings), 158 | ), 159 | label: 'SETTINGS', 160 | ), 161 | ], 162 | ), 163 | ), 164 | ); 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /lib/samples/smart_home/features/home/presentation/widgets/background_room_card.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_samples/samples/smart_home/core/core.dart'; 3 | import 'package:google_fonts/google_fonts.dart'; 4 | import 'package:ui_common/ui_common.dart'; 5 | 6 | class BackgroundRoomCard extends StatelessWidget { 7 | const BackgroundRoomCard({ 8 | required this.room, 9 | required this.translation, 10 | super.key, 11 | }); 12 | 13 | final SmartRoom room; 14 | final double translation; 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return Transform( 19 | transform: Matrix4.translationValues(0, 80.h * translation, 0), 20 | child: DecoratedBox( 21 | decoration: BoxDecoration( 22 | color: SHColors.cardColor, 23 | borderRadius: 12.borderRadiusA, 24 | boxShadow: const [ 25 | BoxShadow( 26 | color: Colors.black26, 27 | blurRadius: 12, 28 | offset: Offset(-7, 7), 29 | ), 30 | ], 31 | ), 32 | child: Column( 33 | mainAxisAlignment: MainAxisAlignment.end, 34 | crossAxisAlignment: CrossAxisAlignment.stretch, 35 | children: [ 36 | _RoomInfoRow( 37 | icon: const Icon(SHIcons.thermostat), 38 | label: const Text('Temperature'), 39 | data: '${room.temperature}°', 40 | ), 41 | height4, 42 | _RoomInfoRow( 43 | icon: const Icon(SHIcons.waterDrop), 44 | label: const Text('Air Humidity'), 45 | data: '${room.airHumidity}%', 46 | ), 47 | height4, 48 | const _RoomInfoRow( 49 | icon: Icon(SHIcons.timer), 50 | label: Text('Timer'), 51 | data: null, 52 | ), 53 | height12, 54 | const SHDivider(), 55 | Padding( 56 | padding: EdgeInsets.all(12.sp), 57 | child: Row( 58 | mainAxisAlignment: MainAxisAlignment.spaceAround, 59 | children: [ 60 | _DeviceIconSwitcher( 61 | onTap: (value) {}, 62 | icon: const Icon(SHIcons.lightBulbOutline), 63 | label: const Text('Lights'), 64 | value: room.lights.isOn, 65 | ), 66 | _DeviceIconSwitcher( 67 | onTap: (value) {}, 68 | icon: const Icon(SHIcons.fan), 69 | label: const Text('Air-conditioning'), 70 | value: room.airCondition.isOn, 71 | ), 72 | _DeviceIconSwitcher( 73 | onTap: (value) {}, 74 | icon: const Icon(SHIcons.music), 75 | label: const Text('Music'), 76 | value: room.musicInfo.isOn, 77 | ), 78 | ], 79 | ), 80 | ), 81 | ], 82 | ), 83 | ), 84 | ); 85 | } 86 | } 87 | 88 | class _DeviceIconSwitcher extends StatelessWidget { 89 | const _DeviceIconSwitcher({ 90 | required this.onTap, 91 | required this.label, 92 | required this.icon, 93 | required this.value, 94 | }); 95 | 96 | final Text label; 97 | final Icon icon; 98 | final bool value; 99 | final ValueChanged onTap; 100 | 101 | @override 102 | Widget build(BuildContext context) { 103 | final color = value ? SHColors.selectedColor : SHColors.textColor; 104 | return InkWell( 105 | onTap: () => onTap(!value), 106 | child: Column( 107 | children: [ 108 | IconTheme( 109 | data: IconThemeData(color: color, size: 24.sp), 110 | child: icon, 111 | ), 112 | height4, 113 | DefaultTextStyle( 114 | style: context.bodySmall.copyWith(color: color), 115 | child: label, 116 | ), 117 | Text( 118 | value ? 'ON' : 'OFF', 119 | style: GoogleFonts.montserrat( 120 | fontWeight: FontWeight.w700, 121 | color: color, 122 | ), 123 | ), 124 | ], 125 | ), 126 | ); 127 | } 128 | } 129 | 130 | class _RoomInfoRow extends StatelessWidget { 131 | const _RoomInfoRow({ 132 | required this.icon, 133 | required this.label, 134 | required this.data, 135 | }); 136 | 137 | final Icon icon; 138 | final Text label; 139 | final String? data; 140 | 141 | @override 142 | Widget build(BuildContext context) { 143 | return Row( 144 | children: [ 145 | width32, 146 | IconTheme( 147 | data: context.iconTheme.copyWith(size: 18.sp), 148 | child: icon, 149 | ), 150 | width4, 151 | Expanded( 152 | child: DefaultTextStyle( 153 | style: context.bodySmall.copyWith( 154 | color: data == null ? context.textColor.withOpacity(.6) : null, 155 | ), 156 | child: label, 157 | ), 158 | ), 159 | if (data != null) 160 | Text( 161 | data!, 162 | style: GoogleFonts.montserrat( 163 | fontSize: 14.sp, 164 | fontWeight: FontWeight.w700, 165 | ), 166 | ) 167 | else 168 | Row( 169 | children: [ 170 | const BlueLightDot(), 171 | width4, 172 | Text( 173 | 'OFF', 174 | style: GoogleFonts.montserrat( 175 | fontWeight: FontWeight.w800, 176 | fontSize: 12.sp, 177 | color: SHColors.textColor.withOpacity(.6), 178 | ), 179 | ), 180 | ], 181 | ), 182 | width32, 183 | ], 184 | ); 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /lib/samples/smart_home/features/home/presentation/widgets/lighted_wall_background.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_samples/samples/smart_home/core/theme/sh_colors.dart'; 3 | 4 | class LightedBackground extends StatelessWidget { 5 | const LightedBackground({ 6 | required this.child, 7 | super.key, 8 | }); 9 | 10 | final Widget child; 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return Stack( 15 | fit: StackFit.expand, 16 | children: [ 17 | const ColoredBox(color: SHColors.backgroundColor), 18 | Transform.scale( 19 | scale: 2, 20 | alignment: Alignment.topCenter, 21 | child: Transform( 22 | transform: Matrix4.identity() 23 | ..setEntry(3, 2, .001) 24 | ..rotateY(1.4) 25 | ..rotateX(.1) 26 | ..setTranslationRaw(90, -80, 0), 27 | child: const DecoratedBox( 28 | decoration: BoxDecoration( 29 | gradient: RadialGradient( 30 | focal: Alignment.topCenter, 31 | center: Alignment(0, -.55), 32 | colors: SHColors.dimmedLightColors, 33 | ), 34 | ), 35 | ), 36 | ), 37 | ), 38 | child 39 | ], 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/samples/smart_home/features/home/presentation/widgets/page_view_indicators.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_samples/samples/smart_home/core/theme/sh_colors.dart'; 3 | import 'package:ui_common/ui_common.dart'; 4 | 5 | class PageViewIndicators extends StatelessWidget { 6 | const PageViewIndicators({ 7 | required this.length, 8 | required this.pageIndex, 9 | super.key, 10 | }); 11 | 12 | final int length; 13 | final double pageIndex; 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | final index = pageIndex; 18 | return SizedBox( 19 | height: 12.w, 20 | child: Stack( 21 | alignment: Alignment.center, 22 | children: [ 23 | Padding( 24 | padding: EdgeInsets.symmetric(horizontal: 3.w), 25 | child: Row( 26 | mainAxisSize: MainAxisSize.min, 27 | children: [ 28 | for (int i = 0; i < length; i++) ...[ 29 | const _Dot(), 30 | if (i < length - 1) width16, 31 | ], 32 | ], 33 | ), 34 | ), 35 | Positioned( 36 | left: (16.w * index) + (6.w * index), 37 | child: const _BorderDot(), 38 | ) 39 | ], 40 | ), 41 | ); 42 | } 43 | } 44 | 45 | class _BorderDot extends StatelessWidget { 46 | const _BorderDot(); 47 | 48 | @override 49 | Widget build(BuildContext context) { 50 | return SizedBox( 51 | width: 12.w, 52 | height: 12.w, 53 | child: DecoratedBox( 54 | decoration: BoxDecoration( 55 | border: Border.all(color: Colors.orange, width: 2.5), 56 | color: SHColors.backgroundColor, 57 | shape: BoxShape.circle, 58 | ), 59 | ), 60 | ); 61 | } 62 | } 63 | 64 | class _Dot extends StatelessWidget { 65 | const _Dot(); 66 | 67 | @override 68 | Widget build(BuildContext context) { 69 | return SizedBox( 70 | width: 6.w, 71 | height: 6.w, 72 | child: const DecoratedBox( 73 | decoration: BoxDecoration( 74 | color: SHColors.hintColor, 75 | shape: BoxShape.circle, 76 | ), 77 | ), 78 | ); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /lib/samples/smart_home/features/home/presentation/widgets/smart_rooms_page_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_samples/samples/smart_home/core/shared/domain/entities/smart_room.dart'; 3 | import 'package:flutter_samples/samples/smart_home/core/shared/presentation/widgets/room_card.dart'; 4 | import 'package:flutter_samples/samples/smart_home/features/smart_room/presentation/screens/room_detail_screen.dart'; 5 | import 'package:ui_common/ui_common.dart'; 6 | 7 | class SmartRoomsPageView extends StatelessWidget { 8 | const SmartRoomsPageView({ 9 | required this.pageNotifier, 10 | required this.controller, 11 | required this.roomSelectorNotifier, 12 | super.key, 13 | }); 14 | 15 | final ValueNotifier pageNotifier; 16 | final ValueNotifier roomSelectorNotifier; 17 | final PageController controller; 18 | 19 | double _getOffsetX(double percent) => percent.isNegative ? 30.0 : -30.0; 20 | 21 | Matrix4 _getOutTranslate(double percent, int selected, int index) { 22 | final x = selected != index && selected != -1 ? _getOffsetX(percent) : 0.0; 23 | return Matrix4.translationValues(x, 0, 0); 24 | } 25 | 26 | @override 27 | Widget build(BuildContext context) { 28 | return ValueListenableBuilder( 29 | valueListenable: pageNotifier, 30 | builder: (_, page, __) { 31 | return ValueListenableBuilder( 32 | valueListenable: roomSelectorNotifier, 33 | builder: (_, selected, ___) { 34 | return PageView.builder( 35 | controller: controller, 36 | physics: selected != -1 37 | ? const NeverScrollableScrollPhysics() 38 | : const BouncingScrollPhysics(), 39 | clipBehavior: Clip.none, 40 | itemCount: SmartRoom.fakeValues.length, 41 | itemBuilder: (__, index) { 42 | final percent = page - index; 43 | final isSelected = selected == index; 44 | final room = SmartRoom.fakeValues[index]; 45 | return AnimatedContainer( 46 | duration: kThemeAnimationDuration, 47 | curve: Curves.fastOutSlowIn, 48 | transform: _getOutTranslate(percent, selected, index), 49 | padding: 16.edgeInsetsH, 50 | child: RoomCard( 51 | percent: percent, 52 | expand: isSelected, 53 | room: room, 54 | onSwipeUp: () => roomSelectorNotifier.value = index, 55 | onSwipeDown: () => roomSelectorNotifier.value = -1, 56 | onTap: () async { 57 | if (isSelected) { 58 | await Navigator.push( 59 | context, 60 | PageRouteBuilder( 61 | transitionDuration: 62 | const Duration(milliseconds: 800), 63 | reverseTransitionDuration: 64 | const Duration(milliseconds: 800), 65 | pageBuilder: (_, animation, __) => FadeTransition( 66 | opacity: animation, 67 | child: RoomDetailScreen(room: room), 68 | ), 69 | ), 70 | ); 71 | roomSelectorNotifier.value = -1; 72 | } 73 | }, 74 | ), 75 | ); 76 | }, 77 | ); 78 | }, 79 | ); 80 | }, 81 | ); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /lib/samples/smart_home/features/home/presentation/widgets/widgets.dart: -------------------------------------------------------------------------------- 1 | export 'background_room_card.dart'; 2 | export 'lighted_wall_background.dart'; 3 | export 'page_view_indicators.dart'; 4 | export 'smart_rooms_page_view.dart'; 5 | -------------------------------------------------------------------------------- /lib/samples/smart_home/features/smart_room/presentation/screens/room_detail_screen.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_samples/samples/smart_home/core/core.dart'; 5 | import 'package:flutter_samples/samples/smart_home/core/shared/presentation/widgets/room_card.dart'; 6 | import 'package:flutter_samples/samples/smart_home/features/smart_room/presentation/widgets/widgets.dart'; 7 | import 'package:ui_common/ui_common.dart'; 8 | 9 | class RoomDetailScreen extends StatelessWidget { 10 | const RoomDetailScreen({ 11 | required this.room, 12 | super.key, 13 | }); 14 | 15 | final SmartRoom room; 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return Scaffold( 20 | extendBodyBehindAppBar: true, 21 | appBar: const SHAppBar(), 22 | body: RoomDetailItems( 23 | topPadding: context.mediaQuery.padding.top, 24 | room: room, 25 | ), 26 | ); 27 | } 28 | } 29 | 30 | class RoomDetailItems extends StatelessWidget { 31 | const RoomDetailItems({ 32 | required this.room, 33 | required this.topPadding, 34 | this.animation = const AlwaysStoppedAnimation(1), 35 | super.key, 36 | }); 37 | 38 | final Animation animation; 39 | final double topPadding; 40 | final SmartRoom room; 41 | 42 | @override 43 | Widget build(BuildContext context) { 44 | final outDx = 200 * animation.value; 45 | final outDy = 100 * animation.value; 46 | final sigma = 10 * animation.value; 47 | return Hero( 48 | tag: room.id, 49 | child: Stack( 50 | alignment: Alignment.center, 51 | fit: StackFit.expand, 52 | children: [ 53 | ParallaxImageCard(imageUrl: room.imageUrl), 54 | ClipRRect( 55 | child: BackdropFilter( 56 | filter: ImageFilter.blur(sigmaY: sigma, sigmaX: sigma), 57 | child: const ColoredBox(color: Colors.transparent), 58 | ), 59 | ), 60 | // -------------------------------------------- 61 | // Animated output elements 62 | // -------------------------------------------- 63 | FadeTransition( 64 | opacity: Tween(begin: 1, end: 0).animate(animation), 65 | child: Stack( 66 | children: [ 67 | Transform.translate( 68 | offset: Offset(-outDx, 0), 69 | child: VerticalRoomTitle(room: room), 70 | ), 71 | Transform.translate( 72 | offset: Offset(outDx, outDy), 73 | child: const CameraIconButton(), 74 | ), 75 | Transform.translate( 76 | offset: Offset(0, outDy), 77 | child: const AnimatedUpwardArrows(), 78 | ), 79 | ], 80 | ), 81 | ), 82 | // -------------------------------------------- 83 | // Animated room controls 84 | // -------------------------------------------- 85 | FadeTransition( 86 | opacity: animation, 87 | child: Container( 88 | transform: 89 | Matrix4.translationValues(0, -200 * (1 - animation.value), 0), 90 | padding: EdgeInsets.only(top: topPadding + 12), 91 | child: Column( 92 | crossAxisAlignment: CrossAxisAlignment.stretch, 93 | children: [ 94 | Text( 95 | room.name.replaceAll(' ', '\n'), 96 | textAlign: TextAlign.center, 97 | style: context.displaySmall.copyWith(height: .9), 98 | ), 99 | const Text('SETTINGS', textAlign: TextAlign.center), 100 | Expanded( 101 | child: RoomDetailsPageView( 102 | animation: animation, 103 | room: room, 104 | ), 105 | ) 106 | ], 107 | ), 108 | ), 109 | ), 110 | ], 111 | ), 112 | ); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /lib/samples/smart_home/features/smart_room/presentation/widgets/air_conditioner_controls_card.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_samples/samples/smart_home/core/core.dart'; 3 | import 'package:google_fonts/google_fonts.dart'; 4 | import 'package:ui_common/ui_common.dart'; 5 | 6 | class AirConditionerControlsCard extends StatelessWidget { 7 | const AirConditionerControlsCard({ 8 | required this.room, 9 | super.key, 10 | }); 11 | 12 | final SmartRoom room; 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return SHCard( 17 | childrenPadding: 12.edgeInsetsA, 18 | children: [ 19 | _AirSwitcher(room: room), 20 | const _AirIcons(), 21 | Column( 22 | children: [ 23 | Row( 24 | children: [ 25 | Container( 26 | width: 120.w, 27 | height: 50.h, 28 | margin: 8.edgeInsetsA, 29 | decoration: BoxDecoration( 30 | borderRadius: 12.borderRadiusA, 31 | border: Border.all( 32 | width: 10, 33 | color: Colors.white38, 34 | ), 35 | ), 36 | ), 37 | Expanded( 38 | child: FittedBox( 39 | fit: BoxFit.scaleDown, 40 | child: Row( 41 | mainAxisAlignment: MainAxisAlignment.end, 42 | children: [ 43 | const Icon( 44 | SHIcons.waterDrop, 45 | color: Colors.white38, 46 | size: 20, 47 | ), 48 | Text( 49 | 'Air humidity', 50 | style: GoogleFonts.montserrat( 51 | fontSize: 10.sp, 52 | color: Colors.white60, 53 | fontWeight: FontWeight.w500, 54 | ), 55 | ), 56 | width8, 57 | Text('${room.airHumidity.toInt()}%'), 58 | ], 59 | ), 60 | ), 61 | ) 62 | ], 63 | ) 64 | ], 65 | ) 66 | ], 67 | ); 68 | } 69 | } 70 | 71 | class _AirIcons extends StatelessWidget { 72 | const _AirIcons(); 73 | 74 | @override 75 | Widget build(BuildContext context) { 76 | return IconTheme( 77 | data: IconThemeData(size: 30.sp, color: Colors.white38), 78 | child: const Row( 79 | children: [ 80 | Icon(SHIcons.snowFlake), 81 | width8, 82 | Icon(SHIcons.wind), 83 | width8, 84 | Icon(SHIcons.waterDrop), 85 | width8, 86 | Icon(SHIcons.timer, color: SHColors.selectedColor), 87 | ], 88 | ), 89 | ); 90 | } 91 | } 92 | 93 | class _AirSwitcher extends StatelessWidget { 94 | const _AirSwitcher({ 95 | required this.room, 96 | }); 97 | 98 | final SmartRoom room; 99 | 100 | @override 101 | Widget build(BuildContext context) { 102 | return Column( 103 | crossAxisAlignment: CrossAxisAlignment.start, 104 | children: [ 105 | const Text('Air conditioning'), 106 | height12, 107 | Row( 108 | children: [ 109 | Expanded( 110 | child: SHSwitcher( 111 | icon: const Icon(SHIcons.fan), 112 | value: room.airCondition.isOn, 113 | onChanged: (value) {}, 114 | ), 115 | ), 116 | const Spacer(), 117 | Text( 118 | '${room.airCondition.value}˚', 119 | style: TextStyle(fontSize: 28.sp), 120 | ) 121 | ], 122 | ) 123 | ], 124 | ); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /lib/samples/smart_home/features/smart_room/presentation/widgets/animated_counter.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:ui_common/ui_common.dart'; 3 | 4 | class AnimatedCounter extends StatefulWidget { 5 | const AnimatedCounter({ 6 | required this.value, 7 | super.key, 8 | }); 9 | 10 | final int value; 11 | 12 | @override 13 | State createState() => _AnimatedCounterState(); 14 | } 15 | 16 | class _AnimatedCounterState extends State { 17 | Widget _transition(Widget child, Animation animation) { 18 | return SlideTransition( 19 | position: Tween( 20 | begin: const Offset(0, .5), 21 | end: Offset.zero, 22 | ).animate(animation), 23 | child: child, 24 | ); 25 | } 26 | 27 | @override 28 | Widget build(BuildContext context) { 29 | return Row( 30 | mainAxisAlignment: MainAxisAlignment.center, 31 | children: [ 32 | for (final n in '${widget.value}'.split('')) 33 | AnimatedSwitcher( 34 | duration: const Duration(milliseconds: 100), 35 | transitionBuilder: _transition, 36 | child: Text(n, key: Key(n)), 37 | ), 38 | ], 39 | ); 40 | } 41 | } 42 | 43 | class CounterSlider extends StatefulWidget { 44 | const CounterSlider({super.key}); 45 | 46 | @override 47 | State createState() => _CounterSliderState(); 48 | } 49 | 50 | class _CounterSliderState extends State { 51 | int count = 10; 52 | 53 | @override 54 | Widget build(BuildContext context) { 55 | return Column( 56 | mainAxisAlignment: MainAxisAlignment.center, 57 | children: [ 58 | AnimatedCounter(value: count), 59 | height28, 60 | Slider( 61 | max: 100, 62 | value: count.toDouble(), 63 | onChanged: (value) { 64 | setState(() { 65 | count = value.toInt(); 66 | }); 67 | }, 68 | ) 69 | ], 70 | ); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /lib/samples/smart_home/features/smart_room/presentation/widgets/light_intensity_slider_card.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_samples/samples/smart_home/core/core.dart'; 3 | import 'package:ui_common/ui_common.dart'; 4 | 5 | class LightIntensitySliderCard extends StatelessWidget { 6 | const LightIntensitySliderCard({ 7 | required this.room, 8 | super.key, 9 | }); 10 | 11 | final SmartRoom room; 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return SHCard( 16 | childrenPadding: 12.edgeInsetsA, 17 | children: [ 18 | _LightSwitcher(room: room), 19 | Row( 20 | children: [ 21 | const Icon(SHIcons.lightMin), 22 | Expanded( 23 | child: Slider( 24 | value: .2, 25 | onChanged: (value) {}, 26 | ), 27 | ), 28 | const Icon(SHIcons.lightMax), 29 | ], 30 | ) 31 | ], 32 | ); 33 | } 34 | } 35 | 36 | class _LightSwitcher extends StatelessWidget { 37 | const _LightSwitcher({ 38 | required this.room, 39 | }); 40 | 41 | final SmartRoom room; 42 | 43 | @override 44 | Widget build(BuildContext context) { 45 | return Row( 46 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 47 | children: [ 48 | const Flexible( 49 | child: FittedBox( 50 | fit: BoxFit.scaleDown, 51 | child: Text('Light intensity'), 52 | ), 53 | ), 54 | Flexible( 55 | child: FittedBox( 56 | fit: BoxFit.scaleDown, 57 | child: Text( 58 | '${room.lights.value}%', 59 | style: TextStyle(fontSize: 20.sp), 60 | ), 61 | ), 62 | ), 63 | SHSwitcher( 64 | value: room.lights.isOn, 65 | onChanged: (value) {}, 66 | ), 67 | ], 68 | ); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /lib/samples/smart_home/features/smart_room/presentation/widgets/lights_and_timer_switchers.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_samples/samples/smart_home/core/core.dart'; 3 | import 'package:ui_common/ui_common.dart'; 4 | 5 | class LightsAndTimerSwitchers extends StatelessWidget { 6 | const LightsAndTimerSwitchers({ 7 | required this.room, 8 | super.key, 9 | }); 10 | 11 | final SmartRoom room; 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return SHCard( 16 | childrenPadding: 12.edgeInsetsA, 17 | children: [ 18 | Column( 19 | crossAxisAlignment: CrossAxisAlignment.start, 20 | children: [ 21 | const Text('Lights'), 22 | height8, 23 | SHSwitcher( 24 | value: room.lights.isOn, 25 | onChanged: (value) {}, 26 | icon: const Icon(SHIcons.lightBulbOutline), 27 | ), 28 | ], 29 | ), 30 | Column( 31 | crossAxisAlignment: CrossAxisAlignment.start, 32 | children: [ 33 | const Row( 34 | children: [ 35 | Text('Timer'), 36 | Spacer(), 37 | BlueLightDot(), 38 | ], 39 | ), 40 | height8, 41 | SHSwitcher( 42 | icon: room.timer.isOn 43 | ? const Icon(SHIcons.timer) 44 | : const Icon(SHIcons.timerOff), 45 | value: room.timer.isOn, 46 | onChanged: (value) {}, 47 | ), 48 | ], 49 | ), 50 | ], 51 | ); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/samples/smart_home/features/smart_room/presentation/widgets/music_switchers.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_samples/samples/smart_home/core/shared/domain/entities/smart_room.dart'; 3 | import 'package:flutter_samples/samples/smart_home/core/shared/presentation/widgets/sh_card.dart'; 4 | import 'package:flutter_samples/samples/smart_home/core/shared/presentation/widgets/sh_switch.dart'; 5 | import 'package:flutter_samples/samples/smart_home/core/theme/sh_colors.dart'; 6 | import 'package:flutter_samples/samples/smart_home/core/theme/sh_icons.dart'; 7 | import 'package:google_fonts/google_fonts.dart'; 8 | import 'package:ui_common/ui_common.dart'; 9 | 10 | class MusicSwitchers extends StatelessWidget { 11 | const MusicSwitchers({ 12 | required this.room, 13 | super.key, 14 | }); 15 | 16 | final SmartRoom room; 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return SHCard( 21 | childrenPadding: 12.edgeInsetsA, 22 | children: [ 23 | Column( 24 | crossAxisAlignment: CrossAxisAlignment.stretch, 25 | children: [ 26 | const Row( 27 | children: [ 28 | Text('Music'), 29 | Spacer(), 30 | Icon(Icons.open_in_new_rounded), 31 | ], 32 | ), 33 | height8, 34 | SHSwitcher( 35 | value: room.musicInfo.isOn, 36 | icon: const Icon(SHIcons.music), 37 | onChanged: (value) {}, 38 | ), 39 | ], 40 | ), 41 | Column( 42 | crossAxisAlignment: CrossAxisAlignment.start, 43 | children: [ 44 | Text( 45 | room.musicInfo.currentSong.title, 46 | maxLines: 1, 47 | overflow: TextOverflow.ellipsis, 48 | ), 49 | height4, 50 | Text( 51 | room.musicInfo.currentSong.artist, 52 | style: GoogleFonts.montserrat( 53 | color: SHColors.selectedColor, 54 | fontWeight: FontWeight.w500, 55 | fontSize: 12, 56 | ), 57 | ), 58 | IconTheme( 59 | data: IconThemeData(size: 20.sp, color: Colors.white), 60 | child: Row( 61 | mainAxisAlignment: MainAxisAlignment.center, 62 | children: [ 63 | const Flexible(child: Icon(Icons.fast_rewind)), 64 | width8, 65 | Flexible( 66 | child: room.musicInfo.isOn 67 | ? const Icon(Icons.pause) 68 | : const Icon(Icons.play_arrow), 69 | ), 70 | width8, 71 | const Flexible(child: Icon(Icons.fast_forward)), 72 | ], 73 | ), 74 | ), 75 | ], 76 | ), 77 | ], 78 | ); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /lib/samples/smart_home/features/smart_room/presentation/widgets/room_details_page_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_samples/samples/smart_home/core/core.dart'; 3 | import 'package:flutter_samples/samples/smart_home/features/smart_room/presentation/widgets/widgets.dart'; 4 | import 'package:google_fonts/google_fonts.dart'; 5 | import 'package:ui_common/ui_common.dart'; 6 | 7 | class RoomDetailsPageView extends StatelessWidget { 8 | const RoomDetailsPageView({ 9 | required this.animation, 10 | required this.room, 11 | super.key, 12 | }); 13 | 14 | final Animation animation; 15 | final SmartRoom room; 16 | 17 | Animation get _interval1 => CurvedAnimation( 18 | parent: animation, 19 | curve: const Interval(0.4, 1, curve: Curves.easeIn), 20 | ); 21 | 22 | Animation get _interval2 => CurvedAnimation( 23 | parent: animation, 24 | curve: const Interval(0.6, 1, curve: Curves.easeIn), 25 | ); 26 | 27 | Animation get _interval3 => CurvedAnimation( 28 | parent: animation, 29 | curve: const Interval(0.8, 1, curve: Curves.easeIn), 30 | ); 31 | 32 | @override 33 | Widget build(BuildContext context) { 34 | return PageView( 35 | physics: const BouncingScrollPhysics(), 36 | children: [ 37 | Column( 38 | children: [ 39 | SlideTransition( 40 | position: Tween( 41 | begin: const Offset(-1, 1), 42 | end: Offset.zero, 43 | ).animate(animation), 44 | child: Align( 45 | alignment: Alignment.centerLeft, 46 | child: TextButton.icon( 47 | onPressed: () => Navigator.pop(context), 48 | style: TextButton.styleFrom( 49 | alignment: Alignment.centerLeft, 50 | padding: 8.edgeInsetsA.copyWith(bottom: 0), 51 | ), 52 | icon: const Icon(Icons.keyboard_arrow_left), 53 | label: const Text('BACK'), 54 | ), 55 | ), 56 | ), 57 | Expanded( 58 | child: DefaultTextStyle( 59 | style: GoogleFonts.montserrat( 60 | fontSize: 14.sp, 61 | fontWeight: FontWeight.w700, 62 | ), 63 | child: ListView( 64 | physics: const BouncingScrollPhysics(), 65 | padding: 20.edgeInsetsA.copyWith(top: 12.h), 66 | children: [ 67 | SlideTransition( 68 | position: Tween( 69 | begin: const Offset(0, 2), 70 | end: Offset.zero, 71 | ).animate(_interval1), 72 | child: FadeTransition( 73 | opacity: _interval1, 74 | child: Row( 75 | children: [ 76 | Expanded( 77 | child: LightsAndTimerSwitchers(room: room), 78 | ), 79 | width20, 80 | Expanded(child: MusicSwitchers(room: room)), 81 | ], 82 | ), 83 | ), 84 | ), 85 | height20, 86 | SlideTransition( 87 | position: Tween( 88 | begin: const Offset(0, 2), 89 | end: Offset.zero, 90 | ).animate(_interval2), 91 | child: FadeTransition( 92 | opacity: _interval2, 93 | child: LightIntensitySliderCard(room: room), 94 | ), 95 | ), 96 | height20, 97 | SlideTransition( 98 | position: Tween( 99 | begin: const Offset(0, 2), 100 | end: Offset.zero, 101 | ).animate(_interval1), 102 | child: FadeTransition( 103 | opacity: _interval3, 104 | child: AirConditionerControlsCard(room: room), 105 | ), 106 | ), 107 | ], 108 | ), 109 | ), 110 | ), 111 | ], 112 | ), 113 | ], 114 | ); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /lib/samples/smart_home/features/smart_room/presentation/widgets/widgets.dart: -------------------------------------------------------------------------------- 1 | export 'air_conditioner_controls_card.dart'; 2 | export 'light_intensity_slider_card.dart'; 3 | export 'lights_and_timer_switchers.dart'; 4 | export 'music_switchers.dart'; 5 | export 'room_details_page_view.dart'; 6 | -------------------------------------------------------------------------------- /lib/samples/vice_app/core/app/vice_app.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_samples/samples/vice_app/core/core.dart'; 3 | import 'package:flutter_samples/samples/vice_app/features/home/presentation/screens/home_screen.dart'; 4 | import 'package:ui_common/ui_common.dart'; 5 | 6 | class ViceApp extends StatelessWidget { 7 | const ViceApp({super.key}); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return ScreenUtilInit( 12 | builder: (_, child) => MaterialApp( 13 | debugShowCheckedModeBanner: false, 14 | title: 'Material App', 15 | theme: ViceTheme.theme, 16 | home: const HomeScreen(), 17 | ), 18 | ); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/samples/vice_app/core/constants/constants.dart: -------------------------------------------------------------------------------- 1 | export 'vice_ui_consts.dart'; 2 | -------------------------------------------------------------------------------- /lib/samples/vice_app/core/constants/vice_ui_consts.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_samples/samples/vice_app/core/theme/vice_colors.dart'; 3 | import 'package:ui_common/ui_common.dart'; 4 | 5 | class ViceUIConsts { 6 | ViceUIConsts._(); 7 | 8 | static const BoxDecoration gradientDecoration = BoxDecoration( 9 | gradient: LinearGradient( 10 | begin: Alignment.topCenter, 11 | end: Alignment.bottomCenter, 12 | stops: [0.3, 1], 13 | colors: ViceColors.scaffoldColors, 14 | ), 15 | ); 16 | 17 | static double headerHeight = 0.65.sh; 18 | } 19 | -------------------------------------------------------------------------------- /lib/samples/vice_app/core/core.dart: -------------------------------------------------------------------------------- 1 | export 'app/vice_app.dart'; 2 | export 'constants/constants.dart'; 3 | export 'shared/shared.dart'; 4 | export 'theme/theme.dart'; 5 | -------------------------------------------------------------------------------- /lib/samples/vice_app/core/shared/domain/domain.dart: -------------------------------------------------------------------------------- 1 | export 'entities/entities.dart'; 2 | -------------------------------------------------------------------------------- /lib/samples/vice_app/core/shared/domain/entities/entities.dart: -------------------------------------------------------------------------------- 1 | export 'magazine.dart'; 2 | -------------------------------------------------------------------------------- /lib/samples/vice_app/core/shared/domain/entities/magazine.dart: -------------------------------------------------------------------------------- 1 | class Magazine { 2 | const Magazine({ 3 | required this.id, 4 | required this.assetImage, 5 | required this.description, 6 | }); 7 | 8 | final String id; 9 | final String assetImage; 10 | final String description; 11 | static final List fakeMagazinesValues = List.generate( 12 | 13, 13 | (index) => Magazine( 14 | id: '$index', 15 | assetImage: 'assets/img/vice/vice${index + 1}.png', 16 | description: 17 | 'Lorem Ipsum is simply dummy text of the printing and typesetting ' 18 | "industry. Lorem Ipsum has been the industry's standard dummy " 19 | 'text ever since the 1500s, when an unknown printer took a galley ' 20 | 'of type and scrambled it to make a type specimen book. It has ' 21 | 'survived not only five centuries, but also the leap into ' 22 | 'electronic typesetting, remaining essentially unchanged. It was ' 23 | 'popularised in the 1960s with the release of word set sheets ' 24 | 'containing Lorem Ipsum passages, and more recently with desktop' 25 | ' publishing software like Aldus PageMaker including versions of ' 26 | 'Lorem Ipsum', 27 | ), 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /lib/samples/vice_app/core/shared/presentation/presentation.dart: -------------------------------------------------------------------------------- 1 | export 'widgets/widgets.dart'; 2 | -------------------------------------------------------------------------------- /lib/samples/vice_app/core/shared/presentation/widgets/custom_tween_animation.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | class CustomTweenAnimation extends StatelessWidget { 6 | const CustomTweenAnimation({ 7 | required this.child, 8 | this.onlyScale = false, 9 | super.key, 10 | }); 11 | 12 | final Widget child; 13 | final bool onlyScale; 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return TweenAnimationBuilder( 18 | duration: const Duration(milliseconds: 600), 19 | curve: Curves.fastOutSlowIn, 20 | tween: Tween(begin: 1, end: 0), 21 | builder: (_, value, child) { 22 | if (onlyScale) { 23 | return Transform.scale( 24 | scale: lerpDouble(1, -1, value)?.clamp(0, 1), 25 | child: child, 26 | ); 27 | } 28 | return Transform.translate( 29 | offset: Offset(0, 50 * value), 30 | child: Opacity(opacity: 1 - value, child: child), 31 | ); 32 | }, 33 | child: child, 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/samples/vice_app/core/shared/presentation/widgets/magazine_cover_image.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_samples/samples/vice_app/core/shared/domain/entities/magazine.dart'; 3 | 4 | class MagazineCoverImage extends StatelessWidget { 5 | const MagazineCoverImage({ 6 | required this.magazine, 7 | super.key, 8 | this.height, 9 | }); 10 | 11 | final Magazine magazine; 12 | final double? height; 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return SizedBox( 17 | height: height, 18 | child: AspectRatio( 19 | aspectRatio: .75, 20 | child: DecoratedBox( 21 | decoration: BoxDecoration( 22 | image: DecorationImage( 23 | image: AssetImage(magazine.assetImage), 24 | fit: BoxFit.cover, 25 | ), 26 | boxShadow: const [ 27 | BoxShadow( 28 | color: Colors.black54, 29 | blurRadius: 40, 30 | offset: Offset(-20, 20), 31 | ) 32 | ], 33 | ), 34 | ), 35 | ), 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/samples/vice_app/core/shared/presentation/widgets/menu_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_samples/samples/vice_app/core/core.dart'; 3 | 4 | class MenuButton extends StatelessWidget { 5 | const MenuButton({ 6 | super.key, 7 | this.color = Colors.white70, 8 | }); 9 | 10 | final Color color; 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return Hero( 15 | tag: 'menu-button', 16 | child: Material( 17 | type: MaterialType.transparency, 18 | child: IconButton( 19 | onPressed: () {}, 20 | icon: Icon(ViceIcons.menu, size: 30, color: color), 21 | ), 22 | ), 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/samples/vice_app/core/shared/presentation/widgets/widgets.dart: -------------------------------------------------------------------------------- 1 | export 'custom_tween_animation.dart'; 2 | export 'magazine_cover_image.dart'; 3 | export 'menu_button.dart'; 4 | -------------------------------------------------------------------------------- /lib/samples/vice_app/core/shared/shared.dart: -------------------------------------------------------------------------------- 1 | export 'domain/domain.dart'; 2 | export 'presentation/presentation.dart'; 3 | -------------------------------------------------------------------------------- /lib/samples/vice_app/core/theme/theme.dart: -------------------------------------------------------------------------------- 1 | export 'vice_colors.dart'; 2 | export 'vice_icons.dart'; 3 | export 'vice_theme.dart'; 4 | -------------------------------------------------------------------------------- /lib/samples/vice_app/core/theme/vice_colors.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class ViceColors { 4 | const ViceColors._(); 5 | 6 | static const purple = Colors.purpleAccent; 7 | static const lightGreen = Colors.greenAccent; 8 | static const textColor = Colors.black; 9 | static const List scaffoldColors = [ 10 | Color(0xff9876cc), 11 | Color(0xff80c7a9), 12 | ]; 13 | } 14 | -------------------------------------------------------------------------------- /lib/samples/vice_app/core/theme/vice_icons.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class ViceIcons { 5 | const ViceIcons._(); 6 | 7 | static const IconData search = CupertinoIcons.search; 8 | static const IconData menu = CupertinoIcons.line_horizontal_3_decrease; 9 | static const IconData lock = CupertinoIcons.lock_fill; 10 | static const IconData home = CupertinoIcons.home; 11 | static const IconData settings = CupertinoIcons.settings; 12 | static const IconData share = Icons.share_outlined; 13 | static const IconData heart = CupertinoIcons.heart; 14 | static const IconData close = CupertinoIcons.clear_circled_solid; 15 | static const IconData save = CupertinoIcons.bookmark; 16 | } 17 | -------------------------------------------------------------------------------- /lib/samples/vice_app/core/theme/vice_theme.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_samples/samples/vice_app/core/core.dart'; 3 | import 'package:ui_common/ui_common.dart'; 4 | 5 | class ViceTheme { 6 | const ViceTheme._(); 7 | 8 | static ThemeData get theme => ThemeData( 9 | primarySwatch: Colors.purple, 10 | primaryColor: ViceColors.purple, 11 | scaffoldBackgroundColor: Colors.transparent, 12 | appBarTheme: const AppBarTheme( 13 | backgroundColor: Colors.transparent, 14 | elevation: 0, 15 | centerTitle: true, 16 | ), 17 | inputDecorationTheme: InputDecorationTheme( 18 | filled: true, 19 | fillColor: Colors.white54, 20 | contentPadding: 10.edgeInsetsA, 21 | border: OutlineInputBorder( 22 | borderRadius: 40.borderRadiusA, 23 | borderSide: BorderSide.none, 24 | ), 25 | ), 26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /lib/samples/vice_app/features/home/presentation/screens/home_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_samples/samples/vice_app/core/core.dart'; 3 | import 'package:flutter_samples/samples/vice_app/features/home/presentation/widgets/all_editions_list_view.dart'; 4 | import 'package:flutter_samples/samples/vice_app/features/home/presentation/widgets/infinite_draggable_slider.dart'; 5 | import 'package:flutter_samples/samples/vice_app/features/magazines_details/presentation/screens/magazines_details_screen.dart'; 6 | import 'package:ui_common/ui_common.dart'; 7 | 8 | class HomeScreen extends StatefulWidget { 9 | const HomeScreen({ 10 | super.key, 11 | this.enableEntryAnimation = false, 12 | this.initialIndex = 0, 13 | }); 14 | 15 | final bool enableEntryAnimation; 16 | final int initialIndex; 17 | 18 | @override 19 | State createState() => _HomeScreenState(); 20 | } 21 | 22 | class _HomeScreenState extends State with TickerProviderStateMixin { 23 | late final AnimationController outAnimationController; 24 | late final AnimationController entryAnimationController; 25 | final List magazines = Magazine.fakeMagazinesValues; 26 | late int currentIndex; 27 | List> entryAnimations = 28 | List.filled(4, const AlwaysStoppedAnimation(1)); 29 | 30 | @override 31 | void initState() { 32 | currentIndex = widget.initialIndex; 33 | outAnimationController = AnimationController( 34 | vsync: this, 35 | duration: kThemeChangeDuration, 36 | value: 1, 37 | ); 38 | entryAnimationController = AnimationController( 39 | vsync: this, 40 | duration: const Duration(milliseconds: 1200), 41 | value: 0, 42 | ); 43 | if (widget.enableEntryAnimation) { 44 | entryAnimations = List.generate( 45 | 4, 46 | (index) => CurvedAnimation( 47 | parent: entryAnimationController, 48 | curve: Interval(0, .2 * (index + 1), curve: Curves.fastOutSlowIn), 49 | ), 50 | ); 51 | Future.delayed( 52 | const Duration(milliseconds: 400), 53 | () => entryAnimationController.forward(), 54 | ); 55 | } 56 | super.initState(); 57 | } 58 | 59 | @override 60 | void dispose() { 61 | outAnimationController.dispose(); 62 | entryAnimationController.dispose(); 63 | super.dispose(); 64 | } 65 | 66 | void openMagazineDetail( 67 | BuildContext context, 68 | int index, 69 | ) { 70 | outAnimationController.reverse(); 71 | setState(() => currentIndex = index); 72 | MagazinesDetailsScreen.push( 73 | context, 74 | magazines: magazines, 75 | index: currentIndex, 76 | ); 77 | } 78 | 79 | @override 80 | Widget build(BuildContext context) { 81 | return DecoratedBox( 82 | decoration: ViceUIConsts.gradientDecoration, 83 | child: Scaffold( 84 | resizeToAvoidBottomInset: false, 85 | appBar: _AppBar( 86 | outAnimation: outAnimationController, 87 | entryAnimation: entryAnimations[0], 88 | ), 89 | body: Column( 90 | children: [ 91 | height12, 92 | _EntryOutTransition( 93 | entryAnimation: entryAnimations[0], 94 | outAnimation: outAnimationController, 95 | entryBeginOffset: const Offset(0, 1), 96 | outBeginOffset: const Offset(0, -1), 97 | child: Padding( 98 | padding: 20.edgeInsetsH, 99 | child: const TextField( 100 | decoration: InputDecoration( 101 | prefixIcon: Icon(ViceIcons.search), 102 | ), 103 | ), 104 | ), 105 | ), 106 | height20, 107 | _EntryOutTransition( 108 | entryAnimation: entryAnimations[0], 109 | entryBeginOffset: const Offset(0, 3), 110 | child: const Text( 111 | 'THE ARCHIVE', 112 | style: TextStyle( 113 | fontWeight: FontWeight.w700, 114 | color: Colors.white, 115 | ), 116 | ), 117 | ), 118 | height12, 119 | Expanded( 120 | child: _EntryOutTransition( 121 | entryAnimation: entryAnimations[1], 122 | entryBeginOffset: const Offset(0, .2), 123 | child: Hero( 124 | tag: magazines[currentIndex].id, 125 | child: InfiniteDraggableSlider( 126 | index: currentIndex, 127 | itemCount: magazines.length, 128 | shrinkAnimation: outAnimationController, 129 | onTapItem: (index) => openMagazineDetail(context, index), 130 | itemBuilder: (_, int index) => 131 | MagazineCoverImage(magazine: magazines[index]), 132 | ), 133 | ), 134 | ), 135 | ), 136 | height52, 137 | _EntryOutTransition( 138 | entryAnimation: entryAnimations[2], 139 | outAnimation: outAnimationController, 140 | entryBeginOffset: const Offset(0, .5), 141 | outBeginOffset: const Offset(0, .5), 142 | child: SizedBox( 143 | height: 120.h, 144 | child: AllEditionsListView(magazines: magazines), 145 | ), 146 | ), 147 | height12, 148 | ], 149 | ), 150 | bottomNavigationBar: _EntryOutTransition( 151 | entryAnimation: entryAnimations[3], 152 | outAnimation: outAnimationController, 153 | entryBeginOffset: const Offset(0, .8), 154 | outBeginOffset: const Offset(0, .8), 155 | child: SafeArea( 156 | child: SizedBox( 157 | height: kToolbarHeight, 158 | child: Row( 159 | mainAxisAlignment: MainAxisAlignment.spaceAround, 160 | children: [ 161 | IconButton( 162 | onPressed: () {}, 163 | icon: const Icon(ViceIcons.home), 164 | ), 165 | IconButton( 166 | onPressed: () {}, 167 | icon: const Icon(ViceIcons.settings), 168 | ), 169 | IconButton( 170 | onPressed: () {}, 171 | icon: const Icon(ViceIcons.share), 172 | ), 173 | IconButton( 174 | onPressed: () {}, 175 | icon: const Icon(ViceIcons.heart), 176 | ), 177 | ], 178 | ), 179 | ), 180 | ), 181 | ), 182 | ), 183 | ); 184 | } 185 | } 186 | 187 | class _EntryOutTransition extends StatelessWidget { 188 | const _EntryOutTransition({ 189 | required this.child, 190 | required this.entryAnimation, 191 | this.outAnimation = const AlwaysStoppedAnimation(1), 192 | this.entryBeginOffset = Offset.zero, 193 | this.outBeginOffset = Offset.zero, 194 | }); 195 | 196 | final Animation entryAnimation; 197 | final Animation outAnimation; 198 | final Offset entryBeginOffset; 199 | final Offset outBeginOffset; 200 | final Widget child; 201 | 202 | @override 203 | Widget build(BuildContext context) { 204 | return SlideTransition( 205 | position: 206 | Tween(begin: outBeginOffset, end: Offset.zero).animate(outAnimation), 207 | child: FadeTransition( 208 | opacity: outAnimation, 209 | child: SlideTransition( 210 | position: Tween(begin: entryBeginOffset, end: Offset.zero) 211 | .animate(entryAnimation), 212 | child: FadeTransition(opacity: entryAnimation, child: child), 213 | ), 214 | ), 215 | ); 216 | } 217 | } 218 | 219 | class _AppBar extends StatelessWidget implements PreferredSize { 220 | const _AppBar({ 221 | required this.outAnimation, 222 | required this.entryAnimation, 223 | }); 224 | 225 | final Animation outAnimation; 226 | final Animation entryAnimation; 227 | 228 | @override 229 | Widget build(BuildContext context) { 230 | return AppBar( 231 | clipBehavior: Clip.none, 232 | title: _EntryOutTransition( 233 | entryAnimation: entryAnimation, 234 | outAnimation: outAnimation, 235 | entryBeginOffset: const Offset(0, 2), 236 | outBeginOffset: const Offset(0, -1), 237 | child: Image.asset( 238 | 'assets/img/vice/vice-logo.png', 239 | height: 30, 240 | color: Colors.white, 241 | ), 242 | ), 243 | actions: [ 244 | FadeTransition( 245 | opacity: entryAnimation, 246 | child: const MenuButton(), 247 | ), 248 | ], 249 | ); 250 | } 251 | 252 | @override 253 | Widget get child => this; 254 | 255 | @override 256 | Size get preferredSize => const Size.fromHeight(kToolbarHeight); 257 | } 258 | -------------------------------------------------------------------------------- /lib/samples/vice_app/features/home/presentation/widgets/all_editions_list_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_samples/samples/vice_app/core/core.dart'; 3 | import 'package:ui_common/ui_common.dart'; 4 | 5 | class AllEditionsListView extends StatelessWidget { 6 | const AllEditionsListView({ 7 | required this.magazines, 8 | super.key, 9 | }); 10 | 11 | final List magazines; 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return Column( 16 | crossAxisAlignment: CrossAxisAlignment.stretch, 17 | children: [ 18 | Padding( 19 | padding: 20.edgeInsetsH, 20 | child: const Text( 21 | 'ALL EDITIONS', 22 | style: TextStyle(fontWeight: FontWeight.w600), 23 | ), 24 | ), 25 | height4, 26 | Expanded( 27 | child: ListView.builder( 28 | padding: 20.edgeInsetsH, 29 | itemCount: magazines.length, 30 | scrollDirection: Axis.horizontal, 31 | itemBuilder: (context, index) { 32 | final magazine = magazines[index]; 33 | return Padding( 34 | padding: 12.edgeInsetsR, 35 | child: AspectRatio( 36 | aspectRatio: 1, 37 | child: Image.asset( 38 | magazine.assetImage, 39 | fit: BoxFit.cover, 40 | alignment: Alignment.topCenter, 41 | ), 42 | ), 43 | ); 44 | }, 45 | ), 46 | ) 47 | ], 48 | ); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/samples/vice_app/features/home/presentation/widgets/draggable_widget.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math' as math; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:ui_common/ui_common.dart'; 5 | 6 | /// Indicates the direction in which the user swiped the card 7 | enum SlideDirection { left, right } 8 | 9 | extension SlideDirectoExt on SlideDirection { 10 | bool get isRight => this == SlideDirection.right; 11 | 12 | bool get isLeft => this == SlideDirection.left; 13 | } 14 | 15 | class DraggableWidget extends StatefulWidget { 16 | /// Widget that can be dragged around the screen 17 | const DraggableWidget({ 18 | required this.child, 19 | required this.enableDrag, 20 | super.key, 21 | this.onSlideOut, 22 | this.onPressed, 23 | }); 24 | 25 | /// [ValueChanged] that is called when the user swipe left or right quickly 26 | final ValueChanged? onSlideOut; 27 | 28 | /// [VoidCallback] that is executed when the item is tapped 29 | final VoidCallback? onPressed; 30 | 31 | /// Allows you to enable or disable being able to drag the widget 32 | final bool enableDrag; 33 | 34 | /// Widget that is wrapped in order to give it the ability to be draggable 35 | final Widget child; 36 | 37 | @override 38 | State createState() => _DraggableWidgetState(); 39 | } 40 | 41 | class _DraggableWidgetState extends State 42 | with SingleTickerProviderStateMixin { 43 | // Animation controller when user releases drag without swiping out 44 | late final AnimationController restoreController; 45 | 46 | // Global key assigned to the child widget's container to retrieve its 47 | // position on the screen and its size 48 | final GlobalKey _widgetKey = GlobalKey(); 49 | 50 | // Value of the initial position of the cursor when the user begins dragging 51 | Offset startOffset = Offset.zero; 52 | 53 | // Value of the cursor position while the user is dragging 54 | Offset panOffset = Offset.zero; 55 | 56 | // Size of the child widget 57 | Size size = Size.zero; 58 | 59 | // Lean angle when dragging 60 | double angle = 0; 61 | 62 | // Value that detects if the user performed a slide out 63 | bool itWasMadeSlide = false; 64 | 65 | // Width that must be outside the screen for the slide out to take place 66 | double get outSizeLimit => size.width * .65; 67 | 68 | // Use the global key of the child widget's container to return the position 69 | // the widget is in 70 | Offset get currentPosition { 71 | final renderBox = 72 | _widgetKey.currentContext?.findRenderObject() as RenderBox?; 73 | return renderBox?.localToGlobal(Offset.zero) ?? Offset.zero; 74 | } 75 | 76 | // Gets the value of the tilt angle based on the position in which the widget 77 | // is and its size 78 | double get currentAngle { 79 | return currentPosition.dx < 0 80 | ? (math.pi * .2) * currentPosition.dx / size.width 81 | : currentPosition.dx + size.width > 1.sw 82 | ? (math.pi * .2) * 83 | (currentPosition.dx + size.width - 1.sw) / 84 | size.width 85 | : 0; 86 | } 87 | 88 | // Assigns the initial touch point when dragging the widget 89 | void onPanStart(DragStartDetails details) { 90 | if (restoreController.isAnimating) return; 91 | setState(() => startOffset = details.globalPosition); 92 | } 93 | 94 | // Updates the position and tilt angle based on the global position of 95 | // the touch point and the initial touch point 96 | void onPanUpdate(DragUpdateDetails details) { 97 | if (restoreController.isAnimating) return; 98 | setState(() { 99 | panOffset = details.globalPosition - startOffset; 100 | angle = currentAngle; 101 | }); 102 | } 103 | 104 | // Obtains the speed when the drag is released and the position of the widget 105 | // and based on these values the slide out function is executed 106 | void onPanEnd(DragEndDetails details) { 107 | if (restoreController.isAnimating) return; 108 | final velocityX = details.velocity.pixelsPerSecond.dx; 109 | final positionX = currentPosition.dx; 110 | if (velocityX < -1000 || positionX < -outSizeLimit) { 111 | itWasMadeSlide = widget.onSlideOut != null; 112 | widget.onSlideOut?.call(SlideDirection.left); 113 | } 114 | if (velocityX > 1000 || positionX > (1.sw - outSizeLimit)) { 115 | itWasMadeSlide = widget.onSlideOut != null; 116 | widget.onSlideOut?.call(SlideDirection.right); 117 | } 118 | restoreController.forward(); 119 | } 120 | 121 | void restoreControllerListener() { 122 | if (restoreController.isCompleted) { 123 | restoreController.reset(); 124 | panOffset = Offset.zero; 125 | itWasMadeSlide = false; 126 | angle = 0; 127 | setState(() {}); 128 | } 129 | } 130 | 131 | void getChildSize() { 132 | size = 133 | (_widgetKey.currentContext?.findRenderObject() as RenderBox?)?.size ?? 134 | Size.zero; 135 | } 136 | 137 | @override 138 | void initState() { 139 | restoreController = AnimationController( 140 | vsync: this, 141 | duration: kThemeAnimationDuration, 142 | )..addListener(restoreControllerListener); 143 | WidgetsBinding.instance.addPostFrameCallback((_) { 144 | getChildSize(); 145 | }); 146 | super.initState(); 147 | } 148 | 149 | @override 150 | void dispose() { 151 | restoreController 152 | ..removeListener(restoreControllerListener) 153 | ..dispose(); 154 | super.dispose(); 155 | } 156 | 157 | @override 158 | Widget build(BuildContext context) { 159 | final child = SizedBox(key: _widgetKey, child: widget.child); 160 | if (!widget.enableDrag) return child; 161 | return GestureDetector( 162 | onTap: widget.onPressed, 163 | onPanStart: onPanStart, 164 | onPanUpdate: onPanUpdate, 165 | onPanEnd: onPanEnd, 166 | child: AnimatedBuilder( 167 | animation: restoreController, 168 | builder: (context, child) { 169 | final value = 1 - restoreController.value; 170 | return Transform.translate( 171 | offset: panOffset * (itWasMadeSlide ? 1 : value), 172 | child: Transform.rotate( 173 | angle: angle * (itWasMadeSlide ? 1 : value), 174 | child: child, 175 | ), 176 | ); 177 | }, 178 | child: child, 179 | ), 180 | ); 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /lib/samples/vice_app/features/home/presentation/widgets/infinite_draggable_slider.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math' as math; 2 | import 'dart:ui' as ui; 3 | 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_samples/samples/vice_app/features/home/presentation/widgets/draggable_widget.dart'; 6 | import 'package:ui_common/ui_common.dart'; 7 | 8 | class InfiniteDraggableSlider extends StatefulWidget { 9 | const InfiniteDraggableSlider({ 10 | required this.itemBuilder, 11 | super.key, 12 | this.onTapItem, 13 | this.shrinkAnimation = const AlwaysStoppedAnimation(1), 14 | this.index = 0, 15 | this.itemCount, 16 | }); 17 | 18 | /// [Animation] that shrinks the slider elements 19 | final Animation shrinkAnimation; 20 | 21 | /// [ValueChanged] that is executed when tapping on the element and returns 22 | /// the index that it has 23 | final ValueChanged? onTapItem; 24 | 25 | /// Initial index of the first element displayed in the slider 26 | final int index; 27 | 28 | /// Maximum number of elements inside the slider 29 | final int? itemCount; 30 | 31 | /// Widget Function that builds the elements shown in the slider based on its 32 | /// index and its context 33 | final Widget Function(BuildContext context, int index) itemBuilder; 34 | 35 | @override 36 | State createState() => 37 | _InfiniteDraggableSliderState(); 38 | } 39 | 40 | class _InfiniteDraggableSliderState extends State 41 | with TickerProviderStateMixin { 42 | late final AnimationController controller; 43 | late int index; 44 | static const inclineLeft = -math.pi * .1; 45 | static const inclineRight = math.pi * .1; 46 | SlideDirection direction = SlideDirection.left; 47 | 48 | void animationListener() { 49 | if (controller.isCompleted) { 50 | setState(() { 51 | if (widget.itemCount == ++index) { 52 | index = 0; 53 | } 54 | }); 55 | controller.reset(); 56 | } 57 | } 58 | 59 | /// Called when the user slides out and assigns the value of the direction 60 | /// in which the user slid the element 61 | void onSlideOut(SlideDirection direction) { 62 | this.direction = direction; 63 | controller.forward(); 64 | } 65 | 66 | /// Gets the scale of the item according to its position in the [Stack] 67 | double getScale(int stackIndex) => 68 | { 69 | 0: ui.lerpDouble(.6, .9, controller.value)!, 70 | 1: ui.lerpDouble(.9, .95, controller.value)!, 71 | 2: ui.lerpDouble(.95, 1, controller.value)!, 72 | }[stackIndex] ?? 73 | 1.0; 74 | 75 | /// Gets the [Offset] on the item according to its position in the [Stack] 76 | Offset getOffset(int stackIndex) => 77 | { 78 | 0: Offset(ui.lerpDouble(0, -70, controller.value)!, 30), 79 | 1: Offset(ui.lerpDouble(-70, 70, controller.value)!, 30), 80 | 2: const Offset(70, 30) * (1 - controller.value), 81 | }[stackIndex] ?? 82 | Offset(1.sw * controller.value * (direction.isLeft ? -1 : 1), 0); 83 | 84 | /// Gets the incline angle of the item according to its position in the [Stack] 85 | double getAngle(int stackIndex) => 86 | { 87 | 0: ui.lerpDouble(0, inclineLeft, controller.value)!, 88 | 1: ui.lerpDouble(inclineLeft, inclineRight, controller.value)!, 89 | 2: ui.lerpDouble(inclineRight, 0, controller.value)!, 90 | }[stackIndex] ?? 91 | math.pi * .1 * controller.value * (direction.isLeft ? -1 : 1); 92 | 93 | @override 94 | void initState() { 95 | index = widget.index; 96 | controller = 97 | AnimationController(vsync: this, duration: kThemeAnimationDuration) 98 | ..addListener(animationListener); 99 | 100 | super.initState(); 101 | } 102 | 103 | @override 104 | void dispose() { 105 | controller 106 | ..removeListener(animationListener) 107 | ..dispose(); 108 | super.dispose(); 109 | } 110 | 111 | @override 112 | Widget build(BuildContext context) { 113 | return AnimatedBuilder( 114 | animation: controller, 115 | builder: (context, _) { 116 | final length = widget.itemCount; 117 | return Stack( 118 | children: List.generate( 119 | 4, 120 | (stackIndex) { 121 | final scale = getScale(stackIndex); 122 | final offset = getOffset(stackIndex); 123 | final angle = getAngle(stackIndex); 124 | final modIndex = length != null 125 | ? (index + 3 - stackIndex) % length 126 | : (index + 3 - stackIndex); 127 | return FadeTransition( 128 | opacity: stackIndex == 0 129 | ? controller 130 | : stackIndex != 3 131 | ? widget.shrinkAnimation 132 | : const AlwaysStoppedAnimation(1), 133 | child: AnimatedBuilder( 134 | animation: widget.shrinkAnimation, 135 | builder: (_, child) => _CustomTransform( 136 | scale: scale, 137 | offset: offset * widget.shrinkAnimation.value, 138 | angle: angle * widget.shrinkAnimation.value, 139 | child: child!, 140 | ), 141 | child: DraggableWidget( 142 | onPressed: () => widget.onTapItem?.call(modIndex), 143 | enableDrag: stackIndex == 3, 144 | onSlideOut: onSlideOut, 145 | child: widget.itemBuilder(context, modIndex), 146 | ), 147 | ), 148 | ); 149 | }, 150 | ), 151 | ); 152 | }, 153 | ); 154 | } 155 | } 156 | 157 | class _CustomTransform extends StatelessWidget { 158 | const _CustomTransform({ 159 | required this.child, 160 | this.offset = Offset.zero, 161 | this.angle = 0.0, 162 | this.scale = 1.0, 163 | }); 164 | 165 | final Offset offset; 166 | final double angle; 167 | final double scale; 168 | final Widget child; 169 | 170 | @override 171 | Widget build(BuildContext context) { 172 | return Transform( 173 | alignment: Alignment.center, 174 | transform: Matrix4.identity() 175 | ..translate(offset.dx, offset.dy) 176 | ..scale(scale), 177 | child: Transform.rotate( 178 | angle: angle, 179 | child: child, 180 | ), 181 | ); 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /lib/samples/vice_app/features/home/presentation/widgets/widgets.dart: -------------------------------------------------------------------------------- 1 | export 'all_editions_list_view.dart'; 2 | export 'draggable_widget.dart'; 3 | export 'infinite_draggable_slider.dart'; 4 | -------------------------------------------------------------------------------- /lib/samples/vice_app/features/magazines_details/presentation/screens/back_to_home_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'package:flutter_samples/samples/vice_app/features/home/presentation/screens/home_screen.dart'; 4 | 5 | class BackToHomeScreen extends StatefulWidget { 6 | const BackToHomeScreen._(this.index); 7 | 8 | final int index; 9 | 10 | static void push(BuildContext context, int index) { 11 | Navigator.of(context).push( 12 | PageRouteBuilder( 13 | transitionDuration: const Duration(milliseconds: 400), 14 | pageBuilder: (_, animation, __) { 15 | final offset = Tween(begin: const Offset(0, 1), end: Offset.zero) 16 | .animate(animation); 17 | return FadeTransition( 18 | opacity: animation, 19 | child: SlideTransition( 20 | position: offset, 21 | child: BackToHomeScreen._(index), 22 | ), 23 | ); 24 | }, 25 | ), 26 | ); 27 | } 28 | 29 | @override 30 | State createState() => _BackToHomeScreenState(); 31 | } 32 | 33 | class _BackToHomeScreenState extends State { 34 | void backToHome() { 35 | Navigator.of(context).pushAndRemoveUntil( 36 | PageRouteBuilder( 37 | transitionDuration: const Duration(milliseconds: 400), 38 | pageBuilder: (_, animation, __) { 39 | final offset = Tween(begin: const Offset(0, 1), end: Offset.zero) 40 | .animate(animation); 41 | return FadeTransition( 42 | opacity: animation, 43 | child: SlideTransition( 44 | position: offset, 45 | child: HomeScreen( 46 | enableEntryAnimation: true, 47 | initialIndex: widget.index, 48 | ), 49 | ), 50 | ); 51 | }, 52 | ), 53 | ModalRoute.withName(''), 54 | ); 55 | } 56 | 57 | @override 58 | void initState() { 59 | Future.delayed(const Duration(milliseconds: 1000), backToHome); 60 | super.initState(); 61 | } 62 | 63 | @override 64 | Widget build(BuildContext context) { 65 | return AnnotatedRegion( 66 | value: SystemUiOverlayStyle.light.copyWith( 67 | statusBarColor: Colors.transparent, 68 | ), 69 | child: WillPopScope( 70 | onWillPop: () async => false, 71 | child: const Scaffold( 72 | backgroundColor: Colors.white, 73 | ), 74 | ), 75 | ); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /lib/samples/vice_app/features/magazines_details/presentation/screens/magazines_details_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'package:flutter_samples/samples/vice_app/core/core.dart'; 4 | import 'package:flutter_samples/samples/vice_app/features/magazines_details/presentation/screens/back_to_home_screen.dart'; 5 | import 'package:flutter_samples/samples/vice_app/features/magazines_details/presentation/widgets/content_magazines_page_view.dart'; 6 | import 'package:flutter_samples/samples/vice_app/features/magazines_details/presentation/widgets/widgets.dart'; 7 | import 'package:ui_common/ui_common.dart'; 8 | 9 | class MagazinesDetailsScreen extends StatefulWidget { 10 | const MagazinesDetailsScreen({ 11 | required this.index, 12 | required this.magazines, 13 | super.key, 14 | }); 15 | 16 | final int index; 17 | final List magazines; 18 | 19 | static void push( 20 | BuildContext context, { 21 | required int index, 22 | required List magazines, 23 | }) => 24 | Navigator.push( 25 | context, 26 | PageRouteBuilder( 27 | pageBuilder: (_, animation, __) => FadeTransition( 28 | opacity: animation, 29 | child: MagazinesDetailsScreen( 30 | index: index, 31 | magazines: magazines, 32 | ), 33 | ), 34 | ), 35 | ); 36 | 37 | @override 38 | State createState() => _MagazinesDetailsScreenState(); 39 | } 40 | 41 | class _MagazinesDetailsScreenState extends State { 42 | late final ScrollController scrollController; 43 | late ValueNotifier indexNotifier; 44 | double headerPercent = 0; 45 | 46 | void scrollListener() { 47 | headerPercent = 48 | (scrollController.offset / ViceUIConsts.headerHeight).clamp(0, 1); 49 | if (headerPercent < 1) { 50 | SystemChrome.setSystemUIOverlayStyle( 51 | SystemUiOverlayStyle.light.copyWith(statusBarColor: Colors.transparent), 52 | ); 53 | } 54 | setState(() {}); 55 | } 56 | 57 | void backToHome(BuildContext context) { 58 | scrollController.animateTo( 59 | 1.sh, 60 | duration: const Duration(milliseconds: 800), 61 | curve: Curves.decelerate, 62 | ); 63 | BackToHomeScreen.push(context, indexNotifier.value); 64 | } 65 | 66 | @override 67 | void initState() { 68 | scrollController = ScrollController()..addListener(scrollListener); 69 | indexNotifier = ValueNotifier(widget.index); 70 | super.initState(); 71 | } 72 | 73 | @override 74 | void dispose() { 75 | scrollController 76 | ..removeListener(scrollListener) 77 | ..dispose(); 78 | super.dispose(); 79 | } 80 | 81 | @override 82 | Widget build(BuildContext context) { 83 | return WillPopScope( 84 | onWillPop: () async { 85 | backToHome(context); 86 | return false; 87 | }, 88 | child: Scaffold( 89 | backgroundColor: Colors.white, 90 | body: Stack( 91 | children: [ 92 | CustomScrollView( 93 | physics: const BouncingScrollPhysics(), 94 | controller: scrollController, 95 | slivers: [ 96 | SliverPersistentHeader( 97 | delegate: BuilderPersistentDelegate( 98 | minExtent: 0, 99 | maxExtent: ViceUIConsts.headerHeight, 100 | builder: (percent) => Stack( 101 | children: [ 102 | MagazinesCube3DPageView( 103 | sizePercent: percent, 104 | initialIndex: indexNotifier.value, 105 | onPageChanged: (value) => indexNotifier.value = value, 106 | magazines: widget.magazines, 107 | ), 108 | RectanglePageViewIndicators( 109 | percent: percent, 110 | indexNotifier: indexNotifier, 111 | length: widget.magazines.length, 112 | ) 113 | ], 114 | ), 115 | ), 116 | ), 117 | StickySliverAppBar( 118 | sizePercent: headerPercent, 119 | indexNotifier: indexNotifier, 120 | ), 121 | SliverToBoxAdapter( 122 | child: ContentMagazinesPageView( 123 | indexNotifier: indexNotifier, 124 | magazines: widget.magazines, 125 | ), 126 | ), 127 | ], 128 | ), 129 | HeartAndSaveButtons(movePercent: headerPercent), 130 | SafeArea( 131 | child: Row( 132 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 133 | children: [ 134 | CustomTweenAnimation( 135 | onlyScale: true, 136 | child: IconButton( 137 | color: Color.lerp( 138 | Colors.white60, 139 | Colors.black, 140 | headerPercent, 141 | ), 142 | onPressed: () => backToHome(context), 143 | icon: const Icon(ViceIcons.close), 144 | ), 145 | ), 146 | MenuButton( 147 | color: Color.lerp( 148 | Colors.white60, 149 | Colors.black, 150 | headerPercent, 151 | )!, 152 | ), 153 | ], 154 | ), 155 | ), 156 | ], 157 | ), 158 | ), 159 | ); 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /lib/samples/vice_app/features/magazines_details/presentation/widgets/content_magazines_page_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_samples/samples/vice_app/core/core.dart'; 3 | import 'package:ui_common/ui_common.dart'; 4 | 5 | class ContentMagazinesPageView extends StatefulWidget { 6 | const ContentMagazinesPageView({ 7 | required this.indexNotifier, 8 | required this.magazines, 9 | super.key, 10 | }); 11 | 12 | final ValueNotifier indexNotifier; 13 | final List magazines; 14 | 15 | @override 16 | State createState() => 17 | _ContentMagazinesPageViewState(); 18 | } 19 | 20 | class _ContentMagazinesPageViewState extends State { 21 | late final PageController controller; 22 | Size? sizeWidget; 23 | 24 | void indexListener() { 25 | controller.animateToPage( 26 | widget.indexNotifier.value, 27 | duration: const Duration(milliseconds: 600), 28 | curve: Curves.fastOutSlowIn, 29 | ); 30 | } 31 | 32 | @override 33 | void initState() { 34 | controller = PageController(initialPage: widget.indexNotifier.value); 35 | widget.indexNotifier.addListener(indexListener); 36 | super.initState(); 37 | } 38 | 39 | @override 40 | void dispose() { 41 | widget.indexNotifier.removeListener(indexListener); 42 | controller.dispose(); 43 | super.dispose(); 44 | } 45 | 46 | @override 47 | Widget build(BuildContext context) { 48 | return CustomTweenAnimation( 49 | child: SizedBox( 50 | height: sizeWidget?.height ?? 1.sh, 51 | child: PageView.builder( 52 | controller: controller, 53 | physics: const NeverScrollableScrollPhysics(), 54 | itemCount: widget.magazines.length, 55 | itemBuilder: (_, index) { 56 | final magazine = widget.magazines[index]; 57 | return SingleChildScrollView( 58 | physics: const NeverScrollableScrollPhysics(), 59 | padding: 20.edgeInsetsH, 60 | child: SizeNotifierWidget( 61 | onSizeChange: (value) => setState(() => sizeWidget = value), 62 | child: Column( 63 | crossAxisAlignment: CrossAxisAlignment.stretch, 64 | mainAxisSize: MainAxisSize.min, 65 | children: [ 66 | height20, 67 | for (int x = 0; x < 5; x++) ...[ 68 | Text( 69 | 'TITLE TEST ${magazine.id}', 70 | style: context.titleLarge.copyWith(letterSpacing: 2), 71 | ), 72 | height12, 73 | Padding( 74 | padding: 20.edgeInsetsR, 75 | child: Text( 76 | magazine.description, 77 | style: context.bodyMedium.copyWith(letterSpacing: 1), 78 | ), 79 | ), 80 | height12, 81 | Padding( 82 | padding: 20.edgeInsetsR, 83 | child: Text( 84 | magazine.description, 85 | style: context.bodyMedium.copyWith(letterSpacing: 1), 86 | ), 87 | ), 88 | height12, 89 | Image.asset( 90 | magazine.assetImage, 91 | height: 220, 92 | fit: BoxFit.cover, 93 | ), 94 | height28, 95 | ] 96 | ], 97 | ), 98 | ), 99 | ); 100 | }, 101 | ), 102 | ), 103 | ); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /lib/samples/vice_app/features/magazines_details/presentation/widgets/heart_and_save_button.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui' as ui; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_samples/samples/vice_app/core/constants/constants.dart'; 5 | import 'package:flutter_samples/samples/vice_app/core/theme/vice_icons.dart'; 6 | import 'package:ui_common/ui_common.dart'; 7 | 8 | class HeartAndSaveButtons extends StatelessWidget { 9 | const HeartAndSaveButtons({ 10 | required this.movePercent, 11 | super.key, 12 | }); 13 | 14 | final double movePercent; 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | final height = 50.r; 19 | final width = 50.r; 20 | final bottom = 1.sh - ViceUIConsts.headerHeight; 21 | return Positioned( 22 | bottom: ui.lerpDouble(bottom - height, height, movePercent), 23 | right: 24.w, 24 | child: Column( 25 | children: [ 26 | SizedBox( 27 | height: height, 28 | width: width, 29 | child: ColoredBox( 30 | color: Colors.black, 31 | child: FittedBox( 32 | child: IconButton( 33 | onPressed: () {}, 34 | icon: const Icon(ViceIcons.heart, color: Colors.white), 35 | ), 36 | ), 37 | ), 38 | ), 39 | SizedBox( 40 | height: height, 41 | width: width, 42 | child: ColoredBox( 43 | color: Colors.green.shade500, 44 | child: FittedBox( 45 | child: IconButton( 46 | onPressed: () {}, 47 | icon: const Icon(ViceIcons.save, color: Colors.white), 48 | ), 49 | ), 50 | ), 51 | ), 52 | ], 53 | ), 54 | ); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lib/samples/vice_app/features/magazines_details/presentation/widgets/magazines_3d_page_view.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math' as math; 2 | import 'dart:ui' as ui; 3 | 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_samples/samples/vice_app/core/core.dart'; 6 | import 'package:flutter_samples/samples/vice_app/features/home/presentation/widgets/infinite_draggable_slider.dart'; 7 | import 'package:ui_common/ui_common.dart'; 8 | 9 | class MagazinesCube3DPageView extends StatefulWidget { 10 | const MagazinesCube3DPageView({ 11 | required this.magazines, 12 | required this.initialIndex, 13 | required this.onPageChanged, 14 | required this.sizePercent, 15 | super.key, 16 | }); 17 | 18 | final List magazines; 19 | final ValueChanged onPageChanged; 20 | final int initialIndex; 21 | final double sizePercent; 22 | 23 | @override 24 | State createState() => 25 | _MagazinesCube3DPageViewState(); 26 | } 27 | 28 | class _MagazinesCube3DPageViewState extends State { 29 | /// Value in decimals of the page displayed in the [PageView] 30 | late final PageController pageController; 31 | 32 | late double page; 33 | 34 | final double rotation = -(math.pi / 180 * 10); 35 | 36 | Widget buildCustomHero(_, Animation animation, __, ___, ____) { 37 | return InfiniteDraggableSlider( 38 | index: page.floor(), 39 | itemCount: widget.magazines.length, 40 | shrinkAnimation: Tween(begin: 1, end: 0).animate(animation), 41 | itemBuilder: (_, index) => MagazineCoverImage( 42 | magazine: widget.magazines[index], 43 | ), 44 | ); 45 | } 46 | 47 | void _pageListener() { 48 | setState(() { 49 | page = pageController.page ?? 0; 50 | }); 51 | } 52 | 53 | @override 54 | void initState() { 55 | page = widget.initialIndex.toDouble(); 56 | pageController = PageController(initialPage: widget.initialIndex) 57 | ..addListener(_pageListener); 58 | super.initState(); 59 | } 60 | 61 | @override 62 | void dispose() { 63 | pageController.dispose(); 64 | super.dispose(); 65 | } 66 | 67 | @override 68 | Widget build(BuildContext context) { 69 | return PageView.builder( 70 | itemCount: widget.magazines.length, 71 | controller: pageController, 72 | onPageChanged: (value) => widget.onPageChanged(value), 73 | clipBehavior: Clip.none, 74 | itemBuilder: (_, index) { 75 | final magazine = widget.magazines[index]; 76 | final percent = index - page; 77 | final isComingOut = (index - page) <= 0; 78 | return Transform( 79 | alignment: isComingOut ? Alignment.centerRight : Alignment.centerLeft, 80 | transform: Matrix4.identity() 81 | ..setEntry(3, 2, 0.014) 82 | ..rotateY(rotation * percent), 83 | child: Stack( 84 | fit: StackFit.expand, 85 | children: [ 86 | Positioned.fill( 87 | top: -200 * widget.sizePercent, 88 | bottom: -400 * widget.sizePercent, 89 | child: Image.asset( 90 | magazine.assetImage, 91 | fit: BoxFit.cover, 92 | ), 93 | ), 94 | ClipRect( 95 | clipBehavior: Clip.antiAlias, 96 | child: BackdropFilter( 97 | filter: ui.ImageFilter.blur(sigmaX: 10, sigmaY: 10), 98 | child: const ColoredBox(color: Colors.black26), 99 | ), 100 | ), 101 | Positioned.fill( 102 | top: context.mediaQuery.padding.top, 103 | bottom: -ViceUIConsts.headerHeight * widget.sizePercent, 104 | child: Center( 105 | child: Hero( 106 | tag: magazine.id, 107 | flightShuttleBuilder: buildCustomHero, 108 | child: MagazineCoverImage( 109 | magazine: magazine, 110 | height: ui.lerpDouble(.35.sh, .25.sh, widget.sizePercent), 111 | ), 112 | ), 113 | ), 114 | ), 115 | ], 116 | ), 117 | ); 118 | }, 119 | ); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /lib/samples/vice_app/features/magazines_details/presentation/widgets/rectangle_page_view_indicators.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_samples/samples/vice_app/core/constants/constants.dart'; 5 | import 'package:ui_common/ui_common.dart'; 6 | 7 | class RectanglePageViewIndicators extends StatelessWidget { 8 | const RectanglePageViewIndicators({ 9 | required this.percent, 10 | required this.indexNotifier, 11 | required this.length, 12 | super.key, 13 | }); 14 | 15 | final double percent; 16 | final ValueNotifier indexNotifier; 17 | final int length; 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | return Positioned.fill( 22 | top: null, 23 | bottom: lerpDouble(.05.sh, -ViceUIConsts.headerHeight, percent), 24 | child: ValueListenableBuilder( 25 | valueListenable: indexNotifier, 26 | builder: (__, value, _) => Row( 27 | mainAxisAlignment: MainAxisAlignment.center, 28 | crossAxisAlignment: CrossAxisAlignment.end, 29 | children: List.generate( 30 | length, 31 | (index) { 32 | final isSelected = index == value; 33 | return AnimatedContainer( 34 | duration: kThemeAnimationDuration, 35 | margin: index != (length - 1) ? 4.edgeInsetsR : null, 36 | height: isSelected ? 5.r : 3.r, 37 | width: isSelected ? 12.r : 8.r, 38 | color: isSelected ? Colors.white : Colors.white38, 39 | ); 40 | }, 41 | ), 42 | ), 43 | ), 44 | ); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/samples/vice_app/features/magazines_details/presentation/widgets/sticky_sliver_app_bar.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | import 'dart:ui'; 3 | 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_samples/samples/vice_app/core/core.dart'; 6 | import 'package:ui_common/ui_common.dart'; 7 | 8 | class StickySliverAppBar extends StatefulWidget { 9 | const StickySliverAppBar({ 10 | required this.sizePercent, 11 | required this.indexNotifier, 12 | super.key, 13 | }); 14 | 15 | final double sizePercent; 16 | final ValueNotifier indexNotifier; 17 | 18 | @override 19 | State createState() => _StickySliverAppBarState(); 20 | } 21 | 22 | class _StickySliverAppBarState extends State { 23 | late final PageController controller; 24 | 25 | void indexListener() { 26 | controller.animateToPage( 27 | widget.indexNotifier.value, 28 | duration: const Duration(milliseconds: 500), 29 | curve: Curves.fastOutSlowIn, 30 | ); 31 | } 32 | 33 | @override 34 | void initState() { 35 | controller = PageController(initialPage: widget.indexNotifier.value); 36 | widget.indexNotifier.addListener(indexListener); 37 | super.initState(); 38 | } 39 | 40 | @override 41 | void dispose() { 42 | controller.dispose(); 43 | widget.indexNotifier.removeListener(indexListener); 44 | super.dispose(); 45 | } 46 | 47 | @override 48 | Widget build(BuildContext context) { 49 | return SliverAppBar( 50 | toolbarHeight: lerpDouble(150.h, 50.h, widget.sizePercent)!, 51 | leading: const SizedBox.shrink(), 52 | backgroundColor: Colors.white, 53 | foregroundColor: Colors.black, 54 | elevation: 10 * widget.sizePercent, 55 | shadowColor: Colors.white60, 56 | pinned: true, 57 | actions: [ 58 | Expanded( 59 | child: CustomTweenAnimation( 60 | child: PageView.builder( 61 | controller: controller, 62 | physics: const NeverScrollableScrollPhysics(), 63 | itemBuilder: (_, index) => Container( 64 | padding: 20.edgeInsetsH, 65 | decoration: const BoxDecoration( 66 | gradient: LinearGradient( 67 | begin: Alignment.topCenter, 68 | end: Alignment.bottomCenter, 69 | stops: [0.6, 1], 70 | colors: [ 71 | Colors.white, 72 | Colors.white10, 73 | ], 74 | ), 75 | ), 76 | child: Column( 77 | crossAxisAlignment: CrossAxisAlignment.stretch, 78 | children: [ 79 | Flexible( 80 | child: FittedBox( 81 | alignment: Alignment(-1 * (1 - widget.sizePercent), 0), 82 | child: Text( 83 | 'ISSUE N', 84 | style: context.titleSmall, 85 | ), 86 | ), 87 | ), 88 | Flexible( 89 | flex: 10, 90 | child: FittedBox( 91 | alignment: Alignment(-1 * (1 - widget.sizePercent), 0), 92 | child: Stack( 93 | children: [ 94 | Text( 95 | '${index < 9 ? '0' : ''}${index + 1}', 96 | style: 97 | const TextStyle(fontWeight: FontWeight.bold), 98 | ), 99 | Positioned.fill( 100 | child: Transform.rotate( 101 | angle: -pi * .1, 102 | child: const Divider( 103 | color: Colors.black, 104 | thickness: .3, 105 | ), 106 | ), 107 | ) 108 | ], 109 | ), 110 | ), 111 | ), 112 | ], 113 | ), 114 | ), 115 | ), 116 | ), 117 | ), 118 | ], 119 | ); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /lib/samples/vice_app/features/magazines_details/presentation/widgets/widgets.dart: -------------------------------------------------------------------------------- 1 | export 'heart_and_save_button.dart'; 2 | export 'magazines_3d_page_view.dart'; 3 | export 'rectangle_page_view_indicators.dart'; 4 | export 'sticky_sliver_app_bar.dart'; 5 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" 9 | url: "https://pub.dev" 10 | source: hosted 11 | version: "2.11.0" 12 | boolean_selector: 13 | dependency: transitive 14 | description: 15 | name: boolean_selector 16 | sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" 17 | url: "https://pub.dev" 18 | source: hosted 19 | version: "2.1.1" 20 | cached_network_image: 21 | dependency: "direct main" 22 | description: 23 | name: cached_network_image 24 | sha256: fd3d0dc1d451f9a252b32d95d3f0c3c487bc41a75eba2e6097cb0b9c71491b15 25 | url: "https://pub.dev" 26 | source: hosted 27 | version: "3.2.3" 28 | cached_network_image_platform_interface: 29 | dependency: transitive 30 | description: 31 | name: cached_network_image_platform_interface 32 | sha256: bb2b8403b4ccdc60ef5f25c70dead1f3d32d24b9d6117cfc087f496b178594a7 33 | url: "https://pub.dev" 34 | source: hosted 35 | version: "2.0.0" 36 | cached_network_image_web: 37 | dependency: transitive 38 | description: 39 | name: cached_network_image_web 40 | sha256: b8eb814ebfcb4dea049680f8c1ffb2df399e4d03bf7a352c775e26fa06e02fa0 41 | url: "https://pub.dev" 42 | source: hosted 43 | version: "1.0.2" 44 | characters: 45 | dependency: transitive 46 | description: 47 | name: characters 48 | sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" 49 | url: "https://pub.dev" 50 | source: hosted 51 | version: "1.3.0" 52 | clock: 53 | dependency: transitive 54 | description: 55 | name: clock 56 | sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf 57 | url: "https://pub.dev" 58 | source: hosted 59 | version: "1.1.1" 60 | collection: 61 | dependency: transitive 62 | description: 63 | name: collection 64 | sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 65 | url: "https://pub.dev" 66 | source: hosted 67 | version: "1.17.2" 68 | crypto: 69 | dependency: transitive 70 | description: 71 | name: crypto 72 | sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67 73 | url: "https://pub.dev" 74 | source: hosted 75 | version: "3.0.2" 76 | cupertino_icons: 77 | dependency: "direct main" 78 | description: 79 | name: cupertino_icons 80 | sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d 81 | url: "https://pub.dev" 82 | source: hosted 83 | version: "1.0.6" 84 | fake_async: 85 | dependency: transitive 86 | description: 87 | name: fake_async 88 | sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" 89 | url: "https://pub.dev" 90 | source: hosted 91 | version: "1.3.1" 92 | ffi: 93 | dependency: transitive 94 | description: 95 | name: ffi 96 | sha256: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978 97 | url: "https://pub.dev" 98 | source: hosted 99 | version: "2.0.1" 100 | file: 101 | dependency: transitive 102 | description: 103 | name: file 104 | sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" 105 | url: "https://pub.dev" 106 | source: hosted 107 | version: "6.1.4" 108 | flutter: 109 | dependency: "direct main" 110 | description: flutter 111 | source: sdk 112 | version: "0.0.0" 113 | flutter_blurhash: 114 | dependency: transitive 115 | description: 116 | name: flutter_blurhash 117 | sha256: "05001537bd3fac7644fa6558b09ec8c0a3f2eba78c0765f88912882b1331a5c6" 118 | url: "https://pub.dev" 119 | source: hosted 120 | version: "0.7.0" 121 | flutter_cache_manager: 122 | dependency: transitive 123 | description: 124 | name: flutter_cache_manager 125 | sha256: "8207f27539deb83732fdda03e259349046a39a4c767269285f449ade355d54ba" 126 | url: "https://pub.dev" 127 | source: hosted 128 | version: "3.3.1" 129 | flutter_screenutil: 130 | dependency: transitive 131 | description: 132 | name: flutter_screenutil 133 | sha256: "8cf100b8e4973dc570b6415a2090b0bfaa8756ad333db46939efc3e774ee100d" 134 | url: "https://pub.dev" 135 | source: hosted 136 | version: "5.9.0" 137 | flutter_test: 138 | dependency: "direct dev" 139 | description: flutter 140 | source: sdk 141 | version: "0.0.0" 142 | fluttericon: 143 | dependency: "direct main" 144 | description: 145 | name: fluttericon 146 | sha256: "252fa8043826e93d972a602497a260cb3d62b5aea6d045793e4381590f2c1e99" 147 | url: "https://pub.dev" 148 | source: hosted 149 | version: "2.0.0" 150 | google_fonts: 151 | dependency: "direct main" 152 | description: 153 | name: google_fonts 154 | sha256: e20ff62b158b96f392bfc8afe29dee1503c94fbea2cbe8186fd59b756b8ae982 155 | url: "https://pub.dev" 156 | source: hosted 157 | version: "5.1.0" 158 | http: 159 | dependency: transitive 160 | description: 161 | name: http 162 | sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" 163 | url: "https://pub.dev" 164 | source: hosted 165 | version: "1.1.0" 166 | http_parser: 167 | dependency: transitive 168 | description: 169 | name: http_parser 170 | sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" 171 | url: "https://pub.dev" 172 | source: hosted 173 | version: "4.0.2" 174 | matcher: 175 | dependency: transitive 176 | description: 177 | name: matcher 178 | sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" 179 | url: "https://pub.dev" 180 | source: hosted 181 | version: "0.12.16" 182 | material_color_utilities: 183 | dependency: transitive 184 | description: 185 | name: material_color_utilities 186 | sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" 187 | url: "https://pub.dev" 188 | source: hosted 189 | version: "0.5.0" 190 | meta: 191 | dependency: transitive 192 | description: 193 | name: meta 194 | sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" 195 | url: "https://pub.dev" 196 | source: hosted 197 | version: "1.9.1" 198 | octo_image: 199 | dependency: transitive 200 | description: 201 | name: octo_image 202 | sha256: "107f3ed1330006a3bea63615e81cf637433f5135a52466c7caa0e7152bca9143" 203 | url: "https://pub.dev" 204 | source: hosted 205 | version: "1.0.2" 206 | path: 207 | dependency: transitive 208 | description: 209 | name: path 210 | sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" 211 | url: "https://pub.dev" 212 | source: hosted 213 | version: "1.8.3" 214 | path_provider: 215 | dependency: transitive 216 | description: 217 | name: path_provider 218 | sha256: dcea5feb97d8abf90cab9e9030b497fb7c3cbf26b7a1fe9e3ef7dcb0a1ddec95 219 | url: "https://pub.dev" 220 | source: hosted 221 | version: "2.0.12" 222 | path_provider_android: 223 | dependency: transitive 224 | description: 225 | name: path_provider_android 226 | sha256: a776c088d671b27f6e3aa8881d64b87b3e80201c64e8869b811325de7a76c15e 227 | url: "https://pub.dev" 228 | source: hosted 229 | version: "2.0.22" 230 | path_provider_foundation: 231 | dependency: transitive 232 | description: 233 | name: path_provider_foundation 234 | sha256: "62a68e7e1c6c459f9289859e2fae58290c981ce21d1697faf54910fe1faa4c74" 235 | url: "https://pub.dev" 236 | source: hosted 237 | version: "2.1.1" 238 | path_provider_linux: 239 | dependency: transitive 240 | description: 241 | name: path_provider_linux 242 | sha256: ab0987bf95bc591da42dffb38c77398fc43309f0b9b894dcc5d6f40c4b26c379 243 | url: "https://pub.dev" 244 | source: hosted 245 | version: "2.1.7" 246 | path_provider_platform_interface: 247 | dependency: transitive 248 | description: 249 | name: path_provider_platform_interface 250 | sha256: f0abc8ebd7253741f05488b4813d936b4d07c6bae3e86148a09e342ee4b08e76 251 | url: "https://pub.dev" 252 | source: hosted 253 | version: "2.0.5" 254 | path_provider_windows: 255 | dependency: transitive 256 | description: 257 | name: path_provider_windows 258 | sha256: bcabbe399d4042b8ee687e17548d5d3f527255253b4a639f5f8d2094a9c2b45c 259 | url: "https://pub.dev" 260 | source: hosted 261 | version: "2.1.3" 262 | platform: 263 | dependency: transitive 264 | description: 265 | name: platform 266 | sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" 267 | url: "https://pub.dev" 268 | source: hosted 269 | version: "3.1.0" 270 | plugin_platform_interface: 271 | dependency: transitive 272 | description: 273 | name: plugin_platform_interface 274 | sha256: dbf0f707c78beedc9200146ad3cb0ab4d5da13c246336987be6940f026500d3a 275 | url: "https://pub.dev" 276 | source: hosted 277 | version: "2.1.3" 278 | process: 279 | dependency: transitive 280 | description: 281 | name: process 282 | sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" 283 | url: "https://pub.dev" 284 | source: hosted 285 | version: "4.2.4" 286 | rxdart: 287 | dependency: transitive 288 | description: 289 | name: rxdart 290 | sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb" 291 | url: "https://pub.dev" 292 | source: hosted 293 | version: "0.27.7" 294 | sky_engine: 295 | dependency: transitive 296 | description: flutter 297 | source: sdk 298 | version: "0.0.99" 299 | source_span: 300 | dependency: transitive 301 | description: 302 | name: source_span 303 | sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" 304 | url: "https://pub.dev" 305 | source: hosted 306 | version: "1.10.0" 307 | sqflite: 308 | dependency: transitive 309 | description: 310 | name: sqflite 311 | sha256: "78324387dc81df14f78df06019175a86a2ee0437624166c382e145d0a7fd9a4f" 312 | url: "https://pub.dev" 313 | source: hosted 314 | version: "2.2.4+1" 315 | sqflite_common: 316 | dependency: transitive 317 | description: 318 | name: sqflite_common 319 | sha256: bfd6973aaeeb93475bc0d875ac9aefddf7965ef22ce09790eb963992ffc5183f 320 | url: "https://pub.dev" 321 | source: hosted 322 | version: "2.4.2+2" 323 | stack_trace: 324 | dependency: transitive 325 | description: 326 | name: stack_trace 327 | sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 328 | url: "https://pub.dev" 329 | source: hosted 330 | version: "1.11.0" 331 | stream_channel: 332 | dependency: transitive 333 | description: 334 | name: stream_channel 335 | sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" 336 | url: "https://pub.dev" 337 | source: hosted 338 | version: "2.1.1" 339 | string_scanner: 340 | dependency: transitive 341 | description: 342 | name: string_scanner 343 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" 344 | url: "https://pub.dev" 345 | source: hosted 346 | version: "1.2.0" 347 | synchronized: 348 | dependency: transitive 349 | description: 350 | name: synchronized 351 | sha256: "33b31b6beb98100bf9add464a36a8dd03eb10c7a8cf15aeec535e9b054aaf04b" 352 | url: "https://pub.dev" 353 | source: hosted 354 | version: "3.0.1" 355 | term_glyph: 356 | dependency: transitive 357 | description: 358 | name: term_glyph 359 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 360 | url: "https://pub.dev" 361 | source: hosted 362 | version: "1.2.1" 363 | test_api: 364 | dependency: transitive 365 | description: 366 | name: test_api 367 | sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" 368 | url: "https://pub.dev" 369 | source: hosted 370 | version: "0.6.0" 371 | typed_data: 372 | dependency: transitive 373 | description: 374 | name: typed_data 375 | sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5" 376 | url: "https://pub.dev" 377 | source: hosted 378 | version: "1.3.1" 379 | ui_common: 380 | dependency: "direct main" 381 | description: 382 | path: "." 383 | ref: HEAD 384 | resolved-ref: "9b6c28121f84dd313bd2f523ff1f84b6a7cd2483" 385 | url: "https://github.com/brocodev/ui_common.git" 386 | source: git 387 | version: "1.0.0+1" 388 | uuid: 389 | dependency: transitive 390 | description: 391 | name: uuid 392 | sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313" 393 | url: "https://pub.dev" 394 | source: hosted 395 | version: "3.0.7" 396 | vector_math: 397 | dependency: transitive 398 | description: 399 | name: vector_math 400 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" 401 | url: "https://pub.dev" 402 | source: hosted 403 | version: "2.1.4" 404 | very_good_analysis: 405 | dependency: "direct dev" 406 | description: 407 | name: very_good_analysis 408 | sha256: "9ae7f3a3bd5764fb021b335ca28a34f040cd0ab6eec00a1b213b445dae58a4b8" 409 | url: "https://pub.dev" 410 | source: hosted 411 | version: "5.1.0" 412 | web: 413 | dependency: transitive 414 | description: 415 | name: web 416 | sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 417 | url: "https://pub.dev" 418 | source: hosted 419 | version: "0.1.4-beta" 420 | win32: 421 | dependency: transitive 422 | description: 423 | name: win32 424 | sha256: c9ebe7ee4ab0c2194e65d3a07d8c54c5d00bb001b76081c4a04cdb8448b59e46 425 | url: "https://pub.dev" 426 | source: hosted 427 | version: "3.1.3" 428 | xdg_directories: 429 | dependency: transitive 430 | description: 431 | name: xdg_directories 432 | sha256: bd512f03919aac5f1313eb8249f223bacf4927031bf60b02601f81f687689e86 433 | url: "https://pub.dev" 434 | source: hosted 435 | version: "0.2.0+3" 436 | sdks: 437 | dart: ">=3.1.0-185.0.dev <4.0.0" 438 | flutter: ">=3.10.0" 439 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_samples 2 | description: A new Flutter project. 3 | 4 | publish_to: 'none' 5 | 6 | version: 1.0.0+1 7 | 8 | environment: 9 | sdk: '>=3.0.6 <4.0.0' 10 | 11 | dependencies: 12 | cached_network_image: ^3.2.3 13 | cupertino_icons: ^1.0.6 14 | flutter: 15 | sdk: flutter 16 | fluttericon: ^2.0.0 17 | google_fonts: ^5.1.0 18 | ui_common: 19 | git: https://github.com/brocodev/ui_common.git 20 | 21 | dev_dependencies: 22 | flutter_test: 23 | sdk: flutter 24 | very_good_analysis: ^5.1.0 25 | 26 | 27 | flutter: 28 | uses-material-design: true 29 | assets: 30 | - assets/img/samples/ 31 | - assets/img/vice/ 32 | --------------------------------------------------------------------------------