├── .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 |
--------------------------------------------------------------------------------